From 26c32431fa5969c93e69b91378fc716695eb3e11 Mon Sep 17 00:00:00 2001 From: Lilith River Date: Mon, 9 Mar 2026 23:17:59 -0600 Subject: [PATCH] test: regenerate seq-* test images using libtiff C API Replace Python-generated test TIFFs with libtiff-generated ones to eliminate circular validation. The C program (gen_seq_tiff.c) uses libtiff's TIFFWriteScanline with MSB-first packed bit streams. Cross-validated: tifffile independently confirms all 27 images match the original Python-generated versions byte-for-byte. tiffinfo validates all IFD structures. All 27 image-tiff decode tests pass unchanged. --- tests/gen_seq_images.py | 540 ------------------------ tests/gen_seq_tiff.c | 260 ++++++++++++ tests/images/seq-1c-10b-hpredict.tiff | Bin 494 -> 466 bytes tests/images/seq-1c-10b-miniswhite.tiff | Bin 482 -> 466 bytes tests/images/seq-1c-10b.tiff | Bin 482 -> 466 bytes tests/images/seq-1c-12b-hpredict.tiff | Bin 558 -> 530 bytes tests/images/seq-1c-12b-miniswhite.tiff | Bin 546 -> 530 bytes tests/images/seq-1c-12b.tiff | Bin 546 -> 530 bytes tests/images/seq-1c-14b-hpredict.tiff | Bin 622 -> 594 bytes tests/images/seq-1c-14b-miniswhite.tiff | Bin 610 -> 594 bytes tests/images/seq-1c-14b.tiff | Bin 610 -> 594 bytes tests/images/seq-1c-24b-hpredict.tiff | Bin 942 -> 914 bytes tests/images/seq-1c-24b-miniswhite.tiff | Bin 930 -> 914 bytes tests/images/seq-1c-24b.tiff | Bin 930 -> 914 bytes tests/images/seq-1c-3b-miniswhite.tiff | Bin 258 -> 242 bytes tests/images/seq-1c-3b.tiff | Bin 258 -> 242 bytes tests/images/seq-1c-5b-miniswhite.tiff | Bin 322 -> 306 bytes tests/images/seq-1c-5b.tiff | Bin 322 -> 306 bytes tests/images/seq-1c-6b-miniswhite.tiff | Bin 354 -> 338 bytes tests/images/seq-1c-7b-miniswhite.tiff | Bin 386 -> 370 bytes tests/images/seq-1c-7b.tiff | Bin 386 -> 370 bytes tests/images/seq-3c-10b-contig.tiff | Bin 1152 -> 1118 bytes tests/images/seq-3c-10b-planar.tiff | Bin 1176 -> 1136 bytes tests/images/seq-3c-12b-contig.tiff | Bin 1344 -> 1310 bytes tests/images/seq-3c-12b-planar.tiff | Bin 1368 -> 1328 bytes tests/images/seq-3c-14b-contig.tiff | Bin 1536 -> 1502 bytes tests/images/seq-3c-14b-planar.tiff | Bin 1560 -> 1520 bytes tests/images/seq-3c-24b-contig.tiff | Bin 2496 -> 2462 bytes tests/images/seq-3c-24b-planar.tiff | Bin 2520 -> 2480 bytes 29 files changed, 260 insertions(+), 540 deletions(-) delete mode 100644 tests/gen_seq_images.py create mode 100644 tests/gen_seq_tiff.c diff --git a/tests/gen_seq_images.py b/tests/gen_seq_images.py deleted file mode 100644 index 6a44da2..0000000 --- a/tests/gen_seq_images.py +++ /dev/null @@ -1,540 +0,0 @@ -#!/usr/bin/env python3 -"""Generate sequential-value TIFF test images for bit-error detection. - -Uses manual TIFF structure writing to produce packed-sample images at -non-standard bit depths. Each image contains pixel values cycling 0..max, -so any bit-level decode error changes the checksum. - -Images are 16x16 (256 pixels per channel). - -Output: tests/images/seq-*.tiff + Rust test functions printed to stdout. -""" - -import numpy as np -import struct -import os -from pathlib import Path - -OUTDIR = Path(__file__).parent / "images" -OUTDIR.mkdir(exist_ok=True) - -W, H = 16, 16 -NPIX = W * H # 256 pixels per channel - - -def sequential_values(max_val, count): - """Generate cycling sequential values 0..max_val.""" - return [i % (max_val + 1) for i in range(count)] - - -def compute_sum(values): - """Compute checksum as sum of all sample values.""" - return sum(values) - - -def pack_bits(values, bits, width): - """Pack values into MSB-first bit stream, row by row. - - Each row is packed independently and padded to a byte boundary, - matching TIFF strip/row semantics. - """ - height = len(values) // width - all_bytes = bytearray() - for y in range(height): - row_vals = values[y * width:(y + 1) * width] - bit_buf = 0 - bit_count = 0 - for v in row_vals: - bit_buf = (bit_buf << bits) | v - bit_count += bits - while bit_count >= 8: - bit_count -= 8 - all_bytes.append((bit_buf >> bit_count) & 0xFF) - if bit_count > 0: - all_bytes.append((bit_buf << (8 - bit_count)) & 0xFF) - return bytes(all_bytes) - - -def pack_bits_multichannel_contig(values, bits, width, channels): - """Pack interleaved multichannel data: RGBRGBRGB... - - values is a flat list of length width*height*channels, already interleaved. - Rows are packed independently. - """ - height = len(values) // (width * channels) - all_bytes = bytearray() - for y in range(height): - start = y * width * channels - row_vals = values[start:start + width * channels] - bit_buf = 0 - bit_count = 0 - for v in row_vals: - bit_buf = (bit_buf << bits) | v - bit_count += bits - while bit_count >= 8: - bit_count -= 8 - all_bytes.append((bit_buf >> bit_count) & 0xFF) - if bit_count > 0: - all_bytes.append((bit_buf << (8 - bit_count)) & 0xFF) - return bytes(all_bytes) - - -def pack_bits_planar(values, bits, width, height, channels): - """Pack planar data: all R samples, then all G, then all B. - - values is a flat list of length width*height*channels. - First width*height are channel 0, next are channel 1, etc. - Each plane's rows are packed independently. - - Returns (bytes, plane_sizes) where plane_sizes is a list of byte counts per plane. - """ - plane_size = width * height - all_bytes = bytearray() - plane_byte_sizes = [] - for c in range(channels): - plane = values[c * plane_size:(c + 1) * plane_size] - plane_bytes = pack_bits(plane, bits, width) - plane_byte_sizes.append(len(plane_bytes)) - all_bytes.extend(plane_bytes) - return bytes(all_bytes), plane_byte_sizes - - -def apply_horizontal_predictor(values, bits, width): - """Apply TIFF horizontal differencing predictor. - - For each row, first sample is unchanged, subsequent samples become - (current - previous) mod 2^bits. - """ - height = len(values) // width - mod = 1 << bits - result = list(values) - for y in range(height): - for x in range(width - 1, 0, -1): - idx = y * width + x - result[idx] = (result[idx] - result[idx - 1]) % mod - return result - - -def apply_horizontal_predictor_rgb(values, bits, width, channels): - """Apply horizontal predictor for interleaved RGB data.""" - height = len(values) // (width * channels) - mod = 1 << bits - result = list(values) - stride = width * channels - for y in range(height): - row_start = y * stride - # Work backwards to avoid overwriting values we still need - for x in range(width - 1, 0, -1): - for c in range(channels): - idx = row_start + x * channels + c - prev = row_start + (x - 1) * channels + c - result[idx] = (result[idx] - result[prev]) % mod - return result - - -def write_tiff(path, raw_data, width, height, bits, photometric=1, - samples_per_pixel=1, planar=False, predictor=1, - plane_sizes=None): - """Write a valid TIFF file with precise IFD structure. - - photometric: 0=WhiteIsZero, 1=BlackIsZero, 2=RGB - plane_sizes: list of byte sizes per plane (for planar images) - """ - # For planar images, we need separate strips per plane - if planar and samples_per_pixel > 1: - num_strips = samples_per_pixel - if plane_sizes is None: - plane_size = len(raw_data) // samples_per_pixel - plane_sizes = [plane_size] * samples_per_pixel - else: - num_strips = 1 - plane_sizes = [len(raw_data)] - - # Collect IFD entries as (tag, type_id, count, inline_or_None, offset_data_or_None) - # We'll resolve offsets in a second pass - entries = [] - - def add_short(tag, value): - entries.append((tag, 3, 1, value, None)) - - add_short(256, width) # ImageWidth - add_short(257, height) # ImageLength - add_short(259, 1) # Compression = None - add_short(262, photometric) # PhotometricInterpretation - add_short(278, height) # RowsPerStrip = all rows - add_short(296, 1) # ResolutionUnit = None - - if samples_per_pixel > 1: - add_short(277, samples_per_pixel) # SamplesPerPixel - add_short(284, 2 if planar else 1) # PlanarConfiguration - - if predictor > 1: - add_short(317, predictor) # Predictor - - # These need offset data; will be filled in during layout - # Use sentinel tag numbers, actual entries added below - entries.sort(key=lambda e: e[0]) - - # Now compute layout - # We need to know all entries to compute IFD size - # Extra entries: BitsPerSample(258), StripOffsets(273), StripByteCounts(279), - # XResolution(282), YResolution(283) - total_entries = len(entries) + 5 - - ifd_offset = 8 - ifd_size = 2 + total_entries * 12 + 4 - - extra_start = ifd_offset + ifd_size - extra = bytearray() - - # BitsPerSample - if samples_per_pixel > 1: - bps_offset = extra_start + len(extra) - for _ in range(samples_per_pixel): - extra.extend(struct.pack(' + * + * Compile: cc -O2 -o gen_seq_tiff gen_seq_tiff.c -ltiff -lm + */ + +#include +#include +#include +#include +#include + +#define W 16 +#define H 16 +#define NPIX (W * H) + +/* Pack a single sample value into an MSB-first bit stream at the given bit offset. */ +static void pack_value(uint8_t *buf, int *bit_offset, uint32_t val, int bits) +{ + /* Write bits MSB-first */ + for (int i = bits - 1; i >= 0; i--) { + int byte_idx = *bit_offset / 8; + int bit_in_byte = *bit_offset % 8; + if ((val >> i) & 1) + buf[byte_idx] |= (uint8_t)(0x80 >> bit_in_byte); + (*bit_offset)++; + } +} + +/* Write a single-channel grayscale TIFF with N-bit packed samples. + * libtiff expects packed MSB-first data for all non-standard bit depths. */ +static int write_gray(const char *path, int bits, + uint16_t photometric, uint16_t predictor) +{ + 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); + TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE); + TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, H); + if (predictor != PREDICTOR_NONE) + TIFFSetField(tif, TIFFTAG_PREDICTOR, predictor); + + uint32_t max_val = (1u << bits) - 1; + 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); + int bit_offset = 0; + + for (int x = 0; x < W; x++) { + uint32_t val = (y * W + x) % (max_val + 1); + /* For WhiteIsZero, store inverted values (decoder will invert back) */ + if (photometric == PHOTOMETRIC_MINISWHITE) + val = max_val - val; + pack_value(buf, &bit_offset, val, bits); + } + + 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 3-channel RGB TIFF with N-bit packed samples, contiguous layout. */ +static int write_rgb_contig(const char *path, int bits) +{ + 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, H); + + uint32_t max_val = (1u << bits) - 1; + 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); + int bit_offset = 0; + + 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); + pack_value(buf, &bit_offset, val, bits); + } + } + + 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 3-channel RGB TIFF with N-bit packed samples, planar (separate) layout. */ +static int write_rgb_planar(const char *path, int bits) +{ + 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_SEPARATE); + TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, H); + + uint32_t max_val = (1u << bits) - 1; + tsize_t scanline_size = TIFFScanlineSize(tif); + uint8_t *buf = (uint8_t *)_TIFFmalloc(scanline_size); + if (!buf) { TIFFClose(tif); return -1; } + + for (int s = 0; s < 3; s++) { + for (int y = 0; y < H; y++) { + memset(buf, 0, scanline_size); + int bit_offset = 0; + + for (int x = 0; x < W; x++) { + /* Plane 0: cycling 0..max, plane 1: +100, plane 2: +200 */ + uint32_t val = ((y * W + x) + s * 100) % (max_val + 1); + pack_value(buf, &bit_offset, val, bits); + } + + if (TIFFWriteScanline(tif, buf, y, (uint16_t)s) < 0) { + fprintf(stderr, "Error writing scanline %d plane %d to %s\n", + y, s, path); + _TIFFfree(buf); + TIFFClose(tif); + return -1; + } + } + } + + _TIFFfree(buf); + TIFFClose(tif); + return 0; +} + +int main(int argc, char **argv) +{ + if (argc < 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + const char *dir = argv[1]; + char path[512]; + int ok = 1; + + printf("Generating libtiff test images in %s/\n\n", dir); + + /* Sub-byte BlackIsZero */ + int subbyte_biz[] = {3, 5, 7}; + for (int i = 0; i < 3; i++) { + int b = subbyte_biz[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; } + } + + /* Sub-byte WhiteIsZero */ + int subbyte_wiz[] = {3, 5, 6, 7}; + for (int i = 0; i < 4; i++) { + int b = subbyte_wiz[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; } + } + + /* Extended BlackIsZero */ + int ext_bits[] = {10, 12, 14, 24}; + for (int i = 0; i < 4; i++) { + int b = ext_bits[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; } + } + + /* Extended WhiteIsZero */ + for (int i = 0; i < 4; i++) { + int b = ext_bits[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; } + } + + /* Extended HPredict — libtiff may not support predictor for packed non-standard depths. + * Try it and report failure gracefully. */ + for (int i = 0; i < 4; i++) { + int b = ext_bits[i]; + snprintf(path, sizeof(path), "%s/seq-1c-%db-hpredict.tiff", dir, b); + if (write_gray(path, b, PHOTOMETRIC_MINISBLACK, PREDICTOR_HORIZONTAL) == 0) + printf(" OK seq-1c-%db-hpredict.tiff\n", b); + else { printf(" FAIL seq-1c-%db-hpredict.tiff (may not be supported)\n", b); } + } + + /* RGB Contiguous */ + for (int i = 0; i < 4; i++) { + int b = ext_bits[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; } + } + + /* RGB Planar */ + for (int i = 0; i < 4; i++) { + int b = ext_bits[i]; + snprintf(path, sizeof(path), "%s/seq-3c-%db-planar.tiff", dir, b); + if (write_rgb_planar(path, b) == 0) + printf(" OK seq-3c-%db-planar.tiff\n", b); + else { printf(" FAIL seq-3c-%db-planar.tiff\n", b); ok = 0; } + } + + printf("\nDone. %s\n", ok ? "All succeeded." : "Some failed!"); + return ok ? 0 : 1; +} diff --git a/tests/images/seq-1c-10b-hpredict.tiff b/tests/images/seq-1c-10b-hpredict.tiff index a74e1a6ac5794e6f367e49709d4546552990856c..1553c9a4024f6ffe9ef1cf4055887f3aeb20a0f9 100644 GIT binary patch delta 385 zcmaFIe2KZ<(^HGVgOLFU1UQ%(83H8OnHVN$a5FPpu;6E56z~vcWekWAXJed@Av=Z&t;4ACcMHPmrZ$tBd(kAPR_V(&U?Ayz6GCf$74&r z;2F=Y_$IG-ZOwOi$9o%o;S-;2`GaqKx8tAu;u-8FcKTcWdHz4oNXZh literal 494 zcmZwBF$;q*5Cz~njYW!sg0w{)I&_fY;Nak(;8_2pV@H2uU*v=oUf`0)$6b4L znTW=sNI6P_99h1?W6Qqp668YNF}uTW!R+09!9bY*z>Vx&mK2jHz6ZQNRlK;Mv_jFbdw|*Ns=TV2RR!0Sy{_C1!!vE4YxhF?mpZ)C@wHBGbbedIyV}05-G|zLtiz`|ey-D( zI)63r&Dih8{V@Kg3BOGIZPFi;|EgvH(rf@QqPs^8mYsF(_7yX) l5)Z`frnxzGi=5k^=e#0*C0-N15x)~}h_}Q$;t%3a;yYmp4K)A& diff --git a/tests/images/seq-1c-12b-miniswhite.tiff b/tests/images/seq-1c-12b-miniswhite.tiff index 4684ffe96bd4c955536268811862ace87bedbae5..0fb04533632cd31334e584a745739c8b4f101f34 100644 GIT binary patch delta 64 zcmZ3)GKq!F(^HF~gK^`6CdP>zZoC`}3=E=-%nXb`nsK7Pw2Tal0~82GVgtDh08}Ri A{Qv*} delta 81 zcmbQlvWSJ<(^HFqgMndkJEJ?}qRF=zrOl)mSs54^7#Jo2u{4xD3&_@BWCn|a(2{r%# diff --git a/tests/images/seq-1c-12b.tiff b/tests/images/seq-1c-12b.tiff index 87c0c373b94dc46bb00bb3c716677be462ac7585..8e588b66957e8cc838891ae9d58f7f771fe010bc 100644 GIT binary patch delta 64 zcmZ3)GKq!F(^HF~gK^`6CdP>zZoC`}3=E=-%nXb`nsK7Pw2Tal0~82GVgtDh08}Ri A{Qv*} delta 82 zcmbQlvWSJl(^HFqgMoozatEUuLE+1cFNt2m~)d zAP`J~Kp?mTfk1Ex0)b!?1SAM1K_C!Zf4t;tC@$+i+DR*zLH+-N5a@b?yaz3O6_p%`|R`F1r)AxF5P*xXpvm&)|-t$fUR{ zV(l#MsWfuCabIPTpTh&ys+c`^C=TpiJW}n7+lR-hQ}Oe7qEI#a@lc{RdUaLXum+(eY&185h0oWsWr_-7{iVr%g`DJ|6t-3je zPm+o~j?cPXcPH>gcj|rxUo{eQ65k{$dkWumH*u%&L-!KDik~`fm^1h#`PsAht@{mk z4uABZ;n(mtCSpy)k2JEmfYk^mQr9CqXNUv< literal 622 zcmaKnK?=e^3`O&kDlUrDje=f4yhEW&dn-@k(xqqU(fm^)%%bxL!aRPGdCWXN>|kcS z8NxssSg6>OkIW;v%2u+8;d9{&;S1pl;S1sW0k@tYmjD0& diff --git a/tests/images/seq-1c-14b-miniswhite.tiff b/tests/images/seq-1c-14b-miniswhite.tiff index c053514aed50785010e26432fb0468ae061c1693..4e5cdfc421127f147361ca5bcc1871a6f5514007 100644 GIT binary patch delta 64 zcmaFFa*2h_(^HG#1mng9hZrYvxbbo@FffQRGBYp&X~v2E(lRnI4p1N%i4Ej30053B B2mJs5 delta 81 zcmcb_@`#1q(^HFqgMnf4aYlE>MU$nOq|KxlSs54^7#Jo2u{4xD3&_@BWCn|akgNB>BXnJw8$gQ)DXDpW=`Wshtc z$ll&N_s9GE^?iQN_xbO6;^X5)gn3*6k`e5}Nlpq<62TLsA~k79OFGgMNd_|VB$_C3iC8YC`vJk^9&^@NhwNGhO(5SJQb)& zB`Q;eXQ|3_RHHgIc%GWnqBiloz>CzOF7&I?ZU#8?>M$ zZ_`-lZ@7=+6KKGKj$p;XQ^jjQ9C~ z4;jt~Mly=gjNv23GLG>~U?P*4%oILmDxWZo>C9jzvzW~s=JF|@F^|vrf-m`s`7GdT z7P5$M_?GWj%o3KejODCgCExP{t60q%*0PQt`H7!d&jvQ~3%~Lko7l`2w(>jM*v<}i zvWwm9VK4jmgFo5N0SM&jTK&CJ-hd@va3zI9U?M1ZQDMoZzJh;S7QB#NCKs9u<;DCvjvj R&z$5W{8wy-pj0L7W1Y>+xOB(@+U3s~@2C7sbQN00) v>K#Z_A3&n|1QOL3kf^?aMD-I$RKI{k^&3c3e}F{w7f4k9fJF5lNKy{~Kz$86 diff --git a/tests/images/seq-1c-24b-miniswhite.tiff b/tests/images/seq-1c-24b-miniswhite.tiff index f4c6fddca5c3d71365270bc611ce20d18ed1eff2..47902a29f1b95f47a36b2f135def45f0d10cb359 100644 GIT binary patch delta 64 zcmZ3)K8c;p(^HFqgL&fuCgzD8ZoC`}3=E=-%nXb`nsK7Pw2Tal0~82GVgtDh03mAz A2LJ#7 delta 81 zcmbQlzKEUO(^HFqgMnc(JF`3EqRG+B(q>YOtPG3{3=ETiSQ^Tn1!QY5GK0lIav9IS}vw NVh|fbZ)9U)1^}mr2g3jW diff --git a/tests/images/seq-1c-3b-miniswhite.tiff b/tests/images/seq-1c-3b-miniswhite.tiff index 33756beca5b4782cfaf2f42b3e4c098e1228cd41..b4c2c6b079d3668366a41fec0d7dd58f8cbcfafc 100644 GIT binary patch delta 62 ycmZo-`ozfU>8Zt#F>!wKL=HDz4h9AWQATD4Mj*{N(O+6d2F3vj1S7G5Tm}F}*ap=A delta 84 zcmeyw*u=!)>8ZuQ!N9;UF~f~<(Zt=-7E+9?42(d5NkA+OWzPb#H5i$};vhK?@Bm^E N8%9qQ44=4>38w%6 diff --git a/tests/images/seq-1c-3b.tiff b/tests/images/seq-1c-3b.tiff index 40d959c9d37d1c6618b92d3625481b5a6620fb6b..ac08203776761259166330a1bf8951f46286b1a7 100644 GIT binary patch delta 62 ycmZo-`ozfU>8Zt#F>!wKL=HDz4h9AWQATD4Mj*{N(O+6d2F3vj1S7G5Tm}F}*ap=A literal 258 zcmebD)MDUZU|`^8U|?isU<9%RfS3`9%>-pL1I3x4Y>+xOB(@+U3s~8Zu=V&eSA6FJ;?IT#oiL>ZYG7=bk7M1N@+85jpB5RAkIav1=6&8ZuQ!N9;U@r4`XqRG~b(xy_3tPG4mkx4)-4Q0;)vNaf)!Qvn}5bywE L5F1QSWPJ<(*G36% diff --git a/tests/images/seq-1c-7b.tiff b/tests/images/seq-1c-7b.tiff index d49e153b0beba3b74d61c8cf6b762a81ea34f4f0..733f9541a32b6abd66ddb8661bbc176d30ba978a 100644 GIT binary patch delta 62 ycmZo-{=~%U>8Zu=V&eSA6FJ;?IT#oiL>ZYG7=bk7M1N@+85jpB5RAkIav1=6&8ZuQ!N9;U@r4`XqRG~b(xy_3tPG4mkx4)-4Q0;)vNaf)!Qvn}5bywE L5F1QSWPJ<(*G36% diff --git a/tests/images/seq-3c-10b-contig.tiff b/tests/images/seq-3c-10b-contig.tiff index e339b64127fab207a3dc955cde1c0ac51d05da4b..5093c2330826e75afa6d7d2231be525d2cc10a82 100644 GIT binary patch delta 72 zcmZqRyvM=j>8ZtVf_dY+>8ZuQ!N9=4JNYEDBGW2{$#0mI7!OS5XHoZ%Vq|4t1WIiHVreLQ2aqkp c$P5+-$!Q?5fm{YIF!TVjK(Y{eBikWn0AIBX7XSbN diff --git a/tests/images/seq-3c-10b-planar.tiff b/tests/images/seq-3c-10b-planar.tiff index ca530e1cf14675c14c1655cebeef99aaae3ae178..8d9fb476888360dfbb790ad2041f531cf2bfe959 100644 GIT binary patch delta 101 zcmbQi`GJGY(^HG#1oOsghnN|;Cvqw>1+h%jQDTZ?nHV7B!py+Tz`zi}!oVQI$jrb9 iWHSMAFpv!r&jN~r02dfKFoGZlknaIh*a1|?2!;T0Sqvfo delta 164 zcmeysF@uxS(^HFqgMop8ck)SQMW$5@lix5aF>PR&EWjdD&%(eAl-UEsQjDw&j6nJX z5KBYZ7l3RTMrN=$6OgR|WrNg#02dhE0J0>2(h)$T9Dq_V>;V)9sf5uC3<4a?j0^!1 T>`V+3G`N`=E?8`oUds#stsW7+ diff --git a/tests/images/seq-3c-12b-contig.tiff b/tests/images/seq-3c-12b-contig.tiff index bb94c187925f996d68cda5d71781954f3ef6f364..6c19ffef64bb08a2112d350b26f48e142a569282 100644 GIT binary patch delta 72 zcmX@WHIIwU(^HF~gJt8sCKg8SiJXc|LaY;Ylo&ZC`m1ZmFfubRGBAK~Fp$j*WJ|CD O2_WDBLq?z+BLe`^IS6e4 delta 108 zcmbQob%2Z0(^HFqgMop8cXB6-BGW2{$!l1Y7!OQ7&!X-j#mLIQ2$b3Y#L`gq4j@~G ckr^xwlG8w91Gx-5VCVs4fn*`{Mz$sv0B~vyBLDyZ diff --git a/tests/images/seq-3c-12b-planar.tiff b/tests/images/seq-3c-12b-planar.tiff index a9650c0355b2a6a1a7e07e700a77e1da2a335d52..926576bc83b95bec5322da4eaa245c57b9f034c4 100644 GIT binary patch delta 101 zcmcb?wSkMx(^HF~gJt8jCKg8SiJXc|LaY;Yl$hjLCkDv4Ff%YSFfd54GBC(6GBYp& i*-Stj3}l1EwSeLvzypR2j3CGXzQ7_=&%(eAl-UEsQjDw&j6nJX z5KBYZ7l3RTMrN=$6OgR|WrNg#01p`60J0*0(l>xcH2|ex*aIjIQVF9$JOyS32Udmz Qc7_H{h6UUV8>Mqu02z}K^8f$< diff --git a/tests/images/seq-3c-14b-contig.tiff b/tests/images/seq-3c-14b-contig.tiff index 16c6a54f1adb07dc969ea3eca496bb2c14351d1a..2abbf278a80b33a44d624bc98e74962e3c677648 100644 GIT binary patch delta 72 zcmZqRxyQ}s>8Zuw!Mbsu6DuS4L{3GfORN)hlo&ZC`m1ZmFfubRGBAK~Fp$j*WZz%~ O5i delta 108 zcmcb|-N3`?>8ZuQ!N9=4JK2*}k!cmfkSv7W$mYZf05WU~FaQ7m diff --git a/tests/images/seq-3c-14b-planar.tiff b/tests/images/seq-3c-14b-planar.tiff index aa8620e66acfc6334e3d42ad36a5f80744ddd8b6..1b9fc080275e6bfcd11a40b44bd4d1204c01ce6d 100644 GIT binary patch delta 101 zcmbQi^MRYq(^HGVgLUIICss!8iJXc|mslt2C^6k*ofshF!py+Tz`$^Wm4QKqk(q%J j$YuiKU?3YL{t74#0(@Y2fDr^afcz6cg&jZzj9>@=#9|LU delta 164 zcmeysJ%fkS(^HFqgMop8cd{p|BGW2{$vLb_OdA*`Phgd)XJKFl%IpDRDMnTXMj(9x zh^3+I3qZCEBQsc>3CPxfvO(%VfDa6B09iAD(h)2S3gJWrU-(^HFqgLC6PCQe4~iJXc|lQ<{pC^2$O^jFuAVPs}tWMBZ}U?7_r$ezIo OB!EBy3>krPj0^z6c?h)t delta 108 zcmbOyd_b7f(^HFqgMop8cQPlZBGW2{$vT`$j0YzBbE7d6aqpHAfE#$$-%||R1bmxC)o;( delta 164 zcmdlWd_$Pi(^HFqgMop8cQPlZBGW2{$vT`$OdA*`2XM;NvoJ6NW%dBE6eBAGBal7; z#L`gq1t437kr^z`1Y~PK*&uZwAOVIqfUFxpV{Whk86e}J*aOG}sf5u$CRhMy5>N#T M0|P6=M(MX4044+vQ2+n{