Validate all frames in APNGs
This commit is contained in:
parent
4ab941e305
commit
041e5f084b
1 changed files with 27 additions and 14 deletions
|
|
@ -1,15 +1,14 @@
|
||||||
use image::{DynamicImage, GenericImageView, ImageFormat, Pixel};
|
use image::{codecs::png::PngDecoder, *};
|
||||||
use log::{error, warn};
|
use log::{error, warn};
|
||||||
use std::io::Cursor;
|
|
||||||
|
|
||||||
/// Validate that the output png data still matches the original image
|
/// Validate that the output png data still matches the original image
|
||||||
pub fn validate_output(output: &[u8], original_data: &[u8]) -> bool {
|
pub fn validate_output(output: &[u8], original_data: &[u8]) -> bool {
|
||||||
let (old_png, new_png) = rayon::join(
|
let (old_frames, new_frames) = rayon::join(
|
||||||
|| load_png_image_from_memory(original_data),
|
|| load_png_image_from_memory(original_data),
|
||||||
|| load_png_image_from_memory(output),
|
|| load_png_image_from_memory(output),
|
||||||
);
|
);
|
||||||
|
|
||||||
match (new_png, old_png) {
|
match (new_frames, old_frames) {
|
||||||
(Err(new_err), _) => {
|
(Err(new_err), _) => {
|
||||||
error!("Failed to read output image for validation: {}", new_err);
|
error!("Failed to read output image for validation: {}", new_err);
|
||||||
false
|
false
|
||||||
|
|
@ -21,26 +20,40 @@ pub fn validate_output(output: &[u8], original_data: &[u8]) -> bool {
|
||||||
warn!("Failed to read input image for validation: {}", old_err);
|
warn!("Failed to read input image for validation: {}", old_err);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
(Ok(new_png), Ok(old_png)) => images_equal(&old_png, &new_png),
|
(Ok(new_frames), Ok(old_frames)) if new_frames.len() != old_frames.len() => false,
|
||||||
|
(Ok(new_frames), Ok(old_frames)) => {
|
||||||
|
for (a, b) in old_frames.iter().zip(new_frames) {
|
||||||
|
if !images_equal(&a, &b) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loads a PNG image from memory to a [DynamicImage]
|
/// Loads a PNG image from memory to frames of [RgbaImage]
|
||||||
fn load_png_image_from_memory(png_data: &[u8]) -> Result<DynamicImage, image::ImageError> {
|
fn load_png_image_from_memory(png_data: &[u8]) -> Result<Vec<RgbaImage>, image::ImageError> {
|
||||||
let mut reader = image::io::Reader::new(Cursor::new(png_data));
|
let decoder = PngDecoder::new(png_data)?;
|
||||||
reader.set_format(ImageFormat::Png);
|
if decoder.is_apng() {
|
||||||
reader.no_limits();
|
decoder
|
||||||
reader.decode()
|
.apng()
|
||||||
|
.into_frames()
|
||||||
|
.map(|f| f.map(|f| f.into_buffer()))
|
||||||
|
.collect()
|
||||||
|
} else {
|
||||||
|
DynamicImage::from_decoder(decoder).map(|i| vec![i.into_rgba8()])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compares images pixel by pixel for equivalent content
|
/// Compares images pixel by pixel for equivalent content
|
||||||
fn images_equal(old_png: &DynamicImage, new_png: &DynamicImage) -> bool {
|
fn images_equal(old_png: &RgbaImage, new_png: &RgbaImage) -> bool {
|
||||||
let a = old_png.pixels().filter(|x| {
|
let a = old_png.pixels().filter(|x| {
|
||||||
let p = x.2.channels();
|
let p = x.channels();
|
||||||
!(p.len() == 4 && p[3] == 0)
|
!(p.len() == 4 && p[3] == 0)
|
||||||
});
|
});
|
||||||
let b = new_png.pixels().filter(|x| {
|
let b = new_png.pixels().filter(|x| {
|
||||||
let p = x.2.channels();
|
let p = x.channels();
|
||||||
!(p.len() == 4 && p[3] == 0)
|
!(p.len() == 4 && p[3] == 0)
|
||||||
});
|
});
|
||||||
a.eq(b)
|
a.eq(b)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue