diff --git a/tests/decode_images.rs b/tests/decode_images.rs index 5ccc79c..c278bee 100644 --- a/tests/decode_images.rs +++ b/tests/decode_images.rs @@ -8,6 +8,7 @@ use std::io::{Read, Seek}; use std::path::PathBuf; const TEST_IMAGE_DIR: &str = "./tests/images/"; +const SEQ_IMAGE_DIR: &str = "./tests/images/seq/"; const LIBTIFFPIC_DEPTH_DIR: &str = "./tests/libtiffpic/depth/"; macro_rules! test_image_sum { @@ -36,7 +37,6 @@ test_image_sum!(test_image_sum_u8, U8, u64); test_image_sum!(test_image_sum_i8, I8, i64); test_image_sum!(test_image_sum_u16, U16, u64); test_image_sum!(test_image_sum_i16, I16, i64); -test_image_sum!(test_image_sum_i32, I32, i64); test_image_sum!(test_image_sum_u32, U32, u64); test_image_sum!(test_image_sum_u64, U64, u64); test_image_sum!(test_image_sum_f32, F32, f32); @@ -68,6 +68,236 @@ macro_rules! test_image_sum_in { test_image_sum_in!(test_depth_sum_u16, U16, u64); test_image_sum_in!(test_depth_sum_u32, U32, u64); +/// Compute a CRC32 hash of the full decoded TIFF output including metadata. +/// +/// Hashes: page count, and for each page: dimensions, color type, and decoded +/// sample data (in little-endian byte order for multi-byte types). This catches +/// wrong dimensions, wrong color type, byte-order errors, bit-alignment errors, +/// stride/padding errors, and pixel value errors. +fn compute_tiff_hash(reader: R) -> u32 { + let mut decoder = Decoder::open(reader).expect("Cannot create decoder"); + let mut hasher = crc32fast::Hasher::new(); + + let mut page = 0u32; + loop { + decoder.next_image().expect("Cannot read image IFD"); + let (width, height) = decoder.dimensions().unwrap(); + let color_type = decoder.colortype().unwrap(); + let result = decoder.read_image().unwrap(); + + // Hash page index + dimensions + hasher.update(&page.to_le_bytes()); + hasher.update(&width.to_le_bytes()); + hasher.update(&height.to_le_bytes()); + + // Hash color type (discriminant + fields) + match color_type { + ColorType::Gray(n) => hasher.update(&[0, n]), + ColorType::RGB(n) => hasher.update(&[1, n]), + ColorType::Palette(n) => hasher.update(&[2, n]), + ColorType::GrayA(n) => hasher.update(&[3, n]), + ColorType::RGBA(n) => hasher.update(&[4, n]), + ColorType::CMYK(n) => hasher.update(&[5, n]), + ColorType::CMYKA(n) => hasher.update(&[6, n]), + ColorType::YCbCr(n) => hasher.update(&[7, n]), + ColorType::Lab(n) => hasher.update(&[8, n]), + ColorType::Multiband { + bit_depth, + num_samples, + } => { + hasher.update(&[9, bit_depth]); + hasher.update(&num_samples.to_le_bytes()); + } + _ => hasher.update(&[255]), + } + + // Hash decoded data with type discriminant + LE sample bytes + match result { + DecodingResult::U8(ref data) => { + hasher.update(&[0]); + hasher.update(data); + } + DecodingResult::U16(ref data) => { + hasher.update(&[1]); + for v in data { + hasher.update(&v.to_le_bytes()); + } + } + DecodingResult::U32(ref data) => { + hasher.update(&[2]); + for v in data { + hasher.update(&v.to_le_bytes()); + } + } + DecodingResult::U64(ref data) => { + hasher.update(&[3]); + for v in data { + hasher.update(&v.to_le_bytes()); + } + } + DecodingResult::F16(ref data) => { + hasher.update(&[4]); + for v in data { + hasher.update(&v.to_le_bytes()); + } + } + DecodingResult::F32(ref data) => { + hasher.update(&[5]); + for v in data { + hasher.update(&v.to_le_bytes()); + } + } + DecodingResult::F64(ref data) => { + hasher.update(&[6]); + for v in data { + hasher.update(&v.to_le_bytes()); + } + } + DecodingResult::I8(ref data) => { + hasher.update(&[7]); + for v in data { + hasher.update(&v.to_le_bytes()); + } + } + DecodingResult::I16(ref data) => { + hasher.update(&[8]); + for v in data { + hasher.update(&v.to_le_bytes()); + } + } + DecodingResult::I32(ref data) => { + hasher.update(&[9]); + for v in data { + hasher.update(&v.to_le_bytes()); + } + } + DecodingResult::I64(ref data) => { + hasher.update(&[10]); + for v in data { + hasher.update(&v.to_le_bytes()); + } + } + } + + page += 1; + if !decoder.more_images() { + break; + } + } + + // Hash total page count at the end + hasher.update(&page.to_le_bytes()); + hasher.finalize() +} + +/// Verify all TIFF files in tests/images/seq/ against their filename-embedded CRC32 hash. +/// +/// Each file is named `-<8hex>.tiff` where the last segment before `.tiff` +/// is the expected CRC32 hash of (dimensions, color type, decoded data) for all pages. +#[test] +fn verify_all_seq_images() { + let seq_dir = std::path::Path::new(SEQ_IMAGE_DIR); + if !seq_dir.is_dir() { + panic!( + "seq image directory not found: {}", + seq_dir.display() + ); + } + + let mut files: Vec<_> = std::fs::read_dir(seq_dir) + .expect("Cannot read seq image directory") + .filter_map(|e| e.ok()) + .filter(|e| { + e.path() + .extension() + .is_some_and(|ext| ext == "tiff" || ext == "tif") + }) + .collect(); + files.sort_by_key(|e| e.file_name()); + + assert!( + !files.is_empty(), + "No .tiff files found in {}", + seq_dir.display() + ); + + let mut failures = Vec::new(); + for entry in &files { + let path = entry.path(); + let stem = path.file_stem().unwrap().to_str().unwrap(); + + // Extract expected hash: last '-'-delimited segment + let expected_hash = stem + .rsplit('-') + .next() + .expect("filename must contain a '-' before the hash"); + + // Validate it looks like an 8-char hex string + if expected_hash.len() != 8 || !expected_hash.chars().all(|c| c.is_ascii_hexdigit()) { + failures.push(format!( + "{}: filename does not end with 8-char hex hash (got '{}')", + stem, expected_hash + )); + continue; + } + + let file = File::open(&path).expect("Cannot open test image"); + let actual_hash = format!("{:08x}", compute_tiff_hash(file)); + + if actual_hash != expected_hash { + failures.push(format!( + "{}: expected hash {} but got {}", + stem, expected_hash, actual_hash + )); + } + } + + if !failures.is_empty() { + panic!( + "{} of {} seq images failed hash verification:\n {}", + failures.len(), + files.len(), + failures.join("\n ") + ); + } + + eprintln!("Verified {} seq images", files.len()); +} + +/// Helper: print the correct hash-suffixed filename for every *.tiff in tests/images/seq/. +/// Run with: cargo test print_seq_hashes -- --ignored --nocapture +#[test] +#[ignore] +fn print_seq_hashes() { + let dir = std::path::Path::new(SEQ_IMAGE_DIR); + let mut files: Vec<_> = std::fs::read_dir(dir) + .expect("Cannot read image directory") + .filter_map(|e| e.ok()) + .filter(|e| { + e.file_name() + .to_str() + .is_some_and(|n| n.starts_with("seq-") && n.ends_with(".tiff")) + }) + .collect(); + files.sort_by_key(|e| e.file_name()); + + for entry in &files { + let path = entry.path(); + let name = path.file_name().unwrap().to_str().unwrap(); + let file = File::open(&path).expect("Cannot open test image"); + let hash = format!("{:08x}", compute_tiff_hash(file)); + // Strip the old hash (last -XXXXXXXX before .tiff) and print with new hash + let stem = path.file_stem().unwrap().to_str().unwrap(); + let base = &stem[..stem.rfind('-').unwrap_or(stem.len())]; + let new_name = format!("{base}-{hash}.tiff"); + if new_name != name { + eprintln!("RENAME: {name} -> {new_name}"); + } else { + eprintln!("OK: {name}"); + } + } +} + /// 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 /// unsupported. @@ -1000,443 +1230,3 @@ fn test_gray_u12_hpredict() { test_image_sum_u16("hpredict-1c-12b.tiff", ColorType::Gray(12), 11000); } -// ===== Sequential-value test functions ===== -// Generated by gen_seq_images.py using manual TIFF writer -// Each image has cycling 0..max values; any bit error changes the sum. -// 16x16 images (256 pixels per channel). -// Sub-byte checksums are sums of packed bytes (the decoder returns packed data). -// Planar checksums are first-plane only (read_image() reads one plane). - -// --- Sub-byte BlackIsZero --- -#[test] -fn test_seq_1c_3b() { - test_image_sum_u8("seq-1c-3b.tiff", ColorType::Gray(3), 5792); -} - -#[test] -fn test_seq_1c_5b() { - test_image_sum_u8("seq-1c-5b.tiff", ColorType::Gray(5), 18944); -} - -#[test] -fn test_seq_1c_7b() { - test_image_sum_u8("seq-1c-7b.tiff", ColorType::Gray(7), 24992); -} - -// --- Sub-byte WhiteIsZero --- -#[test] -fn test_seq_1c_3b_miniswhite() { - test_image_sum_u8("seq-1c-3b-miniswhite.tiff", ColorType::Gray(3), 5792); -} - -#[test] -fn test_seq_1c_5b_miniswhite() { - test_image_sum_u8("seq-1c-5b-miniswhite.tiff", ColorType::Gray(5), 18944); -} - -#[test] -fn test_seq_1c_6b_miniswhite() { - test_image_sum_u8("seq-1c-6b-miniswhite.tiff", ColorType::Gray(6), 25728); -} - -#[test] -fn test_seq_1c_7b_miniswhite() { - test_image_sum_u8("seq-1c-7b-miniswhite.tiff", ColorType::Gray(7), 24992); -} - -// --- Extended BlackIsZero --- -#[test] -fn test_seq_1c_10b() { - test_image_sum_u16("seq-1c-10b.tiff", ColorType::Gray(10), 32640); -} - -#[test] -fn test_seq_1c_12b() { - test_image_sum_u16("seq-1c-12b.tiff", ColorType::Gray(12), 32640); -} - -#[test] -fn test_seq_1c_14b() { - test_image_sum_u16("seq-1c-14b.tiff", ColorType::Gray(14), 32640); -} - -#[test] -fn test_seq_1c_24b() { - test_image_sum_u32("seq-1c-24b.tiff", ColorType::Gray(24), 32640); -} - -// --- Extended WhiteIsZero --- -#[test] -fn test_seq_1c_10b_miniswhite() { - test_image_sum_u16("seq-1c-10b-miniswhite.tiff", ColorType::Gray(10), 32640); -} - -#[test] -fn test_seq_1c_12b_miniswhite() { - test_image_sum_u16("seq-1c-12b-miniswhite.tiff", ColorType::Gray(12), 32640); -} - -#[test] -fn test_seq_1c_14b_miniswhite() { - test_image_sum_u16("seq-1c-14b-miniswhite.tiff", ColorType::Gray(14), 32640); -} - -#[test] -fn test_seq_1c_24b_miniswhite() { - test_image_sum_u32("seq-1c-24b-miniswhite.tiff", ColorType::Gray(24), 32640); -} - -// --- Extended HPredict --- -#[test] -fn test_seq_1c_10b_hpredict() { - test_image_sum_u16("seq-1c-10b-hpredict.tiff", ColorType::Gray(10), 32640); -} - -#[test] -fn test_seq_1c_12b_hpredict() { - test_image_sum_u16("seq-1c-12b-hpredict.tiff", ColorType::Gray(12), 32640); -} - -#[test] -fn test_seq_1c_14b_hpredict() { - test_image_sum_u16("seq-1c-14b-hpredict.tiff", ColorType::Gray(14), 32640); -} - -#[test] -fn test_seq_1c_24b_hpredict() { - test_image_sum_u32("seq-1c-24b-hpredict.tiff", ColorType::Gray(24), 32640); -} - -// --- RGB Contiguous --- -#[test] -fn test_seq_3c_10b_contig() { - test_image_sum_u16("seq-3c-10b-contig.tiff", ColorType::RGB(10), 294528); -} - -#[test] -fn test_seq_3c_12b_contig() { - test_image_sum_u16("seq-3c-12b-contig.tiff", ColorType::RGB(12), 294528); -} - -#[test] -fn test_seq_3c_14b_contig() { - test_image_sum_u16("seq-3c-14b-contig.tiff", ColorType::RGB(14), 294528); -} - -#[test] -fn test_seq_3c_24b_contig() { - test_image_sum_u32("seq-3c-24b-contig.tiff", ColorType::RGB(24), 294528); -} - -// --- RGB Planar (first plane only via read_image) --- -#[test] -fn test_seq_3c_10b_planar() { - test_image_sum_u16("seq-3c-10b-planar.tiff", ColorType::RGB(10), 32640); -} - -#[test] -fn test_seq_3c_12b_planar() { - test_image_sum_u16("seq-3c-12b-planar.tiff", ColorType::RGB(12), 32640); -} - -#[test] -fn test_seq_3c_14b_planar() { - test_image_sum_u16("seq-3c-14b-planar.tiff", ColorType::RGB(14), 32640); -} - -#[test] -fn test_seq_3c_24b_planar() { - test_image_sum_u32("seq-3c-24b-planar.tiff", ColorType::RGB(24), 32640); -} - -// --- Compressed (LZW, Deflate, PackBits) --- -#[test] -fn test_seq_1c_8b_lzw() { - test_image_sum_u8("seq-1c-8b-lzw.tiff", ColorType::Gray(8), 32640); -} - -#[test] -fn test_seq_1c_8b_deflate() { - test_image_sum_u8("seq-1c-8b-deflate.tiff", ColorType::Gray(8), 32640); -} - -#[test] -fn test_seq_1c_8b_packbits() { - test_image_sum_u8("seq-1c-8b-packbits.tiff", ColorType::Gray(8), 32640); -} - -#[test] -fn test_seq_1c_16b_lzw() { - test_image_sum_u16("seq-1c-16b-lzw.tiff", ColorType::Gray(16), 32640); -} - -#[test] -fn test_seq_1c_16b_deflate() { - test_image_sum_u16("seq-1c-16b-deflate.tiff", ColorType::Gray(16), 32640); -} - -#[test] -fn test_seq_1c_8b_lzw_hpredict() { - test_image_sum_u8("seq-1c-8b-lzw-hpredict.tiff", ColorType::Gray(8), 32640); -} - -#[test] -fn test_seq_3c_8b_lzw() { - test_image_sum_u8("seq-3c-8b-lzw.tiff", ColorType::RGB(8), 97920); -} - -// --- Tiled layout --- -#[test] -fn test_seq_1c_8b_tiled() { - test_image_sum_u8("seq-1c-8b-tiled.tiff", ColorType::Gray(8), 32640); -} - -#[test] -fn test_seq_1c_16b_tiled() { - test_image_sum_u16("seq-1c-16b-tiled.tiff", ColorType::Gray(16), 32640); -} - -#[test] -fn test_seq_3c_8b_tiled() { - test_image_sum_u8("seq-3c-8b-tiled.tiff", ColorType::RGB(8), 97920); -} - -// --- BigTIFF --- -#[test] -fn test_seq_1c_8b_bigtiff() { - test_image_sum_u8("seq-1c-8b-bigtiff.tiff", ColorType::Gray(8), 32640); -} - -#[test] -fn test_seq_3c_16b_bigtiff() { - test_image_sum_u16("seq-3c-16b-bigtiff.tiff", ColorType::RGB(16), 294528); -} - -// --- Float samples --- -#[test] -fn test_seq_1c_32f() { - test_image_sum_f32("seq-1c-32f.tiff", ColorType::Gray(32), 128.00009); -} - -#[test] -fn test_seq_1c_64f() { - test_image_sum_f64("seq-1c-64f.tiff", ColorType::Gray(64), 127.99999999999986); -} - -#[test] -fn test_seq_3c_32f() { - test_image_sum_f32("seq-3c-32f.tiff", ColorType::RGB(32), 384.0); -} - -// --- Signed integer --- -#[test] -fn test_seq_1c_i8() { - test_image_sum_i8("seq-1c-i8.tiff", ColorType::Gray(8), -128); -} - -#[test] -fn test_seq_1c_i16() { - test_image_sum_i16("seq-1c-i16.tiff", ColorType::Gray(16), -128); -} - -#[test] -fn test_seq_1c_i32() { - test_image_sum_i32("seq-1c-i32.tiff", ColorType::Gray(32), -128); -} - -// --- CMYK --- -#[test] -fn test_seq_4c_8b_cmyk() { - test_image_sum_u8("seq-4c-8b-cmyk.tiff", ColorType::CMYK(8), 130560); -} - -#[test] -fn test_seq_4c_16b_cmyk() { - test_image_sum_u16("seq-4c-16b-cmyk.tiff", ColorType::CMYK(16), 523776); -} - -// --- Palette (indexed) --- -#[test] -fn test_seq_1c_8b_palette() { - test_image_sum_u8("seq-1c-8b-palette.tiff", ColorType::Palette(8), 32640); -} - -#[test] -fn test_seq_1c_4b_palette() { - test_image_sum_u8("seq-1c-4b-palette.tiff", ColorType::Palette(4), 15360); -} - -// --- RGBA (associated alpha) --- -#[test] -fn test_seq_4c_8b_rgba() { - test_image_sum_u8("seq-4c-8b-rgba.tiff", ColorType::RGBA(8), 130560); -} - -#[test] -fn test_seq_4c_16b_rgba() { - test_image_sum_u16("seq-4c-16b-rgba.tiff", ColorType::RGBA(16), 523776); -} - -// --- Multi-page (reads first page only via standard macro) --- -#[test] -fn test_seq_1c_8b_multipage() { - test_image_sum_u8("seq-1c-8b-multipage.tiff", ColorType::Gray(8), 32640); -} - -#[test] -fn test_seq_1c_8b_multipage_all_pages() { - let path = PathBuf::from(TEST_IMAGE_DIR).join("seq-1c-8b-multipage.tiff"); - let img_file = File::open(path).expect("Cannot find test image!"); - let mut decoder = Decoder::open(img_file).expect("Cannot create decoder"); - - let expected_sums: [u64; 3] = [32640, 32640, 32640]; - for (page, &expected) in expected_sums.iter().enumerate() { - decoder.next_image().expect("Cannot read image IFD"); - assert_eq!(decoder.colortype().unwrap(), ColorType::Gray(8)); - match decoder.read_image().unwrap() { - DecodingResult::U8(res) => { - let sum: u64 = res.into_iter().map(u64::from).sum(); - assert_eq!(sum, expected, "Page {} sum mismatch", page); - } - _ => panic!("Wrong bit depth for page {}", page), - } - } -} - -// --- Big-endian byte order --- -#[test] -fn test_seq_1c_8b_bigendian() { - test_image_sum_u8("seq-1c-8b-bigendian.tiff", ColorType::Gray(8), 32640); -} - -#[test] -fn test_seq_1c_16b_bigendian() { - test_image_sum_u16("seq-1c-16b-bigendian.tiff", ColorType::Gray(16), 32640); -} - -#[test] -fn test_seq_3c_8b_bigendian() { - test_image_sum_u8("seq-3c-8b-bigendian.tiff", ColorType::RGB(8), 97920); -} - -// --- Standard sub-byte depths --- -#[test] -fn test_seq_1c_1b() { - test_image_sum_u8("seq-1c-1b.tiff", ColorType::Gray(1), 2720); -} - -#[test] -fn test_seq_1c_2b() { - test_image_sum_u8("seq-1c-2b.tiff", ColorType::Gray(2), 1728); -} - -#[test] -fn test_seq_1c_4b() { - test_image_sum_u8("seq-1c-4b.tiff", ColorType::Gray(4), 15360); -} - -#[test] -fn test_seq_1c_1b_miniswhite() { - test_image_sum_u8("seq-1c-1b-miniswhite.tiff", ColorType::Gray(1), 2720); -} - -#[test] -fn test_seq_1c_4b_miniswhite() { - test_image_sum_u8("seq-1c-4b-miniswhite.tiff", ColorType::Gray(4), 15360); -} - -// --- Multi-strip (RowsPerStrip=4) --- -#[test] -fn test_seq_1c_8b_multistrip() { - test_image_sum_u8("seq-1c-8b-multistrip.tiff", ColorType::Gray(8), 32640); -} - -#[test] -fn test_seq_1c_16b_multistrip() { - test_image_sum_u16("seq-1c-16b-multistrip.tiff", ColorType::Gray(16), 32640); -} - -#[test] -fn test_seq_3c_8b_multistrip() { - test_image_sum_u8("seq-3c-8b-multistrip.tiff", ColorType::RGB(8), 97920); -} - -// --- Tiled + compressed --- -#[test] -fn test_seq_1c_8b_tiled_lzw() { - test_image_sum_u8("seq-1c-8b-tiled-lzw.tiff", ColorType::Gray(8), 32640); -} - -#[test] -fn test_seq_1c_8b_tiled_deflate() { - test_image_sum_u8("seq-1c-8b-tiled-deflate.tiff", ColorType::Gray(8), 32640); -} - -// --- Float + floating-point predictor --- -#[test] -fn test_seq_1c_32f_deflate_fpredict() { - test_image_sum_f32("seq-1c-32f-deflate-fpredict.tiff", ColorType::Gray(32), 128.00009); -} - -#[test] -fn test_seq_1c_64f_deflate_fpredict() { - test_image_sum_f64( - "seq-1c-64f-deflate-fpredict.tiff", - ColorType::Gray(64), - 127.99999999999986, - ); -} - -// --- Unassociated alpha --- -#[test] -fn test_seq_4c_8b_rgba_unassoc() { - test_image_sum_u8("seq-4c-8b-rgba-unassoc.tiff", ColorType::RGBA(8), 130560); -} - -// --- Sub-byte RGB contiguous --- -#[test] -fn test_seq_3c_5b_contig() { - test_image_sum_u8("seq-3c-5b-contig.tiff", ColorType::RGB(5), 56832); -} - -#[test] -fn test_seq_3c_7b_contig() { - test_image_sum_u8("seq-3c-7b-contig.tiff", ColorType::RGB(7), 74976); -} - -// --- Signed RGB (decoded as Multiband since PHOTOMETRIC_MINISBLACK) --- -#[test] -fn test_seq_3c_i8() { - test_image_sum_i8( - "seq-3c-i8.tiff", - ColorType::Multiband { - bit_depth: 8, - num_samples: 3, - }, - -384, - ); -} - -#[test] -fn test_seq_3c_i16() { - test_image_sum_i16( - "seq-3c-i16.tiff", - ColorType::Multiband { - bit_depth: 16, - num_samples: 3, - }, - -384, - ); -} - -// --- Float f64 RGB --- -#[test] -fn test_seq_3c_64f() { - test_image_sum_f64("seq-3c-64f.tiff", ColorType::RGB(64), 383.99999999999994); -} - -// --- Tiled BigTIFF --- -#[test] -fn test_seq_1c_8b_tiled_bigtiff() { - test_image_sum_u8("seq-1c-8b-tiled-bigtiff.tiff", ColorType::Gray(8), 32640); -} diff --git a/tests/images/seq-1c-10b-hpredict.tiff b/tests/images/seq/seq-1c-10b-ebb39b8d.tiff similarity index 100% rename from tests/images/seq-1c-10b-hpredict.tiff rename to tests/images/seq/seq-1c-10b-ebb39b8d.tiff diff --git a/tests/images/seq-1c-10b.tiff b/tests/images/seq/seq-1c-10b-hpredict-ebb39b8d.tiff similarity index 100% rename from tests/images/seq-1c-10b.tiff rename to tests/images/seq/seq-1c-10b-hpredict-ebb39b8d.tiff diff --git a/tests/images/seq-1c-10b-miniswhite.tiff b/tests/images/seq/seq-1c-10b-miniswhite-ebb39b8d.tiff similarity index 100% rename from tests/images/seq-1c-10b-miniswhite.tiff rename to tests/images/seq/seq-1c-10b-miniswhite-ebb39b8d.tiff diff --git a/tests/images/seq-1c-12b-hpredict.tiff b/tests/images/seq/seq-1c-12b-349c9c76.tiff similarity index 100% rename from tests/images/seq-1c-12b-hpredict.tiff rename to tests/images/seq/seq-1c-12b-349c9c76.tiff diff --git a/tests/images/seq-1c-12b.tiff b/tests/images/seq/seq-1c-12b-hpredict-349c9c76.tiff similarity index 100% rename from tests/images/seq-1c-12b.tiff rename to tests/images/seq/seq-1c-12b-hpredict-349c9c76.tiff diff --git a/tests/images/seq-1c-12b-miniswhite.tiff b/tests/images/seq/seq-1c-12b-miniswhite-349c9c76.tiff similarity index 100% rename from tests/images/seq-1c-12b-miniswhite.tiff rename to tests/images/seq/seq-1c-12b-miniswhite-349c9c76.tiff diff --git a/tests/images/seq-1c-14b-hpredict.tiff b/tests/images/seq/seq-1c-14b-7e799edf.tiff similarity index 100% rename from tests/images/seq-1c-14b-hpredict.tiff rename to tests/images/seq/seq-1c-14b-7e799edf.tiff diff --git a/tests/images/seq-1c-14b.tiff b/tests/images/seq/seq-1c-14b-hpredict-7e799edf.tiff similarity index 100% rename from tests/images/seq-1c-14b.tiff rename to tests/images/seq/seq-1c-14b-hpredict-7e799edf.tiff diff --git a/tests/images/seq-1c-14b-miniswhite.tiff b/tests/images/seq/seq-1c-14b-miniswhite-7e799edf.tiff similarity index 100% rename from tests/images/seq-1c-14b-miniswhite.tiff rename to tests/images/seq/seq-1c-14b-miniswhite-7e799edf.tiff diff --git a/tests/images/seq-1c-16b-bigendian.tiff b/tests/images/seq/seq-1c-16b-bigendian-6b088a4a.tiff similarity index 100% rename from tests/images/seq-1c-16b-bigendian.tiff rename to tests/images/seq/seq-1c-16b-bigendian-6b088a4a.tiff diff --git a/tests/images/seq-1c-16b-deflate.tiff b/tests/images/seq/seq-1c-16b-deflate-6b088a4a.tiff similarity index 100% rename from tests/images/seq-1c-16b-deflate.tiff rename to tests/images/seq/seq-1c-16b-deflate-6b088a4a.tiff diff --git a/tests/images/seq-1c-16b-lzw.tiff b/tests/images/seq/seq-1c-16b-lzw-6b088a4a.tiff similarity index 100% rename from tests/images/seq-1c-16b-lzw.tiff rename to tests/images/seq/seq-1c-16b-lzw-6b088a4a.tiff diff --git a/tests/images/seq-1c-16b-multistrip.tiff b/tests/images/seq/seq-1c-16b-multistrip-6b088a4a.tiff similarity index 100% rename from tests/images/seq-1c-16b-multistrip.tiff rename to tests/images/seq/seq-1c-16b-multistrip-6b088a4a.tiff diff --git a/tests/images/seq-1c-16b-tiled.tiff b/tests/images/seq/seq-1c-16b-tiled-6b088a4a.tiff similarity index 100% rename from tests/images/seq-1c-16b-tiled.tiff rename to tests/images/seq/seq-1c-16b-tiled-6b088a4a.tiff diff --git a/tests/images/seq-1c-1b.tiff b/tests/images/seq/seq-1c-1b-f2966d13.tiff similarity index 100% rename from tests/images/seq-1c-1b.tiff rename to tests/images/seq/seq-1c-1b-f2966d13.tiff diff --git a/tests/images/seq-1c-1b-miniswhite.tiff b/tests/images/seq/seq-1c-1b-miniswhite-f2966d13.tiff similarity index 100% rename from tests/images/seq-1c-1b-miniswhite.tiff rename to tests/images/seq/seq-1c-1b-miniswhite-f2966d13.tiff diff --git a/tests/images/seq-1c-24b-hpredict.tiff b/tests/images/seq/seq-1c-24b-8e6d7cb5.tiff similarity index 100% rename from tests/images/seq-1c-24b-hpredict.tiff rename to tests/images/seq/seq-1c-24b-8e6d7cb5.tiff diff --git a/tests/images/seq-1c-24b.tiff b/tests/images/seq/seq-1c-24b-hpredict-8e6d7cb5.tiff similarity index 100% rename from tests/images/seq-1c-24b.tiff rename to tests/images/seq/seq-1c-24b-hpredict-8e6d7cb5.tiff diff --git a/tests/images/seq-1c-24b-miniswhite.tiff b/tests/images/seq/seq-1c-24b-miniswhite-8e6d7cb5.tiff similarity index 100% rename from tests/images/seq-1c-24b-miniswhite.tiff rename to tests/images/seq/seq-1c-24b-miniswhite-8e6d7cb5.tiff diff --git a/tests/images/seq-1c-2b.tiff b/tests/images/seq/seq-1c-2b-eb3e4e3a.tiff similarity index 100% rename from tests/images/seq-1c-2b.tiff rename to tests/images/seq/seq-1c-2b-eb3e4e3a.tiff diff --git a/tests/images/seq-1c-32f.tiff b/tests/images/seq/seq-1c-32f-a6d061ac.tiff similarity index 100% rename from tests/images/seq-1c-32f.tiff rename to tests/images/seq/seq-1c-32f-a6d061ac.tiff diff --git a/tests/images/seq-1c-32f-deflate-fpredict.tiff b/tests/images/seq/seq-1c-32f-deflate-fpredict-a6d061ac.tiff similarity index 100% rename from tests/images/seq-1c-32f-deflate-fpredict.tiff rename to tests/images/seq/seq-1c-32f-deflate-fpredict-a6d061ac.tiff diff --git a/tests/images/seq-1c-3b.tiff b/tests/images/seq/seq-1c-3b-b64646a5.tiff similarity index 100% rename from tests/images/seq-1c-3b.tiff rename to tests/images/seq/seq-1c-3b-b64646a5.tiff diff --git a/tests/images/seq-1c-3b-miniswhite.tiff b/tests/images/seq/seq-1c-3b-miniswhite-b64646a5.tiff similarity index 100% rename from tests/images/seq-1c-3b-miniswhite.tiff rename to tests/images/seq/seq-1c-3b-miniswhite-b64646a5.tiff diff --git a/tests/images/seq-1c-4b.tiff b/tests/images/seq/seq-1c-4b-b3c1805b.tiff similarity index 100% rename from tests/images/seq-1c-4b.tiff rename to tests/images/seq/seq-1c-4b-b3c1805b.tiff diff --git a/tests/images/seq-1c-4b-miniswhite.tiff b/tests/images/seq/seq-1c-4b-miniswhite-b3c1805b.tiff similarity index 100% rename from tests/images/seq-1c-4b-miniswhite.tiff rename to tests/images/seq/seq-1c-4b-miniswhite-b3c1805b.tiff diff --git a/tests/images/seq-1c-4b-palette.tiff b/tests/images/seq/seq-1c-4b-palette-80edc956.tiff similarity index 100% rename from tests/images/seq-1c-4b-palette.tiff rename to tests/images/seq/seq-1c-4b-palette-80edc956.tiff diff --git a/tests/images/seq-1c-5b.tiff b/tests/images/seq/seq-1c-5b-59011bc5.tiff similarity index 100% rename from tests/images/seq-1c-5b.tiff rename to tests/images/seq/seq-1c-5b-59011bc5.tiff diff --git a/tests/images/seq-1c-5b-miniswhite.tiff b/tests/images/seq/seq-1c-5b-miniswhite-59011bc5.tiff similarity index 100% rename from tests/images/seq-1c-5b-miniswhite.tiff rename to tests/images/seq/seq-1c-5b-miniswhite-59011bc5.tiff diff --git a/tests/images/seq-1c-64f.tiff b/tests/images/seq/seq-1c-64f-8a733620.tiff similarity index 100% rename from tests/images/seq-1c-64f.tiff rename to tests/images/seq/seq-1c-64f-8a733620.tiff diff --git a/tests/images/seq-1c-64f-deflate-fpredict.tiff b/tests/images/seq/seq-1c-64f-deflate-fpredict-8a733620.tiff similarity index 100% rename from tests/images/seq-1c-64f-deflate-fpredict.tiff rename to tests/images/seq/seq-1c-64f-deflate-fpredict-8a733620.tiff diff --git a/tests/images/seq-1c-6b-miniswhite.tiff b/tests/images/seq/seq-1c-6b-miniswhite-7142de37.tiff similarity index 100% rename from tests/images/seq-1c-6b-miniswhite.tiff rename to tests/images/seq/seq-1c-6b-miniswhite-7142de37.tiff diff --git a/tests/images/seq-1c-7b.tiff b/tests/images/seq/seq-1c-7b-bd122024.tiff similarity index 100% rename from tests/images/seq-1c-7b.tiff rename to tests/images/seq/seq-1c-7b-bd122024.tiff diff --git a/tests/images/seq-1c-7b-miniswhite.tiff b/tests/images/seq/seq-1c-7b-miniswhite-bd122024.tiff similarity index 100% rename from tests/images/seq-1c-7b-miniswhite.tiff rename to tests/images/seq/seq-1c-7b-miniswhite-bd122024.tiff diff --git a/tests/images/seq-1c-8b-bigendian.tiff b/tests/images/seq/seq-1c-8b-bigendian-86098311.tiff similarity index 100% rename from tests/images/seq-1c-8b-bigendian.tiff rename to tests/images/seq/seq-1c-8b-bigendian-86098311.tiff diff --git a/tests/images/seq-1c-8b-bigtiff.tiff b/tests/images/seq/seq-1c-8b-bigtiff-86098311.tiff similarity index 100% rename from tests/images/seq-1c-8b-bigtiff.tiff rename to tests/images/seq/seq-1c-8b-bigtiff-86098311.tiff diff --git a/tests/images/seq-1c-8b-deflate.tiff b/tests/images/seq/seq-1c-8b-deflate-86098311.tiff similarity index 100% rename from tests/images/seq-1c-8b-deflate.tiff rename to tests/images/seq/seq-1c-8b-deflate-86098311.tiff diff --git a/tests/images/seq-1c-8b-lzw.tiff b/tests/images/seq/seq-1c-8b-lzw-86098311.tiff similarity index 100% rename from tests/images/seq-1c-8b-lzw.tiff rename to tests/images/seq/seq-1c-8b-lzw-86098311.tiff diff --git a/tests/images/seq-1c-8b-lzw-hpredict.tiff b/tests/images/seq/seq-1c-8b-lzw-hpredict-86098311.tiff similarity index 100% rename from tests/images/seq-1c-8b-lzw-hpredict.tiff rename to tests/images/seq/seq-1c-8b-lzw-hpredict-86098311.tiff diff --git a/tests/images/seq-1c-8b-multipage.tiff b/tests/images/seq/seq-1c-8b-multipage-0c8d8775.tiff similarity index 100% rename from tests/images/seq-1c-8b-multipage.tiff rename to tests/images/seq/seq-1c-8b-multipage-0c8d8775.tiff diff --git a/tests/images/seq-1c-8b-multistrip.tiff b/tests/images/seq/seq-1c-8b-multistrip-86098311.tiff similarity index 100% rename from tests/images/seq-1c-8b-multistrip.tiff rename to tests/images/seq/seq-1c-8b-multistrip-86098311.tiff diff --git a/tests/images/seq-1c-8b-packbits.tiff b/tests/images/seq/seq-1c-8b-packbits-86098311.tiff similarity index 100% rename from tests/images/seq-1c-8b-packbits.tiff rename to tests/images/seq/seq-1c-8b-packbits-86098311.tiff diff --git a/tests/images/seq-1c-8b-palette.tiff b/tests/images/seq/seq-1c-8b-palette-7ae71ffd.tiff similarity index 100% rename from tests/images/seq-1c-8b-palette.tiff rename to tests/images/seq/seq-1c-8b-palette-7ae71ffd.tiff diff --git a/tests/images/seq-1c-8b-tiled.tiff b/tests/images/seq/seq-1c-8b-tiled-86098311.tiff similarity index 100% rename from tests/images/seq-1c-8b-tiled.tiff rename to tests/images/seq/seq-1c-8b-tiled-86098311.tiff diff --git a/tests/images/seq-1c-8b-tiled-bigtiff.tiff b/tests/images/seq/seq-1c-8b-tiled-bigtiff-86098311.tiff similarity index 100% rename from tests/images/seq-1c-8b-tiled-bigtiff.tiff rename to tests/images/seq/seq-1c-8b-tiled-bigtiff-86098311.tiff diff --git a/tests/images/seq-1c-8b-tiled-deflate.tiff b/tests/images/seq/seq-1c-8b-tiled-deflate-86098311.tiff similarity index 100% rename from tests/images/seq-1c-8b-tiled-deflate.tiff rename to tests/images/seq/seq-1c-8b-tiled-deflate-86098311.tiff diff --git a/tests/images/seq-1c-8b-tiled-lzw.tiff b/tests/images/seq/seq-1c-8b-tiled-lzw-86098311.tiff similarity index 100% rename from tests/images/seq-1c-8b-tiled-lzw.tiff rename to tests/images/seq/seq-1c-8b-tiled-lzw-86098311.tiff diff --git a/tests/images/seq-1c-i16.tiff b/tests/images/seq/seq-1c-i16-e55284f8.tiff similarity index 100% rename from tests/images/seq-1c-i16.tiff rename to tests/images/seq/seq-1c-i16-e55284f8.tiff diff --git a/tests/images/seq-1c-i32.tiff b/tests/images/seq/seq-1c-i32-5fc9795b.tiff similarity index 100% rename from tests/images/seq-1c-i32.tiff rename to tests/images/seq/seq-1c-i32-5fc9795b.tiff diff --git a/tests/images/seq-1c-i8.tiff b/tests/images/seq/seq-1c-i8-dbcc75e5.tiff similarity index 100% rename from tests/images/seq-1c-i8.tiff rename to tests/images/seq/seq-1c-i8-dbcc75e5.tiff diff --git a/tests/images/seq-3c-10b-contig.tiff b/tests/images/seq/seq-3c-10b-contig-20e8c150.tiff similarity index 100% rename from tests/images/seq-3c-10b-contig.tiff rename to tests/images/seq/seq-3c-10b-contig-20e8c150.tiff diff --git a/tests/images/seq-3c-10b-planar.tiff b/tests/images/seq/seq-3c-10b-planar-bc13e4aa.tiff similarity index 100% rename from tests/images/seq-3c-10b-planar.tiff rename to tests/images/seq/seq-3c-10b-planar-bc13e4aa.tiff diff --git a/tests/images/seq-3c-12b-contig.tiff b/tests/images/seq/seq-3c-12b-contig-5af4bdfa.tiff similarity index 100% rename from tests/images/seq-3c-12b-contig.tiff rename to tests/images/seq/seq-3c-12b-contig-5af4bdfa.tiff diff --git a/tests/images/seq-3c-12b-planar.tiff b/tests/images/seq/seq-3c-12b-planar-633ce351.tiff similarity index 100% rename from tests/images/seq-3c-12b-planar.tiff rename to tests/images/seq/seq-3c-12b-planar-633ce351.tiff diff --git a/tests/images/seq-3c-14b-contig.tiff b/tests/images/seq/seq-3c-14b-contig-c5d06ba3.tiff similarity index 100% rename from tests/images/seq-3c-14b-contig.tiff rename to tests/images/seq/seq-3c-14b-contig-c5d06ba3.tiff diff --git a/tests/images/seq-3c-14b-planar.tiff b/tests/images/seq/seq-3c-14b-planar-29d9e1f8.tiff similarity index 100% rename from tests/images/seq-3c-14b-planar.tiff rename to tests/images/seq/seq-3c-14b-planar-29d9e1f8.tiff diff --git a/tests/images/seq-3c-16b-bigtiff.tiff b/tests/images/seq/seq-3c-16b-bigtiff-8ccce3e0.tiff similarity index 100% rename from tests/images/seq-3c-16b-bigtiff.tiff rename to tests/images/seq/seq-3c-16b-bigtiff-8ccce3e0.tiff diff --git a/tests/images/seq-3c-24b-contig.tiff b/tests/images/seq/seq-3c-24b-contig-e610e399.tiff similarity index 100% rename from tests/images/seq-3c-24b-contig.tiff rename to tests/images/seq/seq-3c-24b-contig-e610e399.tiff diff --git a/tests/images/seq-3c-24b-planar.tiff b/tests/images/seq/seq-3c-24b-planar-82e9baed.tiff similarity index 100% rename from tests/images/seq-3c-24b-planar.tiff rename to tests/images/seq/seq-3c-24b-planar-82e9baed.tiff diff --git a/tests/images/seq-3c-32f.tiff b/tests/images/seq/seq-3c-32f-c90c9901.tiff similarity index 100% rename from tests/images/seq-3c-32f.tiff rename to tests/images/seq/seq-3c-32f-c90c9901.tiff diff --git a/tests/images/seq-3c-5b-contig.tiff b/tests/images/seq/seq-3c-5b-contig-4c1b3a7d.tiff similarity index 100% rename from tests/images/seq-3c-5b-contig.tiff rename to tests/images/seq/seq-3c-5b-contig-4c1b3a7d.tiff diff --git a/tests/images/seq-3c-64f.tiff b/tests/images/seq/seq-3c-64f-16f2fa89.tiff similarity index 100% rename from tests/images/seq-3c-64f.tiff rename to tests/images/seq/seq-3c-64f-16f2fa89.tiff diff --git a/tests/images/seq-3c-7b-contig.tiff b/tests/images/seq/seq-3c-7b-contig-1fa56ce4.tiff similarity index 100% rename from tests/images/seq-3c-7b-contig.tiff rename to tests/images/seq/seq-3c-7b-contig-1fa56ce4.tiff diff --git a/tests/images/seq-3c-8b-bigendian.tiff b/tests/images/seq/seq-3c-8b-bigendian-a837068d.tiff similarity index 100% rename from tests/images/seq-3c-8b-bigendian.tiff rename to tests/images/seq/seq-3c-8b-bigendian-a837068d.tiff diff --git a/tests/images/seq-3c-8b-lzw.tiff b/tests/images/seq/seq-3c-8b-lzw-a837068d.tiff similarity index 100% rename from tests/images/seq-3c-8b-lzw.tiff rename to tests/images/seq/seq-3c-8b-lzw-a837068d.tiff diff --git a/tests/images/seq-3c-8b-multistrip.tiff b/tests/images/seq/seq-3c-8b-multistrip-a837068d.tiff similarity index 100% rename from tests/images/seq-3c-8b-multistrip.tiff rename to tests/images/seq/seq-3c-8b-multistrip-a837068d.tiff diff --git a/tests/images/seq-3c-8b-tiled.tiff b/tests/images/seq/seq-3c-8b-tiled-a837068d.tiff similarity index 100% rename from tests/images/seq-3c-8b-tiled.tiff rename to tests/images/seq/seq-3c-8b-tiled-a837068d.tiff diff --git a/tests/images/seq-3c-i16.tiff b/tests/images/seq/seq-3c-i16-8b69ff38.tiff similarity index 100% rename from tests/images/seq-3c-i16.tiff rename to tests/images/seq/seq-3c-i16-8b69ff38.tiff diff --git a/tests/images/seq-3c-i8.tiff b/tests/images/seq/seq-3c-i8-a5e96bf3.tiff similarity index 100% rename from tests/images/seq-3c-i8.tiff rename to tests/images/seq/seq-3c-i8-a5e96bf3.tiff diff --git a/tests/images/seq-4c-16b-cmyk.tiff b/tests/images/seq/seq-4c-16b-cmyk-e6c1ed1b.tiff similarity index 100% rename from tests/images/seq-4c-16b-cmyk.tiff rename to tests/images/seq/seq-4c-16b-cmyk-e6c1ed1b.tiff diff --git a/tests/images/seq-4c-16b-rgba.tiff b/tests/images/seq/seq-4c-16b-rgba-50a4e810.tiff similarity index 100% rename from tests/images/seq-4c-16b-rgba.tiff rename to tests/images/seq/seq-4c-16b-rgba-50a4e810.tiff diff --git a/tests/images/seq-4c-8b-cmyk.tiff b/tests/images/seq/seq-4c-8b-cmyk-0a0e06d2.tiff similarity index 100% rename from tests/images/seq-4c-8b-cmyk.tiff rename to tests/images/seq/seq-4c-8b-cmyk-0a0e06d2.tiff diff --git a/tests/images/seq-4c-8b-rgba.tiff b/tests/images/seq/seq-4c-8b-rgba-068ac08a.tiff similarity index 100% rename from tests/images/seq-4c-8b-rgba.tiff rename to tests/images/seq/seq-4c-8b-rgba-068ac08a.tiff diff --git a/tests/images/seq-4c-8b-rgba-unassoc.tiff b/tests/images/seq/seq-4c-8b-rgba-unassoc-068ac08a.tiff similarity index 100% rename from tests/images/seq-4c-8b-rgba-unassoc.tiff rename to tests/images/seq/seq-4c-8b-rgba-unassoc-068ac08a.tiff