Fixed Cropping

This commit is contained in:
Piyush मिश्रः 2022-01-13 18:34:31 +05:30
parent e2f92fbce8
commit 6517773cf3
8 changed files with 335 additions and 186 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

View File

@ -15,3 +15,5 @@ rusttype = "0.9"
serde_json = "1.0" serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
lazy_static = "1.4" lazy_static = "1.4"
# rust_decimal = "1.20"
# rust_decimal_macros = "1.20"

BIN
out.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 987 KiB

After

Width:  |  Height:  |  Size: 210 KiB

View File

@ -1,41 +1,48 @@
use crate::utils::{self, ImageContainer}; use crate::utils::{self, Coord, ImageContainer, ImageProperties};
use fltk::{ use fltk::{
app, button::Button, draw, enums::Event, frame::Frame, group::Flex, prelude::*, window::Window, app, button::Button, draw, enums::Event, frame::Frame, group::Flex, prelude::*, window::Window,
}; };
use image::GenericImageView; 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(crate) struct CropWindow {
pub win: Window, pub win: Window,
apply_btn: Button, apply_btn: Button,
img_view: Frame, container: Rc<RefCell<Option<ImageContainer>>>,
img: Rc<RefCell<ImageContainer>>, page: Page,
bound: Rc<RefCell<(u32, u32, u32, u32)>>, }
#[derive(Clone)]
pub(crate) struct Page {
pub(crate) image_view: Frame,
pub(crate) row_flex: Flex,
pub(crate) col_flex: Flex,
} }
impl CropWindow { impl CropWindow {
pub(crate) fn new(container: Rc<RefCell<ImageContainer>>) -> Self { pub(crate) fn new() -> Self {
// let image = &container.borrow().image; let mut win = Window::new(0, 0, 500, 600, "Crop").center_screen();
let (image_width, image_height) = container.borrow().image.dimensions();
let mut win = Window::default()
.with_size(image_width as i32, 600)
.with_label("Crop");
let mut main_flex = Flex::default().size_of_parent().column(); let mut main_flex = Flex::default().size_of_parent().column();
// Work area // Work area
let mut center_row_flex = Flex::default().row(); let center_row_flex = Flex::default().row();
Frame::default(); Frame::default();
let mut center_col_flex = Flex::default().column(); let center_col_flex = Flex::default().column();
Frame::default(); Frame::default();
let img_view = Frame::default(); let img_view = Frame::default();
Frame::default(); Frame::default();
center_col_flex.set_size(&img_view, image_height as i32);
center_col_flex.end(); center_col_flex.end();
Frame::default(); Frame::default();
center_row_flex.set_size(&center_col_flex, image_width as i32);
center_row_flex.end(); center_row_flex.end();
// Panel // Panel
@ -56,16 +63,15 @@ impl CropWindow {
win.end(); win.end();
win.make_resizable(true); 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 { let mut crop_win = Self {
win, win,
apply_btn, apply_btn,
img_view, container: Rc::new(RefCell::new(None)),
img: Rc::clone(&container), page: Page {
bound: Rc::new(RefCell::new((bound_x, bound_y, bound_width, bound_height))), image_view: img_view,
row_flex: center_row_flex,
col_flex: center_col_flex,
},
}; };
crop_win.draw(); crop_win.draw();
@ -73,97 +79,158 @@ impl CropWindow {
crop_win 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) { fn draw(&mut self) {
let cont = Rc::clone(&self.img); let container = Rc::clone(&self.container);
let bound = Rc::clone(&self.bound); self.page.image_view.draw(move |f| {
self.img_view.draw(move |f| { if let Some(cont) = &*container.borrow() {
let image = &cont.borrow().image; let image = &cont.buffer;
let (bound_x, bound_y, bound_width, bound_height) = *bound.borrow();
draw::draw_image( draw::draw_image(
image.as_rgb8().unwrap().as_raw(), image.as_rgb8().unwrap().as_raw(),
f.x(), f.x(),
f.y(), f.y(),
image.width() as i32, image.width() as i32,
image.height() as i32, image.height() as i32,
fltk::enums::ColorDepth::Rgb8, fltk::enums::ColorDepth::Rgb8,
) )
.unwrap(); .unwrap();
draw::set_color_rgb(255, 0, 0);
draw::draw_rect( let prop = cont.properties.read().unwrap();
f.x() + bound_x as i32, let (original_width, original_height) = prop.original_dimension;
f.y() + bound_y as i32, let (original_x, original_y) = prop.crop_position.unwrap();
bound_width as i32, let (resized_width, resized_height) = (image.width() as f64, image.height() as f64);
bound_height as i32, 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) { fn event(&mut self) {
let mut last: Option<(i32, i32)> = None; let mut last: Option<(f64, f64)> = None;
let cont = Rc::clone(&self.img); let container = Rc::clone(&self.container);
let bound = Rc::clone(&self.bound); self.page.image_view.handle(move |f, ev| {
self.img_view.handle(move |f, ev| { if let Some(cont) = &*container.borrow_mut() {
let image = &cont.borrow().image; let image = &cont.buffer;
let (bound_x, bound_y, bound_width, bound_height) = *bound.borrow();
if ev == Event::Push { let mut prop = cont.properties.write().unwrap();
last = Some((app::event_x(), app::event_y()));
} else if ev == Event::Drag { let (original_x, original_y) = prop.crop_position.unwrap();
if let Some((lx, ly)) = last { let (original_width, original_heigth) = prop.original_dimension;
let dx = app::event_x() - lx; let (original_bound_width, original_bound_height) =
if (dx > 0 && bound_x + bound_width < image.width()) || (dx < 0 && bound_x > 0) utils::get_4_5(original_width, original_heigth);
{ let point = original_width / image.width() as f64;
let mut new_x = bound_x as i32 + dx; let (event_x, event_y) = (
if new_x + bound_width as i32 > image.width() as i32 { (app::event_x() - f.x()) as f64 * point,
new_x = (image.width() - bound_width) as i32 (app::event_y() - f.y()) as f64 * point,
} else if new_x < 0 { );
new_x = 0 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; prop.crop_position = prop.crop_position.map(|(x, _)| (x, new_y));
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
} }
bound.borrow_mut().1 = new_y as u32; f.redraw();
last = Some((event_x, event_y));
} }
} else if ev == Event::Released {
f.redraw(); last = None;
last = Some((app::event_x(), app::event_y()));
} }
} else if ev == Event::Released {
last = None;
} }
true true
}); });
let mut wind = self.win.clone(); let mut wind = self.win.clone();
let cont = Rc::clone(&self.img);
let bound = Rc::clone(&self.bound);
self.apply_btn.set_callback(move |_| { 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(); wind.do_callback();
}); });
} }

View File

@ -1,3 +1,5 @@
//! Thread to manage drawing in background
use crate::{ use crate::{
main_window::{MainWindow, Page}, main_window::{MainWindow, Page},
AppMessage, AppMessage,
@ -20,7 +22,8 @@ use crate::utils::{ImageContainer, ImageProperties};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) enum DrawMessage { pub(crate) enum DrawMessage {
Open, Open, // Open file or cropped file
ChangeCrop((f64, f64)),
Recalc, Recalc,
Flush, Flush,
} }
@ -51,6 +54,27 @@ pub(crate) fn spawn_image_thread(
status.set_label("Loading..."); status.set_label("Loading...");
load_image( load_image(
&mut file_choice, &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 quote,
&mut tag, &mut tag,
&mut layer_red, &mut layer_red,
@ -81,6 +105,7 @@ pub(crate) fn spawn_image_thread(
fn load_image( fn load_image(
file_choice: &mut menu::Choice, file_choice: &mut menu::Choice,
crop: Option<(f64, f64)>,
quote: &mut MultilineInput, quote: &mut MultilineInput,
tag: &mut Input, tag: &mut Input,
layer_red: &mut Spinner, layer_red: &mut Spinner,
@ -103,7 +128,6 @@ fn load_image(
if let Some(cont) = container { if let Some(cont) = container {
quote.set_value(""); quote.set_value("");
tag.set_value("");
let file = Path::new(&file); let file = Path::new(&file);
let conf = file.with_extension("conf"); let conf = file.with_extension("conf");
@ -111,19 +135,19 @@ fn load_image(
let properties = Arc::clone(&cont.properties); let properties = Arc::clone(&cont.properties);
let mut use_defaults = true; let mut use_defaults = true;
if conf.exists() { if conf.exists() {
let mut prop = properties.write().unwrap();
let read = fs::read_to_string(&conf).unwrap(); let read = fs::read_to_string(&conf).unwrap();
if let Ok(saved_prop) = serde_json::from_str::<ImageProperties>(&read) { 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_red.set_value(saved_prop.rgba[0] as f64);
layer_green.set_value(saved_prop.rgba[1] as f64); layer_green.set_value(saved_prop.rgba[1] as f64);
layer_blue.set_value(saved_prop.rgba[2] as f64); layer_blue.set_value(saved_prop.rgba[2] as f64);
layer_alpha.set_value(saved_prop.rgba[3] as f64); layer_alpha.set_value(saved_prop.rgba[3] as f64);
quote.set_value(&saved_prop.quote); quote.set_value(&saved_prop.quote);
tag.set_value(&saved_prop.tag); tag.set_value(&saved_prop.tag);
quote_position.set_range(0.0, prop.original_dimension.1 as f64); quote_position.set_range(0.0, prop.original_dimension.1);
quote_position.set_value(saved_prop.quote_position as f64); quote_position.set_value(saved_prop.quote_position);
tag_position.set_range(0.0, prop.original_dimension.1 as f64); tag_position.set_range(0.0, prop.original_dimension.1);
tag_position.set_value(saved_prop.tag_position as f64); tag_position.set_value(saved_prop.tag_position);
prop.quote = saved_prop.quote; prop.quote = saved_prop.quote;
prop.tag = saved_prop.tag; prop.tag = saved_prop.tag;
@ -131,10 +155,17 @@ fn load_image(
prop.tag_position = saved_prop.quote_position; prop.tag_position = saved_prop.quote_position;
prop.rgba = saved_prop.rgba; prop.rgba = saved_prop.rgba;
use_defaults = false; use_defaults = false;
let saved = prop.is_saved;
drop(prop); drop(prop);
if let Some((x, y)) = saved_prop.crop_position { if saved {
cont.apply_crop_pos(x, y); 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 { if use_defaults {
let mut prop = properties.write().unwrap(); let mut prop = properties.write().unwrap();
prop.quote = "".to_owned(); prop.quote = "".to_owned();
prop.tag = "".to_owned();
quote_position.set_range(0.0, prop.original_dimension.1 as f64); quote_position.set_range(0.0, prop.original_dimension.1);
quote_position.set_value(prop.quote_position as f64); quote_position.set_value(prop.quote_position);
tag_position.set_range(0.0, prop.original_dimension.1 as f64); tag_position.set_range(0.0, prop.original_dimension.1);
tag_position.set_value(prop.tag_position as f64); tag_position.set_value(prop.tag_position);
prop.rgba = [ prop.rgba = [
layer_red.value() as u8, layer_red.value() as u8,
@ -156,7 +186,11 @@ fn load_image(
layer_alpha.value() as u8, layer_alpha.value() as u8,
]; ];
drop(prop); drop(prop);
cont.apply_crop();
match crop {
Some((x, y)) => cont.apply_crop_pos(x, y),
None => cont.apply_crop(),
}
} }
cont.apply_scale(); cont.apply_scale();

View File

@ -1,5 +1,5 @@
mod config; mod config;
// mod crop_window; mod crop_window;
mod draw_thread; mod draw_thread;
mod main_window; mod main_window;
mod properties; mod properties;

View File

@ -1,5 +1,6 @@
use crate::crop_window::CropWindow;
use crate::draw_thread::*;
use crate::utils::ImageProperties; use crate::utils::ImageProperties;
use crate::{draw_thread::*, properties};
use fltk::{ use fltk::{
app, app,
button::Button, button::Button,
@ -15,7 +16,7 @@ use fltk::{
window::Window, window::Window,
}; };
use std::sync::{mpsc, RwLock}; 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) struct MainWindow {
pub(crate) win: Window, pub(crate) win: Window,
@ -33,7 +34,6 @@ pub(crate) struct MainWindow {
pub(crate) quote_position: Spinner, pub(crate) quote_position: Spinner,
pub(crate) tag_position: Spinner, pub(crate) tag_position: Spinner,
pub(crate) crop_btn: Button, pub(crate) crop_btn: Button,
pub(crate) reset_btn: Button,
pub(crate) status: Frame, pub(crate) status: Frame,
pub(crate) page: Page, pub(crate) page: Page,
pub(crate) draw_buff: Arc<RwLock<Vec<u8>>>, pub(crate) draw_buff: Arc<RwLock<Vec<u8>>>,
@ -55,9 +55,7 @@ impl MainWindow {
) -> Self { ) -> Self {
let color = [25, 29, 34, 190]; let color = [25, 29, 34, 190];
let mut win = Window::default() let mut win = Window::new(0, 0, 1000, 600, "Post Maker").center_screen();
.with_size(1000, 600)
.with_label("Post Maker");
let mut main_flex = Flex::default().size_of_parent().column(); let mut main_flex = Flex::default().size_of_parent().column();
let menubar = menu::SysMenuBar::default(); let menubar = menu::SysMenuBar::default();
@ -144,8 +142,6 @@ impl MainWindow {
Frame::default(); Frame::default();
let crop_btn = Button::default().with_label("Crop"); let crop_btn = Button::default().with_label("Crop");
actions_flex.set_size(&crop_btn, 100); 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(); Frame::default();
actions_flex.end(); actions_flex.end();
controls_flex.set_size(&actions_flex, 30); controls_flex.set_size(&actions_flex, 30);
@ -196,7 +192,6 @@ impl MainWindow {
quote_position, quote_position,
tag_position, tag_position,
crop_btn, crop_btn,
reset_btn,
status, status,
draw_buff, draw_buff,
properties: Arc::clone(&properties), properties: Arc::clone(&properties),
@ -216,17 +211,7 @@ impl MainWindow {
fn menu(&mut self) { fn menu(&mut self) {
let mut file_choice = self.file_choice.clone(); 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 sender = self.sender.clone();
// let properties = Arc::clone(&self.properties);
let mut win = self.win.clone(); let mut win = self.win.clone();
self.menubar.add( self.menubar.add(
"&File/Open Folder...\t", "&File/Open Folder...\t",
@ -242,7 +227,14 @@ impl MainWindow {
win.activate(); win.activate();
return; 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(); let mut text = String::new();
for file in files { for file in files {
let file = file.unwrap(); let file = file.unwrap();
@ -298,6 +290,18 @@ impl MainWindow {
} }
fn events(&mut self) { 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 mut file_choice = self.file_choice.clone();
let sender = self.sender.clone(); let sender = self.sender.clone();
self.next_btn.set_callback(move |_| { self.next_btn.set_callback(move |_| {
@ -358,7 +362,7 @@ impl MainWindow {
let sender = self.sender.clone(); let sender = self.sender.clone();
self.quote_position.set_callback(move |f| { self.quote_position.set_callback(move |f| {
let mut prop = properties.write().unwrap(); 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::Recalc).unwrap();
sender.send(DrawMessage::Flush).unwrap(); sender.send(DrawMessage::Flush).unwrap();
image.redraw(); image.redraw();
@ -369,7 +373,7 @@ impl MainWindow {
let sender = self.sender.clone(); let sender = self.sender.clone();
self.tag_position.set_callback(move |f| { self.tag_position.set_callback(move |f| {
let mut prop = properties.write().unwrap(); 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::Recalc).unwrap();
sender.send(DrawMessage::Flush).unwrap(); sender.send(DrawMessage::Flush).unwrap();
image.redraw(); image.redraw();

View File

@ -5,7 +5,45 @@ use serde::{Deserialize, Serialize};
use crate::properties; 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) struct ImageContainer {
pub(crate) image: DynamicImage, //plain pub(crate) image: DynamicImage, //plain
pub(crate) buffer: DynamicImage, //buffer to show pub(crate) buffer: DynamicImage, //buffer to show
@ -15,13 +53,14 @@ pub(crate) struct ImageContainer {
impl ImageContainer { impl ImageContainer {
pub(crate) fn new(path: &str, properties: Arc<RwLock<ImageProperties>>) -> Self { pub(crate) fn new(path: &str, properties: Arc<RwLock<ImageProperties>>) -> Self {
let img = image::open(path).unwrap(); 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(); let mut prop = properties.write().unwrap();
prop.path = path.to_owned(); prop.path = Some(path.to_owned());
prop.original_dimension = (width, height); prop.original_dimension = (width, height);
prop.quote_position = height / 2; prop.quote_position = (height * 2.0) / 3.0;
prop.tag_position = (height * 2) / 3; prop.tag_position = height / 2.0;
Self { Self {
image: img.clone(), image: img.clone(),
@ -33,13 +72,11 @@ impl ImageContainer {
pub(crate) fn apply_scale(&mut self) { pub(crate) fn apply_scale(&mut self) {
let mut prop = self.properties.write().unwrap(); let mut prop = self.properties.write().unwrap();
let (width, height) = prop.dimension; let (width, height) = prop.dimension;
let (s_width, s_height) = ((width * 500) / height, 500); let (s_width, s_height) = ((width * 500.0) / height, 500.0);
self.image =
self.image self.image = self.image.thumbnail_exact(s_width as u32, s_height as u32);
.resize_exact(s_width, s_height, image::imageops::FilterType::Nearest);
self.buffer = self.image.clone(); self.buffer = self.image.clone();
prop.dimension = (s_width, s_height); prop.dimension = (s_width, s_height);
} }
@ -48,26 +85,28 @@ impl ImageContainer {
let (original_width, original_height) = prop.original_dimension; let (original_width, original_height) = prop.original_dimension;
let (origina_crop_width, origina_crop_height) = get_4_5(original_width, original_height); let (origina_crop_width, origina_crop_height) = get_4_5(original_width, original_height);
prop.crop_position = Some(( prop.crop_position = Some((
original_width / 2 - origina_crop_width / 2, original_width / 2.0 - origina_crop_width / 2.0,
original_height / 2 - origina_crop_height / 2, 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 (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); 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(); 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 mut prop = self.properties.write().unwrap();
let (original_width, original_height) = prop.original_dimension; let (original_width, original_height) = prop.original_dimension;
prop.crop_position = Some((original_x, original_y)); 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 (c_width, c_height) = get_4_5(s_width, s_height);
let (cx, cy) = ( let (cx, cy) = (
(original_x * s_width) / original_width, (original_x * s_width) / original_width,
@ -76,18 +115,21 @@ impl ImageContainer {
prop.dimension = (c_width, c_height); 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(); self.buffer = self.image.clone();
} }
pub(crate) fn recalc(&mut self) { pub(crate) fn recalc(&mut self) {
let prop = self.properties.read().unwrap(); let prop = self.properties.read().unwrap();
let mut tmp = self.image.clone(); 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, |_, _| { let layer =
image::Rgba(prop.rgba) DynamicImage::ImageRgba8(ImageBuffer::from_fn(width as u32, height as u32, |_, _| {
})); image::Rgba(prop.rgba)
}));
image::imageops::overlay(&mut tmp, &layer, 0, 0); image::imageops::overlay(&mut tmp, &layer, 0, 0);
let size = quote_from_height(height); let size = quote_from_height(height);
@ -101,10 +143,10 @@ impl ImageContainer {
imageproc::drawing::draw_text_mut( imageproc::drawing::draw_text_mut(
&mut tmp, &mut tmp,
image::Rgba([255, 255, 255, 255]), image::Rgba([255, 255, 255, 255]),
((width as f32 - text_width) / 2.0) as u32, ((width - text_width) / 2.0) as u32,
(prop.quote_position * height) / prop.original_dimension.1 ((prop.quote_position * height) / prop.original_dimension.1
+ (text_height / 2.0) as u32 + (text_height / 2.0)
+ index as u32 * (text_height * 1.2) as u32, + index as f64 * (text_height * 1.2)) as u32,
rusttype::Scale::uniform(size as f32), rusttype::Scale::uniform(size as f32),
&properties::FONT_QUOTE, &properties::FONT_QUOTE,
line, line,
@ -122,10 +164,10 @@ impl ImageContainer {
imageproc::drawing::draw_text_mut( imageproc::drawing::draw_text_mut(
&mut tmp, &mut tmp,
image::Rgba([255, 255, 255, 255]), image::Rgba([255, 255, 255, 255]),
(width as f32 * 0.99 - text_width) as u32, (width * 0.99 - text_width) as u32,
(prop.tag_position * height) / prop.original_dimension.1 ((prop.tag_position * height) / prop.original_dimension.1
+ (text_height / 2.0) as u32 + (text_height / 2.0)
+ index as u32 * (text_height * 1.2) as u32, + index as f64 * (text_height * 1.2)) as u32,
rusttype::Scale::uniform(size as f32), rusttype::Scale::uniform(size as f32),
&properties::FONT_TAG, &properties::FONT_TAG,
line, line,
@ -138,14 +180,14 @@ impl ImageContainer {
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub(crate) struct ImageProperties { pub(crate) struct ImageProperties {
pub(crate) path: String, pub(crate) path: Option<String>,
pub(crate) dimension: (u32, u32), pub(crate) dimension: (f64, f64),
pub(crate) original_dimension: (u32, u32), pub(crate) original_dimension: (f64, f64),
pub(crate) crop_position: Option<(u32, u32)>, pub(crate) crop_position: Option<(f64, f64)>,
pub(crate) quote: String, pub(crate) quote: String,
pub(crate) tag: String, pub(crate) tag: String,
pub(crate) quote_position: u32, pub(crate) quote_position: f64, // as per original
pub(crate) tag_position: u32, pub(crate) tag_position: f64, // as per original
pub(crate) rgba: [u8; 4], pub(crate) rgba: [u8; 4],
pub(crate) is_saved: bool, pub(crate) is_saved: bool,
} }
@ -153,21 +195,21 @@ pub(crate) struct ImageProperties {
impl ImageProperties { impl ImageProperties {
pub(crate) fn new() -> Self { pub(crate) fn new() -> Self {
Self { Self {
path: "".to_owned(), path: None,
dimension: (0, 0), dimension: (0.0, 0.0),
original_dimension: (0, 0), original_dimension: (0.0, 0.0),
crop_position: None, crop_position: None,
quote: "".to_owned(), quote: "".to_owned(),
tag: "".to_owned(), tag: "".to_owned(),
quote_position: 0, quote_position: 0.0,
tag_position: 0, tag_position: 0.0,
rgba: [0; 4], rgba: [0; 4],
is_saved: true, 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) { if width > width_from_height(height) {
(width_from_height(height), height) (width_from_height(height), height)
} else { } 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 { pub(crate) fn width_from_height(height: f64) -> f64 {
(4 * height) / 5 (4.0 * height) / 5.0
} }
pub(crate) fn height_from_width(width: u32) -> u32 { pub(crate) fn height_from_width(width: f64) -> f64 {
(5 * width) / 4 (5.0 * width) / 4.0
} }
pub(crate) fn quote_from_height(height: u32) -> u32 { pub(crate) fn quote_from_height(height: f64) -> f64 {
(height * 70) / 1350 (height * 60.0) / 1350.0
} }
pub(crate) fn tag_from_height(height: u32) -> u32 { pub(crate) fn tag_from_height(height: f64) -> f64 {
(height * 50) / 1350 (height * 50.0) / 1350.0
} }
pub(crate) fn measure_line( pub(crate) fn measure_line(
font: &rusttype::Font, font: &rusttype::Font,
text: &str, text: &str,
scale: rusttype::Scale, scale: rusttype::Scale,
) -> (f32, f32) { ) -> (f64, f64) {
let width = font let width = font
.layout(text, scale, rusttype::point(0.0, 0.0)) .layout(text, scale, rusttype::point(0.0, 0.0))
.map(|g| g.position().x + g.unpositioned().h_metrics().advance_width) .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 v_metrics = font.v_metrics(scale);
let height = v_metrics.ascent - v_metrics.descent + v_metrics.line_gap; let height = v_metrics.ascent - v_metrics.descent + v_metrics.line_gap;
(width, height) Coord::from((width, height)).into()
} }