diff --git a/crates/typst-library/src/visualize/image/raster.rs b/crates/typst-library/src/visualize/image/raster.rs index 79efae00d..31dbf2d23 100644 --- a/crates/typst-library/src/visualize/image/raster.rs +++ b/crates/typst-library/src/visualize/image/raster.rs @@ -9,7 +9,6 @@ use ecow::{eco_format, EcoString}; use image::codecs::gif::GifDecoder; use image::codecs::jpeg::JpegDecoder; use image::codecs::png::PngDecoder; -use image::imageops::rotate180; use image::{ guess_format, DynamicImage, ImageBuffer, ImageDecoder, ImageResult, Limits, Pixel, }; diff --git a/crates/typst-pdf/src/image.rs b/crates/typst-pdf/src/image.rs index 0dbcc5385..055781f16 100644 --- a/crates/typst-pdf/src/image.rs +++ b/crates/typst-pdf/src/image.rs @@ -61,26 +61,28 @@ pub(crate) fn handle_image( Ok(()) } -/// A wrapper around RasterImage so that we can implement `CustomImage`. -#[derive(Clone)] -struct PdfImage { +struct Repr { /// The original, underlying raster image. raster: RasterImage, /// The alpha channel of the raster image, if existing. - alpha_channel: OnceLock>>>, + alpha_channel: OnceLock>>, /// A (potentially) converted version of the dynamic image stored `raster` that is /// guaranteed to either be in luma8 or rgb8, and thus can be used for the /// `color_channel` method of `CustomImage`. actual_dynamic: OnceLock>, } +/// A wrapper around RasterImage so that we can implement `CustomImage`. +#[derive(Clone)] +struct PdfImage(Arc); + impl PdfImage { pub fn new(raster: RasterImage) -> Self { - Self { + Self(Arc::new(Repr { raster, alpha_channel: OnceLock::new(), actual_dynamic: OnceLock::new(), - } + })) } } @@ -88,15 +90,15 @@ impl Hash for PdfImage { fn hash(&self, state: &mut H) { // `alpha_channel` and `actual_dynamic` are generated from the underlying `RasterImage`, // so this is enough. Since `raster` is prehashed, this is also very cheap. - self.raster.hash(state); + self.0.raster.hash(state); } } impl CustomImage for PdfImage { fn color_channel(&self) -> &[u8] { - self.actual_dynamic + self.0.actual_dynamic .get_or_init(|| { - let dynamic = self.raster.dynamic(); + let dynamic = self.0.raster.dynamic(); let channel_count = dynamic.color().channel_count(); match (dynamic.as_ref(), channel_count) { @@ -113,20 +115,18 @@ impl CustomImage for PdfImage { } fn alpha_channel(&self) -> Option<&[u8]> { - self.alpha_channel + self.0.alpha_channel .get_or_init(|| { - self.raster.dynamic().color().has_alpha().then(|| { - Arc::new( - self.raster - .dynamic() - .pixels() - .map(|(_, _, Rgba([_, _, _, a]))| a) - .collect(), - ) + self.0.raster.dynamic().color().has_alpha().then(|| { + self.0.raster + .dynamic() + .pixels() + .map(|(_, _, Rgba([_, _, _, a]))| a) + .collect() }) }) .as_ref() - .map(|v| &***v) + .map(|v| &**v) } fn bits_per_component(&self) -> BitsPerComponent { @@ -134,18 +134,18 @@ impl CustomImage for PdfImage { } fn size(&self) -> (u32, u32) { - (self.raster.width(), self.raster.height()) + (self.0.raster.width(), self.0.raster.height()) } fn icc_profile(&self) -> Option<&[u8]> { if matches!( - self.raster.dynamic().as_ref(), + self.0.raster.dynamic().as_ref(), DynamicImage::ImageLuma8(_) | DynamicImage::ImageLumaA8(_) | DynamicImage::ImageRgb8(_) | DynamicImage::ImageRgba8(_) ) { - self.raster.icc().map(|b| b.as_bytes()) + self.0.raster.icc().map(|b| b.as_bytes()) } else { // In all other cases, the dynamic will be converted into RGB8 or LUMA8, so the ICC // profile may become invalid, and thus we don't include it. @@ -155,7 +155,7 @@ impl CustomImage for PdfImage { fn color_space(&self) -> ImageColorspace { // Remember that we convert all images to either RGB or luma. - if self.raster.dynamic().color().has_color() { + if self.0.raster.dynamic().color().has_color() { ImageColorspace::Rgb } else { ImageColorspace::Luma