Fixed Cropping
This commit is contained in:
parent
e2f92fbce8
commit
6517773cf3
Binary file not shown.
|
After Width: | Height: | Size: 198 KiB |
|
|
@ -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"
|
||||
|
|
|
|||
BIN
out.png
BIN
out.png
Binary file not shown.
|
Before Width: | Height: | Size: 987 KiB After Width: | Height: | Size: 210 KiB |
|
|
@ -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<RefCell<ImageContainer>>,
|
||||
bound: Rc<RefCell<(u32, u32, u32, u32)>>,
|
||||
container: Rc<RefCell<Option<ImageContainer>>>,
|
||||
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<RefCell<ImageContainer>>) -> 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,12 +79,64 @@ 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();
|
||||
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(),
|
||||
|
|
@ -88,6 +146,18 @@ impl CropWindow {
|
|||
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,
|
||||
|
|
@ -95,75 +165,72 @@ impl CropWindow {
|
|||
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();
|
||||
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((app::event_x(), app::event_y()));
|
||||
last = Some((event_x, 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 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 = 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 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;
|
||||
}
|
||||
|
||||
bound.borrow_mut().0 = new_x as u32;
|
||||
prop.crop_position = prop.crop_position.map(|(_, y)| (new_x, y));
|
||||
}
|
||||
|
||||
let dy = app::event_y() - ly;
|
||||
if (dy > 0 && bound_y + bound_height < image.height())
|
||||
|| (dy < 0 && bound_y > 0)
|
||||
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 = 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
|
||||
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;
|
||||
}
|
||||
|
||||
bound.borrow_mut().1 = new_y as u32;
|
||||
prop.crop_position = prop.crop_position.map(|(x, _)| (x, new_y));
|
||||
}
|
||||
|
||||
f.redraw();
|
||||
last = Some((app::event_x(), app::event_y()));
|
||||
last = Some((event_x, event_y));
|
||||
}
|
||||
} 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();
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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::<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);
|
||||
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,23 +155,29 @@ 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 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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
mod config;
|
||||
// mod crop_window;
|
||||
mod crop_window;
|
||||
mod draw_thread;
|
||||
mod main_window;
|
||||
mod properties;
|
||||
|
|
|
|||
|
|
@ -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<RwLock<Vec<u8>>>,
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
142
src/utils.rs
142
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<RwLock<ImageProperties>>) -> 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,16 +115,19 @@ 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, |_, _| {
|
||||
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);
|
||||
|
|
@ -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<String>,
|
||||
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()
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue