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
This commit is contained in:
parent
c96a1cbdd1
commit
101adc7a89
3 changed files with 75 additions and 105 deletions
41
src/kra.rs
Normal file
41
src/kra.rs
Normal file
|
|
@ -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<Box<dyn ImageDecoder + 'a>> {
|
||||||
|
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()?))
|
||||||
|
}
|
||||||
|
|
@ -7,6 +7,7 @@ use iced::{
|
||||||
};
|
};
|
||||||
use image::{EncodableLayout, RgbaImage, imageops};
|
use image::{EncodableLayout, RgbaImage, imageops};
|
||||||
|
|
||||||
|
mod kra;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
@ -209,6 +210,7 @@ impl State {
|
||||||
|
|
||||||
fn main() -> Result<(), iced::Error> {
|
fn main() -> Result<(), iced::Error> {
|
||||||
jxl_oxide::integration::register_image_decoding_hook();
|
jxl_oxide::integration::register_image_decoding_hook();
|
||||||
|
kra::register();
|
||||||
|
|
||||||
iced::application(State::new, State::update, State::view)
|
iced::application(State::new, State::update, State::view)
|
||||||
.subscription(State::subscription)
|
.subscription(State::subscription)
|
||||||
|
|
|
||||||
143
src/utils.rs
143
src/utils.rs
|
|
@ -1,124 +1,51 @@
|
||||||
use std::{
|
use std::path::{Path, PathBuf};
|
||||||
fs,
|
|
||||||
io::{self, Read},
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
};
|
|
||||||
|
|
||||||
use iced::widget;
|
use iced::widget;
|
||||||
use image::{DynamicImage, ImageDecoder, ImageReader, RgbaImage};
|
use image::{DynamicImage, ImageDecoder, ImageReader, RgbaImage};
|
||||||
use rfd::FileDialog;
|
use rfd::FileDialog;
|
||||||
use zip::ZipArchive;
|
|
||||||
|
|
||||||
pub fn load_image(path: impl AsRef<Path>) -> Result<RgbaImage, String> {
|
pub fn load_image(path: impl AsRef<Path>) -> Result<RgbaImage, String> {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
|
|
||||||
let decoded_image = if path.extension().is_some_and(|ext| ext == "kra") {
|
let image_reader = match ImageReader::open(path) {
|
||||||
let reader = match fs::File::open(path).map(io::BufReader::new) {
|
Ok(r) => r,
|
||||||
Ok(r) => r,
|
Err(e) => {
|
||||||
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) {
|
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"error when reading file in zip archive '{}': {e}",
|
"failed to create image reader for '{}': {e}",
|
||||||
path.display()
|
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())
|
Ok(decoded_image.into_rgba8())
|
||||||
}
|
}
|
||||||
pub fn pick_image() -> Result<PathBuf, String> {
|
pub fn pick_image() -> Result<PathBuf, String> {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue