diff --git a/imgs/75e253532946877dea6aaa5a60ffd51a.conf b/imgs/75e253532946877dea6aaa5a60ffd51a.conf new file mode 100644 index 0000000..3a1739d --- /dev/null +++ b/imgs/75e253532946877dea6aaa5a60ffd51a.conf @@ -0,0 +1 @@ +{"path":"/home/piyush/Projects/post_maker/75e253532946877dea6aaa5a60ffd51a.jpg","dimension":[400.0,500.0],"original_dimension":[720.0,1280.0],"crop_position":[0.0,190.0],"quote":"wow!","tag":"","quote_position":853.3333333333334,"tag_position":853.3333333333334,"rgba":[25,29,34,190],"is_saved":false} \ No newline at end of file diff --git a/75e253532946877dea6aaa5a60ffd51a.jpg b/imgs/75e253532946877dea6aaa5a60ffd51a.jpg similarity index 100% rename from 75e253532946877dea6aaa5a60ffd51a.jpg rename to imgs/75e253532946877dea6aaa5a60ffd51a.jpg diff --git a/imgs/bearbrickjia-20211228-0002.conf b/imgs/bearbrickjia-20211228-0002.conf new file mode 100644 index 0000000..a836b51 --- /dev/null +++ b/imgs/bearbrickjia-20211228-0002.conf @@ -0,0 +1 @@ +{"path":"/home/piyush/Projects/post_maker/bearbrickjia-20211228-0002.jpg","dimension":[400.0,500.0],"original_dimension":[1440.0,1124.0],"crop_position":[270.4,0.0],"quote":"hello","tag":"","quote_position":749.3333333333334,"tag_position":749.3333333333334,"rgba":[25,29,34,190],"is_saved":false} \ No newline at end of file diff --git a/bearbrickjia-20211228-0002.jpg b/imgs/bearbrickjia-20211228-0002.jpg similarity index 100% rename from bearbrickjia-20211228-0002.jpg rename to imgs/bearbrickjia-20211228-0002.jpg diff --git a/eae7e510f03c7afc0d44863525df184f.jpg b/imgs/eae7e510f03c7afc0d44863525df184f.jpg similarity index 100% rename from eae7e510f03c7afc0d44863525df184f.jpg rename to imgs/eae7e510f03c7afc0d44863525df184f.jpg diff --git a/src/draw_thread.rs b/src/draw_thread.rs index 598003d..3b56eee 100644 --- a/src/draw_thread.rs +++ b/src/draw_thread.rs @@ -26,6 +26,7 @@ pub(crate) enum DrawMessage { ChangeCrop((f64, f64)), Recalc, Flush, + Save, } pub(crate) fn spawn_image_thread( @@ -98,6 +99,13 @@ pub(crate) fn spawn_image_thread( DrawMessage::Flush => { flush_buffer(&app_sender, &mut _container); } + DrawMessage::Save => { + if let Some(cont) = &mut _container { + status.set_label("Saving..."); + cont.save(); + status.set_label(""); + } + } } } }); @@ -154,6 +162,7 @@ fn load_image( prop.quote_position = saved_prop.quote_position; prop.tag_position = saved_prop.quote_position; prop.rgba = saved_prop.rgba; + prop.is_saved = true; use_defaults = false; let saved = prop.is_saved; drop(prop); @@ -173,7 +182,6 @@ fn load_image( if use_defaults { let mut prop = properties.write().unwrap(); prop.quote = "".to_owned(); - 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); @@ -185,11 +193,18 @@ fn load_image( layer_blue.value() as u8, layer_alpha.value() as u8, ]; - drop(prop); match crop { - Some((x, y)) => cont.apply_crop_pos(x, y), - None => cont.apply_crop(), + Some((x, y)) => { + prop.is_saved = false; + drop(prop); + cont.apply_crop_pos(x, y); + } + None => { + prop.is_saved = true; + drop(prop); + cont.apply_crop(); + } } } diff --git a/src/main_window.rs b/src/main_window.rs index 3e3c185..4ae7028 100644 --- a/src/main_window.rs +++ b/src/main_window.rs @@ -254,11 +254,12 @@ impl MainWindow { }, ); + let sender = self.sender.clone(); self.menubar.add( "&File/Save...\t", Shortcut::Ctrl | 's', menu::MenuFlag::Normal, - |_| {}, + move |_| sender.send(DrawMessage::Save).unwrap(), ); self.menubar.add( @@ -290,21 +291,37 @@ impl MainWindow { } fn events(&mut self) { + let sender = self.sender.clone(); + self.save_btn + .set_callback(move |_| sender.send(DrawMessage::Save).unwrap()); + 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(); + let mut prop = properties.write().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(); + prop.is_saved = false; } } }); let mut file_choice = self.file_choice.clone(); let sender = self.sender.clone(); + let properties = Arc::clone(&self.properties); self.next_btn.set_callback(move |_| { + let prop = properties.read().unwrap(); + if !prop.is_saved { + let save = fltk::dialog::choice_default("Save?", "yes", "no", "cancel"); + match save { + 0 => sender.send(DrawMessage::Save).unwrap(), + 1 => {} + _ => return, + } + } + if file_choice.value() == file_choice.size() - 2 { file_choice.set_value(0); } else { @@ -315,7 +332,18 @@ impl MainWindow { let mut file_choice = self.file_choice.clone(); let sender = self.sender.clone(); + let properties = Arc::clone(&self.properties); self.back_btn.set_callback(move |_| { + let prop = properties.read().unwrap(); + if !prop.is_saved { + let save = fltk::dialog::choice_default("Save?", "yes", "no", "cancel"); + match save { + 0 => sender.send(DrawMessage::Save).unwrap(), + 1 => {} + _ => return, + } + } + if file_choice.value() == 0 { file_choice.set_value(file_choice.size() - 2); } else { @@ -325,7 +353,17 @@ impl MainWindow { }); let sender = self.sender.clone(); + let properties = Arc::clone(&self.properties); self.file_choice.set_callback(move |_| { + let prop = properties.read().unwrap(); + if !prop.is_saved { + let save = fltk::dialog::choice_default("Save?", "yes", "no", "cancel"); + match save { + 0 => sender.send(DrawMessage::Save).unwrap(), + 1 => {} + _ => return, + } + } sender.send(DrawMessage::Open).unwrap(); }); @@ -336,6 +374,7 @@ impl MainWindow { if ev == enums::Event::KeyUp { let mut prop = properties.write().unwrap(); prop.quote = f.value(); + prop.is_saved = false; sender.send(DrawMessage::Recalc).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); @@ -350,6 +389,7 @@ impl MainWindow { if ev == enums::Event::KeyUp { let mut prop = properties.write().unwrap(); prop.tag = f.value(); + prop.is_saved = false; sender.send(DrawMessage::Recalc).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); @@ -363,6 +403,7 @@ impl MainWindow { self.quote_position.set_callback(move |f| { let mut prop = properties.write().unwrap(); prop.quote_position = f.value(); + prop.is_saved = false; sender.send(DrawMessage::Recalc).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); @@ -374,6 +415,7 @@ impl MainWindow { self.tag_position.set_callback(move |f| { let mut prop = properties.write().unwrap(); prop.tag_position = f.value(); + prop.is_saved = false; sender.send(DrawMessage::Recalc).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); @@ -385,6 +427,7 @@ impl MainWindow { self.layer_red.set_callback(move |f| { let mut prop = properties.write().unwrap(); prop.rgba[0] = f.value() as u8; + prop.is_saved = false; sender.send(DrawMessage::Recalc).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); @@ -396,6 +439,7 @@ impl MainWindow { self.layer_green.set_callback(move |f| { let mut prop = properties.write().unwrap(); prop.rgba[1] = f.value() as u8; + prop.is_saved = false; sender.send(DrawMessage::Recalc).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); @@ -407,6 +451,7 @@ impl MainWindow { self.layer_blue.set_callback(move |f| { let mut prop = properties.write().unwrap(); prop.rgba[2] = f.value() as u8; + prop.is_saved = false; sender.send(DrawMessage::Recalc).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); @@ -418,6 +463,7 @@ impl MainWindow { self.layer_alpha.set_callback(move |f| { let mut prop = properties.write().unwrap(); prop.rgba[3] = f.value() as u8; + prop.is_saved = false; sender.send(DrawMessage::Recalc).unwrap(); sender.send(DrawMessage::Flush).unwrap(); image.redraw(); diff --git a/src/utils.rs b/src/utils.rs index c4a2849..e9a5d2e 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,4 +1,8 @@ -use std::sync::{Arc, RwLock}; +use std::{ + fs, + path::Path, + sync::{Arc, RwLock}, +}; use image::{DynamicImage, GenericImageView, ImageBuffer}; use serde::{Deserialize, Serialize}; @@ -176,6 +180,92 @@ impl ImageContainer { self.buffer = tmp; } + + pub(crate) fn save(&self) { + let prop = self.properties.read().unwrap(); + + let path_original = match &prop.path { + Some(p) => Path::new(p), + None => return, + }; + let path_conf = path_original.with_extension("conf"); + let export = path_original + .parent() + .unwrap() + .join("export") + .join(path_original.file_name().unwrap().to_str().unwrap()); + + fs::write(&path_conf, serde_json::to_string(&*prop).unwrap()).unwrap(); + + let mut img = image::open(&path_original).unwrap(); + let (width, height): (f64, f64) = Coord::from(img.dimensions()).into(); + let (crop_x, crop_y) = prop.crop_position.unwrap(); + let (crop_width, crop_height) = get_4_5(width, height); + let mut img = img.crop( + crop_x as u32, + crop_y as u32, + crop_width as u32, + crop_height as u32, + ); + + let layer = DynamicImage::ImageRgba8(ImageBuffer::from_fn( + crop_width as u32, + crop_height as u32, + |_, _| image::Rgba(prop.rgba), + )); + image::imageops::overlay(&mut img, &layer, 0, 0); + + let size = quote_from_height(crop_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 img, + image::Rgba([255, 255, 255, 255]), + ((crop_width - text_width) / 2.0) as u32, + ((prop.quote_position * crop_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, + ); + } + + let size = tag_from_height(crop_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 img, + image::Rgba([255, 255, 255, 255]), + (crop_width * 0.99 - text_width) as u32, + ((prop.tag_position * crop_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, + ); + } + + image::save_buffer( + export, + img.as_rgb8().unwrap().as_raw(), + crop_width as u32, + crop_height as u32, + image::ColorType::Rgb8, + ) + .unwrap(); + } } #[derive(Serialize, Deserialize, Debug)]