diff --git a/75e253532946877dea6aaa5a60ffd51a.jpg b/75e253532946877dea6aaa5a60ffd51a.jpg new file mode 100644 index 0000000..c3da35f Binary files /dev/null and b/75e253532946877dea6aaa5a60ffd51a.jpg differ diff --git a/Cargo.toml b/Cargo.toml index 4ab46bb..858d42c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,3 +15,5 @@ rusttype = "0.9" serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } lazy_static = "1.4" +# rust_decimal = "1.20" +# rust_decimal_macros = "1.20" diff --git a/out.png b/out.png index 948157b..3550863 100644 Binary files a/out.png and b/out.png differ diff --git a/src/crop_window.rs b/src/crop_window.rs index fe975dc..b128e1b 100644 --- a/src/crop_window.rs +++ b/src/crop_window.rs @@ -1,41 +1,48 @@ -use crate::utils::{self, ImageContainer}; +use crate::utils::{self, Coord, ImageContainer, ImageProperties}; use fltk::{ app, button::Button, draw, enums::Event, frame::Frame, group::Flex, prelude::*, window::Window, }; use image::GenericImageView; -use std::{cell::RefCell, rc::Rc}; +use std::{ + cell::RefCell, + rc::Rc, + sync::{Arc, RwLock}, +}; +static mut PATH: String = String::new(); + +/// Window to crop the existing image pub(crate) struct CropWindow { pub win: Window, apply_btn: Button, - img_view: Frame, - img: Rc>, - bound: Rc>, + container: Rc>>, + page: Page, +} + +#[derive(Clone)] +pub(crate) struct Page { + pub(crate) image_view: Frame, + pub(crate) row_flex: Flex, + pub(crate) col_flex: Flex, } impl CropWindow { - pub(crate) fn new(container: Rc>) -> Self { - // let image = &container.borrow().image; - let (image_width, image_height) = container.borrow().image.dimensions(); - let mut win = Window::default() - .with_size(image_width as i32, 600) - .with_label("Crop"); + pub(crate) fn new() -> Self { + let mut win = Window::new(0, 0, 500, 600, "Crop").center_screen(); let mut main_flex = Flex::default().size_of_parent().column(); // Work area - let mut center_row_flex = Flex::default().row(); + let center_row_flex = Flex::default().row(); Frame::default(); - let mut center_col_flex = Flex::default().column(); + let center_col_flex = Flex::default().column(); Frame::default(); let img_view = Frame::default(); Frame::default(); - center_col_flex.set_size(&img_view, image_height as i32); center_col_flex.end(); Frame::default(); - center_row_flex.set_size(¢er_col_flex, image_width as i32); center_row_flex.end(); // Panel @@ -56,16 +63,15 @@ impl CropWindow { win.end(); win.make_resizable(true); - let (bound_width, bound_height) = utils::get_4_5(image_width, image_height); - let bound_x = image_width / 2 - bound_width / 2; - let bound_y = image_height / 2 - bound_height / 2; - let mut crop_win = Self { win, apply_btn, - img_view, - img: Rc::clone(&container), - bound: Rc::new(RefCell::new((bound_x, bound_y, bound_width, bound_height))), + container: Rc::new(RefCell::new(None)), + page: Page { + image_view: img_view, + row_flex: center_row_flex, + col_flex: center_col_flex, + }, }; crop_win.draw(); @@ -73,97 +79,158 @@ impl CropWindow { crop_win } + /// Call it to show window to crop image + pub(crate) fn load_to_crop( + &mut self, + path: &str, + crop_pos: Option<(f64, f64)>, + ) -> Option<(f64, f64)> { + unsafe { + PATH = path.to_owned(); + } + + let mut container = + ImageContainer::new(path, Arc::new(RwLock::new(ImageProperties::new()))); + { + let prop = &mut container.properties.write().unwrap(); + prop.dimension = prop.original_dimension; + prop.crop_position = match crop_pos { + Some(a) => Some(a), + None => Some((0.0, 0.0)), + }; + } + + container.apply_scale(); + let (image_width, image_height): (f64, f64) = + Coord::from(container.image.dimensions()).into(); + self.win.set_size(image_width as i32, 600); + + self.page + .row_flex + .set_size(&self.page.col_flex, image_width as i32); + self.page.row_flex.recalc(); + + self.page + .col_flex + .set_size(&self.page.image_view, image_height as i32); + self.page.col_flex.recalc(); + + *self.container.borrow_mut() = Some(container); + + self.page.image_view.redraw(); + self.win.show(); + self.win.make_modal(true); + while self.win.shown() { + app::wait(); + } + + if let Some(cont) = &*self.container.borrow() { + cont.properties.read().unwrap().crop_position + } else { + None + } + } + fn draw(&mut self) { - let cont = Rc::clone(&self.img); - let bound = Rc::clone(&self.bound); - self.img_view.draw(move |f| { - let image = &cont.borrow().image; - let (bound_x, bound_y, bound_width, bound_height) = *bound.borrow(); - draw::draw_image( - image.as_rgb8().unwrap().as_raw(), - f.x(), - f.y(), - image.width() as i32, - image.height() as i32, - fltk::enums::ColorDepth::Rgb8, - ) - .unwrap(); - draw::set_color_rgb(255, 0, 0); - draw::draw_rect( - f.x() + bound_x as i32, - f.y() + bound_y as i32, - bound_width as i32, - bound_height as i32, - ); + let container = Rc::clone(&self.container); + self.page.image_view.draw(move |f| { + if let Some(cont) = &*container.borrow() { + let image = &cont.buffer; + + draw::draw_image( + image.as_rgb8().unwrap().as_raw(), + f.x(), + f.y(), + image.width() as i32, + image.height() as i32, + fltk::enums::ColorDepth::Rgb8, + ) + .unwrap(); + + let prop = cont.properties.read().unwrap(); + let (original_width, original_height) = prop.original_dimension; + let (original_x, original_y) = prop.crop_position.unwrap(); + let (resized_width, resized_height) = (image.width() as f64, image.height() as f64); + let (bound_width, bound_height) = utils::get_4_5(resized_width, resized_height); + + let (bound_x, bound_y) = ( + (original_x * resized_width as f64) / original_width, + (original_y * resized_height as f64) / original_height, + ); + + draw::set_color_rgb(255, 0, 0); + draw::draw_rect( + f.x() + bound_x as i32, + f.y() + bound_y as i32, + bound_width as i32, + bound_height as i32, + ); + } }); } fn event(&mut self) { - let mut last: Option<(i32, i32)> = None; - let cont = Rc::clone(&self.img); - let bound = Rc::clone(&self.bound); - self.img_view.handle(move |f, ev| { - let image = &cont.borrow().image; - let (bound_x, bound_y, bound_width, bound_height) = *bound.borrow(); - if ev == Event::Push { - last = Some((app::event_x(), app::event_y())); - } else if ev == Event::Drag { - if let Some((lx, ly)) = last { - let dx = app::event_x() - lx; - if (dx > 0 && bound_x + bound_width < image.width()) || (dx < 0 && bound_x > 0) - { - let mut new_x = bound_x as i32 + dx; - if new_x + bound_width as i32 > image.width() as i32 { - new_x = (image.width() - bound_width) as i32 - } else if new_x < 0 { - new_x = 0 + let mut last: Option<(f64, f64)> = None; + let container = Rc::clone(&self.container); + self.page.image_view.handle(move |f, ev| { + if let Some(cont) = &*container.borrow_mut() { + let image = &cont.buffer; + + let mut prop = cont.properties.write().unwrap(); + + let (original_x, original_y) = prop.crop_position.unwrap(); + let (original_width, original_heigth) = prop.original_dimension; + let (original_bound_width, original_bound_height) = + utils::get_4_5(original_width, original_heigth); + let point = original_width / image.width() as f64; + let (event_x, event_y) = ( + (app::event_x() - f.x()) as f64 * point, + (app::event_y() - f.y()) as f64 * point, + ); + if ev == Event::Push { + last = Some((event_x, event_y)); + } else if ev == Event::Drag { + if let Some((lx, ly)) = last { + let dx = event_x - lx; + if (dx > 0.0 && original_x + original_bound_width < original_width) + || (dx < 0.0 && original_x > 0.0) + { + let mut new_x = original_x + dx; + if new_x + original_bound_width > original_width { + new_x = original_width - original_bound_width; + } else if new_x < 0.0 { + new_x = 0.0; + } + + prop.crop_position = prop.crop_position.map(|(_, y)| (new_x, y)); } - bound.borrow_mut().0 = new_x as u32; - } + let dy = event_y - ly; + if (dy > 0.0 && original_y + original_bound_height < original_heigth) + || (dy < 0.0 && original_y > 0.0) + { + let mut new_y = original_y + dy; + if new_y + original_bound_height > original_heigth { + new_y = original_heigth - original_bound_height; + } else if new_y < 0.0 { + new_y = 0.0; + } - let dy = app::event_y() - ly; - if (dy > 0 && bound_y + bound_height < image.height()) - || (dy < 0 && bound_y > 0) - { - let mut new_y = bound_y as i32 + dy; - if new_y + bound_height as i32 > image.height() as i32 { - new_y = (image.height() - bound_height) as i32 - } else if new_y < 0 { - new_y = 0 + prop.crop_position = prop.crop_position.map(|(x, _)| (x, new_y)); } - bound.borrow_mut().1 = new_y as u32; + f.redraw(); + last = Some((event_x, event_y)); } - - f.redraw(); - last = Some((app::event_x(), app::event_y())); + } else if ev == Event::Released { + last = None; } - } else if ev == Event::Released { - last = None; } true }); let mut wind = self.win.clone(); - let cont = Rc::clone(&self.img); - let bound = Rc::clone(&self.bound); self.apply_btn.set_callback(move |_| { - let (bound_x, bound_y, bound_width, bound_height) = *bound.borrow(); - - let image = cont - .borrow_mut() - .image - .crop(bound_x, bound_y, bound_width, bound_height); - - cont.borrow_mut().image = image; - - let (width, height) = cont.borrow().original_dimension; - cont.borrow_mut().crop_position = Some(( - (bound_x * width) / bound_width, - (bound_y * height) / bound_height, - )); - wind.do_callback(); }); } diff --git a/src/draw_thread.rs b/src/draw_thread.rs index 32d64a1..598003d 100644 --- a/src/draw_thread.rs +++ b/src/draw_thread.rs @@ -1,3 +1,5 @@ +//! Thread to manage drawing in background + use crate::{ main_window::{MainWindow, Page}, AppMessage, @@ -20,7 +22,8 @@ use crate::utils::{ImageContainer, ImageProperties}; #[derive(Debug, Clone)] pub(crate) enum DrawMessage { - Open, + Open, // Open file or cropped file + ChangeCrop((f64, f64)), Recalc, Flush, } @@ -51,6 +54,27 @@ pub(crate) fn spawn_image_thread( status.set_label("Loading..."); load_image( &mut file_choice, + None, + &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, + ); + status.set_label(""); + } + DrawMessage::ChangeCrop((x, y)) => { + status.set_label("Loading..."); + load_image( + &mut file_choice, + Some((x, y)), &mut quote, &mut tag, &mut layer_red, @@ -81,6 +105,7 @@ pub(crate) fn spawn_image_thread( fn load_image( file_choice: &mut menu::Choice, + crop: Option<(f64, f64)>, quote: &mut MultilineInput, tag: &mut Input, layer_red: &mut Spinner, @@ -103,7 +128,6 @@ fn load_image( if let Some(cont) = container { quote.set_value(""); - tag.set_value(""); let file = Path::new(&file); let conf = file.with_extension("conf"); @@ -111,19 +135,19 @@ fn load_image( let properties = Arc::clone(&cont.properties); let mut use_defaults = true; if conf.exists() { + let mut prop = properties.write().unwrap(); let read = fs::read_to_string(&conf).unwrap(); if let Ok(saved_prop) = serde_json::from_str::(&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); + quote_position.set_range(0.0, prop.original_dimension.1); + quote_position.set_value(saved_prop.quote_position); + tag_position.set_range(0.0, prop.original_dimension.1); + tag_position.set_value(saved_prop.tag_position); prop.quote = saved_prop.quote; prop.tag = saved_prop.tag; @@ -131,10 +155,17 @@ fn load_image( prop.tag_position = saved_prop.quote_position; prop.rgba = saved_prop.rgba; use_defaults = false; - + let saved = prop.is_saved; drop(prop); - if let Some((x, y)) = saved_prop.crop_position { - cont.apply_crop_pos(x, y); + if saved { + if let Some((x, y)) = saved_prop.crop_position { + cont.apply_crop_pos(x, y); + } + } else { + match crop { + Some((x, y)) => cont.apply_crop_pos(x, y), + None => cont.apply_crop(), + } } } } @@ -142,12 +173,11 @@ fn load_image( if use_defaults { let mut prop = properties.write().unwrap(); prop.quote = "".to_owned(); - prop.tag = "".to_owned(); - 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); + quote_position.set_range(0.0, prop.original_dimension.1); + quote_position.set_value(prop.quote_position); + tag_position.set_range(0.0, prop.original_dimension.1); + tag_position.set_value(prop.tag_position); prop.rgba = [ layer_red.value() as u8, @@ -156,7 +186,11 @@ fn load_image( layer_alpha.value() as u8, ]; drop(prop); - cont.apply_crop(); + + match crop { + Some((x, y)) => cont.apply_crop_pos(x, y), + None => cont.apply_crop(), + } } cont.apply_scale(); diff --git a/src/main.rs b/src/main.rs index 5d5ae46..3a58993 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ mod config; -// mod crop_window; +mod crop_window; mod draw_thread; mod main_window; mod properties; diff --git a/src/main_window.rs b/src/main_window.rs index df74978..3e3c185 100644 --- a/src/main_window.rs +++ b/src/main_window.rs @@ -1,5 +1,6 @@ +use crate::crop_window::CropWindow; +use crate::draw_thread::*; use crate::utils::ImageProperties; -use crate::{draw_thread::*, properties}; use fltk::{ app, button::Button, @@ -15,7 +16,7 @@ use fltk::{ window::Window, }; use std::sync::{mpsc, RwLock}; -use std::{ffi::OsStr, fs, path::Path, sync::Arc}; +use std::{ffi::OsStr, fs, sync::Arc}; pub(crate) struct MainWindow { pub(crate) win: Window, @@ -33,7 +34,6 @@ pub(crate) struct MainWindow { 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>>, @@ -55,9 +55,7 @@ impl MainWindow { ) -> Self { let color = [25, 29, 34, 190]; - let mut win = Window::default() - .with_size(1000, 600) - .with_label("Post Maker"); + let mut win = Window::new(0, 0, 1000, 600, "Post Maker").center_screen(); let mut main_flex = Flex::default().size_of_parent().column(); let menubar = menu::SysMenuBar::default(); @@ -144,8 +142,6 @@ impl MainWindow { Frame::default(); let crop_btn = Button::default().with_label("Crop"); actions_flex.set_size(&crop_btn, 100); - let reset_btn = Button::default().with_label("Reset Values"); - actions_flex.set_size(&reset_btn, 100); Frame::default(); actions_flex.end(); controls_flex.set_size(&actions_flex, 30); @@ -196,7 +192,6 @@ impl MainWindow { quote_position, tag_position, crop_btn, - reset_btn, status, draw_buff, properties: Arc::clone(&properties), @@ -216,17 +211,7 @@ 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 sender = self.sender.clone(); - // let properties = Arc::clone(&self.properties); let mut win = self.win.clone(); self.menubar.add( "&File/Open Folder...\t", @@ -242,7 +227,14 @@ impl MainWindow { win.activate(); return; } - let files = fs::read_dir(path).unwrap(); + let expost_dir = path.join("export"); + if !expost_dir.exists() { + if let Err(_) = fs::create_dir(expost_dir) { + fltk::dialog::message_default("Failed: Readonly folder!"); + return; + } + } + let files = fs::read_dir(&path).unwrap(); let mut text = String::new(); for file in files { let file = file.unwrap(); @@ -298,6 +290,18 @@ impl MainWindow { } fn events(&mut self) { + let properties = Arc::clone(&self.properties); + let mut crop_win = CropWindow::new(); + let sender = self.sender.clone(); + self.crop_btn.set_callback(move |_| { + let prop = properties.read().unwrap(); + if let Some(path) = &prop.path { + if let Some((x, y)) = crop_win.load_to_crop(path, prop.crop_position) { + sender.send(DrawMessage::ChangeCrop((x, y))).unwrap(); + } + } + }); + let mut file_choice = self.file_choice.clone(); let sender = self.sender.clone(); self.next_btn.set_callback(move |_| { @@ -358,7 +362,7 @@ impl MainWindow { 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; + prop.quote_position = f.value(); sender.send(DrawMessage::Recalc).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); @@ -369,7 +373,7 @@ impl MainWindow { 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; + prop.tag_position = f.value(); sender.send(DrawMessage::Recalc).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); diff --git a/src/utils.rs b/src/utils.rs index f460453..c4a2849 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -5,7 +5,45 @@ use serde::{Deserialize, Serialize}; use crate::properties; -#[derive(Debug)] +pub(crate) struct Coord(pub(crate) f64, pub(crate) f64); + +impl From<(i32, i32)> for Coord { + fn from((x, y): (i32, i32)) -> Self { + Coord(x as f64, y as f64) + } +} + +impl From<(u32, u32)> for Coord { + fn from((x, y): (u32, u32)) -> Self { + Coord(x as f64, y as f64) + } +} + +impl From<(f32, f32)> for Coord { + fn from((x, y): (f32, f32)) -> Self { + Coord(x as f64, y as f64) + } +} + +impl Into<(f64, f64)> for Coord { + fn into(self) -> (f64, f64) { + (self.0, self.1) + } +} + +impl Into<(u32, u32)> for Coord { + fn into(self) -> (u32, u32) { + (self.0 as u32, self.1 as u32) + } +} + +impl Into<(i32, i32)> for Coord { + fn into(self) -> (i32, i32) { + (self.0 as i32, self.1 as i32) + } +} + +#[derive(Debug, Clone)] pub(crate) struct ImageContainer { pub(crate) image: DynamicImage, //plain pub(crate) buffer: DynamicImage, //buffer to show @@ -15,13 +53,14 @@ pub(crate) struct ImageContainer { impl ImageContainer { pub(crate) fn new(path: &str, properties: Arc>) -> Self { let img = image::open(path).unwrap(); - let (width, height) = img.dimensions(); + let (width, height): (f64, f64) = Coord::from(img.dimensions()).into(); + let (width, height) = (width, height); let mut prop = properties.write().unwrap(); - prop.path = path.to_owned(); + prop.path = Some(path.to_owned()); prop.original_dimension = (width, height); - prop.quote_position = height / 2; - prop.tag_position = (height * 2) / 3; + prop.quote_position = (height * 2.0) / 3.0; + prop.tag_position = height / 2.0; Self { image: img.clone(), @@ -33,13 +72,11 @@ impl ImageContainer { pub(crate) fn apply_scale(&mut self) { let mut prop = self.properties.write().unwrap(); let (width, height) = prop.dimension; - let (s_width, s_height) = ((width * 500) / height, 500); - self.image = - self.image - .resize_exact(s_width, s_height, image::imageops::FilterType::Nearest); + let (s_width, s_height) = ((width * 500.0) / height, 500.0); + + self.image = self.image.thumbnail_exact(s_width as u32, s_height as u32); self.buffer = self.image.clone(); - prop.dimension = (s_width, s_height); } @@ -48,26 +85,28 @@ impl ImageContainer { 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, + original_width / 2.0 - origina_crop_width / 2.0, + original_height / 2.0 - origina_crop_height / 2.0, )); - let (s_width, s_height) = self.image.dimensions(); + let (s_width, s_height): (f64, f64) = Coord::from(self.image.dimensions()).into(); 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); + let (cx, cy) = ((s_width - c_width) / 2.0, (s_height - c_height) / 2.0); prop.dimension = (c_width, c_height); - self.image = self.image.crop(cx, cy, c_width, c_height); + self.image = self + .image + .crop(cx as u32, cy as u32, c_width as u32, c_height as u32); self.buffer = self.image.clone(); } - pub(crate) fn apply_crop_pos(&mut self, original_x: u32, original_y: u32) { + pub(crate) fn apply_crop_pos(&mut self, original_x: f64, original_y: f64) { 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 (s_width, s_height): (f64, f64) = Coord::from(self.image.dimensions()).into(); let (c_width, c_height) = get_4_5(s_width, s_height); let (cx, cy) = ( (original_x * s_width) / original_width, @@ -76,18 +115,21 @@ impl ImageContainer { prop.dimension = (c_width, c_height); - self.image = self.image.crop(cx, cy, c_width, c_height); + self.image = self + .image + .crop(cx as u32, cy as u32, c_width as u32, c_height as u32); 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 (width, height): (f64, f64) = Coord::from(tmp.dimensions()).into(); - let layer = DynamicImage::ImageRgba8(ImageBuffer::from_fn(width, height, |_, _| { - image::Rgba(prop.rgba) - })); + let layer = + DynamicImage::ImageRgba8(ImageBuffer::from_fn(width as u32, height as u32, |_, _| { + image::Rgba(prop.rgba) + })); image::imageops::overlay(&mut tmp, &layer, 0, 0); let size = quote_from_height(height); @@ -101,10 +143,10 @@ impl ImageContainer { 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, + ((width - text_width) / 2.0) as u32, + ((prop.quote_position * height) / prop.original_dimension.1 + + (text_height / 2.0) + + index as f64 * (text_height * 1.2)) as u32, rusttype::Scale::uniform(size as f32), &properties::FONT_QUOTE, line, @@ -122,10 +164,10 @@ impl ImageContainer { 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, + (width * 0.99 - text_width) as u32, + ((prop.tag_position * height) / prop.original_dimension.1 + + (text_height / 2.0) + + index as f64 * (text_height * 1.2)) as u32, rusttype::Scale::uniform(size as f32), &properties::FONT_TAG, line, @@ -138,14 +180,14 @@ impl ImageContainer { #[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) path: Option, + pub(crate) dimension: (f64, f64), + pub(crate) original_dimension: (f64, f64), + pub(crate) crop_position: Option<(f64, f64)>, pub(crate) quote: String, pub(crate) tag: String, - pub(crate) quote_position: u32, - pub(crate) tag_position: u32, + pub(crate) quote_position: f64, // as per original + pub(crate) tag_position: f64, // as per original pub(crate) rgba: [u8; 4], pub(crate) is_saved: bool, } @@ -153,21 +195,21 @@ pub(crate) struct ImageProperties { impl ImageProperties { pub(crate) fn new() -> Self { Self { - path: "".to_owned(), - dimension: (0, 0), - original_dimension: (0, 0), + path: None, + dimension: (0.0, 0.0), + original_dimension: (0.0, 0.0), crop_position: None, quote: "".to_owned(), tag: "".to_owned(), - quote_position: 0, - tag_position: 0, + quote_position: 0.0, + tag_position: 0.0, rgba: [0; 4], is_saved: true, } } } -pub(crate) fn get_4_5(width: u32, height: u32) -> (u32, u32) { +pub(crate) fn get_4_5(width: f64, height: f64) -> (f64, f64) { if width > width_from_height(height) { (width_from_height(height), height) } else { @@ -175,27 +217,27 @@ pub(crate) fn get_4_5(width: u32, height: u32) -> (u32, u32) { } } -pub(crate) fn width_from_height(height: u32) -> u32 { - (4 * height) / 5 +pub(crate) fn width_from_height(height: f64) -> f64 { + (4.0 * height) / 5.0 } -pub(crate) fn height_from_width(width: u32) -> u32 { - (5 * width) / 4 +pub(crate) fn height_from_width(width: f64) -> f64 { + (5.0 * width) / 4.0 } -pub(crate) fn quote_from_height(height: u32) -> u32 { - (height * 70) / 1350 +pub(crate) fn quote_from_height(height: f64) -> f64 { + (height * 60.0) / 1350.0 } -pub(crate) fn tag_from_height(height: u32) -> u32 { - (height * 50) / 1350 +pub(crate) fn tag_from_height(height: f64) -> f64 { + (height * 50.0) / 1350.0 } pub(crate) fn measure_line( font: &rusttype::Font, text: &str, scale: rusttype::Scale, -) -> (f32, f32) { +) -> (f64, f64) { let width = font .layout(text, scale, rusttype::point(0.0, 0.0)) .map(|g| g.position().x + g.unpositioned().h_metrics().advance_width) @@ -205,5 +247,5 @@ pub(crate) fn measure_line( let v_metrics = font.v_metrics(scale); let height = v_metrics.ascent - v_metrics.descent + v_metrics.line_gap; - (width, height) + Coord::from((width, height)).into() }