use miniz_oxide::deflate::compress_to_vec_zlib;
use crate::tiny_skia::decode_raster_to_pixmap;
pub(super) struct DecodedImage {
pub(super) width: u32,
pub(super) height: u32,
pub(super) rgb_flate: Vec<u8>,
pub(super) alpha_flate: Option<Vec<u8>>,
}
pub(super) fn decode_for_pdf(bytes: &[u8]) -> Option<DecodedImage> {
let pixmap = decode_raster_to_pixmap(bytes)?;
let width = pixmap.width();
let height = pixmap.height();
if width == 0 || height == 0 {
return None;
}
let px = pixmap.pixels();
let mut straight = Vec::with_capacity(px.len() * 4);
for p in px {
let a = p.alpha();
let s = p.demultiply();
straight.push(s.red());
straight.push(s.green());
straight.push(s.blue());
straight.push(a);
}
decoded_image_from_straight_rgba(&straight, width, height)
}
pub(super) fn decoded_image_from_straight_rgba(
rgba: &[u8],
width: u32,
height: u32,
) -> Option<DecodedImage> {
if width == 0 || height == 0 {
return None;
}
let expected = (width as usize)
.checked_mul(height as usize)
.and_then(|n| n.checked_mul(4))?;
if rgba.len() != expected {
return None;
}
let pixel_count = rgba.len() / 4;
let mut rgb = Vec::with_capacity(pixel_count * 3);
let mut alpha = Vec::with_capacity(pixel_count);
let mut any_transparent = false;
for chunk in rgba.chunks_exact(4) {
rgb.push(chunk[0]);
rgb.push(chunk[1]);
rgb.push(chunk[2]);
let a = chunk[3];
alpha.push(a);
if a != 255 {
any_transparent = true;
}
}
let level = 6; let rgb_flate = compress_to_vec_zlib(&rgb, level);
let alpha_flate = if any_transparent {
Some(compress_to_vec_zlib(&alpha, level))
} else {
None
};
Some(DecodedImage {
width,
height,
rgb_flate,
alpha_flate,
})
}