use crate::api::{JxlDecoderOptions, decode_with};
use crate::headers::Orientation;
fn transpose_fixture() -> Vec<u8> {
let path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../fuzz/seed_corpus/decode/orientation5_transpose.jxl");
std::fs::read(&path)
.unwrap_or_else(|e| panic!("missing committed fixture {}: {e}", path.display()))
}
fn coded_pixel_for_display(
orientation: Orientation,
dest: (usize, usize),
coded: (usize, usize),
) -> (usize, usize) {
let (cw, ch) = coded;
let (dx, dy) = dest;
match orientation {
Orientation::Identity => (dx, dy),
Orientation::FlipHorizontal => (cw - 1 - dx, dy),
Orientation::Rotate180 => (cw - 1 - dx, ch - 1 - dy),
Orientation::FlipVertical => (dx, ch - 1 - dy),
Orientation::Transpose => (dy, dx),
Orientation::Rotate90Cw => (dy, ch - 1 - dx),
Orientation::AntiTranspose => (ch - 1 - dy, cw - 1 - dx),
Orientation::Rotate90Ccw => (cw - 1 - dy, dx),
}
}
#[test]
fn adjust_orientation_correct_bakes_and_reports_identity() {
let data = transpose_fixture();
let img = decode_with(
&data,
JxlDecoderOptions {
adjust_orientation: true,
..Default::default()
},
)
.expect("decode (Correct) failed");
assert_eq!(
img.info.intrinsic_orientation,
Orientation::Transpose,
"fixture should carry a Transpose intrinsic orientation"
);
assert_eq!(
img.info.orientation,
Orientation::Identity,
"Correct mode bakes orientation, so the residual must be Identity"
);
let (cw, ch) = img.info.coded_size;
assert_eq!(
img.info.size,
(ch, cw),
"Correct mode must report display (transposed) dimensions"
);
assert_eq!(
(img.width, img.height),
img.info.size,
"emitted buffer dims must match reported size"
);
}
#[test]
fn adjust_orientation_preserve_skips_bake_and_surfaces_orientation() {
let data = transpose_fixture();
let img = decode_with(
&data,
JxlDecoderOptions {
adjust_orientation: false,
..Default::default()
},
)
.expect("decode (Preserve) failed");
assert_eq!(
img.info.intrinsic_orientation,
Orientation::Transpose,
"intrinsic orientation must always be the stored value"
);
assert_eq!(
img.info.orientation,
Orientation::Transpose,
"Preserve mode must surface the stored orientation as the residual"
);
assert_eq!(
img.info.size, img.info.coded_size,
"Preserve mode must report stored (coded) dimensions"
);
assert_eq!(
(img.width, img.height),
img.info.coded_size,
"emitted buffer dims must equal the coded dims in Preserve mode"
);
}
#[test]
fn correct_equals_preserve_under_exact_orientation_transform() {
let data = transpose_fixture();
let baked = decode_with(
&data,
JxlDecoderOptions {
adjust_orientation: true,
..Default::default()
},
)
.expect("decode (Correct) failed");
let unbaked = decode_with(
&data,
JxlDecoderOptions {
adjust_orientation: false,
..Default::default()
},
)
.expect("decode (Preserve) failed");
assert_eq!(baked.channels, unbaked.channels);
assert_eq!(baked.is_grayscale, unbaked.is_grayscale);
let orientation = unbaked.info.intrinsic_orientation;
assert_ne!(
orientation,
Orientation::Identity,
"this test only proves anything for a non-Identity orientation"
);
let coded = (unbaked.width, unbaked.height);
let display = (baked.width, baked.height);
let ch = baked.channels;
assert_eq!(
display,
Orientation::Transpose.map_size(coded),
"display geometry must be the transposed coded geometry"
);
let baked_row = display.0 * ch;
let coded_row = coded.0 * ch;
let mut mismatches = 0usize;
for dy in 0..display.1 {
for dx in 0..display.0 {
let (sx, sy) = coded_pixel_for_display(orientation, (dx, dy), coded);
let b = &baked.data[dy * baked_row + dx * ch..][..ch];
let u = &unbaked.data[sy * coded_row + sx * ch..][..ch];
if b != u {
mismatches += 1;
if mismatches <= 4 {
eprintln!(
"mismatch at display ({dx},{dy}) <- coded ({sx},{sy}): \
baked {b:?} vs unbaked {u:?}"
);
}
}
}
}
assert_eq!(
mismatches, 0,
"baked output must equal un-baked output mapped through the stored \
orientation, bit-for-bit ({mismatches} mismatched pixels)"
);
}