diff --git a/Cargo.lock b/Cargo.lock index e2122e6..3485eff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -626,7 +626,7 @@ dependencies = [ [[package]] name = "post_maker" -version = "0.2.6" +version = "0.3.0" dependencies = [ "clap", "dirs", diff --git a/Cargo.toml b/Cargo.toml index 634e234..5288833 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "post_maker" -version = "0.2.6" +version = "0.3.0" edition = "2021" description = "Post Maker helps you to make post for Instagram and other Social Media apps easily." authors = ["PiyushXCoder "] diff --git a/src/config.rs b/src/config.rs index 84c4369..e03260a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -128,6 +128,7 @@ pub(crate) struct ConfigFile { pub(crate) tag2_position_ratio: f64, pub(crate) image_ratio: (f64, f64), pub(crate) color_layer: [u8; 4], + pub(crate) image_format: String, } impl Default for ConfigFile { @@ -138,7 +139,7 @@ impl Default for ConfigFile { subquote2_font: String::new(), tag_font: String::new(), tag2_font: String::new(), - quote_font_ratio: 230.0, + quote_font_ratio: 250.0, subquote_font_ratio: 230.0, subquote2_font_ratio: 230.0, tag_font_ratio: 150.0, @@ -150,6 +151,7 @@ impl Default for ConfigFile { tag2_position_ratio: 0.95, image_ratio: (4.0, 5.0), color_layer: [20, 22, 25, 197], + image_format: "png".to_owned(), } } } diff --git a/src/config_window.rs b/src/config_window.rs index 908d909..889915d 100644 --- a/src/config_window.rs +++ b/src/config_window.rs @@ -19,11 +19,11 @@ use std::{cell::RefCell, collections::HashMap, rc::Rc}; use fltk::{ app, browser::{Browser, BrowserType}, - button::Button, + button::{Button, RadioRoundButton}, dialog::{self, FileDialogOptions, NativeFileChooser}, enums::{self, Align, Event, Font}, frame::Frame, - group::Flex, + group::{Flex, Scroll}, image::SvgImage, output::Output, prelude::*, @@ -68,6 +68,8 @@ pub(crate) struct ConfigWindow { pub(crate) translucent_layer_rgb: Button, /// opacity value of top translucent layer pub(crate) translucent_layer_alpha: ValueInput, + pub(crate) png_format: RadioRoundButton, + pub(crate) jpeg_format: RadioRoundButton, pub(crate) defaults_btn: Button, pub(crate) save_btn: Button, pub(crate) cancel_btn: Button, @@ -78,12 +80,16 @@ pub(crate) struct ConfigWindow { impl ConfigWindow { pub(crate) fn new() -> Self { let configs = config::get_configs().unwrap_or(HashMap::new()); - let mut win = Window::new(0, 0, 900, 680, "Config").center_screen(); + let mut win = Window::new(0, 0, 900, 600, "Config").center_screen(); win.set_icon(Some( SvgImage::from_data(globals::ICON.to_str().unwrap()).unwrap(), )); - let mut row = Flex::default().with_size(890, 670).with_pos(5, 5).row(); - let mut config_picker_flex = Flex::default().column(); + + // Config picking area + let mut config_picker_flex = Flex::default() + .with_size(200, win.height() - 50) + .with_pos(5, 5) + .column(); // Picker let browse = Browser::default().with_type(BrowserType::Hold); @@ -103,9 +109,30 @@ impl ConfigWindow { config_picker_flex.set_size(&panel_flex, 30); config_picker_flex.set_size(&bottom_padding_btn, 5); config_picker_flex.end(); - row.set_size(&config_picker_flex, 200); - let mut col = Flex::default().column(); + // Bottom Panel + let mut panel_grp = Flex::default() + .with_size(win.width() - 10, 30) + .with_pos(5, win.height() - 40) + .row(); + Frame::default(); + let defaults_btn = Button::default().with_label("Defaults"); + let save_btn = Button::default().with_label("Save"); + let cancel_btn = Button::default().with_label("Cancel"); + panel_grp.set_size(&defaults_btn, 100); + panel_grp.set_size(&save_btn, 100); + panel_grp.set_size(&cancel_btn, 100); + panel_grp.end(); + + // Rest everything + let mut scroll = Scroll::default() + .with_size(win.width() - 210, win.height() - 50) + .with_pos(205, 5); + + let mut col = Flex::default() + .with_size(scroll.width() - 35, 700) + .column() + .with_pos(100, 0); let mut label = Frame::default().with_label("Fonts:"); label.set_label_font(enums::Font::HelveticaBold); @@ -358,7 +385,7 @@ impl ConfigWindow { col.set_size(&hint, 20); let mut translucent_layer_flex = Flex::default().row(); - translucent_layer_flex.set_pad(2); + translucent_layer_flex.set_size(&Frame::default(), 20); translucent_layer_flex.set_size(&Frame::default().with_label("Colour"), 50); let mut translucent_layer_rgb = Button::default(); translucent_layer_rgb.set_frame(enums::FrameType::BorderBox); @@ -368,25 +395,32 @@ impl ConfigWindow { translucent_layer_flex.end(); col.set_size(&translucent_layer_flex, 30); + let mut label = Frame::default().with_label("Export Format:"); + label.set_label_font(enums::Font::HelveticaBold); + col.set_size(&label, 15); + + let mut hint = Frame::default().with_label("Image format to export image"); + hint.set_label_font(Font::CourierItalic); + hint.set_label_size(12); + col.set_size(&hint, 20); + + let mut image_format_flex = Flex::default().row(); + image_format_flex.set_size(&Frame::default(), 20); + let mut png_format = RadioRoundButton::default().with_label("Png"); + png_format.set_value(true); + let jpeg_format = RadioRoundButton::default().with_label("Jpeg"); + image_format_flex.end(); + col.set_size(&image_format_flex, 30); + Frame::default(); - - let mut panel_grp = Flex::default().row(); - Frame::default(); - let defaults_btn = Button::default().with_label("Defaults"); - let save_btn = Button::default().with_label("Save"); - let cancel_btn = Button::default().with_label("Cancel"); - panel_grp.set_size(&defaults_btn, 100); - panel_grp.set_size(&save_btn, 100); - panel_grp.set_size(&cancel_btn, 100); - panel_grp.end(); - - col.set_size(&panel_grp, 30); - col.end(); - row.end(); + + scroll.end(); + scroll.make_resizable(true); + scroll.scroll_to(-1 * col.x() - 5, -1 * col.y() - 5); + win.end(); win.make_modal(true); - win.make_resizable(true); let mut config_window = Self { win, @@ -418,6 +452,8 @@ impl ConfigWindow { image_ratio_height, translucent_layer_rgb, translucent_layer_alpha, + png_format, + jpeg_format, defaults_btn, save_btn, cancel_btn, @@ -1006,6 +1042,30 @@ impl ConfigWindow { true }); + // Png Image format + let browse = self.browse.clone(); + let configs = Rc::clone(&self.configs); + self.png_format.set_callback(move |_| { + if let Some(conf) = configs + .borrow_mut() + .get_mut(&browse.selected_text().unwrap()) + { + conf.image_format = "png".to_owned(); + } + }); + + // Jpeg Image format + let browse = self.browse.clone(); + let configs = Rc::clone(&self.configs); + self.jpeg_format.set_callback(move |_| { + if let Some(conf) = configs + .borrow_mut() + .get_mut(&browse.selected_text().unwrap()) + { + conf.image_format = "jpeg".to_owned(); + } + }); + // Reset to default configuation button let mut quote_font = self.quote_font.clone(); let mut subquote_font = self.subquote_font.clone(); @@ -1026,6 +1086,8 @@ impl ConfigWindow { let mut image_ratio_height = self.image_ratio_height.clone(); let mut layer_rgb = self.translucent_layer_rgb.clone(); let mut layer_alpha = self.translucent_layer_alpha.clone(); + let mut png_format = self.png_format.clone(); + let mut jpeg_format = self.jpeg_format.clone(); let configs = Rc::clone(&self.configs); let browse = self.browse.clone(); self.defaults_btn.set_callback(move |_| { @@ -1050,6 +1112,8 @@ impl ConfigWindow { utils::set_color_btn_rgba(conf.color_layer, &mut layer_rgb); layer_rgb.redraw(); layer_alpha.set_value(conf.color_layer[3] as f64); + png_format.set_value(true); + jpeg_format.set_value(false); configs .borrow_mut() .insert(browse.selected_text().unwrap(), conf); diff --git a/src/utils.rs b/src/utils.rs index 590bbc2..6b3b96e 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -19,7 +19,7 @@ use std::{ }; use fltk::{button::Button, dialog, enums, prelude::*}; -use image::{DynamicImage, GenericImageView, ImageBuffer}; +use image::{DynamicImage, GenericImageView, ImageBuffer, ImageEncoder}; use serde::{Deserialize, Serialize}; use crate::globals; @@ -181,7 +181,7 @@ impl ImageContainer { self.buffer = tmp; } - /// Save image anf properities + /// Save image and properities pub(crate) fn save(&self) { let prop = self.properties.read().unwrap(); @@ -190,9 +190,11 @@ impl ImageContainer { None => return, }; let path_properties = path_original.with_extension("prop"); + let config = globals::CONFIG.read().unwrap(); + let export_format = config.image_format.as_str(); let export = path_original.parent().unwrap().join("export").join( path_original - .with_extension("jpg") + .with_extension(export_format) .file_name() .unwrap() .to_str() @@ -245,12 +247,33 @@ impl ImageContainer { } }; - let mut encoder = image::codecs::jpeg::JpegEncoder::new_with_quality(&mut output, 100); - encoder.set_pixel_density(image::codecs::jpeg::PixelDensity::dpi(300)); + match export_format { + "png" => { + let encoder = image::codecs::png::PngEncoder::new_with_quality( + &mut output, + image::png::CompressionType::Default, + image::png::FilterType::NoFilter, + ); - if let Err(e) = encoder.encode_image(&img) { - dialog::alert_default("Failed to export Image!"); - warn!("Failed to export Image!\n{:?}", e); + let (w, h) = img.dimensions(); + if let Err(e) = + encoder.write_image(&img.into_rgba8(), w, h, image::ColorType::Rgba8) + { + dialog::alert_default("Failed to export Image!"); + warn!("Failed to export Image!\n{:?}", e); + } + } + "jpeg" => { + let mut encoder = + image::codecs::jpeg::JpegEncoder::new_with_quality(&mut output, 100); + encoder.set_pixel_density(image::codecs::jpeg::PixelDensity::dpi(300)); + + if let Err(e) = encoder.encode_image(&img) { + dialog::alert_default("Failed to export Image!"); + warn!("Failed to export Image!\n{:?}", e); + } + } + _ => (), } }