Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions crates/j2k-jpeg-metal/src/compute/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,120 @@ fn restart_work_for_mcu_range_slices_to_overlapping_restart_segments() {
assert_eq!(offsets, &[20, 30]);
}

#[test]
fn restart_roi_gpu_does_not_validate_an_unselected_empty_entropy_segment() {
if !should_run_metal_runtime() {
return;
}

let dimensions = (128, 128);
let rgb = j2k_test_support::patterned_rgb8(dimensions.0, dimensions.1);
let encoded = j2k_jpeg::encode_jpeg_baseline(
j2k_jpeg::JpegSamples::Rgb8 {
data: &rgb,
width: dimensions.0,
height: dimensions.1,
},
j2k_jpeg::JpegEncodeOptions {
quality: 90,
subsampling: j2k_jpeg::JpegSubsampling::Ybr420,
restart_interval: Some(4),
backend: j2k_jpeg::JpegBackend::Cpu,
},
)
.expect("encode restart JPEG");

let decoder = CpuDecoder::new(&encoded.data).expect("valid CPU decoder");
let mut structural_packet =
j2k_jpeg::adapter::build_fast420_packet(&encoded.data).expect("valid packet");
assert!(structural_packet.restart_offsets.len() > 2);
let roi = j2k_jpeg::Rect {
x: 112,
y: 112,
w: 16,
h: 16,
};
let full = j2k_jpeg::Rect {
x: 0,
y: 0,
w: dimensions.0,
h: dimensions.1,
};
let decode = |packet: &j2k_jpeg::adapter::JpegFast420PacketV1, rect| {
with_runtime(|runtime| {
try_decode_fast420_region_to_surface(
runtime,
&decoder,
Some(packet),
PixelFormat::Rgb8,
rect,
)?
.ok_or_else(|| Error::MetalKernel {
message: "expected fast420 surface".to_string(),
})
})
.expect("GPU status accepts the restart segments")
.as_bytes()
.expect("completed surface bytes")
.to_vec()
};
let valid_full = decode(&structural_packet, full);
let valid_roi = decode(&structural_packet, roi);

// Remove the complete first entropy interval while retaining RST0. Header
// parsing and marker structure remain valid, but the existing checkpoint
// builder must entropy-decode the now-empty first interval.
let sos = encoded
.data
.windows(2)
.position(|bytes| bytes == [0xff, 0xda])
.expect("SOS marker");
let sos_len = usize::from(u16::from_be_bytes([
encoded.data[sos + 2],
encoded.data[sos + 3],
]));
let entropy_start = sos + 2 + sos_len;
let first_rst = encoded.data[entropy_start..]
.windows(2)
.position(|bytes| bytes[0] == 0xff && (0xd0..=0xd7).contains(&bytes[1]))
.map(|offset| entropy_start + offset)
.expect("first restart marker");
let mut malformed = encoded.data.clone();
malformed.drain(entropy_start..first_rst);
let old_error = j2k_jpeg::adapter::build_fast420_packet(&malformed)
.expect_err("full checkpoint preparation must reject the empty first interval");
assert!(matches!(
old_error,
j2k_jpeg::adapter::FastPacketError::Decode(j2k_jpeg::JpegError::HuffmanDecode { .. })
));

// Model a marker-only structural plan derived from that malformed stream:
// the first interval is empty and every later restart segment is unchanged.
let removed = usize::try_from(structural_packet.restart_offsets[1])
.expect("first entropy interval length");
structural_packet.entropy_bytes.drain(..removed);
let removed_u32 = u32::try_from(removed).expect("first entropy interval fits in u32");
for offset in &mut structural_packet.restart_offsets[1..] {
*offset = offset
.checked_sub(removed_u32)
.expect("later restart offset follows first interval");
}

// Full-image dispatch currently gives every restart thread the global
// entropy length rather than its segment end. Segment zero therefore starts
// at the same offset as segment one, decodes valid bytes belonging to that
// next segment, and reports success despite producing the wrong first MCUs.
assert_ne!(
decode(&structural_packet, full),
valid_full,
"successful GPU status must not be mistaken for valid full-image pixels"
);

// The ROI lies wholly in the final MCU, so its selected restart segment
// does not include malformed segment zero.
assert_eq!(decode(&structural_packet, roi), valid_roi);
}

#[test]
fn runtime_initialization_error_classifies_device_unavailable() {
assert!(matches!(
Expand Down
Loading