use pdfboss_render::{ImageFormat, Pixmap, PngCompression};
fn gradient_pixmap(width: u32, height: u32) -> Pixmap {
let mut pix = Pixmap::new(width, height);
for y in 0..height {
for x in 0..width {
let i = ((y * width + x) * 4) as usize;
pix.data[i] = (x * 7) as u8;
pix.data[i + 1] = (y * 11) as u8;
pix.data[i + 2] = (x + y) as u8;
pix.data[i + 3] = 255;
}
}
pix
}
fn u16le(bytes: &[u8], at: usize) -> u16 {
u16::from_le_bytes([bytes[at], bytes[at + 1]])
}
fn u32le(bytes: &[u8], at: usize) -> u32 {
u32::from_le_bytes([bytes[at], bytes[at + 1], bytes[at + 2], bytes[at + 3]])
}
fn i32le(bytes: &[u8], at: usize) -> i32 {
u32le(bytes, at) as i32
}
#[test]
fn ppm_is_p6_with_the_pixmap_dimensions_and_rgb_rows() {
let pix = gradient_pixmap(5, 3);
let ppm = pix.encode(ImageFormat::Ppm).unwrap();
let header = b"P6\n5 3\n255\n";
assert!(
ppm.starts_with(header),
"header: {:?}",
&ppm[..header.len()]
);
let pixels = &ppm[header.len()..];
assert_eq!(pixels.len(), 5 * 3 * 3);
for (rgb, rgba) in pixels
.as_chunks::<3>()
.0
.iter()
.zip(pix.data.as_chunks::<4>().0)
{
assert_eq!(&rgb[..], &rgba[..3]);
}
}
#[test]
fn bmp_header_describes_a_24_bit_bottom_up_image() {
let pix = gradient_pixmap(5, 3);
let bmp = pix.encode(ImageFormat::Bmp).unwrap();
let stride = 16; assert_eq!(&bmp[..2], b"BM");
assert_eq!(u32le(&bmp, 2) as usize, bmp.len(), "file size field");
assert_eq!(u32le(&bmp, 10), 54, "pixel data offset");
assert_eq!(u32le(&bmp, 14), 40, "BITMAPINFOHEADER size");
assert_eq!(i32le(&bmp, 18), 5, "width");
assert_eq!(i32le(&bmp, 22), 3, "positive height means bottom-up rows");
assert_eq!(u16le(&bmp, 26), 1, "planes");
assert_eq!(u16le(&bmp, 28), 24, "bits per pixel");
assert_eq!(u32le(&bmp, 30), 0, "BI_RGB, uncompressed");
assert_eq!(u32le(&bmp, 34) as usize, stride * 3, "image size");
assert_eq!(bmp.len(), 54 + stride * 3);
}
#[test]
fn bmp_rows_are_bgr_bottom_up_and_padded_to_four_bytes() {
let pix = gradient_pixmap(3, 2);
let bmp = pix.encode(ImageFormat::Bmp).unwrap();
let stride = 12; for y in 0..2usize {
for x in 0..3usize {
let src = (y * 3 + x) * 4;
let dst = 54 + (1 - y) * stride + x * 3;
assert_eq!(
&bmp[dst..dst + 3],
&[pix.data[src + 2], pix.data[src + 1], pix.data[src]],
"pixel ({x}, {y})"
);
}
let pad = 54 + (1 - y) * stride + 9;
assert_eq!(&bmp[pad..pad + 3], &[0, 0, 0], "row {y} padding");
}
}
#[test]
fn bmp_size_matches_the_padded_stride_for_widths_one_to_four() {
for width in 1..=4u32 {
let bmp = gradient_pixmap(width, 2).encode(ImageFormat::Bmp).unwrap();
let stride = ((width as usize * 3) + 3) & !3;
assert_eq!(bmp.len(), 54 + stride * 2, "width {width}");
}
}
#[test]
fn png_through_encode_is_byte_identical_to_encode_png_with() {
let pix = gradient_pixmap(64, 48);
for level in [
PngCompression::None,
PngCompression::Fast,
PngCompression::Balanced,
PngCompression::Best,
] {
assert_eq!(
pix.encode(ImageFormat::Png(level)).unwrap(),
pix.encode_png_with(level).unwrap(),
"{level:?}"
);
}
}
#[test]
fn from_name_accepts_the_three_formats_case_insensitively_and_rejects_others() {
assert_eq!(
ImageFormat::from_name("png"),
Some(ImageFormat::Png(PngCompression::default()))
);
assert_eq!(ImageFormat::from_name("PNG"), ImageFormat::from_name("png"));
assert_eq!(ImageFormat::from_name("ppm"), Some(ImageFormat::Ppm));
assert_eq!(ImageFormat::from_name("bmp"), Some(ImageFormat::Bmp));
assert_eq!(ImageFormat::from_name("tiff"), None);
assert_eq!(ImageFormat::from_name(""), None);
}