From 101adc7a89ba0750c1955b73e5074c7e819fb95f Mon Sep 17 00:00:00 2001 From: electria Date: Thu, 30 Jul 2026 23:40:14 -0700 Subject: [PATCH] refactor: split kra (krita) logic into image hook may further split this into its own crate; seems quite useful! (albeit somewhat short and easy) since there aren't any runtime dependancies, it could even go into image-extras maybe --- src/kra.rs | 41 +++++++++++++++ src/main.rs | 2 + src/utils.rs | 143 +++++++++++++-------------------------------------- 3 files changed, 78 insertions(+), 108 deletions(-) create mode 100644 src/kra.rs diff --git a/src/kra.rs b/src/kra.rs new file mode 100644 index 0000000..5e3d626 --- /dev/null +++ b/src/kra.rs @@ -0,0 +1,41 @@ +use std::{ + ffi::OsString, + io::{self, Read}, +}; + +use image::{ + ImageDecoder, ImageError, ImageReader, ImageResult, + error::DecodingError, + hooks::{self, GenericReader}, +}; +use zip::ZipArchive; + +pub fn register() -> bool { + hooks::register_decoding_hook(OsString::from("kra"), Box::new(hook)) +} + +fn hook<'a>(reader: GenericReader<'a>) -> ImageResult> { + let mut zip = ZipArchive::new(reader).map_err(|e| { + ImageError::Decoding(DecodingError::new( + image::error::ImageFormatHint::PathExtension("kra".into()), + e, + )) + })?; + + let mut reader = zip.by_name("mergedimage.png").map_err(|e| { + ImageError::Decoding(DecodingError::new( + image::error::ImageFormatHint::PathExtension("kra".into()), + e, + )) + })?; + + // reading it all and wrapping it with a Cursor + // is the only way I know to give it Seek + // (which is required by ImageReader) + let mut buf = Vec::new(); + reader.read_to_end(&mut buf)?; + + let image_reader = ImageReader::with_format(io::Cursor::new(buf), image::ImageFormat::Png); + + Ok(Box::new(image_reader.into_decoder()?)) +} diff --git a/src/main.rs b/src/main.rs index 9d88b1d..3e65e8d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ use iced::{ }; use image::{EncodableLayout, RgbaImage, imageops}; +mod kra; mod utils; #[derive(Clone, Debug)] @@ -209,6 +210,7 @@ impl State { fn main() -> Result<(), iced::Error> { jxl_oxide::integration::register_image_decoding_hook(); + kra::register(); iced::application(State::new, State::update, State::view) .subscription(State::subscription) diff --git a/src/utils.rs b/src/utils.rs index 626ceb8..3d3ea0b 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,124 +1,51 @@ -use std::{ - fs, - io::{self, Read}, - path::{Path, PathBuf}, -}; +use std::path::{Path, PathBuf}; use iced::widget; use image::{DynamicImage, ImageDecoder, ImageReader, RgbaImage}; use rfd::FileDialog; -use zip::ZipArchive; pub fn load_image(path: impl AsRef) -> Result { let path = path.as_ref(); - let decoded_image = if path.extension().is_some_and(|ext| ext == "kra") { - let reader = match fs::File::open(path).map(io::BufReader::new) { - Ok(r) => r, - Err(e) => { - return Err(format!( - "failed to create reader for '{}': {e}", - path.display() - )); - } - }; - - let mut zip = match ZipArchive::new(reader) { - Ok(z) => z, - Err(e) => { - return Err(format!( - "failed to open zip archive '{}': {e}", - path.display() - )); - } - }; - - let mut reader = match zip.by_name("mergedimage.png") { - Ok(r) => r, - Err(e) => { - return Err(format!( - "failed to open file in zip archive '{}': {e}", - path.display() - )); - } - }; - - // reading it all and wrapping it with a Cursor - // is the only way I know to give it Seek - // (which is required by ImageReader) - let mut buf = Vec::new(); - if let Err(e) = reader.read_to_end(&mut buf) { + let image_reader = match ImageReader::open(path) { + Ok(r) => r, + Err(e) => { return Err(format!( - "error when reading file in zip archive '{}': {e}", + "failed to create image reader for '{}': {e}", path.display() )); - }; - - let image_reader = ImageReader::with_format(io::Cursor::new(buf), image::ImageFormat::Png); - - let decoder = match image_reader.into_decoder() { - Ok(d) => d, - Err(e) => { - return Err(format!( - "failed to create decoder for '{}': {e}", - path.display() - )); - } - }; - - let decoded_image = match DynamicImage::from_decoder(decoder) { - Ok(i) => i, - Err(e) => { - return Err(format!("failed to decode image '{}': {e}", path.display())); - } - }; - - decoded_image - } else { - let image_reader = match ImageReader::open(path) { - Ok(r) => r, - Err(e) => { - return Err(format!( - "failed to create image reader for '{}': {e}", - path.display() - )); - } - }; - - let mut decoder = match image_reader.into_decoder() { - Ok(d) => d, - Err(e) => { - return Err(format!( - "failed to create decoder for '{}': {e}", - path.display() - )); - } - }; - - // we don't need to check this for the kra branch - // since PNGs don't have metadata oreintation - let oreintation = match decoder.orientation() { - Ok(o) => o, - Err(e) => { - return Err(format!( - "failed to get oreintation of '{}': {e}", - path.display() - )); - } - }; - - let mut decoded_image = match DynamicImage::from_decoder(decoder) { - Ok(i) => i, - Err(e) => { - return Err(format!("failed to decode image '{}': {e}", path.display())); - } - }; - - decoded_image.apply_orientation(oreintation); - - decoded_image + } }; + let mut decoder = match image_reader.into_decoder() { + Ok(d) => d, + Err(e) => { + return Err(format!( + "failed to create decoder for '{}': {e}", + path.display() + )); + } + }; + + let oreintation = match decoder.orientation() { + Ok(o) => o, + Err(e) => { + return Err(format!( + "failed to get oreintation of '{}': {e}", + path.display() + )); + } + }; + + let mut decoded_image = match DynamicImage::from_decoder(decoder) { + Ok(i) => i, + Err(e) => { + return Err(format!("failed to decode image '{}': {e}", path.display())); + } + }; + + decoded_image.apply_orientation(oreintation); + Ok(decoded_image.into_rgba8()) } pub fn pick_image() -> Result {