Skip to content

Commit

Permalink
Add tests for custom images
Browse files Browse the repository at this point in the history
  • Loading branch information
LaurenzV committed Dec 1, 2024
1 parent 3e7aa25 commit ac1fe73
Show file tree
Hide file tree
Showing 9 changed files with 325 additions and 8 deletions.
44 changes: 39 additions & 5 deletions crates/krilla/src/object/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,6 @@ impl Image {
/// Panics if the dimensions of the image and the length of the
/// data doesn't match.
pub fn from_custom<T: CustomImage>(image: T) -> Option<Image> {
// TODO: Add tests
let hash = image.sip_hash();
let metadata = ImageMetadata {
size: image.size(),
Expand Down Expand Up @@ -525,8 +524,6 @@ fn gif_metadata(data: &[u8]) -> Option<ImageMetadata> {

Some(ImageMetadata {
size: (size.width as u32, size.height as u32),
// We always set this as the output color space when decoding a GIF.
// TODO: VAlidate this.
color_space: ImageColorspace::Rgb,
icc: None,
})
Expand Down Expand Up @@ -610,6 +607,7 @@ fn handle_u8_image(data: &[u8], cs: ColorSpace) -> (Vec<u8>, Option<Vec<u8>>, Bi
.collect::<Vec<_>>();
deflate_encode(&data)
}
// PNG/WEBP/GIF only support those three, so should be enough?
_ => unimplemented!(),
};

Expand Down Expand Up @@ -677,7 +675,7 @@ fn handle_u16_image(data: &[u16], cs: ColorSpace) -> (Vec<u8>, Option<Vec<u8>>,
.collect::<Vec<_>>();
deflate_encode(&data)
}
// TODO: Remove?
// PNG/WEBP/GIF only support those three, so should be enough?
_ => unimplemented!(),
};

Expand All @@ -695,7 +693,7 @@ mod tests {
use crate::image::Image;
use crate::serialize::SerializerContext;
use crate::surface::Surface;
use crate::tests::{load_gif_image, load_jpg_image, load_png_image, load_webp_image};
use crate::tests::{load_custom_image, load_custom_image_with_icc, load_gif_image, load_jpg_image, load_png_image, load_webp_image};
use crate::Document;
use krilla_macros::{snapshot, visreg};
use tiny_skia_path::Size;
Expand All @@ -705,6 +703,11 @@ mod tests {
sc.register_image(load_png_image("luma8.png"));
}

#[snapshot]
fn image_custom_luma8_png(sc: &mut SerializerContext) {
sc.register_image(load_custom_image("luma8.png"));
}

#[snapshot]
fn image_luma16_png(sc: &mut SerializerContext) {
sc.register_image(load_png_image("luma16.png"));
Expand All @@ -715,6 +718,17 @@ mod tests {
sc.register_image(load_png_image("rgb8.png"));
}

#[snapshot]
fn image_custom_rgb8_png(sc: &mut SerializerContext) {
sc.register_image(load_custom_image("rgb8.png"));
}

// ICC profile should be ignored.
#[snapshot]
fn image_custom_rgb8_png_invalid_icc(sc: &mut SerializerContext) {
sc.register_image(load_custom_image_with_icc("rgb8.png", std::fs::read(crate::tests::ASSETS_PATH.join("icc/eciCMYK_v2.icc")).unwrap()));
}

#[snapshot]
fn image_rgb16_png(sc: &mut SerializerContext) {
sc.register_image(load_png_image("rgb16.png"));
Expand All @@ -725,6 +739,11 @@ mod tests {
sc.register_image(load_png_image("rgba8.png"));
}

#[snapshot]
fn image_custom_rgba8_png(sc: &mut SerializerContext) {
sc.register_image(load_custom_image("rgba8.png"));
}

#[snapshot]
fn image_rgba16_png(sc: &mut SerializerContext) {
sc.register_image(load_png_image("rgba16.png"));
Expand Down Expand Up @@ -771,6 +790,11 @@ mod tests {
image_visreg_impl(surface, "luma8.png", load_png_image);
}

#[visreg]
fn image_luma8_custom_png(surface: &mut Surface) {
image_visreg_impl(surface, "luma8.png", load_custom_image);
}

#[visreg(all)]
fn image_luma16_png(surface: &mut Surface) {
image_visreg_impl(surface, "luma16.png", load_png_image);
Expand All @@ -781,6 +805,11 @@ mod tests {
image_visreg_impl(surface, "rgb8.png", load_png_image);
}

#[visreg]
fn image_rgb8_custom_png(surface: &mut Surface) {
image_visreg_impl(surface, "rgb8.png", load_custom_image);
}

#[visreg(all)]
fn image_rgb16_png(surface: &mut Surface) {
image_visreg_impl(surface, "rgb16.png", load_png_image);
Expand All @@ -796,6 +825,11 @@ mod tests {
image_visreg_impl(surface, "rgba16.png", load_png_image);
}

#[visreg]
fn image_rgba16_custom_png(surface: &mut Surface) {
image_visreg_impl(surface, "rgba16.png", load_custom_image);
}

#[visreg(pdfium, mupdf, pdfbox, pdfjs, poppler, quartz)]
fn image_luma8_jpg(surface: &mut Surface) {
image_visreg_impl(surface, "luma8.jpg", load_jpg_image);
Expand Down
91 changes: 88 additions & 3 deletions crates/krilla/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::annotation::{Annotation, LinkAnnotation, Target};
use crate::color::{cmyk, luma, rgb, ICCProfile};
use crate::document::{Document, PageSettings};
use crate::font::{Font, GlyphUnits};
use crate::image::Image;
use crate::image::{BitsPerComponent, CustomImage, Image, ImageColorspace};
use crate::mask::{Mask, MaskType};
use crate::paint::{Stop, Stops};
use crate::path::{Fill, Stroke};
Expand All @@ -14,7 +14,7 @@ use crate::validation::Validator;
use crate::version::PdfVersion;
use crate::{SerializeSettings, SvgSettings};
use difference::{Changeset, Difference};
use image::{load_from_memory, Rgba, RgbaImage};
use image::{load_from_memory, DynamicImage, GenericImageView, Rgba, RgbaImage};
use once_cell::sync::Lazy;
use oxipng::{InFile, OutFile};
use sitro::{
Expand All @@ -26,8 +26,9 @@ use skrifa::raw::TableProvider;
use skrifa::{GlyphId, MetadataProvider};
use std::cmp::max;
use std::env;
use std::hash::{Hash, Hasher};
use std::path::PathBuf;
use std::sync::{Arc, LazyLock};
use std::sync::{Arc, LazyLock, OnceLock};
use tiny_skia_path::{NormalizedF32, Path, PathBuilder, Point, Rect, Transform};

#[allow(dead_code)]
Expand Down Expand Up @@ -104,6 +105,80 @@ lazy_font!(TWITTER_COLOR_EMOJI, FONT_PATH.join("TwitterColorEmoji.subset.ttf"));
#[rustfmt::skip]
lazy_font!(SVG_EXTRA, FONT_PATH.join("SVG_extra.ttf"));

#[derive(Clone)]
struct TestImage {
original_dynamic: Arc<DynamicImage>,
alpha_channel: OnceLock<Option<Arc<Vec<u8>>>>,
actual_dynamic: OnceLock<Arc<DynamicImage>>,
icc: Option<Vec<u8>>
}

impl TestImage {
pub fn new(data: Vec<u8>, icc: Option<Vec<u8>>) -> Self {
let image = image::load_from_memory(&data).unwrap();
Self {
original_dynamic: Arc::new(image),
alpha_channel: OnceLock::new(),
actual_dynamic: OnceLock::new(),
icc,
}
}
}

impl Hash for TestImage {
fn hash<H: Hasher>(&self, state: &mut H) {
self.original_dynamic.as_bytes().hash(state);
}
}


impl CustomImage for TestImage {
fn color_channel(&self) -> &[u8] {
self.actual_dynamic.get_or_init(|| {
let dynamic = self.original_dynamic.clone();
let channel_count = dynamic.color().channel_count();

match (dynamic.as_ref(), channel_count) {
(DynamicImage::ImageLuma8(_), _) => dynamic.clone(),
(DynamicImage::ImageRgb8(_), _) => dynamic.clone(),
(_, 1 | 2) => Arc::new(DynamicImage::ImageLuma8(dynamic.to_luma8())),
_ => Arc::new(DynamicImage::ImageRgb8(dynamic.to_rgb8())),
}
}).as_bytes()
}

fn alpha_channel(&self) -> Option<&[u8]> {
self.alpha_channel.get_or_init(||
self.original_dynamic.color().has_alpha()
.then(|| Arc::new(self.original_dynamic
.pixels()
.map(|(_, _, Rgba([_, _, _, a]))| a)
.collect())
)
).as_ref().map(|v| &***v)
}

fn bits_per_component(&self) -> BitsPerComponent {
BitsPerComponent::Eight
}

fn size(&self) -> (u32, u32) {
self.original_dynamic.dimensions()
}

fn icc_profile(&self) -> Option<&[u8]> {
self.icc.as_deref()
}

fn color_space(&self) -> ImageColorspace {
if self.original_dynamic.color().has_color() {
ImageColorspace::Rgb
} else {
ImageColorspace::Luma
}
}
}

pub fn green_fill(opacity: f32) -> Fill {
Fill {
paint: rgb::Color::new(0, 255, 0).into(),
Expand Down Expand Up @@ -222,6 +297,16 @@ pub fn load_webp_image(name: &str) -> Image {
.unwrap()
}

pub fn load_custom_image(name: &str) -> Image {
Image::from_custom(TestImage::new(std::fs::read(ASSETS_PATH.join("images").join(name)).unwrap(), None))
.unwrap()
}

pub fn load_custom_image_with_icc(name: &str, icc: Vec<u8>) -> Image {
Image::from_custom(TestImage::new(std::fs::read(ASSETS_PATH.join("images").join(name)).unwrap(), Some(icc)))
.unwrap()
}

fn write_snapshot_to_store(name: &str, content: &[u8]) {
let mut path = STORE_PATH.clone().join("snapshots");
let _ = std::fs::create_dir_all(&path);
Expand Down
28 changes: 28 additions & 0 deletions refs/snapshots/image_custom_luma8_png.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
%PDF-1.7
%AAAA

1 0 obj
<<
/Length 689
/Type /XObject
/Subtype /Image
/Filter [/ASCIIHexDecode /FlateDecode]
/Width 200
/Height 200
/ColorSpace /DeviceGray
/BitsPerComponent 8
>>
stream
789CEDCFC74A02000000D0FEB3BD682F69D19E54487B21454551D1A22824A20C11B161
8224E5A18B1E853EA24E7D827478EF0F5E4909000000F01F7DFF29140AF95C3E9FCBE5
BF7E7D643FB3EF994C3A9D7E4BA55E93C997E744E2291E8FC5A2D1C7C843E4FEEEEEF6
361C0EDFDC5C5F5D5E5E5C9C9F9E9D9E1C1F1D1D1EEC1FECEFEDEE6C6F6D8542A1CDCD
8DF5B5D595E5A5A5C585F9603038373B33333D353539313E363A323C34383830D0DFD7
DBD31D0804BA3A3BDADB5A5B9A9B9A1A1BEAEBEA6A6BAAAB2A2BCACB4A3D3C3C3C3C3C
3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C
3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C
3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C
3C3C3C3C3C3C3C3C3C3C3C3C8AF7000000008AEB07811557E1
endstream
endobj

47 changes: 47 additions & 0 deletions refs/snapshots/image_custom_rgb8_png.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
%PDF-1.7
%AAAA

1 0 obj
<<
/Length 1988
/Type /XObject
/Subtype /Image
/Filter [/ASCIIHexDecode /FlateDecode]
/Width 200
/Height 200
/ColorSpace /DeviceRGB
/BitsPerComponent 8
>>
stream
789CEDD2696B170400C7F15F1145417440041D50540441104504121112518420445154
205450411111A288E231759EF33EA7F3984EA6735E9B8AF31A733AF1D6794FE7356FD9
3C1FFEDDDE808F04513E5FBE2FE1934892244992244992244992244992244992F47057
28DCEBF6423A0AB95EC88D1BB97833976F757DF676CEDD4EDB9DB4B6E7547B8E77A4A5
23872FE7E895345FCDC16BD97721072E66CFA5EC6DCBAEB66C3F9FAD67D274368DE7D2
702A0DA7537F269B5AB3BE35752DA93B917527537B3CAB5B5273342B8F65C5E1541FC9
D243A96ACEE2E6541ECCA203A9D897F2FD99B72773F7A66C77CA76A5747766ECCCF49D
99B62393B7677253263665FCB6946CCB98AD19D998518D19B125C50D19569FA2CD1952
9F419B337063066C48FF8DE9B7217DD7A74F5D7AAFCBFF6BF3DF9AFCDB796DFEA9CDDF
B5F9AB267FD6E48F55F97D657E5B915F97A7D7B2F4AACE2FD5F979697EAACA8F55F961
49BEAFCC7795F9B6323D17A567457A2C4C8F8A7CB3205F97E7ABF27C393F5FCC4BF7B9
E93E279F97E5B3B27C3A3BDD66A55B693E29CDC733BBFE687A3E9C960FA6E6FDCEA7E4
BD495DBF3B29EF4CCCDB13F2D6F8BC392E6F94E4F592BC3636AF8EC92BA3F372E7A3F2
D2C8BC38222F14E7F9E23C373CCF0ECB3343F374519E2ACA9343F2C4E03C3E288F0DCC
838A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8
E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8
E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8
E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8
E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8
E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8
E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8
E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8
E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8
E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8
E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8
E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8
E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8
E28A2BAEB8E28AAB47C3952449922449922449922449922449922449F7ABBBAEAAAB14

endstream
endobj

47 changes: 47 additions & 0 deletions refs/snapshots/image_custom_rgb8_png_invalid_icc.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
%PDF-1.7
%AAAA

1 0 obj
<<
/Length 1988
/Type /XObject
/Subtype /Image
/Filter [/ASCIIHexDecode /FlateDecode]
/Width 200
/Height 200
/ColorSpace /DeviceRGB
/BitsPerComponent 8
>>
stream
789CEDD2696B170400C7F15F1145417440041D50540441104504121112518420445154
205450411111A288E231759EF33EA7F3984EA6735E9B8AF31A733AF1D6794FE7356FD9
3C1FFEDDDE808F04513E5FBE2FE1934892244992244992244992244992244992F47057
28DCEBF6423A0AB95EC88D1BB97833976F757DF676CEDD4EDB9DB4B6E7547B8E77A4A5
23872FE7E895345FCDC16BD97721072E66CFA5EC6DCBAEB66C3F9FAD67D274368DE7D2
702A0DA7537F269B5AB3BE35752DA93B917527537B3CAB5B5273342B8F65C5E1541FC9
D243A96ACEE2E6541ECCA203A9D897F2FD99B72773F7A66C77CA76A5747766ECCCF49D
99B62393B7677253263665FCB6946CCB98AD19D998518D19B125C50D19569FA2CD1952
9F419B337063066C48FF8DE9B7217DD7A74F5D7AAFCBFF6BF3DF9AFCDB796DFEA9CDDF
B5F9AB267FD6E48F55F97D657E5B915F97A7D7B2F4AACE2FD5F979697EAACA8F55F961
49BEAFCC7795F9B6323D17A567457A2C4C8F8A7CB3205F97E7ABF27C393F5FCC4BF7B9
E93E279F97E5B3B27C3A3BDD66A55B693E29CDC733BBFE687A3E9C960FA6E6FDCEA7E4
BD495DBF3B29EF4CCCDB13F2D6F8BC392E6F94E4F592BC3636AF8EC92BA3F372E7A3F2
D2C8BC38222F14E7F9E23C373CCF0ECB3343F374519E2ACA9343F2C4E03C3E288F0DCC
838A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8
E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8
E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8
E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8
E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8
E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8
E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8
E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8
E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8
E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8
E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8
E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8
E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8E28A2BAEB8
E28A2BAEB8E28AAB47C3952449922449922449922449922449922449F7ABBBAEAAAB14

endstream
endobj

Loading

0 comments on commit ac1fe73

Please sign in to comment.