From 7593f7748fc5ec2b03c5d78b2c14223d7bc50001 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Sun, 21 Apr 2024 22:24:55 +0200 Subject: [PATCH] fix dezoomifying to jpeg and re-enable disabled test that hid the problem --- Cargo.lock | 18 +++---- Cargo.toml | 2 +- src/encoder/canvas.rs | 98 +++++++++++++++++++++++++++---------- src/encoder/mod.rs | 11 ++--- tests/local_dezoomifying.rs | 1 - 5 files changed, 86 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4aed8b6..8be0821 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -586,7 +586,7 @@ checksum = "4f8a51dd197fa6ba5b4dc98a990a43cc13693c23eb0089ebb0fcc1f04152bca6" [[package]] name = "dezoomify-rs" -version = "2.12.2" +version = "2.12.3" dependencies = [ "aes", "base64", @@ -2124,9 +2124,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.32" +version = "0.38.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +checksum = "e3cc72858054fcff6d7dea32df2aeaee6a7c24227366d7ea429aada2f26b16ad" dependencies = [ "bitflags 2.5.0", "errno", @@ -2373,9 +2373,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -2528,18 +2528,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.58" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.58" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 909b91d..29f0774 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dezoomify-rs" -version = "2.12.2" +version = "2.12.3" authors = ["lovasoa"] edition = "2021" license-file = "LICENSE" diff --git a/src/encoder/canvas.rs b/src/encoder/canvas.rs index 8188138..80c9e7a 100644 --- a/src/encoder/canvas.rs +++ b/src/encoder/canvas.rs @@ -1,51 +1,88 @@ -use image::{GenericImage, ImageBuffer, ImageResult, PixelWithColorType, Rgba}; +use image::{ + ExtendedColorType, GenericImageView, ImageBuffer, ImageResult, Pixel, PixelWithColorType, Rgb, + Rgba, +}; use log::debug; use std::io; use std::path::{Path, PathBuf}; -use crate::encoder::{crop_tile, Encoder}; +use crate::encoder::Encoder; use crate::tile::Tile; use crate::Vec2d; use crate::ZoomError; use std::fs::File; use std::io::BufWriter; -type SubPix = u8; -type Pix = Rgba; -type CanvasBuffer = ImageBuffer>; +type CanvasBuffer = ImageBuffer::Subpixel>>; -fn empty_buffer(size: Vec2d) -> CanvasBuffer { - ImageBuffer::from_fn(size.x, size.y, |_, _| Pix::from([0, 0, 0, 0])) -} - -pub struct Canvas { - image: CanvasBuffer, +pub struct Canvas> { + image: CanvasBuffer, destination: PathBuf, image_writer: ImageWriter, } -impl Canvas { - pub fn new( +impl Canvas { + pub fn new_generic(destination: PathBuf, size: Vec2d) -> Result { + Ok(Canvas { + image: ImageBuffer::new(size.x, size.y), + destination, + image_writer: ImageWriter::Generic, + }) + } + + pub fn new_jpeg( destination: PathBuf, size: Vec2d, - image_writer: ImageWriter, - ) -> Result { - Ok(Canvas { - image: empty_buffer(size), + quality: u8, + ) -> Result>, ZoomError> { + Ok(Canvas::> { + image: ImageBuffer::new(size.x, size.y), destination, - image_writer, + image_writer: ImageWriter::Jpeg { quality }, }) } } -impl Encoder for Canvas { +trait FromRgba { + fn from_rgba(rgba: Rgba) -> Self; +} + +impl FromRgba for Rgba { + fn from_rgba(rgba: Rgba) -> Self { + rgba + } +} + +impl FromRgba for Rgb { + fn from_rgba(rgba: Rgba) -> Self { + rgba.to_rgb() + } +} + +impl + PixelWithColorType + Send + FromRgba + 'static> Encoder + for Canvas +{ fn add_tile(&mut self, tile: Tile) -> io::Result<()> { - let sub_tile = crop_tile(&tile, self.size()); - let Vec2d { x, y } = tile.position(); debug!("Copying tile data from {:?}", tile); - self.image - .copy_from(&*sub_tile, x, y) - .map_err(|_err| io::Error::new(io::ErrorKind::InvalidData, "tile too large for image")) + let min_pos = tile.position(); + let canvas_size = self.size(); + if !min_pos.fits_inside(canvas_size) { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "tile too large for image", + )); + } + let max_pos = tile.bottom_right().min(canvas_size); + let size = max_pos - min_pos; + for y in 0..size.y { + let canvas_y = y + min_pos.y; + for x in 0..size.x { + let canvas_x = x + min_pos.x; + let p = tile.image.get_pixel(x, y); + self.image.put_pixel(canvas_x, canvas_y, Pix::from_rgba(p)); + } + } + Ok(()) } fn finalize(&mut self) -> io::Result<()> { @@ -69,13 +106,22 @@ pub enum ImageWriter { } impl ImageWriter { - fn write(&self, image: &CanvasBuffer, destination: &Path) -> ImageResult<()> { + fn write + PixelWithColorType>( + &self, + image: &CanvasBuffer, + destination: &Path, + ) -> ImageResult<()> { match *self { ImageWriter::Jpeg { quality } => { let file = File::create(destination)?; let fout = &mut BufWriter::new(file); let mut encoder = image::codecs::jpeg::JpegEncoder::new_with_quality(fout, quality); - encoder.encode(image, image.width(), image.height(), Pix::COLOR_TYPE)?; + encoder.encode( + image.as_raw(), + image.width(), + image.height(), + ExtendedColorType::Rgb8, + )?; } ImageWriter::Generic => { image.save(destination)?; diff --git a/src/encoder/mod.rs b/src/encoder/mod.rs index 911b932..a407086 100644 --- a/src/encoder/mod.rs +++ b/src/encoder/mod.rs @@ -1,9 +1,8 @@ use std::path::PathBuf; -use image::{DynamicImage, GenericImageView, SubImage}; +use image::{DynamicImage, GenericImageView, Rgb, Rgba, SubImage}; use log::debug; -use crate::encoder::canvas::ImageWriter; use crate::tile::Tile; use crate::{max_size_in_rect, Vec2d, ZoomError}; @@ -47,21 +46,19 @@ fn encoder_for_name( )?)) } else if extension == "jpeg" || extension == "jpg" { debug!("Using the jpeg encoder with a quality of {}", quality); - let image_writer = ImageWriter::Jpeg { quality }; - Ok(Box::new(canvas::Canvas::new( + Ok(Box::new(canvas::Canvas::>::new_jpeg( destination, size, - image_writer, + quality, )?)) } else { debug!( "Using the generic canvas implementation {}", &destination.to_string_lossy() ); - Ok(Box::new(canvas::Canvas::new( + Ok(Box::new(canvas::Canvas::>::new_generic( destination, size, - ImageWriter::Generic, )?)) } } diff --git a/tests/local_dezoomifying.rs b/tests/local_dezoomifying.rs index 3dac851..dbe5b5c 100644 --- a/tests/local_dezoomifying.rs +++ b/tests/local_dezoomifying.rs @@ -9,7 +9,6 @@ use image_hasher::HasherConfig; use dezoomify_rs::{dezoomify, Arguments, ZoomError}; /// Dezoom a file locally -#[ignore] // Ignore this test by default because it's slow in debug mode #[tokio::test(flavor = "multi_thread")] pub async fn custom_size_local_zoomify_tiles() { test_image(