test: expand test suite to 74 images with libtiff reference decoder
Add 19 new libtiff-generated test images and a reference decoder (verify_seq_tiff.c) that independently verifies all checksums using libtiff's own read path, giving three-way agreement: libtiff writer → libtiff reader → image-tiff reader New coverage: - Standard sub-byte: 1-bit, 2-bit, 4-bit grayscale (BIZ + WIZ) - Multi-strip: RowsPerStrip=4 (gray 8/16, RGB 8) - Tiled + compressed: LZW and Deflate tiles - Float predictor: f32/f64 + Deflate + predictor=3 - Unassociated alpha: RGBA 8-bit with EXTRASAMPLE_UNASSALPHA - Sub-byte RGB: 5-bit and 7-bit contiguous - Signed RGB: i8 and i16 (3-channel, decoded as Multiband) - Float f64 RGB - Tiled BigTIFF verify_seq_tiff.c: reads each TIFF with libtiff, computes matching checksums (handling packed sub-byte, unpacked extended, float, signed), and verifies 68/68 cases pass. Total: 74 test images (66.8 KB), 317 tests all passing.
This commit is contained in:
1 parent
a4cbf7bbaf
commit
c3977c4513
22 files changed
+952
No files matched your search
@@ -1318,3 +1318,125 @@ fn test_seq_1c_16b_bigendian() {
|
|||||||
fn test_seq_3c_8b_bigendian() {
|
fn test_seq_3c_8b_bigendian() {
|
||||||
test_image_sum_u8("seq-3c-8b-bigendian.tiff", ColorType::RGB(8), 97920);
|
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);
|
||||||
|
}
|
||||||
@@ -671,6 +671,230 @@ static int write_rgba(const char *path, int bits, const char *mode)
|
|||||||
return 0;
|
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. */
|
/* 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)
|
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");
|
printf(" OK seq-3c-8b-bigendian.tiff\n");
|
||||||
else { printf(" FAIL seq-3c-8b-bigendian.tiff\n"); ok = 0; }
|
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!");
|
printf("\nDone. %s\n", ok ? "All succeeded." : "Some failed!");
|
||||||
return ok ? 0 : 1;
|
return ok ? 0 : 1;
|
||||||
}
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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 <image_dir>
|
||||||
|
* Compile: cc -O2 -o verify_seq_tiff verify_seq_tiff.c -ltiff -lm
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <tiffio.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#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 <image_dir>\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;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user