Wrap whole CustomImage in Arc

This commit is contained in:
Laurenz Stampfl 2025-03-17 23:47:55 +01:00
parent 8f0cb71db8
commit a330866ad2
2 changed files with 23 additions and 24 deletions

View File

@ -9,7 +9,6 @@ use ecow::{eco_format, EcoString};
use image::codecs::gif::GifDecoder; use image::codecs::gif::GifDecoder;
use image::codecs::jpeg::JpegDecoder; use image::codecs::jpeg::JpegDecoder;
use image::codecs::png::PngDecoder; use image::codecs::png::PngDecoder;
use image::imageops::rotate180;
use image::{ use image::{
guess_format, DynamicImage, ImageBuffer, ImageDecoder, ImageResult, Limits, Pixel, guess_format, DynamicImage, ImageBuffer, ImageDecoder, ImageResult, Limits, Pixel,
}; };

View File

@ -61,26 +61,28 @@ pub(crate) fn handle_image(
Ok(()) Ok(())
} }
/// A wrapper around RasterImage so that we can implement `CustomImage`. struct Repr {
#[derive(Clone)]
struct PdfImage {
/// The original, underlying raster image. /// The original, underlying raster image.
raster: RasterImage, raster: RasterImage,
/// The alpha channel of the raster image, if existing. /// The alpha channel of the raster image, if existing.
alpha_channel: OnceLock<Option<Arc<Vec<u8>>>>, alpha_channel: OnceLock<Option<Vec<u8>>>,
/// A (potentially) converted version of the dynamic image stored `raster` that is /// 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 /// guaranteed to either be in luma8 or rgb8, and thus can be used for the
/// `color_channel` method of `CustomImage`. /// `color_channel` method of `CustomImage`.
actual_dynamic: OnceLock<Arc<DynamicImage>>, actual_dynamic: OnceLock<Arc<DynamicImage>>,
} }
/// A wrapper around RasterImage so that we can implement `CustomImage`.
#[derive(Clone)]
struct PdfImage(Arc<Repr>);
impl PdfImage { impl PdfImage {
pub fn new(raster: RasterImage) -> Self { pub fn new(raster: RasterImage) -> Self {
Self { Self(Arc::new(Repr {
raster, raster,
alpha_channel: OnceLock::new(), alpha_channel: OnceLock::new(),
actual_dynamic: OnceLock::new(), actual_dynamic: OnceLock::new(),
} }))
} }
} }
@ -88,15 +90,15 @@ impl Hash for PdfImage {
fn hash<H: Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
// `alpha_channel` and `actual_dynamic` are generated from the underlying `RasterImage`, // `alpha_channel` and `actual_dynamic` are generated from the underlying `RasterImage`,
// so this is enough. Since `raster` is prehashed, this is also very cheap. // 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 { impl CustomImage for PdfImage {
fn color_channel(&self) -> &[u8] { fn color_channel(&self) -> &[u8] {
self.actual_dynamic self.0.actual_dynamic
.get_or_init(|| { .get_or_init(|| {
let dynamic = self.raster.dynamic(); let dynamic = self.0.raster.dynamic();
let channel_count = dynamic.color().channel_count(); let channel_count = dynamic.color().channel_count();
match (dynamic.as_ref(), channel_count) { match (dynamic.as_ref(), channel_count) {
@ -113,20 +115,18 @@ impl CustomImage for PdfImage {
} }
fn alpha_channel(&self) -> Option<&[u8]> { fn alpha_channel(&self) -> Option<&[u8]> {
self.alpha_channel self.0.alpha_channel
.get_or_init(|| { .get_or_init(|| {
self.raster.dynamic().color().has_alpha().then(|| { self.0.raster.dynamic().color().has_alpha().then(|| {
Arc::new( self.0.raster
self.raster .dynamic()
.dynamic() .pixels()
.pixels() .map(|(_, _, Rgba([_, _, _, a]))| a)
.map(|(_, _, Rgba([_, _, _, a]))| a) .collect()
.collect(),
)
}) })
}) })
.as_ref() .as_ref()
.map(|v| &***v) .map(|v| &**v)
} }
fn bits_per_component(&self) -> BitsPerComponent { fn bits_per_component(&self) -> BitsPerComponent {
@ -134,18 +134,18 @@ impl CustomImage for PdfImage {
} }
fn size(&self) -> (u32, u32) { 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]> { fn icc_profile(&self) -> Option<&[u8]> {
if matches!( if matches!(
self.raster.dynamic().as_ref(), self.0.raster.dynamic().as_ref(),
DynamicImage::ImageLuma8(_) DynamicImage::ImageLuma8(_)
| DynamicImage::ImageLumaA8(_) | DynamicImage::ImageLumaA8(_)
| DynamicImage::ImageRgb8(_) | DynamicImage::ImageRgb8(_)
| DynamicImage::ImageRgba8(_) | DynamicImage::ImageRgba8(_)
) { ) {
self.raster.icc().map(|b| b.as_bytes()) self.0.raster.icc().map(|b| b.as_bytes())
} else { } else {
// In all other cases, the dynamic will be converted into RGB8 or LUMA8, so the ICC // 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. // profile may become invalid, and thus we don't include it.
@ -155,7 +155,7 @@ impl CustomImage for PdfImage {
fn color_space(&self) -> ImageColorspace { fn color_space(&self) -> ImageColorspace {
// Remember that we convert all images to either RGB or luma. // 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 ImageColorspace::Rgb
} else { } else {
ImageColorspace::Luma ImageColorspace::Luma