tons of stuffs, specially fltk comps in thread and loading images from other thread
This commit is contained in:
parent
887d5d9e58
commit
a5b4b84eec
|
|
@ -0,0 +1,45 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Debug executable 'post_maker'",
|
||||
"cargo": {
|
||||
"args": [
|
||||
"build",
|
||||
"--bin=post_maker",
|
||||
"--package=post_maker"
|
||||
],
|
||||
"filter": {
|
||||
"name": "post_maker",
|
||||
"kind": "bin"
|
||||
}
|
||||
},
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Debug unit tests in executable 'post_maker'",
|
||||
"cargo": {
|
||||
"args": [
|
||||
"test",
|
||||
"--no-run",
|
||||
"--bin=post_maker",
|
||||
"--package=post_maker"
|
||||
],
|
||||
"filter": {
|
||||
"name": "post_maker",
|
||||
"kind": "bin"
|
||||
}
|
||||
},
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -556,6 +556,7 @@ dependencies = [
|
|||
"fltk-theme",
|
||||
"image",
|
||||
"imageproc",
|
||||
"lazy_static",
|
||||
"rusttype",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
|
|
|||
|
|
@ -14,3 +14,4 @@ imageproc = "0.22"
|
|||
rusttype = "0.9"
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
lazy_static = "1.4"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,182 @@
|
|||
use crate::{
|
||||
main_window::{MainWindow, Page},
|
||||
AppMessage,
|
||||
};
|
||||
use fltk::{
|
||||
app,
|
||||
input::{Input, MultilineInput},
|
||||
menu,
|
||||
misc::Spinner,
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
use std::{
|
||||
fs,
|
||||
path::Path,
|
||||
sync::{mpsc, Arc, RwLock},
|
||||
};
|
||||
|
||||
use crate::utils::{ImageContainer, ImageProperties};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) enum DrawMessage {
|
||||
Open,
|
||||
Recalc,
|
||||
Flush,
|
||||
}
|
||||
|
||||
pub(crate) fn spawn_image_thread(
|
||||
reciver: mpsc::Receiver<DrawMessage>,
|
||||
app_sender: app::Sender<crate::AppMessage>,
|
||||
properties: Arc<RwLock<ImageProperties>>,
|
||||
main_win: &MainWindow,
|
||||
) {
|
||||
let mut file_choice = main_win.file_choice.clone();
|
||||
let mut quote = main_win.quote.clone();
|
||||
let mut tag = main_win.tag.clone();
|
||||
let mut layer_red = main_win.layer_red.clone();
|
||||
let mut layer_green = main_win.layer_green.clone();
|
||||
let mut layer_blue = main_win.layer_blue.clone();
|
||||
let mut layer_alpha = main_win.layer_alpha.clone();
|
||||
let mut quote_position = main_win.quote_position.clone();
|
||||
let mut tag_position = main_win.tag_position.clone();
|
||||
let mut page = main_win.page.clone();
|
||||
|
||||
let mut _container: Option<ImageContainer> = None;
|
||||
std::thread::spawn(move || loop {
|
||||
if let Ok(val) = reciver.recv() {
|
||||
match val {
|
||||
DrawMessage::Open => load_image(
|
||||
&mut file_choice,
|
||||
&mut quote,
|
||||
&mut tag,
|
||||
&mut layer_red,
|
||||
&mut layer_green,
|
||||
&mut layer_blue,
|
||||
&mut layer_alpha,
|
||||
&mut quote_position,
|
||||
&mut tag_position,
|
||||
&mut page,
|
||||
&app_sender,
|
||||
&properties,
|
||||
&mut _container,
|
||||
),
|
||||
DrawMessage::Recalc => {
|
||||
if let Some(cont) = &mut _container {
|
||||
cont.recalc();
|
||||
}
|
||||
}
|
||||
// DrawMessage::CropPos(x, y) => {
|
||||
// if let Some(cont) = &mut _container {
|
||||
// cont.apply_crop_pos(x, y);
|
||||
// }
|
||||
// }
|
||||
// DrawMessage::Crop => {
|
||||
// if let Some(cont) = &mut _container {
|
||||
// cont.apply_crop();
|
||||
// }
|
||||
// }
|
||||
DrawMessage::Flush => {
|
||||
flush_buffer(&app_sender, &mut _container);
|
||||
println!("recived");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn load_image(
|
||||
file_choice: &mut menu::Choice,
|
||||
quote: &mut MultilineInput,
|
||||
tag: &mut Input,
|
||||
layer_red: &mut Spinner,
|
||||
layer_green: &mut Spinner,
|
||||
layer_blue: &mut Spinner,
|
||||
layer_alpha: &mut Spinner,
|
||||
quote_position: &mut Spinner,
|
||||
tag_position: &mut Spinner,
|
||||
page: &mut Page,
|
||||
app_sender: &app::Sender<crate::AppMessage>,
|
||||
properties: &Arc<RwLock<ImageProperties>>,
|
||||
container: &mut Option<ImageContainer>,
|
||||
) {
|
||||
let file: String = match file_choice.choice() {
|
||||
Some(val) => val,
|
||||
None => return,
|
||||
};
|
||||
|
||||
*container = Some(ImageContainer::new(&file, Arc::clone(properties)));
|
||||
|
||||
if let Some(cont) = container {
|
||||
let file = Path::new(&file);
|
||||
let conf = file.with_extension("conf");
|
||||
|
||||
let properties = Arc::clone(&cont.properties);
|
||||
let mut use_defaults = true;
|
||||
if conf.exists() {
|
||||
let read = fs::read_to_string(&conf).unwrap();
|
||||
if let Ok(saved_prop) = serde_json::from_str::<ImageProperties>(&read) {
|
||||
let mut prop = properties.write().unwrap();
|
||||
layer_red.set_value(saved_prop.rgba[0] as f64);
|
||||
layer_green.set_value(saved_prop.rgba[1] as f64);
|
||||
layer_blue.set_value(saved_prop.rgba[2] as f64);
|
||||
layer_alpha.set_value(saved_prop.rgba[3] as f64);
|
||||
quote.set_value(&saved_prop.quote);
|
||||
tag.set_value(&saved_prop.tag);
|
||||
quote_position.set_range(0.0, prop.original_dimension.1 as f64);
|
||||
quote_position.set_value(saved_prop.quote_position as f64);
|
||||
tag_position.set_range(0.0, prop.original_dimension.1 as f64);
|
||||
tag_position.set_value(saved_prop.tag_position as f64);
|
||||
|
||||
prop.quote = saved_prop.quote;
|
||||
prop.tag = saved_prop.tag;
|
||||
prop.quote_position = saved_prop.quote_position;
|
||||
prop.tag_position = saved_prop.quote_position;
|
||||
prop.rgba = saved_prop.rgba;
|
||||
use_defaults = false;
|
||||
|
||||
drop(prop);
|
||||
if let Some((x, y)) = saved_prop.crop_position {
|
||||
cont.apply_crop_pos(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if use_defaults {
|
||||
let mut prop = properties.write().unwrap();
|
||||
quote.set_value("");
|
||||
tag.set_value("");
|
||||
|
||||
quote_position.set_range(0.0, prop.original_dimension.1 as f64);
|
||||
quote_position.set_value(prop.quote_position as f64);
|
||||
tag_position.set_range(0.0, prop.original_dimension.1 as f64);
|
||||
tag_position.set_value(prop.tag_position as f64);
|
||||
|
||||
prop.rgba = [
|
||||
layer_red.value() as u8,
|
||||
layer_green.value() as u8,
|
||||
layer_blue.value() as u8,
|
||||
layer_alpha.value() as u8,
|
||||
];
|
||||
drop(prop);
|
||||
cont.apply_crop();
|
||||
}
|
||||
|
||||
let prop = properties.read().unwrap();
|
||||
let (width, height) = prop.dimension;
|
||||
page.col_flex.set_size(&page.image, height as i32);
|
||||
page.row_flex.set_size(&page.col_flex, width as i32);
|
||||
page.col_flex.recalc();
|
||||
page.row_flex.recalc();
|
||||
cont.recalc();
|
||||
}
|
||||
flush_buffer(&app_sender, &container);
|
||||
}
|
||||
|
||||
fn flush_buffer(app_sender: &app::Sender<crate::AppMessage>, container: &Option<ImageContainer>) {
|
||||
if let Some(cont) = container {
|
||||
app_sender.send(AppMessage::RedrawMainWindowImage(
|
||||
cont.buffer.as_rgb8().unwrap().as_raw().to_owned(),
|
||||
));
|
||||
}
|
||||
}
|
||||
35
src/main.rs
35
src/main.rs
|
|
@ -1,15 +1,25 @@
|
|||
mod config;
|
||||
// mod crop_window;
|
||||
mod draw_thread;
|
||||
mod main_window;
|
||||
mod properties;
|
||||
mod utils;
|
||||
|
||||
// use crop_window::CropWindow;
|
||||
use fltk::{app::App, enums::Font};
|
||||
use fltk::{
|
||||
app::{channel, App},
|
||||
enums::Font,
|
||||
prelude::*,
|
||||
};
|
||||
use fltk_theme::WidgetTheme;
|
||||
|
||||
use main_window::MainWindow;
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
use utils::ImageContainer;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) enum AppMessage {
|
||||
RedrawMainWindowImage(Vec<u8>),
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let app = App::default();
|
||||
|
|
@ -21,14 +31,25 @@ fn main() {
|
|||
.into(),
|
||||
)
|
||||
.apply();
|
||||
|
||||
let f1 = Font::load_font("ReenieBeanie-Regular.ttf").unwrap();
|
||||
let f2 = Font::load_font("Kalam-Regular.ttf").unwrap();
|
||||
Font::set_font(Font::Times, &f1);
|
||||
Font::set_font(Font::TimesItalic, &f2);
|
||||
|
||||
let container: Rc<RefCell<Option<ImageContainer>>> = Rc::new(RefCell::new(None));
|
||||
let draw_buff: Arc<RwLock<Vec<u8>>> = Arc::new(RwLock::new(vec![]));
|
||||
let (main_sender, main_receiver) = channel::<AppMessage>();
|
||||
let mut main_window = MainWindow::new(main_sender, Arc::clone(&draw_buff));
|
||||
|
||||
let main_win = MainWindow::new(Rc::clone(&container));
|
||||
app.run().unwrap();
|
||||
while app.wait() {
|
||||
if let Some(msg) = main_receiver.recv() {
|
||||
match msg {
|
||||
AppMessage::RedrawMainWindowImage(data) => {
|
||||
let mut buff = draw_buff.write().unwrap();
|
||||
*buff = data;
|
||||
println!("copy buff");
|
||||
main_window.win.redraw();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use crate::utils::ImageContainer;
|
||||
use crate::{properties::Properties, utils};
|
||||
use crate::utils::ImageProperties;
|
||||
use crate::{draw_thread::*, properties};
|
||||
use fltk::{
|
||||
app,
|
||||
button::Button,
|
||||
dialog::NativeFileChooser,
|
||||
draw as dr, enums,
|
||||
|
|
@ -13,40 +14,45 @@ use fltk::{
|
|||
prelude::*,
|
||||
window::Window,
|
||||
};
|
||||
use image::GenericImageView;
|
||||
use std::io::Read;
|
||||
use std::{cell::RefCell, ffi::OsStr, fs, path::Path, rc::Rc};
|
||||
use std::sync::{mpsc, RwLock};
|
||||
use std::{ffi::OsStr, fs, path::Path, sync::Arc};
|
||||
|
||||
pub(crate) struct MainWindow {
|
||||
pub(crate) win: Window,
|
||||
menubar: menu::SysMenuBar,
|
||||
back_btn: Button,
|
||||
next_btn: Button,
|
||||
save_btn: Button,
|
||||
file_choice: menu::Choice,
|
||||
quote: MultilineInput,
|
||||
tag: Input,
|
||||
layer_red: Spinner,
|
||||
layer_green: Spinner,
|
||||
layer_blue: Spinner,
|
||||
layer_alpha: Spinner,
|
||||
quote_position: Spinner,
|
||||
tag_position: Spinner,
|
||||
crop_btn: Button,
|
||||
reset_btn: Button,
|
||||
page: Page,
|
||||
container: Rc<RefCell<Option<ImageContainer>>>,
|
||||
pub(crate) menubar: menu::SysMenuBar,
|
||||
pub(crate) back_btn: Button,
|
||||
pub(crate) next_btn: Button,
|
||||
pub(crate) save_btn: Button,
|
||||
pub(crate) file_choice: menu::Choice,
|
||||
pub(crate) quote: MultilineInput,
|
||||
pub(crate) tag: Input,
|
||||
pub(crate) layer_red: Spinner,
|
||||
pub(crate) layer_green: Spinner,
|
||||
pub(crate) layer_blue: Spinner,
|
||||
pub(crate) layer_alpha: Spinner,
|
||||
pub(crate) quote_position: Spinner,
|
||||
pub(crate) tag_position: Spinner,
|
||||
pub(crate) crop_btn: Button,
|
||||
pub(crate) reset_btn: Button,
|
||||
pub(crate) status: Frame,
|
||||
pub(crate) page: Page,
|
||||
pub(crate) draw_buff: Arc<RwLock<Vec<u8>>>,
|
||||
pub(crate) properties: Arc<RwLock<ImageProperties>>,
|
||||
pub(crate) sender: mpsc::Sender<DrawMessage>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct Page {
|
||||
image: Frame,
|
||||
row_flex: Flex,
|
||||
col_flex: Flex,
|
||||
pub(crate) image: Frame,
|
||||
pub(crate) row_flex: Flex,
|
||||
pub(crate) col_flex: Flex,
|
||||
}
|
||||
|
||||
impl MainWindow {
|
||||
pub(crate) fn new(container: Rc<RefCell<Option<ImageContainer>>>) -> Self {
|
||||
pub(crate) fn new(
|
||||
sender: app::Sender<crate::AppMessage>,
|
||||
draw_buff: Arc<RwLock<Vec<u8>>>,
|
||||
) -> Self {
|
||||
let color = [25, 29, 34, 190];
|
||||
|
||||
let mut win = Window::default()
|
||||
|
|
@ -144,6 +150,11 @@ impl MainWindow {
|
|||
actions_flex.end();
|
||||
controls_flex.set_size(&actions_flex, 30);
|
||||
|
||||
Frame::default();
|
||||
|
||||
let status = Frame::default();
|
||||
controls_flex.set_size(&status, 30);
|
||||
|
||||
controls_flex.end();
|
||||
workspace_flex.set_size(&controls_flex, 360);
|
||||
|
||||
|
|
@ -167,6 +178,8 @@ impl MainWindow {
|
|||
win.make_resizable(true);
|
||||
win.show();
|
||||
|
||||
let properties = Arc::new(RwLock::new(ImageProperties::new()));
|
||||
let (rx, tx) = std::sync::mpsc::channel();
|
||||
let mut main_win = Self {
|
||||
win,
|
||||
menubar,
|
||||
|
|
@ -184,13 +197,17 @@ impl MainWindow {
|
|||
tag_position,
|
||||
crop_btn,
|
||||
reset_btn,
|
||||
container,
|
||||
status,
|
||||
draw_buff,
|
||||
properties: Arc::clone(&properties),
|
||||
page: Page {
|
||||
image: img_view,
|
||||
row_flex: center_row_flex,
|
||||
col_flex: center_col_flex,
|
||||
},
|
||||
sender: rx,
|
||||
};
|
||||
spawn_image_thread(tx, sender, Arc::clone(&properties), &main_win);
|
||||
main_win.menu();
|
||||
main_win.draw();
|
||||
main_win.events();
|
||||
|
|
@ -199,27 +216,30 @@ impl MainWindow {
|
|||
|
||||
fn menu(&mut self) {
|
||||
let mut file_choice = self.file_choice.clone();
|
||||
let mut quote = self.quote.clone();
|
||||
let mut tag = self.tag.clone();
|
||||
let mut layer_red = self.layer_red.clone();
|
||||
let mut layer_green = self.layer_green.clone();
|
||||
let mut layer_blue = self.layer_blue.clone();
|
||||
let mut layer_alpha = self.layer_alpha.clone();
|
||||
let mut quote_position = self.quote_position.clone();
|
||||
let mut tag_position = self.tag_position.clone();
|
||||
let mut page = self.page.clone();
|
||||
let container = Rc::clone(&self.container);
|
||||
// let mut quote = self.quote.clone();
|
||||
// let mut tag = self.tag.clone();
|
||||
// let mut layer_red = self.layer_red.clone();
|
||||
// let mut layer_green = self.layer_green.clone();
|
||||
// let mut layer_blue = self.layer_blue.clone();
|
||||
// let mut layer_alpha = self.layer_alpha.clone();
|
||||
// let mut quote_position = self.quote_position.clone();
|
||||
// let mut tag_position = self.tag_position.clone();
|
||||
// let mut page = self.page.clone();
|
||||
let sender = self.sender.clone();
|
||||
// let properties = Arc::clone(&self.properties);
|
||||
let mut win = self.win.clone();
|
||||
self.menubar.add(
|
||||
"&File/Open Folder...\t",
|
||||
Shortcut::Ctrl | 'o',
|
||||
menu::MenuFlag::Normal,
|
||||
move |_| {
|
||||
win.redraw();
|
||||
let mut chooser = NativeFileChooser::new(fltk::dialog::FileDialogType::BrowseDir);
|
||||
chooser.set_option(fltk::dialog::FileDialogOptions::NewFolder);
|
||||
chooser.show();
|
||||
let path = chooser.filename();
|
||||
if !path.exists() {
|
||||
win.activate();
|
||||
return;
|
||||
}
|
||||
let files = fs::read_dir(path).unwrap();
|
||||
|
|
@ -232,26 +252,13 @@ impl MainWindow {
|
|||
}
|
||||
}
|
||||
if text.len() == 0 {
|
||||
win.activate();
|
||||
return;
|
||||
}
|
||||
file_choice.clear();
|
||||
file_choice.add_choice(&text[1..]);
|
||||
file_choice.set_value(0);
|
||||
|
||||
load_image(
|
||||
&mut file_choice,
|
||||
&mut quote,
|
||||
&mut tag,
|
||||
&mut layer_red,
|
||||
&mut layer_green,
|
||||
&mut layer_blue,
|
||||
&mut layer_alpha,
|
||||
&mut quote_position,
|
||||
&mut tag_position,
|
||||
&mut page,
|
||||
&container,
|
||||
);
|
||||
win.redraw();
|
||||
sender.send(DrawMessage::Open).unwrap();
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -273,130 +280,193 @@ impl MainWindow {
|
|||
}
|
||||
|
||||
fn draw(&mut self) {
|
||||
let mut buffer = Vec::new();
|
||||
fs::File::open("ReenieBeanie-Regular.ttf")
|
||||
.unwrap()
|
||||
.read_to_end(&mut buffer)
|
||||
.unwrap();
|
||||
let font = rusttype::Font::try_from_vec(buffer).unwrap();
|
||||
|
||||
let container = Rc::clone(&self.container);
|
||||
let quote = self.quote.clone();
|
||||
println!("in draw");
|
||||
let buff = Arc::clone(&self.draw_buff);
|
||||
let properties = Arc::clone(&self.properties);
|
||||
self.page.image.draw(move |f| {
|
||||
if let Some(cont) = &*container.borrow() {
|
||||
let image = cont.image.as_rgb8().unwrap();
|
||||
dr::draw_image(
|
||||
image.as_raw(),
|
||||
f.x(),
|
||||
f.y(),
|
||||
image.width() as i32,
|
||||
image.height() as i32,
|
||||
enums::ColorDepth::Rgb8,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
dr::set_color_rgb(255, 255, 255);
|
||||
|
||||
let size = utils::quote_from_height(image.height());
|
||||
dr::set_font(enums::Font::Times, size as i32);
|
||||
|
||||
let (text_width, text_height) = utils::measure_line(
|
||||
&font,
|
||||
"e.value(),
|
||||
rusttype::Scale::uniform(size as f32),
|
||||
);
|
||||
|
||||
dr::draw_text(
|
||||
"e.value(),
|
||||
f.x() + image.width() as i32 / 2 - text_width as i32 / 2,
|
||||
f.y() + image.height() as i32 / 2 - text_height as i32 / 2,
|
||||
);
|
||||
}
|
||||
let (width, height) = properties.read().unwrap().dimension;
|
||||
let image = &*buff.read().unwrap();
|
||||
dr::draw_image(
|
||||
&image,
|
||||
f.x(),
|
||||
f.y(),
|
||||
width as i32,
|
||||
height as i32,
|
||||
enums::ColorDepth::Rgb8,
|
||||
)
|
||||
.unwrap();
|
||||
})
|
||||
}
|
||||
|
||||
fn events(&mut self) {
|
||||
let mut image = self.page.image.clone();
|
||||
self.quote.handle(move |_, ev| {
|
||||
let properties = Arc::clone(&self.properties);
|
||||
let sender = self.sender.clone();
|
||||
self.quote.handle(move |f, ev| {
|
||||
if ev == enums::Event::KeyUp {
|
||||
let mut prop = properties.write().unwrap();
|
||||
prop.quote = f.value();
|
||||
sender.send(DrawMessage::Recalc).unwrap();
|
||||
sender.send(DrawMessage::Flush).unwrap();
|
||||
image.redraw();
|
||||
}
|
||||
true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn load_image(
|
||||
file_choice: &mut menu::Choice,
|
||||
quote: &mut MultilineInput,
|
||||
tag: &mut Input,
|
||||
layer_red: &mut Spinner,
|
||||
layer_green: &mut Spinner,
|
||||
layer_blue: &mut Spinner,
|
||||
layer_alpha: &mut Spinner,
|
||||
quote_position: &mut Spinner,
|
||||
tag_position: &mut Spinner,
|
||||
page: &mut Page,
|
||||
container: &Rc<RefCell<Option<ImageContainer>>>,
|
||||
) {
|
||||
let file: String = match file_choice.choice() {
|
||||
Some(val) => val,
|
||||
None => return,
|
||||
};
|
||||
|
||||
*container.borrow_mut() = Some(ImageContainer::new(&file));
|
||||
|
||||
let file = Path::new(&file);
|
||||
let conf = file.with_extension("conf");
|
||||
|
||||
let mut use_defaults = true;
|
||||
if conf.exists() {
|
||||
let read = fs::read_to_string(&conf).unwrap();
|
||||
if let Ok(prop) = serde_json::from_str::<Properties>(&read) {
|
||||
if let Some(cont) = &mut *container.borrow_mut() {
|
||||
layer_red.set_value(prop.rgba[0] as f64);
|
||||
layer_green.set_value(prop.rgba[1] as f64);
|
||||
layer_blue.set_value(prop.rgba[2] as f64);
|
||||
layer_alpha.set_value(prop.rgba[3] as f64);
|
||||
quote.set_value(&prop.quote);
|
||||
tag.set_value(&prop.tag);
|
||||
quote_position.set_value(prop.quote_position as f64);
|
||||
tag_position.set_value(prop.tag_position as f64);
|
||||
cont.apply_crop_pos(prop.crop_position.0, prop.crop_position.1);
|
||||
|
||||
cont.quote = prop.quote;
|
||||
cont.tag = prop.tag;
|
||||
cont.quote_position = prop.quote_position;
|
||||
cont.tag_position = prop.quote_position;
|
||||
cont.rgba = prop.rgba;
|
||||
let mut image = self.page.image.clone();
|
||||
let properties = Arc::clone(&self.properties);
|
||||
let sender = self.sender.clone();
|
||||
self.tag.handle(move |f, ev| {
|
||||
if ev == enums::Event::KeyUp {
|
||||
let mut prop = properties.write().unwrap();
|
||||
prop.tag = f.value();
|
||||
sender.send(DrawMessage::Recalc).unwrap();
|
||||
sender.send(DrawMessage::Flush).unwrap();
|
||||
image.redraw();
|
||||
}
|
||||
use_defaults = false;
|
||||
}
|
||||
}
|
||||
true
|
||||
});
|
||||
|
||||
if use_defaults {
|
||||
if let Some(cont) = &mut *container.borrow_mut() {
|
||||
quote.set_value("");
|
||||
tag.set_value("");
|
||||
quote_position.set_value(cont.quote_position as f64);
|
||||
tag_position.set_value(cont.tag_position as f64);
|
||||
cont.apply_crop();
|
||||
let mut image = self.page.image.clone();
|
||||
let properties = Arc::clone(&self.properties);
|
||||
let sender = self.sender.clone();
|
||||
self.quote_position.set_callback(move |f| {
|
||||
let mut prop = properties.write().unwrap();
|
||||
prop.quote_position = f.value() as u32;
|
||||
sender.send(DrawMessage::Recalc).unwrap();
|
||||
sender.send(DrawMessage::Flush).unwrap();
|
||||
image.redraw();
|
||||
});
|
||||
|
||||
cont.rgba = [
|
||||
layer_red.value() as u8,
|
||||
layer_green.value() as u8,
|
||||
layer_blue.value() as u8,
|
||||
layer_alpha.value() as u8,
|
||||
];
|
||||
}
|
||||
}
|
||||
let mut image = self.page.image.clone();
|
||||
let properties = Arc::clone(&self.properties);
|
||||
let sender = self.sender.clone();
|
||||
self.tag_position.set_callback(move |f| {
|
||||
let mut prop = properties.write().unwrap();
|
||||
prop.tag_position = f.value() as u32;
|
||||
sender.send(DrawMessage::Recalc).unwrap();
|
||||
sender.send(DrawMessage::Flush).unwrap();
|
||||
image.redraw();
|
||||
});
|
||||
|
||||
if let Some(cont) = &mut *container.borrow_mut() {
|
||||
cont.apply_layer();
|
||||
let (width, height) = cont.image.dimensions();
|
||||
page.row_flex.set_size(&page.col_flex, width as i32);
|
||||
page.col_flex.set_size(&page.image, height as i32);
|
||||
page.row_flex.recalc();
|
||||
page.col_flex.recalc();
|
||||
let mut image = self.page.image.clone();
|
||||
let properties = Arc::clone(&self.properties);
|
||||
let sender = self.sender.clone();
|
||||
self.layer_red.set_callback(move |f| {
|
||||
let mut prop = properties.write().unwrap();
|
||||
prop.rgba[0] = f.value() as u8;
|
||||
sender.send(DrawMessage::Recalc).unwrap();
|
||||
sender.send(DrawMessage::Flush).unwrap();
|
||||
image.redraw();
|
||||
});
|
||||
|
||||
let mut image = self.page.image.clone();
|
||||
let properties = Arc::clone(&self.properties);
|
||||
let sender = self.sender.clone();
|
||||
self.layer_green.set_callback(move |f| {
|
||||
let mut prop = properties.write().unwrap();
|
||||
prop.rgba[1] = f.value() as u8;
|
||||
sender.send(DrawMessage::Recalc).unwrap();
|
||||
sender.send(DrawMessage::Flush).unwrap();
|
||||
image.redraw();
|
||||
});
|
||||
|
||||
let mut image = self.page.image.clone();
|
||||
let properties = Arc::clone(&self.properties);
|
||||
let sender = self.sender.clone();
|
||||
self.layer_blue.set_callback(move |f| {
|
||||
let mut prop = properties.write().unwrap();
|
||||
prop.rgba[2] = f.value() as u8;
|
||||
sender.send(DrawMessage::Recalc).unwrap();
|
||||
sender.send(DrawMessage::Flush).unwrap();
|
||||
image.redraw();
|
||||
});
|
||||
|
||||
let mut image = self.page.image.clone();
|
||||
let properties = Arc::clone(&self.properties);
|
||||
let sender = self.sender.clone();
|
||||
self.layer_alpha.set_callback(move |f| {
|
||||
let mut prop = properties.write().unwrap();
|
||||
prop.rgba[3] = f.value() as u8;
|
||||
sender.send(DrawMessage::Recalc).unwrap();
|
||||
sender.send(DrawMessage::Flush).unwrap();
|
||||
image.redraw();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// fn load_image(
|
||||
// file_choice: &mut menu::Choice,
|
||||
// quote: &mut MultilineInput,
|
||||
// tag: &mut Input,
|
||||
// layer_red: &mut Spinner,
|
||||
// layer_green: &mut Spinner,
|
||||
// layer_blue: &mut Spinner,
|
||||
// layer_alpha: &mut Spinner,
|
||||
// quote_position: &mut Spinner,
|
||||
// tag_position: &mut Spinner,
|
||||
// page: &mut Page,
|
||||
// properties: &Arc<RwLock<ImageProperties>>,
|
||||
// sender: &mpsc::Sender<DrawMessage>,
|
||||
// ) {
|
||||
// let file: String = match file_choice.choice() {
|
||||
// Some(val) => val,
|
||||
// None => return,
|
||||
// };
|
||||
|
||||
// sender.send(DrawMessage::Open(file.clone())).unwrap();
|
||||
|
||||
// let file = Path::new(&file);
|
||||
// let conf = file.with_extension("conf");
|
||||
|
||||
// let mut prop = properties.write().unwrap();
|
||||
// let mut use_defaults = true;
|
||||
// if conf.exists() {
|
||||
// let read = fs::read_to_string(&conf).unwrap();
|
||||
// if let Ok(saved_prop) = serde_json::from_str::<ImageProperties>(&read) {
|
||||
// layer_red.set_value(saved_prop.rgba[0] as f64);
|
||||
// layer_green.set_value(saved_prop.rgba[1] as f64);
|
||||
// layer_blue.set_value(saved_prop.rgba[2] as f64);
|
||||
// layer_alpha.set_value(saved_prop.rgba[3] as f64);
|
||||
// quote.set_value(&saved_prop.quote);
|
||||
// tag.set_value(&saved_prop.tag);
|
||||
// quote_position.set_range(0.0, prop.original_dimension.1 as f64);
|
||||
// quote_position.set_value(saved_prop.quote_position as f64);
|
||||
// tag_position.set_range(0.0, prop.original_dimension.1 as f64);
|
||||
// tag_position.set_value(saved_prop.tag_position as f64);
|
||||
|
||||
// if let Some((x, y)) = saved_prop.crop_position {
|
||||
// sender.send(DrawMessage::CropPos(x, y)).unwrap();
|
||||
// }
|
||||
|
||||
// prop.quote = saved_prop.quote;
|
||||
// prop.tag = saved_prop.tag;
|
||||
// prop.quote_position = saved_prop.quote_position;
|
||||
// prop.tag_position = saved_prop.quote_position;
|
||||
// prop.rgba = saved_prop.rgba;
|
||||
// use_defaults = false;
|
||||
// }
|
||||
// }
|
||||
|
||||
// if use_defaults {
|
||||
// quote.set_value("");
|
||||
// tag.set_value("");
|
||||
|
||||
// quote_position.set_range(0.0, prop.original_dimension.1 as f64);
|
||||
// quote_position.set_value(prop.quote_position as f64);
|
||||
// tag_position.set_range(0.0, prop.original_dimension.1 as f64);
|
||||
// tag_position.set_value(prop.tag_position as f64);
|
||||
|
||||
// sender.send(DrawMessage::Crop).unwrap();
|
||||
|
||||
// prop.rgba = [
|
||||
// layer_red.value() as u8,
|
||||
// layer_green.value() as u8,
|
||||
// layer_blue.value() as u8,
|
||||
// layer_alpha.value() as u8,
|
||||
// ];
|
||||
// }
|
||||
// sender.send(DrawMessage::Recalc).unwrap();
|
||||
// println!("sent");
|
||||
// sender.send(DrawMessage::Flush).unwrap();
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,11 +1,22 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use lazy_static::lazy_static;
|
||||
use rusttype::Font;
|
||||
use std::io::Read;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub(crate) struct Properties {
|
||||
pub(crate) quote: String,
|
||||
pub(crate) tag: String,
|
||||
pub(crate) quote_position: u32,
|
||||
pub(crate) tag_position: u32,
|
||||
pub(crate) crop_position: (u32, u32),
|
||||
pub(crate) rgba: [u8; 4],
|
||||
lazy_static! {
|
||||
pub static ref FONT_QUOTE: Font<'static> = {
|
||||
let mut buffer = Vec::new();
|
||||
std::fs::File::open("ReenieBeanie-Regular.ttf")
|
||||
.unwrap()
|
||||
.read_to_end(&mut buffer)
|
||||
.unwrap();
|
||||
rusttype::Font::try_from_vec(buffer).unwrap()
|
||||
};
|
||||
pub static ref FONT_TAG: Font<'static> = {
|
||||
let mut buffer = Vec::new();
|
||||
std::fs::File::open("Kalam-Regular.ttf")
|
||||
.unwrap()
|
||||
.read_to_end(&mut buffer)
|
||||
.unwrap();
|
||||
rusttype::Font::try_from_vec(buffer).unwrap()
|
||||
};
|
||||
}
|
||||
|
|
|
|||
195
src/utils.rs
195
src/utils.rs
|
|
@ -1,9 +1,135 @@
|
|||
use fltk::prelude::ImageExt;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use image::{DynamicImage, GenericImageView, ImageBuffer};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::properties;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ImageContainer {
|
||||
pub(crate) image: DynamicImage,
|
||||
pub(crate) image: DynamicImage, //plain
|
||||
pub(crate) buffer: DynamicImage, //buffer to show
|
||||
pub(crate) properties: Arc<RwLock<ImageProperties>>,
|
||||
}
|
||||
|
||||
impl ImageContainer {
|
||||
pub(crate) fn new(path: &str, properties: Arc<RwLock<ImageProperties>>) -> Self {
|
||||
let img = image::open(path).unwrap();
|
||||
let (width, height) = img.dimensions();
|
||||
let (s_width, s_height) = ((width * 500) / height, 500);
|
||||
let img = img.resize(s_width, s_height, image::imageops::FilterType::Triangle);
|
||||
|
||||
let mut prop = properties.write().unwrap();
|
||||
prop.path = path.to_owned();
|
||||
prop.dimension = (s_width, s_height);
|
||||
prop.original_dimension = (width, height);
|
||||
prop.quote_position = height / 2;
|
||||
prop.tag_position = (height * 2) / 3;
|
||||
|
||||
Self {
|
||||
image: img.clone(),
|
||||
buffer: img,
|
||||
properties: Arc::clone(&properties),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn apply_crop(&mut self) {
|
||||
let mut prop = self.properties.write().unwrap();
|
||||
let (original_width, original_height) = prop.original_dimension;
|
||||
let (origina_crop_width, origina_crop_height) = get_4_5(original_width, original_height);
|
||||
prop.crop_position = Some((
|
||||
original_width / 2 - origina_crop_width / 2,
|
||||
original_height / 2 - origina_crop_height / 2,
|
||||
));
|
||||
|
||||
let (s_width, s_height) = self.image.dimensions();
|
||||
let (c_width, c_height) = get_4_5(s_width, s_height);
|
||||
let (cx, cy) = (s_width / 2 - c_width / 2, s_height / 2 - c_height / 2);
|
||||
|
||||
prop.dimension = (c_width, c_height);
|
||||
|
||||
self.image = self.image.crop(cx, cy, c_width, c_height);
|
||||
self.buffer = self.image.clone();
|
||||
}
|
||||
|
||||
pub(crate) fn apply_crop_pos(&mut self, original_x: u32, original_y: u32) {
|
||||
let mut prop = self.properties.write().unwrap();
|
||||
let (original_width, original_height) = prop.original_dimension;
|
||||
prop.crop_position = Some((original_x, original_y));
|
||||
|
||||
let (s_width, s_height) = self.image.dimensions();
|
||||
let (c_width, c_height) = get_4_5(s_width, s_height);
|
||||
let (cx, cy) = (
|
||||
(original_x * s_width) / original_width,
|
||||
(original_y * s_height) / original_height,
|
||||
);
|
||||
|
||||
prop.dimension = (c_width, c_height);
|
||||
|
||||
self.image = self.image.crop(cx, cy, c_width, c_height);
|
||||
self.buffer = self.image.clone();
|
||||
}
|
||||
|
||||
pub(crate) fn recalc(&mut self) {
|
||||
let prop = self.properties.read().unwrap();
|
||||
let mut tmp = self.image.clone();
|
||||
let (width, height) = tmp.dimensions();
|
||||
|
||||
let layer = DynamicImage::ImageRgba8(ImageBuffer::from_fn(width, height, |_, _| {
|
||||
image::Rgba(prop.rgba)
|
||||
}));
|
||||
image::imageops::overlay(&mut tmp, &layer, 0, 0);
|
||||
|
||||
let size = quote_from_height(height);
|
||||
for (index, line) in prop.quote.lines().enumerate() {
|
||||
let (text_width, text_height) = measure_line(
|
||||
&properties::FONT_QUOTE,
|
||||
line,
|
||||
rusttype::Scale::uniform(size as f32),
|
||||
);
|
||||
|
||||
imageproc::drawing::draw_text_mut(
|
||||
&mut tmp,
|
||||
image::Rgba([255, 255, 255, 255]),
|
||||
((width as f32 - text_width) / 2.0) as u32,
|
||||
(prop.quote_position * height) / prop.original_dimension.1
|
||||
+ (text_height / 2.0) as u32
|
||||
+ index as u32 * (text_height * 1.2) as u32,
|
||||
rusttype::Scale::uniform(size as f32),
|
||||
&properties::FONT_QUOTE,
|
||||
line,
|
||||
);
|
||||
}
|
||||
|
||||
let size = tag_from_height(height);
|
||||
for (index, line) in prop.tag.lines().enumerate() {
|
||||
let (text_width, text_height) = measure_line(
|
||||
&properties::FONT_TAG,
|
||||
line,
|
||||
rusttype::Scale::uniform(size as f32),
|
||||
);
|
||||
|
||||
imageproc::drawing::draw_text_mut(
|
||||
&mut tmp,
|
||||
image::Rgba([255, 255, 255, 255]),
|
||||
(width as f32 * 0.99 - text_width) as u32,
|
||||
(prop.tag_position * height) / prop.original_dimension.1
|
||||
+ (text_height / 2.0) as u32
|
||||
+ index as u32 * (text_height * 1.2) as u32,
|
||||
rusttype::Scale::uniform(size as f32),
|
||||
&properties::FONT_TAG,
|
||||
line,
|
||||
);
|
||||
}
|
||||
|
||||
self.buffer = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub(crate) struct ImageProperties {
|
||||
pub(crate) path: String,
|
||||
pub(crate) dimension: (u32, u32),
|
||||
pub(crate) original_dimension: (u32, u32),
|
||||
pub(crate) crop_position: Option<(u32, u32)>,
|
||||
pub(crate) quote: String,
|
||||
|
|
@ -14,62 +140,21 @@ pub(crate) struct ImageContainer {
|
|||
pub(crate) is_saved: bool,
|
||||
}
|
||||
|
||||
impl ImageContainer {
|
||||
pub(crate) fn new(path: &str) -> Self {
|
||||
let img = image::open(path).unwrap();
|
||||
let (width, height) = img.dimensions();
|
||||
let (s_width, s_height) = ((width * 500) / height, 500);
|
||||
let mut img = img.resize(s_width, s_height, image::imageops::FilterType::Triangle);
|
||||
|
||||
impl ImageProperties {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
image: img,
|
||||
original_dimension: (width, height),
|
||||
path: "".to_owned(),
|
||||
dimension: (0, 0),
|
||||
original_dimension: (0, 0),
|
||||
crop_position: None,
|
||||
quote: String::new(),
|
||||
tag: String::new(),
|
||||
quote_position: (width * 2) / 3,
|
||||
tag_position: width / 2,
|
||||
quote: "".to_owned(),
|
||||
tag: "".to_owned(),
|
||||
quote_position: 0,
|
||||
tag_position: 0,
|
||||
rgba: [0; 4],
|
||||
is_saved: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn apply_crop(&mut self) {
|
||||
let (original_width, original_height) = self.original_dimension;
|
||||
let (origina_crop_width, origina_crop_height) = get_4_5(original_width, original_height);
|
||||
self.crop_position = Some((
|
||||
original_width / 2 - origina_crop_width / 2,
|
||||
original_height / 2 - origina_crop_height / 2,
|
||||
));
|
||||
|
||||
let (s_width, s_height) = self.image.dimensions();
|
||||
let (c_width, c_height) = get_4_5(s_width, s_height);
|
||||
let (cx, cy) = (s_width / 2 - c_width / 2, s_height / 2 - c_height / 2);
|
||||
|
||||
self.image = self.image.crop(cx, cy, c_width, c_height);
|
||||
}
|
||||
|
||||
pub(crate) fn apply_crop_pos(&mut self, original_x: u32, original_y: u32) {
|
||||
let (original_width, original_height) = self.original_dimension;
|
||||
self.crop_position = Some((original_x, original_y));
|
||||
|
||||
let (s_width, s_height) = self.image.dimensions();
|
||||
let (c_width, c_height) = get_4_5(s_width, s_height);
|
||||
let (cx, cy) = (
|
||||
(original_x * s_width) / original_width,
|
||||
(original_y * s_height) / original_height,
|
||||
);
|
||||
|
||||
self.image = self.image.crop(cx, cy, c_width, c_height);
|
||||
}
|
||||
|
||||
pub(crate) fn apply_layer(&mut self) {
|
||||
let (width, height) = self.image.dimensions();
|
||||
let layer = DynamicImage::ImageRgba8(ImageBuffer::from_fn(width, height, |_, _| {
|
||||
image::Rgba(self.rgba)
|
||||
}));
|
||||
image::imageops::overlay(&mut self.image, &layer, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_4_5(width: u32, height: u32) -> (u32, u32) {
|
||||
|
|
@ -89,11 +174,11 @@ pub(crate) fn height_from_width(width: u32) -> u32 {
|
|||
}
|
||||
|
||||
pub(crate) fn quote_from_height(height: u32) -> u32 {
|
||||
(height * 65) / 1556
|
||||
(height * 70) / 1350
|
||||
}
|
||||
|
||||
pub(crate) fn id_from_height(height: u32) -> u32 {
|
||||
(height * 30) / 1556
|
||||
pub(crate) fn tag_from_height(height: u32) -> u32 {
|
||||
(height * 50) / 1350
|
||||
}
|
||||
|
||||
pub(crate) fn measure_line(
|
||||
|
|
|
|||
Loading…
Reference in New Issue