use std::{ borrow::Cow, ffi::OsStr, path::{Path, PathBuf}, }; use iced::{clipboard, widget}; use image::{DynamicImage, EncodableLayout, ImageDecoder, ImageReader, ImageResult, RgbaImage}; use rfd::AsyncFileDialog; pub fn load_image(path: impl AsRef) -> Result { load_image_impl(path).map_err(|e| e.to_string()) } fn load_image_impl(path: impl AsRef) -> ImageResult { let mut decoder = ImageReader::open(&path)? .with_guessed_format()? .into_decoder()?; let oreintation = decoder.orientation()?; let mut decoded_image = DynamicImage::from_decoder(decoder)?; // the condition is a workaround to not rotate JXL images twice; // since they are already rotated by the decoder // (while jpegs for instance aren't) if !path .as_ref() .extension() .map(OsStr::to_string_lossy) .is_some_and(|s| s == "jxl") { decoded_image.apply_orientation(oreintation); } Ok(decoded_image.into_rgba8()) } pub async fn pick_image() -> Result> { let Some(filehandle) = AsyncFileDialog::new() .add_filter( "image", &[ "png", "PNG", "jpg", "JPG", "jpeg", "JPEG", "avif", "jxl", "bmp", "exr", "ff", "gif", "hdr", "ico", "pnm", "qoi", "tga", "tiff", "webp", "kra", "svg", "svgz", ], ) .pick_file() .await else { return Err("no path to open provided".into()); }; Ok(filehandle.path().to_owned()) } pub async fn pick_save_path() -> Result> { let Some(filehandle) = AsyncFileDialog::new().save_file().await else { return Err("no path to save provided".into()); }; Ok(filehandle.path().to_owned()) } pub const fn string_from_filter_type(f: widget::image::FilterMethod) -> &'static str { match f { widget::image::FilterMethod::Linear => "bilinear", widget::image::FilterMethod::Nearest => "nearest neighbor", } } pub fn clipboard_image_from_rgbaimage(image: &RgbaImage) -> clipboard::Image { clipboard::Image { size: (image.width(), image.height()).into(), // SAFETY: this is also probably a race condition, // where if the user switches the image before it's done, // then something will die. // // however, especially with larger images, // copying them is just too expensive. rgba: unsafe { std::mem::transmute::<&[u8], &'static [u8]>(image.as_bytes()) }.into(), } } pub fn rgbaimage_from_clipboard_image(image: clipboard::Image) -> RgbaImage { RgbaImage::from_raw(image.size.width, image.size.height, image.rgba.into()).unwrap() }