Move sanity checks to separate file
This commit is contained in:
parent
9285727e95
commit
3034dffb0f
2 changed files with 52 additions and 53 deletions
58
src/lib.rs
58
src/lib.rs
|
|
@ -60,6 +60,8 @@ mod headers;
|
||||||
mod interlace;
|
mod interlace;
|
||||||
mod png;
|
mod png;
|
||||||
mod reduction;
|
mod reduction;
|
||||||
|
#[cfg(feature = "sanity-checks")]
|
||||||
|
mod sanity_checks;
|
||||||
|
|
||||||
/// Private to oxipng; don't use outside tests and benches
|
/// Private to oxipng; don't use outside tests and benches
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
|
@ -70,6 +72,8 @@ pub mod internal_tests {
|
||||||
pub use crate::headers::*;
|
pub use crate::headers::*;
|
||||||
pub use crate::png::*;
|
pub use crate::png::*;
|
||||||
pub use crate::reduction::*;
|
pub use crate::reduction::*;
|
||||||
|
#[cfg(feature = "sanity-checks")]
|
||||||
|
pub use crate::sanity_checks::*;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
@ -597,7 +601,7 @@ fn optimize_png(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "sanity-checks")]
|
#[cfg(feature = "sanity-checks")]
|
||||||
debug_assert!(sanity_checks::validate_output(&output, original_data));
|
assert!(sanity_checks::validate_output(&output, original_data));
|
||||||
|
|
||||||
Ok(output)
|
Ok(output)
|
||||||
}
|
}
|
||||||
|
|
@ -1070,55 +1074,3 @@ fn copy_times(input_path_meta: &Metadata, out_path: &Path) -> PngResult<()> {
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "sanity-checks")]
|
|
||||||
mod sanity_checks {
|
|
||||||
use super::*;
|
|
||||||
use image::{DynamicImage, GenericImageView, ImageFormat, Pixel};
|
|
||||||
use log::error;
|
|
||||||
use std::io::Cursor;
|
|
||||||
|
|
||||||
/// Validate that the output png data still matches the original image
|
|
||||||
pub(super) fn validate_output(output: &[u8], original_data: &[u8]) -> bool {
|
|
||||||
let (old_png, new_png) = rayon::join(
|
|
||||||
|| load_png_image_from_memory(original_data),
|
|
||||||
|| load_png_image_from_memory(output),
|
|
||||||
);
|
|
||||||
|
|
||||||
match (new_png, old_png) {
|
|
||||||
(Err(new_err), _) => {
|
|
||||||
error!("Failed to read output image for validation: {}", new_err);
|
|
||||||
false
|
|
||||||
}
|
|
||||||
(_, Err(old_err)) => {
|
|
||||||
// The original image might be invalid if, for example, there is a CRC error,
|
|
||||||
// and we set fix_errors to true. In that case, all we can do is check that the
|
|
||||||
// new image is decodable.
|
|
||||||
warn!("Failed to read input image for validation: {}", old_err);
|
|
||||||
true
|
|
||||||
}
|
|
||||||
(Ok(new_png), Ok(old_png)) => images_equal(&old_png, &new_png),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Loads a PNG image from memory to a [DynamicImage]
|
|
||||||
fn load_png_image_from_memory(png_data: &[u8]) -> Result<DynamicImage, image::ImageError> {
|
|
||||||
let mut reader = image::io::Reader::new(Cursor::new(png_data));
|
|
||||||
reader.set_format(ImageFormat::Png);
|
|
||||||
reader.no_limits();
|
|
||||||
reader.decode()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Compares images pixel by pixel for equivalent content
|
|
||||||
fn images_equal(old_png: &DynamicImage, new_png: &DynamicImage) -> bool {
|
|
||||||
let a = old_png.pixels().filter(|x| {
|
|
||||||
let p = x.2.channels();
|
|
||||||
!(p.len() == 4 && p[3] == 0)
|
|
||||||
});
|
|
||||||
let b = new_png.pixels().filter(|x| {
|
|
||||||
let p = x.2.channels();
|
|
||||||
!(p.len() == 4 && p[3] == 0)
|
|
||||||
});
|
|
||||||
a.eq(b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
47
src/sanity_checks.rs
Normal file
47
src/sanity_checks.rs
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
use image::{DynamicImage, GenericImageView, ImageFormat, Pixel};
|
||||||
|
use log::{error, warn};
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
/// Validate that the output png data still matches the original image
|
||||||
|
pub fn validate_output(output: &[u8], original_data: &[u8]) -> bool {
|
||||||
|
let (old_png, new_png) = rayon::join(
|
||||||
|
|| load_png_image_from_memory(original_data),
|
||||||
|
|| load_png_image_from_memory(output),
|
||||||
|
);
|
||||||
|
|
||||||
|
match (new_png, old_png) {
|
||||||
|
(Err(new_err), _) => {
|
||||||
|
error!("Failed to read output image for validation: {}", new_err);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
(_, Err(old_err)) => {
|
||||||
|
// The original image might be invalid if, for example, there is a CRC error,
|
||||||
|
// and we set fix_errors to true. In that case, all we can do is check that the
|
||||||
|
// new image is decodable.
|
||||||
|
warn!("Failed to read input image for validation: {}", old_err);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
(Ok(new_png), Ok(old_png)) => images_equal(&old_png, &new_png),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Loads a PNG image from memory to a [DynamicImage]
|
||||||
|
fn load_png_image_from_memory(png_data: &[u8]) -> Result<DynamicImage, image::ImageError> {
|
||||||
|
let mut reader = image::io::Reader::new(Cursor::new(png_data));
|
||||||
|
reader.set_format(ImageFormat::Png);
|
||||||
|
reader.no_limits();
|
||||||
|
reader.decode()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compares images pixel by pixel for equivalent content
|
||||||
|
fn images_equal(old_png: &DynamicImage, new_png: &DynamicImage) -> bool {
|
||||||
|
let a = old_png.pixels().filter(|x| {
|
||||||
|
let p = x.2.channels();
|
||||||
|
!(p.len() == 4 && p[3] == 0)
|
||||||
|
});
|
||||||
|
let b = new_png.pixels().filter(|x| {
|
||||||
|
let p = x.2.channels();
|
||||||
|
!(p.len() == 4 && p[3] == 0)
|
||||||
|
});
|
||||||
|
a.eq(b)
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue