fix: handle WhiteIsZero for extended bit depths, add comprehensive tests
Fix invert_colors to support extended bit depths (9-15 promoted to u16, 17-31 promoted to u32). Byte-wise NOT doesn't work for promoted samples because it flips unused upper bits; use (max - value) instead. Add regression tests covering the extended depth matrix: - Gray 10/14/24-bit (contiguous, BlackIsZero) - RGB 10/14/24-bit (contiguous) - RGB 10/12/14/24-bit (planar) - Gray 12-bit with WhiteIsZero (synthetic 4x2 image) - Gray 12-bit with horizontal predictor (synthetic 4x2 image)
This commit is contained in:
1 parent
4ff374990e
commit
29e43d2314
4 files changed
+167
No files matched your search
@@ -740,6 +740,23 @@ fn invert_colors(
|
|||||||
*x = !*x;
|
*x = !*x;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
(ColorType::Gray(n @ 9..=15), SampleFormat::Uint) => {
|
||||||
|
// Extended depths promoted to u16: invert within the valid bit range.
|
||||||
|
// Byte-wise NOT would flip the upper padding bits, so we use (max - v).
|
||||||
|
let max = (1u16 << n) - 1;
|
||||||
|
for chunk in buf.as_chunks_mut::<2>().0 {
|
||||||
|
let v = u16::from_ne_bytes(*chunk);
|
||||||
|
*chunk = (max - v).to_ne_bytes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(ColorType::Gray(n @ 17..=31), SampleFormat::Uint) => {
|
||||||
|
// Extended depths promoted to u32: invert within the valid bit range.
|
||||||
|
let max = (1u32 << n) - 1;
|
||||||
|
for chunk in buf.as_chunks_mut::<4>().0 {
|
||||||
|
let v = u32::from_ne_bytes(*chunk);
|
||||||
|
*chunk = (max - v).to_ne_bytes();
|
||||||
|
}
|
||||||
|
}
|
||||||
(ColorType::Gray(32), SampleFormat::IEEEFP) => {
|
(ColorType::Gray(32), SampleFormat::IEEEFP) => {
|
||||||
for x in buf.as_chunks_mut::<4>().0 {
|
for x in buf.as_chunks_mut::<4>().0 {
|
||||||
let v = f32::from_ne_bytes(*x);
|
let v = f32::from_ne_bytes(*x);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use std::io::{Read, Seek};
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
const TEST_IMAGE_DIR: &str = "./tests/images/";
|
const TEST_IMAGE_DIR: &str = "./tests/images/";
|
||||||
|
const LIBTIFFPIC_DEPTH_DIR: &str = "./tests/libtiffpic/depth/";
|
||||||
|
|
||||||
macro_rules! test_image_sum {
|
macro_rules! test_image_sum {
|
||||||
($name:ident, $buffer:ident, $sum_ty:ty) => {
|
($name:ident, $buffer:ident, $sum_ty:ty) => {
|
||||||
@@ -40,6 +41,32 @@ test_image_sum!(test_image_sum_u64, U64, u64);
|
|||||||
test_image_sum!(test_image_sum_f32, F32, f32);
|
test_image_sum!(test_image_sum_f32, F32, f32);
|
||||||
test_image_sum!(test_image_sum_f64, F64, f64);
|
test_image_sum!(test_image_sum_f64, F64, f64);
|
||||||
|
|
||||||
|
/// Like test_image_sum but reads from an arbitrary directory.
|
||||||
|
macro_rules! test_image_sum_in {
|
||||||
|
($name:ident, $buffer:ident, $sum_ty:ty) => {
|
||||||
|
fn $name(dir: &str, file: &str, expected_type: ColorType, expected_sum: $sum_ty) {
|
||||||
|
let path = PathBuf::from(dir).join(file);
|
||||||
|
let img_file = File::open(path).expect("Cannot find test image!");
|
||||||
|
let mut decoder = Decoder::open(img_file).expect("Cannot create decoder");
|
||||||
|
|
||||||
|
decoder.next_image().expect("Cannot read image IFD");
|
||||||
|
assert_eq!(decoder.colortype().unwrap(), expected_type);
|
||||||
|
let img_res = decoder.read_image().unwrap();
|
||||||
|
|
||||||
|
match img_res {
|
||||||
|
DecodingResult::$buffer(res) => {
|
||||||
|
let sum: $sum_ty = res.into_iter().map(<$sum_ty>::from).sum();
|
||||||
|
assert_eq!(sum, expected_sum);
|
||||||
|
}
|
||||||
|
_ => panic!("Wrong bit depth"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
test_image_sum_in!(test_depth_sum_u16, U16, u64);
|
||||||
|
test_image_sum_in!(test_depth_sum_u32, U32, u64);
|
||||||
|
|
||||||
/// Tests that a decoder can be constructed for an image and the color type
|
/// Tests that a decoder can be constructed for an image and the color type
|
||||||
/// read from the IFD and is of the appropriate type, but the type is
|
/// read from the IFD and is of the appropriate type, but the type is
|
||||||
/// unsupported.
|
/// unsupported.
|
||||||
@@ -810,3 +837,126 @@ fn test_decode_huge_g4() {
|
|||||||
panic!("Unexpected decoding result type");
|
panic!("Unexpected decoding result type");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- Extended bit depth tests (9-31 bits per sample) ----
|
||||||
|
//
|
||||||
|
// These test the unpacking of packed N-bit samples to standard-width output.
|
||||||
|
// All flower-* images are 73x43 pixels from the libtiffpic depth suite.
|
||||||
|
|
||||||
|
// Gray extended depths (contiguous, BlackIsZero)
|
||||||
|
#[test]
|
||||||
|
fn test_gray_u10() {
|
||||||
|
test_depth_sum_u16(
|
||||||
|
LIBTIFFPIC_DEPTH_DIR,
|
||||||
|
"flower-minisblack-10.tif",
|
||||||
|
ColorType::Gray(10),
|
||||||
|
1175406,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gray_u14() {
|
||||||
|
test_depth_sum_u16(
|
||||||
|
LIBTIFFPIC_DEPTH_DIR,
|
||||||
|
"flower-minisblack-14.tif",
|
||||||
|
ColorType::Gray(14),
|
||||||
|
18847564,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gray_u24() {
|
||||||
|
test_depth_sum_u32(
|
||||||
|
LIBTIFFPIC_DEPTH_DIR,
|
||||||
|
"flower-minisblack-24.tif",
|
||||||
|
ColorType::Gray(24),
|
||||||
|
19201687050,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// RGB contiguous extended depths
|
||||||
|
#[test]
|
||||||
|
fn test_rgb_contig_u10() {
|
||||||
|
test_depth_sum_u16(
|
||||||
|
LIBTIFFPIC_DEPTH_DIR,
|
||||||
|
"flower-rgb-contig-10.tif",
|
||||||
|
ColorType::RGB(10),
|
||||||
|
3302314,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rgb_contig_u14() {
|
||||||
|
test_depth_sum_u16(
|
||||||
|
LIBTIFFPIC_DEPTH_DIR,
|
||||||
|
"flower-rgb-contig-14.tif",
|
||||||
|
ColorType::RGB(14),
|
||||||
|
52955900,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rgb_contig_u24() {
|
||||||
|
test_depth_sum_u32(
|
||||||
|
LIBTIFFPIC_DEPTH_DIR,
|
||||||
|
"flower-rgb-contig-24.tif",
|
||||||
|
ColorType::RGB(24),
|
||||||
|
53926969278,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// RGB planar extended depths (read_image returns first plane only)
|
||||||
|
#[test]
|
||||||
|
fn test_rgb_planar_u10() {
|
||||||
|
test_depth_sum_u16(
|
||||||
|
LIBTIFFPIC_DEPTH_DIR,
|
||||||
|
"flower-rgb-planar-10.tif",
|
||||||
|
ColorType::RGB(10),
|
||||||
|
1214176,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rgb_planar_u12() {
|
||||||
|
test_depth_sum_u16(
|
||||||
|
LIBTIFFPIC_DEPTH_DIR,
|
||||||
|
"flower-rgb-planar-12.tif",
|
||||||
|
ColorType::RGB(12),
|
||||||
|
4865028,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rgb_planar_u14() {
|
||||||
|
test_depth_sum_u16(
|
||||||
|
LIBTIFFPIC_DEPTH_DIR,
|
||||||
|
"flower-rgb-planar-14.tif",
|
||||||
|
ColorType::RGB(14),
|
||||||
|
19468331,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rgb_planar_u24() {
|
||||||
|
test_depth_sum_u32(
|
||||||
|
LIBTIFFPIC_DEPTH_DIR,
|
||||||
|
"flower-rgb-planar-24.tif",
|
||||||
|
ColorType::RGB(24),
|
||||||
|
19835471019,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhiteIsZero + extended bit depth (12-bit gray, synthetic 4x2 image)
|
||||||
|
#[test]
|
||||||
|
fn test_gray_u12_white_is_zero() {
|
||||||
|
// Values: [0, 1000, 2000, 4095, 100, 500, 3000, 3500]
|
||||||
|
// After WhiteIsZero inversion: [4095, 3095, 2095, 0, 3995, 3595, 1095, 595]
|
||||||
|
test_image_sum_u16("miniswhite-1c-12b.tiff", ColorType::Gray(12), 18565);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Horizontal predictor + extended bit depth (12-bit gray, synthetic 4x2 image)
|
||||||
|
#[test]
|
||||||
|
fn test_gray_u12_hpredict() {
|
||||||
|
// Delta-encoded 12-bit values: [100, 200, 300, 400, 1000, 2000, 3000, 4000]
|
||||||
|
test_image_sum_u16("hpredict-1c-12b.tiff", ColorType::Gray(12), 11000);
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Reference in new issue
Block a user