diff --git a/Cargo.lock b/Cargo.lock index 0c6791e..bc4a4d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -812,8 +812,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ "bitflags 2.13.0", - "block2 0.6.2", - "libc", "objc2 0.6.4", ] @@ -1649,7 +1647,6 @@ dependencies = [ "dirs", "iced", "image", - "rfd", ] [[package]] @@ -2722,12 +2719,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "pollster" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" - [[package]] name = "portable-atomic" version = "1.13.1" @@ -3028,33 +3019,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" -[[package]] -name = "rfd" -version = "0.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20dafead71c16a34e1ff357ddefc8afc11e7d51d6d2b9fbd07eaa48e3e540220" -dependencies = [ - "block2 0.6.2", - "dispatch2", - "js-sys", - "libc", - "log", - "objc2 0.6.4", - "objc2-app-kit 0.3.2", - "objc2-core-foundation", - "objc2-foundation 0.3.2", - "percent-encoding", - "pollster", - "raw-window-handle", - "wasm-bindgen", - "wasm-bindgen-futures", - "wayland-backend", - "wayland-client", - "wayland-protocols", - "web-sys", - "windows-sys 0.61.2", -] - [[package]] name = "rgb" version = "0.8.53" diff --git a/Cargo.toml b/Cargo.toml index 668d6f6..5e76228 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,4 +7,3 @@ edition = "2024" iced = { version = "0.14.0", features = [ "image" ] } dirs = "6.0.0" image = "0.25.10" -rfd = "0.17.2" diff --git a/flake.nix b/flake.nix index b86a384..35174c5 100644 --- a/flake.nix +++ b/flake.nix @@ -40,7 +40,7 @@ buildInputs = with pkgs; [ ]; - src = ./.; + src = craneLib.cleanCargoSource ./.; }; LD_LIBRARY_PATH = lib.makeLibraryPath dlDeps; @@ -58,16 +58,6 @@ desktopName = name; icon = name; exec = name; - mimeTypes = [ - "image/png" - "image/jpeg" - "image/gif" - "image/webp" - "image/avif" - "image/tiff" - "image/bmp" - "image/vnd.microsoft.icon" # .ico - ]; }; in craneLib.buildPackage ( @@ -75,16 +65,12 @@ // { inherit cargoArtifacts; - nativeBuildInputs = - commonArgs.nativeBuildInputs - ++ [ - pkgs.autoPatchelfHook - ] - ++ lib.optional pkgs.stdenv.hostPlatform.isLinux pkgs.makeBinaryWrapper; + nativeBuildInputs = commonArgs.nativeBuildInputs ++ [ + pkgs.autoPatchelfHook + ]; buildInputs = commonArgs.buildInputs ++ [ pkgs.libgcc - pkgs.zenity ]; runtimeDependencies = dlDeps; @@ -94,10 +80,6 @@ postFixup = '' mkdir -p "$out/share/applications" ln -s "${desktopItem}"/share/applications/* "$out/share/applications/" - '' - + lib.optionalString pkgs.stdenv.hostPlatform.isLinux /* sh */ '' - wrapProgram $out/bin/${name} --inherit-argv0 \ - --prefix PATH : ${lib.makeBinPath [ pkgs.zenity ]} ''; } ); diff --git a/src/main.rs b/src/main.rs index 663d849..2e6973d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,11 @@ -use std::{env, ffi::OsStr, fs}; +use std::env; use iced::{ - Alignment, Color, Element, Length, Renderer, Subscription, Task, Theme, color, event, + Color, Element, Renderer, Subscription, Task, Theme, color, event, keyboard::{self, Key, key}, theme, widget, window, }; -use image::{EncodableLayout, ImageReader, RgbaImage, codecs::webp::WebPEncoder, imageops}; -use rfd::FileDialog; +use image::{EncodableLayout, ImageReader, Pixel, Rgba, RgbaImage, imageops}; #[derive(Clone, Debug)] enum Message { @@ -17,53 +16,43 @@ enum Message { #[derive(Clone, Debug)] struct State { - image: Option, + image: RgbaImage, image_display: Option, - - error: Option, } impl State { fn new() -> (Self, Task) { + let image = if let Some(path) = env::args().nth(1) { + ImageReader::open(path) + .unwrap() + .decode() + .unwrap() + .into_rgba8() + } else { + let mut image = RgbaImage::new(100, 100); + + imageops::horizontal_gradient( + &mut image, + Rgba::from_slice(&[0, 0, 0, 0]), + Rgba::from_slice(&[255, 255, 255, 255]), + ); + + image + }; + let state = State { image_display: None, - image: env::args().nth(1).and_then(|path| { - Some( - ImageReader::open(path) - .unwrap() - .decode() - .unwrap() - .into_rgba8(), - ) - }), - - error: None, + image, }; let allocate_image = state.allocate_image(); (state, allocate_image) } fn view(&self) -> Element<'_, Message, Theme, Renderer> { - widget::column([ - if let Some(allocation) = self.image_display.as_ref() { - widget::image::viewer(allocation.handle().clone()) - .width(Length::Fill) - .height(Length::Fill) - .into() - } else { - widget::container(widget::text(include_str!("usage.txt"))) - .height(Length::Fill) - .width(Length::Fill) - .align_x(Alignment::Center) - .align_y(Alignment::Center) - .into() - }, - if let Some(error) = self.error.as_ref() { - widget::text(error).style(widget::text::danger).into() - } else { - widget::space().into() - }, - ]) - .into() + if let Some(allocation) = self.image_display.as_ref() { + widget::image(allocation.handle()).expand(true).into() + } else { + widget::space().into() + } } fn update(&mut self, message: Message) -> Task { match message { @@ -85,34 +74,17 @@ impl State { } } - Key::Character("o") => { - self.error = self.open_image(); - return self.allocate_image(); - } + Key::Character("q") => return window::latest().and_then(window::close), Key::Character("r") => { - let Some(image) = self.image.as_ref() else { - eprintln!("no image to rotate"); - return Task::none(); - }; - self.image = Some(imageops::rotate90(image)); + self.image = imageops::rotate90(&self.image); return self.allocate_image(); } Key::Character("i") => { - let Some(image) = self.image.as_mut() else { - eprintln!("no image to invert"); - return Task::none(); - }; - imageops::invert(image); + imageops::invert(&mut self.image); return self.allocate_image(); } - Key::Character("s") => { - self.error = self.save_image(); - } - - Key::Character("q") => return window::latest().and_then(window::close), - // ignore unused keys _ => {} }, @@ -124,78 +96,11 @@ impl State { Task::none() } - fn open_image(&mut self) -> Option { - let Some(path) = FileDialog::new() - .add_filter( - "image", - &[ - "png", "PNG", "jpg", "JPG", "jpeg", "JPEG", "avif", "bmp", "exr", "ff", "gif", - "hdr", "ico", "pnm", "qoi", "tga", "tiff", "webp", - ], - ) - .pick_file() - else { - return Some("no path to open provided".into()); - }; - - let reader = match ImageReader::open(path) { - Ok(r) => r, - Err(e) => return Some(format!("failed to create reader: {e}")), - }; - - let decoded_image = match reader.decode() { - Ok(i) => i, - Err(e) => return Some(format!("failed to decode image: {e}")), - }; - - self.image = Some(decoded_image.into_rgba8()); - - None - } - - fn save_image(&self) -> Option { - let Some(image) = self.image.as_ref() else { - return Some("no image to save".into()); - }; - - let Some(path) = FileDialog::new().save_file() else { - return Some("no path to save provided".into()); - }; - - match fs::File::create(&path) { - Err(e) => return Some(format!("failed to create file: {e}")), - Ok(file) => { - let maybe_encoder = match path.extension().map(OsStr::to_string_lossy).as_deref() { - Some("webp") => Some(WebPEncoder::new_lossless(file)), - - None | Some(_) => None, - }; - - let result = if let Some(encoder) = maybe_encoder { - image.write_with_encoder(encoder) - } else { - image.save(path) - }; - - if let Err(e) = result { - return Some(format!("failed to save image: {e}")); - } - } - }; - - None - } - fn allocate_image(&self) -> Task { - let Some(image) = self.image.as_ref() else { - eprintln!("no image to allocate"); - return Task::none(); - }; - widget::image::allocate(widget::image::Handle::from_rgba( - image.width(), - image.height(), - unsafe { std::mem::transmute::<_, &'static [u8]>(image.as_bytes()) }, + self.image.width(), + self.image.height(), + unsafe { std::mem::transmute::<_, &'static [u8]>(self.image.as_bytes()) }, )) .map(Message::ImageDisplayReady) } diff --git a/src/usage.txt b/src/usage.txt deleted file mode 100644 index 94cf3cf..0000000 --- a/src/usage.txt +++ /dev/null @@ -1,5 +0,0 @@ -"o" to open a new image -"r" to rotate -"i" to invert colors -"s" to save the image -"q" to quit