diff --git a/tests/decode_images.rs b/tests/decode_images.rs index 1170f25..5ccc79c 100644 --- a/tests/decode_images.rs +++ b/tests/decode_images.rs @@ -1318,3 +1318,125 @@ fn test_seq_1c_16b_bigendian() { 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/gen_seq_tiff.c b/tests/gen_seq_tiff.c index 462f649..89f5661 100644 --- a/tests/gen_seq_tiff.c +++ b/tests/gen_seq_tiff.c @@ -671,6 +671,230 @@ static int write_rgba(const char *path, int bits, const char *mode) return 0; } +/* Write a standard-depth grayscale TIFF with configurable RowsPerStrip (multi-strip). */ +static int write_gray_multistrip(const char *path, int bits, int rows_per_strip) +{ + TIFF *tif = TIFFOpen(path, "w"); + if (!tif) { fprintf(stderr, "Cannot open %s\n", path); return -1; } + + TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, W); + TIFFSetField(tif, TIFFTAG_IMAGELENGTH, H); + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1); + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bits); + TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT); + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); + TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE); + TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, rows_per_strip); + + uint32_t max_val = (bits < 32) ? ((1u << bits) - 1) : 0xFFFFFFFFu; + tsize_t scanline_size = TIFFScanlineSize(tif); + uint8_t *buf = (uint8_t *)_TIFFmalloc(scanline_size); + if (!buf) { TIFFClose(tif); return -1; } + + for (int y = 0; y < H; y++) { + memset(buf, 0, scanline_size); + for (int x = 0; x < W; x++) { + uint32_t val = (y * W + x) % (max_val + 1); + if (bits == 8) + buf[x] = (uint8_t)val; + else if (bits == 16) + ((uint16_t *)buf)[x] = (uint16_t)val; + } + if (TIFFWriteScanline(tif, buf, y, 0) < 0) { + fprintf(stderr, "Error writing scanline %d to %s\n", y, path); + _TIFFfree(buf); TIFFClose(tif); return -1; + } + } + + _TIFFfree(buf); + TIFFClose(tif); + return 0; +} + +/* Write a standard-depth RGB TIFF with configurable RowsPerStrip. */ +static int write_rgb_multistrip(const char *path, int bits, int rows_per_strip) +{ + TIFF *tif = TIFFOpen(path, "w"); + if (!tif) { fprintf(stderr, "Cannot open %s\n", path); return -1; } + + TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, W); + TIFFSetField(tif, TIFFTAG_IMAGELENGTH, H); + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3); + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bits); + TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT); + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); + TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE); + TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, rows_per_strip); + + uint32_t max_val = (bits < 32) ? ((1u << bits) - 1) : 0xFFFFFFFFu; + tsize_t scanline_size = TIFFScanlineSize(tif); + uint8_t *buf = (uint8_t *)_TIFFmalloc(scanline_size); + if (!buf) { TIFFClose(tif); return -1; } + + for (int y = 0; y < H; y++) { + memset(buf, 0, scanline_size); + for (int x = 0; x < W; x++) { + for (int c = 0; c < 3; c++) { + uint32_t idx = (y * W + x) * 3 + c; + uint32_t val = idx % (max_val + 1); + if (bits == 8) + buf[x * 3 + c] = (uint8_t)val; + else if (bits == 16) + ((uint16_t *)buf)[x * 3 + c] = (uint16_t)val; + } + } + if (TIFFWriteScanline(tif, buf, y, 0) < 0) { + fprintf(stderr, "Error writing scanline %d to %s\n", y, path); + _TIFFfree(buf); TIFFClose(tif); return -1; + } + } + + _TIFFfree(buf); + TIFFClose(tif); + return 0; +} + +/* Write a tiled grayscale TIFF with compression. */ +static int write_gray_tiled_comp(const char *path, int bits, + uint16_t compression, const char *mode) +{ + TIFF *tif = TIFFOpen(path, mode); + if (!tif) { fprintf(stderr, "Cannot open %s\n", path); return -1; } + + int tw = 16, th = 16; + TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, W); + TIFFSetField(tif, TIFFTAG_IMAGELENGTH, H); + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1); + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bits); + TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT); + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); + TIFFSetField(tif, TIFFTAG_COMPRESSION, compression); + TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField(tif, TIFFTAG_TILEWIDTH, tw); + TIFFSetField(tif, TIFFTAG_TILELENGTH, th); + + uint32_t max_val = (bits < 32) ? ((1u << bits) - 1) : 0xFFFFFFFFu; + tsize_t tile_size = TIFFTileSize(tif); + uint8_t *tile_buf = (uint8_t *)_TIFFmalloc(tile_size); + if (!tile_buf) { TIFFClose(tif); return -1; } + + for (int ty = 0; ty < H; ty += th) { + for (int tx = 0; tx < W; tx += tw) { + memset(tile_buf, 0, tile_size); + for (int row = 0; row < th; row++) { + for (int col = 0; col < tw; col++) { + int y = ty + row, x = tx + col; + uint32_t val = (y * W + x) % (max_val + 1); + if (bits == 8) + tile_buf[row * tw + col] = (uint8_t)val; + else if (bits == 16) + ((uint16_t *)tile_buf)[row * tw + col] = (uint16_t)val; + } + } + if (TIFFWriteTile(tif, tile_buf, tx, ty, 0, 0) < 0) { + fprintf(stderr, "Error writing tile at (%d,%d) to %s\n", tx, ty, path); + _TIFFfree(tile_buf); TIFFClose(tif); return -1; + } + } + } + + _TIFFfree(tile_buf); + TIFFClose(tif); + return 0; +} + +/* Write a grayscale float TIFF with compression + predictor. + * Values are (y*W+x)/255.0 */ +static int write_gray_float_comp(const char *path, int float_bits, + uint16_t compression, uint16_t predictor, + const char *mode) +{ + TIFF *tif = TIFFOpen(path, mode); + if (!tif) { fprintf(stderr, "Cannot open %s\n", path); return -1; } + + TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, W); + TIFFSetField(tif, TIFFTAG_IMAGELENGTH, H); + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1); + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, float_bits); + TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP); + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); + TIFFSetField(tif, TIFFTAG_COMPRESSION, compression); + TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, H); + if (predictor != PREDICTOR_NONE) + TIFFSetField(tif, TIFFTAG_PREDICTOR, predictor); + + tsize_t scanline_size = TIFFScanlineSize(tif); + uint8_t *buf = (uint8_t *)_TIFFmalloc(scanline_size); + if (!buf) { TIFFClose(tif); return -1; } + + for (int y = 0; y < H; y++) { + memset(buf, 0, scanline_size); + for (int x = 0; x < W; x++) { + double val = (double)(y * W + x) / 255.0; + if (float_bits == 32) + ((float *)buf)[x] = (float)val; + else + ((double *)buf)[x] = val; + } + if (TIFFWriteScanline(tif, buf, y, 0) < 0) { + fprintf(stderr, "Error writing scanline %d to %s\n", y, path); + _TIFFfree(buf); TIFFClose(tif); return -1; + } + } + + _TIFFfree(buf); + TIFFClose(tif); + return 0; +} + +/* Write a signed integer RGB TIFF. Values cycle -128..127 per channel. */ +static int write_rgb_signed(const char *path, int bits, const char *mode) +{ + TIFF *tif = TIFFOpen(path, mode); + if (!tif) { fprintf(stderr, "Cannot open %s\n", path); return -1; } + + TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, W); + TIFFSetField(tif, TIFFTAG_IMAGELENGTH, H); + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3); + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bits); + TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_INT); + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); + TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE); + TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, H); + + tsize_t scanline_size = TIFFScanlineSize(tif); + uint8_t *buf = (uint8_t *)_TIFFmalloc(scanline_size); + if (!buf) { TIFFClose(tif); return -1; } + + for (int y = 0; y < H; y++) { + memset(buf, 0, scanline_size); + for (int x = 0; x < W; x++) { + for (int c = 0; c < 3; c++) { + int32_t idx = (y * W + x) * 3 + c; + if (bits == 8) { + int8_t val = (int8_t)((idx % 256) - 128); + ((int8_t *)buf)[x * 3 + c] = val; + } else if (bits == 16) { + int16_t val = (int16_t)((idx % 256) - 128); + ((int16_t *)buf)[x * 3 + c] = val; + } + } + } + if (TIFFWriteScanline(tif, buf, y, 0) < 0) { + fprintf(stderr, "Error writing scanline %d to %s\n", y, path); + _TIFFfree(buf); TIFFClose(tif); return -1; + } + } + + _TIFFfree(buf); + TIFFClose(tif); + return 0; +} + /* Write a multi-page TIFF with 3 gray pages. Page n has values offset by n*50. */ static int write_multipage(const char *path, int bits, const char *mode) { @@ -976,6 +1200,152 @@ int main(int argc, char **argv) printf(" OK seq-3c-8b-bigendian.tiff\n"); else { printf(" FAIL seq-3c-8b-bigendian.tiff\n"); ok = 0; } + printf("\n--- Standard sub-byte depths ---\n"); + + /* 1-bit, 2-bit, 4-bit BlackIsZero */ + { + int stdbits[] = {1, 2, 4}; + for (int i = 0; i < 3; i++) { + int b = stdbits[i]; + snprintf(path, sizeof(path), "%s/seq-1c-%db.tiff", dir, b); + if (write_gray(path, b, PHOTOMETRIC_MINISBLACK, PREDICTOR_NONE) == 0) + printf(" OK seq-1c-%db.tiff\n", b); + else { printf(" FAIL seq-1c-%db.tiff\n", b); ok = 0; } + } + } + + /* 1-bit, 4-bit WhiteIsZero */ + { + int wbits[] = {1, 4}; + for (int i = 0; i < 2; i++) { + int b = wbits[i]; + snprintf(path, sizeof(path), "%s/seq-1c-%db-miniswhite.tiff", dir, b); + if (write_gray(path, b, PHOTOMETRIC_MINISWHITE, PREDICTOR_NONE) == 0) + printf(" OK seq-1c-%db-miniswhite.tiff\n", b); + else { printf(" FAIL seq-1c-%db-miniswhite.tiff\n", b); ok = 0; } + } + } + + printf("\n--- Multi-strip (RowsPerStrip=4) ---\n"); + + snprintf(path, sizeof(path), "%s/seq-1c-8b-multistrip.tiff", dir); + if (write_gray_multistrip(path, 8, 4) == 0) + printf(" OK seq-1c-8b-multistrip.tiff\n"); + else { printf(" FAIL seq-1c-8b-multistrip.tiff\n"); ok = 0; } + + snprintf(path, sizeof(path), "%s/seq-1c-16b-multistrip.tiff", dir); + if (write_gray_multistrip(path, 16, 4) == 0) + printf(" OK seq-1c-16b-multistrip.tiff\n"); + else { printf(" FAIL seq-1c-16b-multistrip.tiff\n"); ok = 0; } + + snprintf(path, sizeof(path), "%s/seq-3c-8b-multistrip.tiff", dir); + if (write_rgb_multistrip(path, 8, 4) == 0) + printf(" OK seq-3c-8b-multistrip.tiff\n"); + else { printf(" FAIL seq-3c-8b-multistrip.tiff\n"); ok = 0; } + + printf("\n--- Tiled + compressed ---\n"); + + snprintf(path, sizeof(path), "%s/seq-1c-8b-tiled-lzw.tiff", dir); + if (write_gray_tiled_comp(path, 8, COMPRESSION_LZW, "w") == 0) + printf(" OK seq-1c-8b-tiled-lzw.tiff\n"); + else { printf(" FAIL seq-1c-8b-tiled-lzw.tiff\n"); ok = 0; } + + snprintf(path, sizeof(path), "%s/seq-1c-8b-tiled-deflate.tiff", dir); + if (write_gray_tiled_comp(path, 8, COMPRESSION_DEFLATE, "w") == 0) + printf(" OK seq-1c-8b-tiled-deflate.tiff\n"); + else { printf(" FAIL seq-1c-8b-tiled-deflate.tiff\n"); ok = 0; } + + printf("\n--- Float + predictor ---\n"); + + snprintf(path, sizeof(path), "%s/seq-1c-32f-deflate-fpredict.tiff", dir); + if (write_gray_float_comp(path, 32, COMPRESSION_DEFLATE, PREDICTOR_FLOATINGPOINT, "w") == 0) + printf(" OK seq-1c-32f-deflate-fpredict.tiff\n"); + else { printf(" FAIL seq-1c-32f-deflate-fpredict.tiff\n"); ok = 0; } + + snprintf(path, sizeof(path), "%s/seq-1c-64f-deflate-fpredict.tiff", dir); + if (write_gray_float_comp(path, 64, COMPRESSION_DEFLATE, PREDICTOR_FLOATINGPOINT, "w") == 0) + printf(" OK seq-1c-64f-deflate-fpredict.tiff\n"); + else { printf(" FAIL seq-1c-64f-deflate-fpredict.tiff\n"); ok = 0; } + + printf("\n--- Unassociated alpha ---\n"); + + /* Reuse write_rgba but with unassociated alpha */ + { + snprintf(path, sizeof(path), "%s/seq-4c-8b-rgba-unassoc.tiff", dir); + TIFF *tif = TIFFOpen(path, "w"); + if (tif) { + uint16_t extra[] = { EXTRASAMPLE_UNASSALPHA }; + TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, W); + TIFFSetField(tif, TIFFTAG_IMAGELENGTH, H); + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 4); + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8); + TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT); + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); + TIFFSetField(tif, TIFFTAG_EXTRASAMPLES, 1, extra); + TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE); + TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, H); + tsize_t sl = TIFFScanlineSize(tif); + uint8_t *b2 = (uint8_t *)_TIFFmalloc(sl); + int good = 1; + if (b2) { + for (int y = 0; y < H && good; y++) { + memset(b2, 0, sl); + for (int x = 0; x < W; x++) + for (int c = 0; c < 4; c++) { + uint32_t idx = (y * W + x) * 4 + c; + b2[x * 4 + c] = (uint8_t)(idx % 256); + } + if (TIFFWriteScanline(tif, b2, y, 0) < 0) good = 0; + } + _TIFFfree(b2); + } else good = 0; + TIFFClose(tif); + if (good) printf(" OK seq-4c-8b-rgba-unassoc.tiff\n"); + else { printf(" FAIL seq-4c-8b-rgba-unassoc.tiff\n"); ok = 0; } + } else { printf(" FAIL seq-4c-8b-rgba-unassoc.tiff\n"); ok = 0; } + } + + printf("\n--- Sub-byte RGB ---\n"); + + /* 5-bit and 7-bit RGB contiguous */ + { + int rgb_sub[] = {5, 7}; + for (int i = 0; i < 2; i++) { + int b = rgb_sub[i]; + snprintf(path, sizeof(path), "%s/seq-3c-%db-contig.tiff", dir, b); + if (write_rgb_contig(path, b) == 0) + printf(" OK seq-3c-%db-contig.tiff\n", b); + else { printf(" FAIL seq-3c-%db-contig.tiff\n", b); ok = 0; } + } + } + + printf("\n--- Signed RGB ---\n"); + + snprintf(path, sizeof(path), "%s/seq-3c-i8.tiff", dir); + if (write_rgb_signed(path, 8, "w") == 0) + printf(" OK seq-3c-i8.tiff\n"); + else { printf(" FAIL seq-3c-i8.tiff\n"); ok = 0; } + + snprintf(path, sizeof(path), "%s/seq-3c-i16.tiff", dir); + if (write_rgb_signed(path, 16, "w") == 0) + printf(" OK seq-3c-i16.tiff\n"); + else { printf(" FAIL seq-3c-i16.tiff\n"); ok = 0; } + + printf("\n--- Float f64 RGB ---\n"); + + snprintf(path, sizeof(path), "%s/seq-3c-64f.tiff", dir); + if (write_rgb_float(path, 64, "w") == 0) + printf(" OK seq-3c-64f.tiff\n"); + else { printf(" FAIL seq-3c-64f.tiff\n"); ok = 0; } + + printf("\n--- Tiled BigTIFF ---\n"); + + snprintf(path, sizeof(path), "%s/seq-1c-8b-tiled-bigtiff.tiff", dir); + if (write_gray_tiled_comp(path, 8, COMPRESSION_NONE, "w8") == 0) + printf(" OK seq-1c-8b-tiled-bigtiff.tiff\n"); + else { printf(" FAIL seq-1c-8b-tiled-bigtiff.tiff\n"); ok = 0; } + printf("\nDone. %s\n", ok ? "All succeeded." : "Some failed!"); return ok ? 0 : 1; } diff --git a/tests/images/seq-1c-16b-multistrip.tiff b/tests/images/seq-1c-16b-multistrip.tiff new file mode 100644 index 0000000..58b842a Binary files /dev/null and b/tests/images/seq-1c-16b-multistrip.tiff differ diff --git a/tests/images/seq-1c-1b-miniswhite.tiff b/tests/images/seq-1c-1b-miniswhite.tiff new file mode 100644 index 0000000..e91ccbd Binary files /dev/null and b/tests/images/seq-1c-1b-miniswhite.tiff differ diff --git a/tests/images/seq-1c-1b.tiff b/tests/images/seq-1c-1b.tiff new file mode 100644 index 0000000..d3c1543 Binary files /dev/null and b/tests/images/seq-1c-1b.tiff differ diff --git a/tests/images/seq-1c-2b.tiff b/tests/images/seq-1c-2b.tiff new file mode 100644 index 0000000..1f59ba7 Binary files /dev/null and b/tests/images/seq-1c-2b.tiff differ diff --git a/tests/images/seq-1c-32f-deflate-fpredict.tiff b/tests/images/seq-1c-32f-deflate-fpredict.tiff new file mode 100644 index 0000000..a5c272f Binary files /dev/null and b/tests/images/seq-1c-32f-deflate-fpredict.tiff differ diff --git a/tests/images/seq-1c-4b-miniswhite.tiff b/tests/images/seq-1c-4b-miniswhite.tiff new file mode 100644 index 0000000..e282a37 Binary files /dev/null and b/tests/images/seq-1c-4b-miniswhite.tiff differ diff --git a/tests/images/seq-1c-4b.tiff b/tests/images/seq-1c-4b.tiff new file mode 100644 index 0000000..0a8aa99 Binary files /dev/null and b/tests/images/seq-1c-4b.tiff differ diff --git a/tests/images/seq-1c-64f-deflate-fpredict.tiff b/tests/images/seq-1c-64f-deflate-fpredict.tiff new file mode 100644 index 0000000..825f10d Binary files /dev/null and b/tests/images/seq-1c-64f-deflate-fpredict.tiff differ diff --git a/tests/images/seq-1c-8b-multistrip.tiff b/tests/images/seq-1c-8b-multistrip.tiff new file mode 100644 index 0000000..4fd3fbf Binary files /dev/null and b/tests/images/seq-1c-8b-multistrip.tiff differ diff --git a/tests/images/seq-1c-8b-tiled-bigtiff.tiff b/tests/images/seq-1c-8b-tiled-bigtiff.tiff new file mode 100644 index 0000000..b427bb6 Binary files /dev/null and b/tests/images/seq-1c-8b-tiled-bigtiff.tiff differ diff --git a/tests/images/seq-1c-8b-tiled-deflate.tiff b/tests/images/seq-1c-8b-tiled-deflate.tiff new file mode 100644 index 0000000..aac05d0 Binary files /dev/null and b/tests/images/seq-1c-8b-tiled-deflate.tiff differ diff --git a/tests/images/seq-1c-8b-tiled-lzw.tiff b/tests/images/seq-1c-8b-tiled-lzw.tiff new file mode 100644 index 0000000..e577eeb Binary files /dev/null and b/tests/images/seq-1c-8b-tiled-lzw.tiff differ diff --git a/tests/images/seq-3c-5b-contig.tiff b/tests/images/seq-3c-5b-contig.tiff new file mode 100644 index 0000000..c3e9860 Binary files /dev/null and b/tests/images/seq-3c-5b-contig.tiff differ diff --git a/tests/images/seq-3c-64f.tiff b/tests/images/seq-3c-64f.tiff new file mode 100644 index 0000000..4b7725a Binary files /dev/null and b/tests/images/seq-3c-64f.tiff differ diff --git a/tests/images/seq-3c-7b-contig.tiff b/tests/images/seq-3c-7b-contig.tiff new file mode 100644 index 0000000..1756461 Binary files /dev/null and b/tests/images/seq-3c-7b-contig.tiff differ diff --git a/tests/images/seq-3c-8b-multistrip.tiff b/tests/images/seq-3c-8b-multistrip.tiff new file mode 100644 index 0000000..8a5528d Binary files /dev/null and b/tests/images/seq-3c-8b-multistrip.tiff differ diff --git a/tests/images/seq-3c-i16.tiff b/tests/images/seq-3c-i16.tiff new file mode 100644 index 0000000..20f416e Binary files /dev/null and b/tests/images/seq-3c-i16.tiff differ diff --git a/tests/images/seq-3c-i8.tiff b/tests/images/seq-3c-i8.tiff new file mode 100644 index 0000000..497d583 Binary files /dev/null and b/tests/images/seq-3c-i8.tiff differ diff --git a/tests/images/seq-4c-8b-rgba-unassoc.tiff b/tests/images/seq-4c-8b-rgba-unassoc.tiff new file mode 100644 index 0000000..76c8694 Binary files /dev/null and b/tests/images/seq-4c-8b-rgba-unassoc.tiff differ diff --git a/tests/verify_seq_tiff.c b/tests/verify_seq_tiff.c new file mode 100644 index 0000000..2f177ba --- /dev/null +++ b/tests/verify_seq_tiff.c @@ -0,0 +1,460 @@ +/* + * verify_seq_tiff.c — Read seq-* test TIFFs with libtiff and verify decoded values. + * + * This is the authoritative cross-check: libtiff wrote these files, and now + * libtiff reads them back. The checksums computed here must match what + * image-tiff's Rust decoder produces (in decode_images.rs tests). + * + * For sub-byte depths (1-7 bit): sums packed bytes (matching image-tiff behavior). + * For extended depths (9-31 bit): unpacks MSB-first to u16/u32, sums those. + * For standard depths: sums native sample values. + * For float: sums as float/double. + * For signed: sums as int64. + * + * Usage: ./verify_seq_tiff + * Compile: cc -O2 -o verify_seq_tiff verify_seq_tiff.c -ltiff -lm + */ + +#include +#include +#include +#include +#include +#include + +#define W 16 +#define H 16 + +/* Extract N bits from MSB-first packed byte stream starting at bit_offset. */ +static uint32_t extract_bits_msb(const uint8_t *buf, int bit_offset, int nbits) +{ + uint32_t val = 0; + for (int i = nbits - 1; i >= 0; i--) { + int byte_idx = bit_offset / 8; + int bit_in_byte = bit_offset % 8; + if (buf[byte_idx] & (0x80 >> bit_in_byte)) + val |= (1u << i); + bit_offset++; + } + return val; +} + +typedef enum { + SUM_U8_PACKED, /* Sum raw packed bytes (sub-byte depths) */ + SUM_U8, /* Sum u8 samples */ + SUM_U16, /* Sum u16 samples */ + SUM_U16_UNPACK, /* Unpack from packed bits, sum as u16 */ + SUM_U32, /* Sum u32 samples */ + SUM_U32_UNPACK, /* Unpack from packed bits, sum as u32 */ + SUM_U64, /* Sum u64 samples */ + SUM_I8, /* Sum i8 samples */ + SUM_I16, /* Sum i16 samples */ + SUM_I32, /* Sum i32 samples */ + SUM_F32, /* Sum f32 samples */ + SUM_F64, /* Sum f64 samples */ +} SumMode; + +typedef struct { + const char *filename; + SumMode mode; + int bits_per_sample; + int samples_per_pixel; + /* Expected checksums — same as Rust test expectations */ + union { + uint64_t u; + int64_t i; + double f; + } expected; +} TestCase; + +static int verify_stripped(const char *dir, const TestCase *tc) +{ + char path[512]; + snprintf(path, sizeof(path), "%s/%s", dir, tc->filename); + + TIFF *tif = TIFFOpen(path, "r"); + if (!tif) { + fprintf(stderr, " SKIP %s (cannot open)\n", tc->filename); + return 0; /* skip, not fail */ + } + + uint32_t w, h; + uint16_t bps, spp, sf, pc; + TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w); + TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h); + TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bps); + TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &spp); + if (!TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &sf)) + sf = SAMPLEFORMAT_UINT; + if (!TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &pc)) + pc = PLANARCONFIG_CONTIG; + + int is_tiled = TIFFIsTiled(tif); + tsize_t scanline_size = TIFFScanlineSize(tif); + + /* For planar images, read_image() only reads plane 0 */ + int nplanes = (pc == PLANARCONFIG_SEPARATE) ? 1 : 1; + /* spp for checksum is always actual spp for contig, 1 for planar (first plane) */ + int eff_spp = (pc == PLANARCONFIG_SEPARATE) ? 1 : spp; + + uint64_t sum_u = 0; + int64_t sum_i = 0; + double sum_f = 0.0; + + if (is_tiled) { + uint32_t tw, th; + TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tw); + TIFFGetField(tif, TIFFTAG_TILELENGTH, &th); + tsize_t tile_size = TIFFTileSize(tif); + uint8_t *tile_buf = (uint8_t *)_TIFFmalloc(tile_size); + if (!tile_buf) { TIFFClose(tif); return -1; } + + /* Assemble tiles into a full image buffer, then checksum */ + int img_bytes; + switch (tc->mode) { + case SUM_U8_PACKED: + case SUM_U8: img_bytes = w * h * eff_spp; break; + case SUM_U16: + case SUM_U16_UNPACK: img_bytes = w * h * eff_spp * 2; break; + case SUM_U32: + case SUM_U32_UNPACK: + case SUM_F32: img_bytes = w * h * eff_spp * 4; break; + case SUM_U64: + case SUM_F64: img_bytes = w * h * eff_spp * 8; break; + case SUM_I8: img_bytes = w * h * eff_spp; break; + case SUM_I16: img_bytes = w * h * eff_spp * 2; break; + case SUM_I32: img_bytes = w * h * eff_spp * 4; break; + default: img_bytes = w * h * eff_spp; break; + } + uint8_t *img = (uint8_t *)calloc(1, img_bytes); + if (!img) { _TIFFfree(tile_buf); TIFFClose(tif); return -1; } + + int bytes_per_sample = bps / 8; + if (bytes_per_sample == 0) bytes_per_sample = 1; + int row_bytes = w * eff_spp * bytes_per_sample; + + for (uint32_t ty = 0; ty < h; ty += th) { + for (uint32_t tx = 0; tx < w; tx += tw) { + TIFFReadTile(tif, tile_buf, tx, ty, 0, 0); + /* Copy tile data into image buffer */ + for (uint32_t row = 0; row < th && (ty + row) < h; row++) { + int src_off = row * tw * eff_spp * bytes_per_sample; + int dst_off = ((ty + row) * w + tx) * eff_spp * bytes_per_sample; + int copy_w = tw; + if (tx + tw > w) copy_w = w - tx; + memcpy(img + dst_off, tile_buf + src_off, + copy_w * eff_spp * bytes_per_sample); + } + } + } + + /* Now checksum the assembled image */ + int npixels = w * h * eff_spp; + switch (tc->mode) { + case SUM_U8_PACKED: + case SUM_U8: + for (int j = 0; j < npixels; j++) sum_u += img[j]; + break; + case SUM_U16: + for (int j = 0; j < npixels; j++) sum_u += ((uint16_t *)img)[j]; + break; + case SUM_U32: + for (int j = 0; j < npixels; j++) sum_u += ((uint32_t *)img)[j]; + break; + case SUM_I8: + for (int j = 0; j < npixels; j++) sum_i += ((int8_t *)img)[j]; + break; + case SUM_F32: + for (int j = 0; j < npixels; j++) sum_f += ((float *)img)[j]; + break; + case SUM_F64: + for (int j = 0; j < npixels; j++) sum_f += ((double *)img)[j]; + break; + default: break; + } + + free(img); + _TIFFfree(tile_buf); + } else { + /* Stripped image */ + uint8_t *buf = (uint8_t *)_TIFFmalloc(scanline_size); + if (!buf) { TIFFClose(tif); return -1; } + + for (uint32_t y = 0; y < h; y++) { + if (TIFFReadScanline(tif, buf, y, 0) < 0) { + fprintf(stderr, " ERROR reading scanline %u of %s\n", y, tc->filename); + _TIFFfree(buf); TIFFClose(tif); return -1; + } + + switch (tc->mode) { + case SUM_U8_PACKED: + /* Sum raw packed bytes — matches image-tiff sub-byte output */ + for (tsize_t j = 0; j < scanline_size; j++) + sum_u += buf[j]; + break; + case SUM_U8: + for (uint32_t x = 0; x < w * eff_spp; x++) + sum_u += buf[x]; + break; + case SUM_U16: + for (uint32_t x = 0; x < w * eff_spp; x++) + sum_u += ((uint16_t *)buf)[x]; + break; + case SUM_U16_UNPACK: { + /* Unpack N-bit MSB-first packed samples to u16 */ + for (uint32_t x = 0; x < w * eff_spp; x++) { + uint32_t val = extract_bits_msb(buf, x * bps, bps); + sum_u += val; + } + break; + } + case SUM_U32: + for (uint32_t x = 0; x < w * eff_spp; x++) + sum_u += ((uint32_t *)buf)[x]; + break; + case SUM_U32_UNPACK: { + for (uint32_t x = 0; x < w * eff_spp; x++) { + uint32_t val = extract_bits_msb(buf, x * bps, bps); + sum_u += val; + } + break; + } + case SUM_U64: + for (uint32_t x = 0; x < w * eff_spp; x++) + sum_u += ((uint64_t *)buf)[x]; + break; + case SUM_I8: + for (uint32_t x = 0; x < w * eff_spp; x++) + sum_i += ((int8_t *)buf)[x]; + break; + case SUM_I16: + for (uint32_t x = 0; x < w * eff_spp; x++) + sum_i += ((int16_t *)buf)[x]; + break; + case SUM_I32: + for (uint32_t x = 0; x < w * eff_spp; x++) + sum_i += ((int32_t *)buf)[x]; + break; + case SUM_F32: { + float *fp = (float *)buf; + for (uint32_t x = 0; x < w * eff_spp; x++) + sum_f += fp[x]; + break; + } + case SUM_F64: { + double *dp = (double *)buf; + for (uint32_t x = 0; x < w * eff_spp; x++) + sum_f += dp[x]; + break; + } + } + } + _TIFFfree(buf); + } + + TIFFClose(tif); + + /* Compare checksums */ + int pass = 0; + char got_str[64], exp_str[64]; + + switch (tc->mode) { + case SUM_U8_PACKED: + case SUM_U8: + case SUM_U16: + case SUM_U16_UNPACK: + case SUM_U32: + case SUM_U32_UNPACK: + case SUM_U64: + snprintf(got_str, sizeof(got_str), "%llu", (unsigned long long)sum_u); + snprintf(exp_str, sizeof(exp_str), "%llu", (unsigned long long)tc->expected.u); + pass = (sum_u == tc->expected.u); + break; + case SUM_I8: + case SUM_I16: + case SUM_I32: + snprintf(got_str, sizeof(got_str), "%lld", (long long)sum_i); + snprintf(exp_str, sizeof(exp_str), "%lld", (long long)tc->expected.i); + pass = (sum_i == tc->expected.i); + break; + case SUM_F32: + snprintf(got_str, sizeof(got_str), "%.10g", sum_f); + snprintf(exp_str, sizeof(exp_str), "%.10g", tc->expected.f); + /* Allow small float rounding tolerance */ + pass = (fabs(sum_f - tc->expected.f) < 0.01); + break; + case SUM_F64: + snprintf(got_str, sizeof(got_str), "%.15g", sum_f); + snprintf(exp_str, sizeof(exp_str), "%.15g", tc->expected.f); + pass = (fabs(sum_f - tc->expected.f) < 1e-10); + break; + } + + if (pass) { + printf(" PASS %-45s sum=%s\n", tc->filename, got_str); + } else { + printf(" FAIL %-45s got=%s expected=%s\n", tc->filename, got_str, exp_str); + } + + return pass ? 0 : 1; +} + +int main(int argc, char **argv) +{ + if (argc < 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + const char *dir = argv[1]; + + /* These expected values MUST match the Rust test expectations in decode_images.rs. + * If they don't, either the libtiff decoder or the image-tiff decoder has a bug. */ + TestCase cases[] = { + /* --- Sub-byte BlackIsZero (packed byte sums) --- */ + {"seq-1c-3b.tiff", SUM_U8_PACKED, 3, 1, {.u = 5792}}, + {"seq-1c-5b.tiff", SUM_U8_PACKED, 5, 1, {.u = 18944}}, + {"seq-1c-7b.tiff", SUM_U8_PACKED, 7, 1, {.u = 24992}}, + + /* --- Sub-byte WhiteIsZero (packed byte sums) --- + * libtiff returns RAW stored values (inverted), unlike image-tiff which + * inverts back to BlackIsZero. These sums verify libtiff round-trips + * the stored data correctly. image-tiff's inversion is verified separately + * by the Rust tests (which expect the same sums as BlackIsZero). */ + {"seq-1c-3b-miniswhite.tiff", SUM_U8_PACKED, 3, 1, {.u = 18688}}, + {"seq-1c-5b-miniswhite.tiff", SUM_U8_PACKED, 5, 1, {.u = 21856}}, + {"seq-1c-6b-miniswhite.tiff", SUM_U8_PACKED, 6, 1, {.u = 23232}}, + {"seq-1c-7b-miniswhite.tiff", SUM_U8_PACKED, 7, 1, {.u = 32128}}, + + /* --- Extended BlackIsZero (unpacked u16/u32 sums) --- */ + {"seq-1c-10b.tiff", SUM_U16_UNPACK, 10, 1, {.u = 32640}}, + {"seq-1c-12b.tiff", SUM_U16_UNPACK, 12, 1, {.u = 32640}}, + {"seq-1c-14b.tiff", SUM_U16_UNPACK, 14, 1, {.u = 32640}}, + {"seq-1c-24b.tiff", SUM_U32_UNPACK, 24, 1, {.u = 32640}}, + + /* --- Extended WhiteIsZero (unpacked sums, decoder inverts) --- */ + /* NOTE: libtiff does NOT invert WhiteIsZero — it returns raw stored values. + * image-tiff inverts them. So for WIZ, libtiff sum ≠ image-tiff sum. + * We verify these separately by checking the raw packed sum. */ + + /* --- Extended HPredict --- */ + {"seq-1c-10b-hpredict.tiff", SUM_U16_UNPACK, 10, 1, {.u = 32640}}, + {"seq-1c-12b-hpredict.tiff", SUM_U16_UNPACK, 12, 1, {.u = 32640}}, + {"seq-1c-14b-hpredict.tiff", SUM_U16_UNPACK, 14, 1, {.u = 32640}}, + {"seq-1c-24b-hpredict.tiff", SUM_U32_UNPACK, 24, 1, {.u = 32640}}, + + /* --- Extended RGB Contiguous (unpacked sums) --- */ + {"seq-3c-10b-contig.tiff", SUM_U16_UNPACK, 10, 3, {.u = 294528}}, + {"seq-3c-12b-contig.tiff", SUM_U16_UNPACK, 12, 3, {.u = 294528}}, + {"seq-3c-14b-contig.tiff", SUM_U16_UNPACK, 14, 3, {.u = 294528}}, + {"seq-3c-24b-contig.tiff", SUM_U32_UNPACK, 24, 3, {.u = 294528}}, + + /* --- Extended RGB Planar (first plane only, unpacked) --- */ + {"seq-3c-10b-planar.tiff", SUM_U16_UNPACK, 10, 3, {.u = 32640}}, + {"seq-3c-12b-planar.tiff", SUM_U16_UNPACK, 12, 3, {.u = 32640}}, + {"seq-3c-14b-planar.tiff", SUM_U16_UNPACK, 14, 3, {.u = 32640}}, + {"seq-3c-24b-planar.tiff", SUM_U32_UNPACK, 24, 3, {.u = 32640}}, + + /* --- Standard depths with compression --- */ + {"seq-1c-8b-lzw.tiff", SUM_U8, 8, 1, {.u = 32640}}, + {"seq-1c-8b-deflate.tiff", SUM_U8, 8, 1, {.u = 32640}}, + {"seq-1c-8b-packbits.tiff", SUM_U8, 8, 1, {.u = 32640}}, + {"seq-1c-16b-lzw.tiff", SUM_U16, 16, 1, {.u = 32640}}, + {"seq-1c-16b-deflate.tiff", SUM_U16, 16, 1, {.u = 32640}}, + {"seq-1c-8b-lzw-hpredict.tiff", SUM_U8, 8, 1, {.u = 32640}}, + {"seq-3c-8b-lzw.tiff", SUM_U8, 8, 3, {.u = 97920}}, + + /* --- Tiled --- */ + {"seq-1c-8b-tiled.tiff", SUM_U8, 8, 1, {.u = 32640}}, + {"seq-1c-16b-tiled.tiff", SUM_U16, 16, 1, {.u = 32640}}, + {"seq-3c-8b-tiled.tiff", SUM_U8, 8, 3, {.u = 97920}}, + + /* --- BigTIFF --- */ + {"seq-1c-8b-bigtiff.tiff", SUM_U8, 8, 1, {.u = 32640}}, + {"seq-3c-16b-bigtiff.tiff", SUM_U16, 16, 3, {.u = 294528}}, + + /* --- Float --- */ + {"seq-1c-32f.tiff", SUM_F32, 32, 1, {.f = 128.0}}, + {"seq-1c-64f.tiff", SUM_F64, 64, 1, {.f = 128.0}}, + {"seq-3c-32f.tiff", SUM_F32, 32, 3, {.f = 384.0}}, + + /* --- Signed integer --- */ + {"seq-1c-i8.tiff", SUM_I8, 8, 1, {.i = -128}}, + {"seq-1c-i16.tiff", SUM_I16, 16, 1, {.i = -128}}, + {"seq-1c-i32.tiff", SUM_I32, 32, 1, {.i = -128}}, + + /* --- CMYK --- */ + {"seq-4c-8b-cmyk.tiff", SUM_U8, 8, 4, {.u = 130560}}, + {"seq-4c-16b-cmyk.tiff", SUM_U16, 16, 4, {.u = 523776}}, + + /* --- Palette (raw packed indices) --- */ + {"seq-1c-8b-palette.tiff", SUM_U8, 8, 1, {.u = 32640}}, + {"seq-1c-4b-palette.tiff", SUM_U8_PACKED, 4, 1, {.u = 15360}}, + + /* --- RGBA --- */ + {"seq-4c-8b-rgba.tiff", SUM_U8, 8, 4, {.u = 130560}}, + {"seq-4c-16b-rgba.tiff", SUM_U16, 16, 4, {.u = 523776}}, + + /* --- Multi-page (first page only) --- */ + {"seq-1c-8b-multipage.tiff", SUM_U8, 8, 1, {.u = 32640}}, + + /* --- Big-endian --- */ + {"seq-1c-8b-bigendian.tiff", SUM_U8, 8, 1, {.u = 32640}}, + {"seq-1c-16b-bigendian.tiff", SUM_U16, 16, 1, {.u = 32640}}, + {"seq-3c-8b-bigendian.tiff", SUM_U8, 8, 3, {.u = 97920}}, + + /* --- Standard sub-byte (packed byte sums) --- */ + {"seq-1c-1b.tiff", SUM_U8_PACKED, 1, 1, {.u = 2720}}, + {"seq-1c-2b.tiff", SUM_U8_PACKED, 2, 1, {.u = 1728}}, + {"seq-1c-4b.tiff", SUM_U8_PACKED, 4, 1, {.u = 15360}}, + + /* --- Multi-strip --- */ + {"seq-1c-8b-multistrip.tiff", SUM_U8, 8, 1, {.u = 32640}}, + {"seq-1c-16b-multistrip.tiff", SUM_U16, 16, 1, {.u = 32640}}, + {"seq-3c-8b-multistrip.tiff", SUM_U8, 8, 3, {.u = 97920}}, + + /* --- Tiled + compressed --- */ + {"seq-1c-8b-tiled-lzw.tiff", SUM_U8, 8, 1, {.u = 32640}}, + {"seq-1c-8b-tiled-deflate.tiff", SUM_U8, 8, 1, {.u = 32640}}, + + /* --- Float + predictor --- */ + {"seq-1c-32f-deflate-fpredict.tiff", SUM_F32, 32, 1, {.f = 128.0}}, + {"seq-1c-64f-deflate-fpredict.tiff", SUM_F64, 64, 1, {.f = 128.0}}, + + /* --- Unassociated alpha --- */ + {"seq-4c-8b-rgba-unassoc.tiff", SUM_U8, 8, 4, {.u = 130560}}, + + /* --- Sub-byte RGB (packed byte sums) --- */ + {"seq-3c-5b-contig.tiff", SUM_U8_PACKED, 5, 3, {.u = 56832}}, + {"seq-3c-7b-contig.tiff", SUM_U8_PACKED, 7, 3, {.u = 74976}}, + + /* --- Signed RGB --- */ + {"seq-3c-i8.tiff", SUM_I8, 8, 3, {.i = -384}}, + {"seq-3c-i16.tiff", SUM_I16, 16, 3, {.i = -384}}, + + /* --- Float f64 RGB --- */ + {"seq-3c-64f.tiff", SUM_F64, 64, 3, {.f = 384.0}}, + + /* --- Tiled BigTIFF --- */ + {"seq-1c-8b-tiled-bigtiff.tiff", SUM_U8, 8, 1, {.u = 32640}}, + }; + + int ncases = sizeof(cases) / sizeof(cases[0]); + int failures = 0; + + printf("Verifying %d test images with libtiff decoder in %s/\n\n", ncases, dir); + + /* Suppress libtiff warnings for non-standard tag 317 (Predictor) */ + TIFFSetWarningHandler(NULL); + + for (int i = 0; i < ncases; i++) { + int ret = verify_stripped(dir, &cases[i]); + if (ret != 0) failures++; + } + + printf("\n%d/%d passed", ncases - failures, ncases); + if (failures > 0) + printf(", %d FAILED", failures); + printf("\n"); + + return failures > 0 ? 1 : 0; +}