PR #22895 opened by sohamukute URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22895 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22895.patch
Adds a synthetic TIFF decoder FATE test that exercises decoder pathsnot reachable via encoder roundtrip, including: big-endian byte order,photometric inversion, PAL8, FillOrder, GRAY12, RATIONAL/ASCII/GeoTIFF metadata, DNG, PackBits, ADOBE_DEFLATE, horizontal predictor, RGB24,GRAY_RESPONSE_CURVE, RGB48LE, 16-bit CMYK (RGBA64BE), and DNG BayerRGGB8 with color matrix processing. From eb1f78e1c8b9a329d0a0dcf0ee8bc5aca4357db4 Mon Sep 17 00:00:00 2001 From: Soham Kute <[email protected]> Date: Thu, 23 Apr 2026 01:48:39 +0530 Subject: [PATCH] avcodec/tests/tiffdec: add synthetic TIFF decoder FATE test Adds a synthetic TIFF decoder test program that exercises decoder paths not reachable via encoder roundtrip, including: big-endian (MM) byte order, GRAY16BE, WHITE_IS_ZERO photometric inversion, PAL8 with ColorMap, FillOrder=2 byte reversal, GRAY12 packed pixels, RATIONAL metadata tags, ASCII metadata, GeoTIFF tags, DNG decode path, PackBits and ADOBE_DEFLATE compression, horizontal predictor (GRAY8 and GRAY16LE), RGB24, GRAY_RESPONSE_CURVE (dng_lut), RGB48LE (init_image case 483), 16-bit CMYK in big-endian (init_image case 644 + RGBA64BE conversion path), and a DNG Bayer RGGB8 image with DNG_COLOR_MATRIX1 and DNG_CAMERA_CALIBRATION1 tags exercising camera_xyz_coeff() and the color-matrix else-branch. --- libavcodec/Makefile | 1 + libavcodec/tests/tiffdec.c | 1162 ++++++++++++++++++++++++++++++++++++ tests/fate/libavcodec.mak | 4 + tests/ref/fate/tiffdec | 21 + 4 files changed, 1188 insertions(+) create mode 100644 libavcodec/tests/tiffdec.c create mode 100644 tests/ref/fate/tiffdec diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 687121e337..1c5d3c5b48 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -1384,6 +1384,7 @@ TESTPROGS-$(CONFIG_H264_METADATA_BSF) += h264_levels TESTPROGS-$(CONFIG_HEVC_METADATA_BSF) += h265_levels TESTPROGS-$(CONFIG_RANGECODER) += rangecoder TESTPROGS-$(CONFIG_SNOW_ENCODER) += snowenc +TESTPROGS-$(CONFIG_TIFF_DECODER) += tiffdec TESTOBJS = dctref.o diff --git a/libavcodec/tests/tiffdec.c b/libavcodec/tests/tiffdec.c new file mode 100644 index 0000000000..acac4ae0a0 --- /dev/null +++ b/libavcodec/tests/tiffdec.c @@ -0,0 +1,1162 @@ +/* + * Synthetic TIFF decoder tests - exercises decoder paths not + * reachable via encoder roundtrip: big-endian (MM) byte order, + * GRAY16BE pixel format, WHITE_IS_ZERO photometric inversion, + * palette-indexed (PAL8) images with a ColorMap tag, FillOrder=2 + * byte reversal, GRAY12 packed pixels, RATIONAL metadata tags + * (XResolution/YResolution for SAR), ASCII metadata (Artist/Software/ + * Date), GeoTIFF tags (ModelPixelScale, ModelTiepoint, + * GeoKeyDirectory, GeoDoubleParams, GeoAsciiParams), DNG_VERSION tag + * triggering the DNG decode path (dng_process_color8, dng_blit), + * PackBits compression (literal and repeat runs), and + * ADOBE_DEFLATE/zlib compression. + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdint.h> +#include <string.h> + +#include "libavcodec/avcodec.h" +#include "libavutil/mem.h" +#include "libavutil/pixdesc.h" + +/* Little-endian byte writers */ +static void w16le(uint8_t *b, int *p, uint16_t v) +{ + b[(*p)++] = v & 0xFF; + b[(*p)++] = (v >> 8) & 0xFF; +} + +static void w32le(uint8_t *b, int *p, uint32_t v) +{ + b[(*p)++] = v & 0xFF; + b[(*p)++] = (v >> 8) & 0xFF; + b[(*p)++] = (v >> 16) & 0xFF; + b[(*p)++] = (v >> 24) & 0xFF; +} + +/* Write an IEEE 754 double as 8 bytes, little-endian. */ +static void w64le(uint8_t *b, int *p, double v) +{ + union { double d; uint64_t u; } cv; + cv.d = v; + w32le(b, p, (uint32_t)(cv.u & 0xFFFFFFFF)); + w32le(b, p, (uint32_t)(cv.u >> 32)); +} + +/* Big-endian byte writers */ +static void w16be(uint8_t *b, int *p, uint16_t v) +{ + b[(*p)++] = (v >> 8) & 0xFF; + b[(*p)++] = v & 0xFF; +} + +static void w32be(uint8_t *b, int *p, uint32_t v) +{ + b[(*p)++] = (v >> 24) & 0xFF; + b[(*p)++] = (v >> 16) & 0xFF; + b[(*p)++] = (v >> 8) & 0xFF; + b[(*p)++] = v & 0xFF; +} + +/* + * Write a 12-byte IFD entry (little-endian). + * SHORT (type 3) count-1 values are left-justified in the 4-byte + * value field per the TIFF spec; all other cases write 4 bytes. + */ +static void ifd_le(uint8_t *b, int *p, uint16_t tag, uint16_t type, + uint32_t count, uint32_t value) +{ + w16le(b, p, tag); + w16le(b, p, type); + w32le(b, p, count); + if (type == 3 && count == 1) { + w16le(b, p, (uint16_t)value); + w16le(b, p, 0); + } else { + w32le(b, p, value); + } +} + +/* + * Write a 12-byte IFD entry (big-endian). + * Same left-justification rule applies. + */ +static void ifd_be(uint8_t *b, int *p, uint16_t tag, uint16_t type, + uint32_t count, uint32_t value) +{ + w16be(b, p, tag); + w16be(b, p, type); + w32be(b, p, count); + if (type == 3 && count == 1) { + w16be(b, p, (uint16_t)value); + w16be(b, p, 0); + } else { + w32be(b, p, value); + } +} + +/* + * Open the TIFF decoder, send one synthetic packet, and return the + * decoded AVPixelFormat on success or a negative error code. + */ +static int decode_tiff(const uint8_t *data, int size) +{ + const AVCodec *codec; + AVCodecContext *avctx; + AVPacket *pkt; + AVFrame *frame; + int ret, fmt = -1; + + codec = avcodec_find_decoder(AV_CODEC_ID_TIFF); + if (!codec) + return -1; + + avctx = avcodec_alloc_context3(codec); + if (!avctx) + return -1; + + if ((ret = avcodec_open2(avctx, codec, NULL)) < 0) { + avcodec_free_context(&avctx); + return ret; + } + + pkt = av_packet_alloc(); + frame = av_frame_alloc(); + if (!pkt || !frame) { + ret = -1; + goto out; + } + + if ((ret = av_new_packet(pkt, size)) < 0) + goto out; + + memcpy(pkt->data, data, size); + ret = avcodec_send_packet(avctx, pkt); + if (ret >= 0) + ret = avcodec_receive_frame(avctx, frame); + if (ret >= 0) + fmt = frame->format; + +out: + av_frame_free(&frame); + av_packet_free(&pkt); + avcodec_free_context(&avctx); + return ret >= 0 ? fmt : ret; +} + +/* + * Big-endian (MM byte order) 2x2 GRAY8 image, RAW compression, + * BLACK_IS_ZERO photometric. Exercises the s->le=0 path throughout + * tiff_decode_tag (all ff_tget calls use AV_RB16/AV_RB32). + * + * Layout: header(8) + IFD(2 + 8*12 + 4) + pixel_data(4) = 114 bytes + */ +static int test_be_gray8(void) +{ + uint8_t buf[114]; + int p = 0; + + memset(buf, 0, sizeof(buf)); + + buf[p++] = 0x4D; buf[p++] = 0x4D; /* "MM" */ + w16be(buf, &p, 42); + w32be(buf, &p, 8); /* IFD at offset 8 */ + + w16be(buf, &p, 8); /* 8 IFD entries */ + ifd_be(buf, &p, 0x0100, 3, 1, 2); /* ImageWidth */ + ifd_be(buf, &p, 0x0101, 3, 1, 2); /* ImageLength */ + ifd_be(buf, &p, 0x0102, 3, 1, 8); /* BitsPerSample */ + ifd_be(buf, &p, 0x0103, 3, 1, 1); /* Compression=RAW */ + ifd_be(buf, &p, 0x0106, 3, 1, 1); /* PhotometricInterpretation=BLACK_IS_ZERO */ + ifd_be(buf, &p, 0x0111, 4, 1, 110); /* StripOffsets */ + ifd_be(buf, &p, 0x0116, 3, 1, 2); /* RowsPerStrip */ + ifd_be(buf, &p, 0x0117, 4, 1, 4); /* StripByteCounts */ + w32be(buf, &p, 0); /* next IFD = none */ + + buf[p++] = 0xFF; buf[p++] = 0x80; + buf[p++] = 0x40; buf[p++] = 0x00; + + return decode_tiff(buf, sizeof(buf)); +} + +/* + * Big-endian (MM) 2x2 GRAY16 image, RAW compression. + * Exercises the AV_PIX_FMT_GRAY16BE pixel-format selection path + * (bpp=16, s->le=0 -> case 161 in init_image). + * + * Layout: header(8) + IFD(2 + 8*12 + 4) + pixel_data(8) = 118 bytes + */ +static int test_be_gray16(void) +{ + uint8_t buf[118]; + int p = 0; + + memset(buf, 0, sizeof(buf)); + + buf[p++] = 0x4D; buf[p++] = 0x4D; + w16be(buf, &p, 42); + w32be(buf, &p, 8); + + w16be(buf, &p, 8); + ifd_be(buf, &p, 0x0100, 3, 1, 2); + ifd_be(buf, &p, 0x0101, 3, 1, 2); + ifd_be(buf, &p, 0x0102, 3, 1, 16); /* BitsPerSample=16 */ + ifd_be(buf, &p, 0x0103, 3, 1, 1); + ifd_be(buf, &p, 0x0106, 3, 1, 1); + ifd_be(buf, &p, 0x0111, 4, 1, 110); + ifd_be(buf, &p, 0x0116, 3, 1, 2); + ifd_be(buf, &p, 0x0117, 4, 1, 8); /* StripByteCounts=8 */ + w32be(buf, &p, 0); + + w16be(buf, &p, 0xFFFF); w16be(buf, &p, 0x8000); + w16be(buf, &p, 0x4000); w16be(buf, &p, 0x0001); + + return decode_tiff(buf, sizeof(buf)); +} + +/* + * Little-endian 2x2 GRAY8 with WHITE_IS_ZERO (MinIsWhite) photometric. + * Exercises the sample-inversion loop near the end of decode_frame + * where each output byte becomes (255 - byte). + * + * Layout: header(8) + IFD(2 + 8*12 + 4) + pixel_data(4) = 114 bytes + */ +static int test_miniswhite(void) +{ + uint8_t buf[114]; + int p = 0; + + memset(buf, 0, sizeof(buf)); + + buf[p++] = 0x49; buf[p++] = 0x49; /* "II" */ + w16le(buf, &p, 42); + w32le(buf, &p, 8); + + w16le(buf, &p, 8); + ifd_le(buf, &p, 0x0100, 3, 1, 2); + ifd_le(buf, &p, 0x0101, 3, 1, 2); + ifd_le(buf, &p, 0x0102, 3, 1, 8); + ifd_le(buf, &p, 0x0103, 3, 1, 1); + ifd_le(buf, &p, 0x0106, 3, 1, 0); /* PhotometricInterpretation=WHITE_IS_ZERO */ + ifd_le(buf, &p, 0x0111, 4, 1, 110); + ifd_le(buf, &p, 0x0116, 3, 1, 2); + ifd_le(buf, &p, 0x0117, 4, 1, 4); + w32le(buf, &p, 0); + + buf[p++] = 0xFF; buf[p++] = 0x80; + buf[p++] = 0x40; buf[p++] = 0x00; + + return decode_tiff(buf, sizeof(buf)); +} + +/* + * Little-endian 2x2 PAL8 image with a ColorMap tag. + * Exercises the TIFF_PAL handler (palette reading, palette_is_set=1) + * and the AV_PIX_FMT_PAL8 pixel-format path in init_image. + * + * Layout: + * header(8) + IFD(2 + 9*12 + 4) + colormap(768*2) + pixel_data(4) + * = 8 + 114 + 1536 + 4 = 1662 bytes + * + * The ColorMap holds 256 R, 256 G, 256 B entries as uint16le. + * The decoder shifts each 16-bit value right by 8 bits, so entry i + * maps to color (i, i, i) with a grayscale ramp of i*257. + */ +static int test_pal8(void) +{ + uint8_t buf[1662]; + int p = 0, i; + + memset(buf, 0, sizeof(buf)); + + buf[p++] = 0x49; buf[p++] = 0x49; + w16le(buf, &p, 42); + w32le(buf, &p, 8); + + w16le(buf, &p, 9); /* 9 IFD entries */ + ifd_le(buf, &p, 0x0100, 3, 1, 2); + ifd_le(buf, &p, 0x0101, 3, 1, 2); + ifd_le(buf, &p, 0x0102, 3, 1, 8); + ifd_le(buf, &p, 0x0103, 3, 1, 1); + ifd_le(buf, &p, 0x0106, 3, 1, 3); /* PhotometricInterpretation=PALETTE */ + ifd_le(buf, &p, 0x0111, 4, 1, 1658); /* StripOffsets */ + ifd_le(buf, &p, 0x0116, 3, 1, 2); + ifd_le(buf, &p, 0x0117, 4, 1, 4); + ifd_le(buf, &p, 0x0140, 3, 768, 122); /* ColorMap at offset 122 */ + w32le(buf, &p, 0); + + for (i = 0; i < 256; i++) w16le(buf, &p, i * 257); /* R[0..255] */ + for (i = 0; i < 256; i++) w16le(buf, &p, i * 257); /* G[0..255] */ + for (i = 0; i < 256; i++) w16le(buf, &p, i * 257); /* B[0..255] */ + + buf[p++] = 0x00; buf[p++] = 0x80; + buf[p++] = 0xFF; buf[p++] = 0x40; + + return decode_tiff(buf, sizeof(buf)); +} + +/* + * Little-endian 2x2 GRAY8 with FillOrder=2 (LSB-to-MSB bit order). + * Exercises the TIFF_FILL_ORDER tag handler (s->fill_order = value - 1) + * and the ff_reverse[src[i]] byte-reversal loop in tiff_unpack_strip + * (TIFF_RAW branch, fill_order != 0 path). + * + * Layout: header(8) + IFD(2 + 9*12 + 4) + pixel_data(4) = 126 bytes + */ +static int test_fill_order(void) +{ + uint8_t buf[126]; + int p = 0; + + memset(buf, 0, sizeof(buf)); + + buf[p++] = 0x49; buf[p++] = 0x49; + w16le(buf, &p, 42); + w32le(buf, &p, 8); + + w16le(buf, &p, 9); /* 9 IFD entries */ + ifd_le(buf, &p, 0x0100, 3, 1, 2); /* ImageWidth */ + ifd_le(buf, &p, 0x0101, 3, 1, 2); /* ImageLength */ + ifd_le(buf, &p, 0x0102, 3, 1, 8); /* BitsPerSample */ + ifd_le(buf, &p, 0x0103, 3, 1, 1); /* Compression=RAW */ + ifd_le(buf, &p, 0x0106, 3, 1, 1); /* Photometric=BLACK_IS_ZERO */ + ifd_le(buf, &p, 0x010A, 3, 1, 2); /* FillOrder=2 (LSB first) */ + ifd_le(buf, &p, 0x0111, 4, 1, 122); /* StripOffsets */ + ifd_le(buf, &p, 0x0116, 3, 1, 2); /* RowsPerStrip */ + ifd_le(buf, &p, 0x0117, 4, 1, 4); /* StripByteCounts */ + w32le(buf, &p, 0); + + buf[p++] = 0xFF; buf[p++] = 0x80; + buf[p++] = 0x40; buf[p++] = 0x00; + + return decode_tiff(buf, sizeof(buf)); +} + +/* + * Little-endian 2x2 GRAY12 image, RAW compression. + * Exercises AV_PIX_FMT_GRAY12 pixel-format selection (bpp=12, case 121 + * in init_image) and the unpack_gray() bitstream-reader path in + * tiff_unpack_strip. + * + * Each row is 3 bytes: 2 pixels x 12 bits = 24 bits packed MSB-first. + * Layout: header(8) + IFD(2 + 8*12 + 4) + pixel_data(6) = 116 bytes + */ +static int test_gray12(void) +{ + uint8_t buf[116]; + int p = 0; + + memset(buf, 0, sizeof(buf)); + + buf[p++] = 0x49; buf[p++] = 0x49; + w16le(buf, &p, 42); + w32le(buf, &p, 8); + + w16le(buf, &p, 8); /* 8 IFD entries */ + ifd_le(buf, &p, 0x0100, 3, 1, 2); /* ImageWidth */ + ifd_le(buf, &p, 0x0101, 3, 1, 2); /* ImageLength */ + ifd_le(buf, &p, 0x0102, 3, 1, 12); /* BitsPerSample=12 */ + ifd_le(buf, &p, 0x0103, 3, 1, 1); /* Compression=RAW */ + ifd_le(buf, &p, 0x0106, 3, 1, 1); /* Photometric=BLACK_IS_ZERO */ + ifd_le(buf, &p, 0x0111, 4, 1, 110); /* StripOffsets */ + ifd_le(buf, &p, 0x0116, 3, 1, 2); /* RowsPerStrip */ + ifd_le(buf, &p, 0x0117, 4, 1, 6); /* StripByteCounts=6 (3 bytes/row x 2) */ + w32le(buf, &p, 0); + + /* 2 rows x 3 bytes: packed 12-bit samples, all bits set */ + buf[p++] = 0xFF; buf[p++] = 0xFF; buf[p++] = 0xFF; + buf[p++] = 0xFF; buf[p++] = 0xFF; buf[p++] = 0xFF; + + return decode_tiff(buf, sizeof(buf)); +} + +/* + * Little-endian 2x2 GRAY8 with RATIONAL XResolution/YResolution tags, + * a PageNumber tag (two packed SHORTs), and ASCII metadata tags for + * Artist, SoftwareName, and Date. + * + * Exercises: set_sar() (XResolution/YResolution RATIONAL path), + * the PageNumber SHORT-count-2 handler, and ff_tadd_string_metadata() + * via ADD_METADATA for all three string tags. + * + * Layout: + * header(8) + IFD(2 + 14*12 + 4) + xres(8) + yres(8) + + * sw(7) + date(20) + artist(5) + pixels(4) = 234 bytes + * + * Tag offsets after IFD end at 182: + * 182: XResolution 72/1 + * 190: YResolution 72/1 + * 198: SoftwareName "FFmpeg\0" + * 205: Date "2024:01:01 00:00:00\0" + * 225: Artist "Test\0" + * 230: pixel data + */ +static int test_metadata(void) +{ + static const char sw[] = "FFmpeg"; + static const char date[] = "2024:01:01 00:00:00"; + static const char artist[] = "Test"; + uint8_t buf[234]; + int p = 0; + + memset(buf, 0, sizeof(buf)); + + buf[p++] = 0x49; buf[p++] = 0x49; + w16le(buf, &p, 42); + w32le(buf, &p, 8); + + w16le(buf, &p, 14); /* 14 IFD entries */ + ifd_le(buf, &p, 0x0100, 3, 1, 2); /* ImageWidth */ + ifd_le(buf, &p, 0x0101, 3, 1, 2); /* ImageLength */ + ifd_le(buf, &p, 0x0102, 3, 1, 8); /* BitsPerSample */ + ifd_le(buf, &p, 0x0103, 3, 1, 1); /* Compression=RAW */ + ifd_le(buf, &p, 0x0106, 3, 1, 1); /* Photometric=BLACK_IS_ZERO */ + ifd_le(buf, &p, 0x0111, 4, 1, 230); /* StripOffsets */ + ifd_le(buf, &p, 0x0116, 3, 1, 2); /* RowsPerStrip */ + ifd_le(buf, &p, 0x0117, 4, 1, 4); /* StripByteCounts */ + ifd_le(buf, &p, 0x011A, 5, 1, 182); /* XResolution: RATIONAL at 182 */ + ifd_le(buf, &p, 0x011B, 5, 1, 190); /* YResolution: RATIONAL at 190 */ + ifd_le(buf, &p, 0x0129, 3, 2, 0u|(1u<<16)); /* PageNumber: page=0, total=1 */ + ifd_le(buf, &p, 0x0131, 2, 7, 198); /* SoftwareName: 7 bytes at 198 */ + ifd_le(buf, &p, 0x0132, 2, 20, 205); /* Date: 20 bytes at 205 */ + ifd_le(buf, &p, 0x013B, 2, 5, 225); /* Artist: 5 bytes at 225 */ + w32le(buf, &p, 0); + + /* p == 182 here */ + w32le(buf, &p, 72); w32le(buf, &p, 1); /* XResolution 72/1 */ + w32le(buf, &p, 72); w32le(buf, &p, 1); /* YResolution 72/1 */ + memcpy(buf + p, sw, sizeof(sw)); p += sizeof(sw); /* offset 198 */ + memcpy(buf + p, date, sizeof(date)); p += sizeof(date); /* offset 205 */ + memcpy(buf + p, artist, sizeof(artist)); p += sizeof(artist); /* offset 225 */ + + buf[p++] = 0xFF; buf[p++] = 0x80; /* pixel data at offset 230 */ + buf[p++] = 0x40; buf[p++] = 0x00; + + return decode_tiff(buf, sizeof(buf)); +} + +/* + * Little-endian 2x2 GRAY8 GeoTIFF with ModelPixelScale, ModelTiepoint, + * GeoKeyDirectory, GeoDoubleParams, and GeoAsciiParams tags. + * + * Exercises: tiff_set_type, free_geotags, get_geokey_name, + * get_geokey_type, get_geokey_val (GT_MODEL_TYPE and ANGULAR_UNITS via + * RET_GEOKEY_VAL, PROJECTED_CS_TYPE via search_keyval/cmp_id_key), + * doubles2str, add_metadata (DOUBLE and STRING paths). + * + * GeoKey directory has 5 keys: + * 1024 (GT_MODEL_TYPE) inline val=1 + * 2049 (GEOG_CITATION) GeoAsciiParams offset=0, count=6 + * 2054 (ANGULAR_UNITS) inline val=9101 + * 2057 (SEMI_MAJOR_AXIS) GeoDoubleParams offset=0, count=1 + * 3072 (PROJECTED_CS) inline val=3 (exercises search_keyval) + * + * Layout: + * header(8) + IFD(2 + 13*12 + 4) + * + mps(24) + mtp(48) + gkd(48) + gdp(8) + gap(6) + pixels(4) + * = 170 + 138 = 308 bytes + * + * Data offsets after IFD end at 170: + * 170: ModelPixelScale 3 doubles + * 194: ModelTiepoint 6 doubles + * 242: GeoKeyDirectory 24 shorts + * 290: GeoDoubleParams 1 double (6378137.0) + * 298: GeoAsciiParams "WGS84|" + * 304: pixel data + */ +static int test_geotiff(void) +{ + uint8_t buf[308]; + int p = 0, i; + + memset(buf, 0, sizeof(buf)); + + buf[p++] = 0x49; buf[p++] = 0x49; + w16le(buf, &p, 42); + w32le(buf, &p, 8); + + w16le(buf, &p, 13); /* 13 IFD entries */ + ifd_le(buf, &p, 0x0100, 3, 1, 2); /* ImageWidth */ + ifd_le(buf, &p, 0x0101, 3, 1, 2); /* ImageLength */ + ifd_le(buf, &p, 0x0102, 3, 1, 8); /* BitsPerSample */ + ifd_le(buf, &p, 0x0103, 3, 1, 1); /* Compression=RAW */ + ifd_le(buf, &p, 0x0106, 3, 1, 1); /* Photometric=BLACK_IS_ZERO */ + ifd_le(buf, &p, 0x0111, 4, 1, 304); /* StripOffsets */ + ifd_le(buf, &p, 0x0116, 3, 1, 2); /* RowsPerStrip */ + ifd_le(buf, &p, 0x0117, 4, 1, 4); /* StripByteCounts */ + ifd_le(buf, &p, 0x830E, 12, 3, 170); /* ModelPixelScale: 3 doubles */ + ifd_le(buf, &p, 0x8482, 12, 6, 194); /* ModelTiepoint: 6 doubles */ + ifd_le(buf, &p, 0x87AF, 3, 24, 242); /* GeoKeyDirectory: 24 shorts */ + ifd_le(buf, &p, 0x87B0, 12, 1, 290); /* GeoDoubleParams: 1 double */ + ifd_le(buf, &p, 0x87B1, 2, 6, 298); /* GeoAsciiParams: 6 bytes */ + w32le(buf, &p, 0); + + /* p == 170 */ + /* ModelPixelScale: scale_x=1.0, scale_y=1.0, scale_z=0.0 */ + w64le(buf, &p, 1.0); w64le(buf, &p, 1.0); w64le(buf, &p, 0.0); + + /* ModelTiepoint: 6 doubles (i,j,k, x,y,z) all zero */ + for (i = 0; i < 6; i++) w64le(buf, &p, 0.0); + + /* GeoKeyDirectory: header + 5 key entries = 24 shorts */ + w16le(buf, &p, 1); w16le(buf, &p, 1); w16le(buf, &p, 0); w16le(buf, &p, 5); + w16le(buf, &p, 1024); w16le(buf, &p, 0); w16le(buf, &p, 1); w16le(buf, &p, 1); + w16le(buf, &p, 2049); w16le(buf, &p, 34737); w16le(buf, &p, 6); w16le(buf, &p, 0); + w16le(buf, &p, 2054); w16le(buf, &p, 0); w16le(buf, &p, 1); w16le(buf, &p, 9101); + w16le(buf, &p, 2057); w16le(buf, &p, 34736); w16le(buf, &p, 1); w16le(buf, &p, 0); + w16le(buf, &p, 3072); w16le(buf, &p, 0); w16le(buf, &p, 1); w16le(buf, &p, 3); + + /* GeoDoubleParams: WGS84 semi-major axis */ + w64le(buf, &p, 6378137.0); + + /* GeoAsciiParams: delimiter-terminated string (last byte replaced with NUL) */ + buf[p++] = 'W'; buf[p++] = 'G'; buf[p++] = 'S'; + buf[p++] = '8'; buf[p++] = '4'; buf[p++] = '|'; + + buf[p++] = 0xFF; buf[p++] = 0x80; /* pixel data at offset 304 */ + buf[p++] = 0x40; buf[p++] = 0x00; + + return decode_tiff(buf, sizeof(buf)); +} + +/* + * Little-endian 2x2 YCbCr 4:2:0 image. + * Exercises: TIFF_YCBCR_SUBSAMPLING tag handler (s->subsampling[]), + * init_image case 243 YCbCr path (AV_PIX_FMT_YUV420P), the is_yuv + * branch in tiff_unpack_strip (yuv_line buffer, width recomputation), + * and unpack_yuv() which writes to the three output planes. + * + * Strip data (6 bytes): [Y00, Y01, Y10, Y11, Cb, Cr] packed YCbCr block. + * + * Layout: + * header(8) + IFD(2+10*12+4) + bps_data(6) + strip(6) = 146 bytes + * + * BitsPerSample=[8,8,8] at offset 134; strip at offset 140. + */ +static int test_ycbcr(void) +{ + uint8_t buf[146]; + int p = 0; + + memset(buf, 0, sizeof(buf)); + + buf[p++] = 0x49; buf[p++] = 0x49; + w16le(buf, &p, 42); + w32le(buf, &p, 8); + + w16le(buf, &p, 10); /* 10 IFD entries */ + ifd_le(buf, &p, 0x0100, 3, 1, 2); /* ImageWidth */ + ifd_le(buf, &p, 0x0101, 3, 1, 2); /* ImageLength */ + ifd_le(buf, &p, 0x0102, 3, 3, 134); /* BitsPerSample=[8,8,8] at 134 */ + ifd_le(buf, &p, 0x0103, 3, 1, 1); /* Compression=RAW */ + ifd_le(buf, &p, 0x0106, 3, 1, 6); /* Photometric=YCBCR */ + ifd_le(buf, &p, 0x0111, 4, 1, 140); /* StripOffsets */ + ifd_le(buf, &p, 0x0115, 3, 1, 3); /* SamplesPerPixel=3 */ + ifd_le(buf, &p, 0x0116, 3, 1, 2); /* RowsPerStrip */ + ifd_le(buf, &p, 0x0117, 4, 1, 6); /* StripByteCounts */ + /* YCbCrSubSampling=[2,2] inline: two SHORTs in 4-byte field */ + ifd_le(buf, &p, 0x0212, 3, 2, 0x00020002); + w32le(buf, &p, 0); + + /* p == 134: BitsPerSample data */ + w16le(buf, &p, 8); w16le(buf, &p, 8); w16le(buf, &p, 8); + + /* p == 140: YCbCr strip: Y00, Y01, Y10, Y11, Cb, Cr */ + buf[p++] = 0x80; buf[p++] = 0x80; + buf[p++] = 0x80; buf[p++] = 0x80; + buf[p++] = 0x80; buf[p++] = 0x80; + + return decode_tiff(buf, sizeof(buf)); +} + +/* + * Little-endian 2x2 CMYK image (SEPARATED photometric). + * Exercises: init_image case 324 (AV_PIX_FMT_RGB0 for SEPARATED), + * the CMYK-to-RGB conversion loop in decode_frame (lines 2360-2373), + * and multi-component BitsPerSample handler (bppcount>1 sum loop). + * + * Layout: + * header(8) + IFD(2+9*12+4) + bps_data(8) + strip(16) = 146 bytes + * + * BitsPerSample=[8,8,8,8] at 122; strip at 130. + */ +static int test_cmyk(void) +{ + uint8_t buf[146]; + int p = 0; + + memset(buf, 0, sizeof(buf)); + + buf[p++] = 0x49; buf[p++] = 0x49; + w16le(buf, &p, 42); + w32le(buf, &p, 8); + + w16le(buf, &p, 9); /* 9 IFD entries */ + ifd_le(buf, &p, 0x0100, 3, 1, 2); /* ImageWidth */ + ifd_le(buf, &p, 0x0101, 3, 1, 2); /* ImageLength */ + ifd_le(buf, &p, 0x0102, 3, 4, 122); /* BitsPerSample=[8,8,8,8] at 122 */ + ifd_le(buf, &p, 0x0103, 3, 1, 1); /* Compression=RAW */ + ifd_le(buf, &p, 0x0106, 3, 1, 5); /* Photometric=SEPARATED (CMYK) */ + ifd_le(buf, &p, 0x0111, 4, 1, 130); /* StripOffsets */ + ifd_le(buf, &p, 0x0115, 3, 1, 4); /* SamplesPerPixel=4 */ + ifd_le(buf, &p, 0x0116, 3, 1, 2); /* RowsPerStrip */ + ifd_le(buf, &p, 0x0117, 4, 1, 16); /* StripByteCounts=16 */ + w32le(buf, &p, 0); + + /* p == 122: BitsPerSample = [8, 8, 8, 8] */ + w16le(buf, &p, 8); w16le(buf, &p, 8); + w16le(buf, &p, 8); w16le(buf, &p, 8); + + /* p == 130: CMYK strip (4 pixels x 4 bytes each = 16 bytes) */ + /* Row 0: pixel0 C=0,M=0,Y=0,K=0 (white); pixel1 C=0,M=0,Y=0,K=255 (black) */ + buf[p++]=0; buf[p++]=0; buf[p++]=0; buf[p++]=0; + buf[p++]=0; buf[p++]=0; buf[p++]=0; buf[p++]=255; + /* Row 1: pixel2 C=255,M=0,Y=0,K=0; pixel3 C=0,M=255,Y=0,K=0 */ + buf[p++]=255; buf[p++]=0; buf[p++]=0; buf[p++]=0; + buf[p++]=0; buf[p++]=255; buf[p++]=0; buf[p++]=0; + + return decode_tiff(buf, sizeof(buf)); +} + +/* + * Little-endian 2x2 GRAY8 with additional ASCII metadata tags: + * DocumentName (0x010D), ImageDescription (0x010E), Make (0x010F), + * Model (0x0110), PageName (0x011D), HostComputer (0x013C). + * Exercises: ADD_METADATA for all six handlers in tiff_decode_tag + * (lines 1747-1764). Each string has count=5 (>4) so ff_tread_tag + * seeks to the off-IFD string data. + * + * Layout: + * header(8) + IFD(2+14*12+4) + 6 strings*5b + pixels(4) = 216 bytes + * + * Strings at 182: "doc1\0","img1\0","cam1\0","mdl1\0","pg1a\0","hst1\0" + */ +static int test_more_metadata(void) +{ + static const char doc[] = "doc1"; + static const char img[] = "img1"; + static const char cam[] = "cam1"; + static const char mdl[] = "mdl1"; + static const char pg[] = "pg1a"; + static const char hst[] = "hst1"; + uint8_t buf[216]; + int p = 0; + + memset(buf, 0, sizeof(buf)); + + buf[p++] = 0x49; buf[p++] = 0x49; + w16le(buf, &p, 42); + w32le(buf, &p, 8); + + w16le(buf, &p, 14); /* 14 IFD entries */ + ifd_le(buf, &p, 0x0100, 3, 1, 2); /* ImageWidth */ + ifd_le(buf, &p, 0x0101, 3, 1, 2); /* ImageLength */ + ifd_le(buf, &p, 0x0102, 3, 1, 8); /* BitsPerSample */ + ifd_le(buf, &p, 0x0103, 3, 1, 1); /* Compression=RAW */ + ifd_le(buf, &p, 0x0106, 3, 1, 1); /* Photometric=BLACK_IS_ZERO */ + ifd_le(buf, &p, 0x010D, 2, 5, 182); /* DocumentName at 182 */ + ifd_le(buf, &p, 0x010E, 2, 5, 187); /* ImageDescription at 187 */ + ifd_le(buf, &p, 0x010F, 2, 5, 192); /* Make at 192 */ + ifd_le(buf, &p, 0x0110, 2, 5, 197); /* Model at 197 */ + ifd_le(buf, &p, 0x0111, 4, 1, 212); /* StripOffsets */ + ifd_le(buf, &p, 0x0116, 3, 1, 2); /* RowsPerStrip */ + ifd_le(buf, &p, 0x0117, 4, 1, 4); /* StripByteCounts */ + ifd_le(buf, &p, 0x011D, 2, 5, 202); /* PageName at 202 */ + ifd_le(buf, &p, 0x013C, 2, 5, 207); /* HostComputer at 207 */ + w32le(buf, &p, 0); + + /* p == 182: string data */ + memcpy(buf + p, doc, 5); p += 5; + memcpy(buf + p, img, 5); p += 5; + memcpy(buf + p, cam, 5); p += 5; + memcpy(buf + p, mdl, 5); p += 5; + memcpy(buf + p, pg, 5); p += 5; + memcpy(buf + p, hst, 5); p += 5; + + /* p == 212: pixel data */ + buf[p++] = 0xFF; buf[p++] = 0x80; + buf[p++] = 0x40; buf[p++] = 0x00; + + return decode_tiff(buf, sizeof(buf)); +} + +/* + * Little-endian 2x2 GRAY16LE with Predictor=2 (horizontal differencing). + * Exercises: TIFF_PREDICTOR tag handler (s->predictor=2, lines 1452-1456), + * and the 16-bit LE predictor loop in decode_frame (lines 2248-2258). + * + * Each row stores pixel differences: actual[j] = stored[j] + actual[j-1]. + * Strip [0x01,0x00, 0x01,0x00, ...] decodes to [1, 2] per row. + * + * Layout: header(8) + IFD(2+9*12+4) + strip(8) = 130 bytes + */ +static int test_predictor_gray16le(void) +{ + uint8_t buf[130]; + int p = 0; + + memset(buf, 0, sizeof(buf)); + + buf[p++] = 0x49; buf[p++] = 0x49; + w16le(buf, &p, 42); + w32le(buf, &p, 8); + + w16le(buf, &p, 9); /* 9 IFD entries */ + ifd_le(buf, &p, 0x0100, 3, 1, 2); /* ImageWidth */ + ifd_le(buf, &p, 0x0101, 3, 1, 2); /* ImageLength */ + ifd_le(buf, &p, 0x0102, 3, 1, 16); /* BitsPerSample=16 */ + ifd_le(buf, &p, 0x0103, 3, 1, 1); /* Compression=RAW */ + ifd_le(buf, &p, 0x0106, 3, 1, 1); /* Photometric=BLACK_IS_ZERO */ + ifd_le(buf, &p, 0x0111, 4, 1, 122); /* StripOffsets */ + ifd_le(buf, &p, 0x0116, 3, 1, 2); /* RowsPerStrip */ + ifd_le(buf, &p, 0x0117, 4, 1, 8); /* StripByteCounts=8 */ + ifd_le(buf, &p, 0x013D, 3, 1, 2); /* Predictor=2 */ + w32le(buf, &p, 0); + + /* 2 rows x 2 pixels x 2 bytes: each pixel stores the delta from previous */ + w16le(buf, &p, 0x0001); w16le(buf, &p, 0x0001); /* row 0: [1, 1+1=2] */ + w16le(buf, &p, 0x0001); w16le(buf, &p, 0x0001); /* row 1: [1, 2] */ + + return decode_tiff(buf, sizeof(buf)); +} + +/* + * Little-endian 2x2 GRAY8 with Predictor=2 (horizontal differencing). + * Exercises the 8-bit (else) branch of the predictor post-processing + * loop in decode_frame (lines 2270-2278). + * + * Row data stores pixel differences; decoding adds each delta to previous. + * Strip [0x80,0x10, 0x40,0x20] decodes to row0=[0x80,0x90], row1=[0x40,0x60]. + * + * Layout: header(8) + IFD(2+9*12+4) + strip(4) = 126 bytes + */ +static int test_predictor_gray8(void) +{ + uint8_t buf[126]; + int p = 0; + + memset(buf, 0, sizeof(buf)); + + buf[p++] = 0x49; buf[p++] = 0x49; + w16le(buf, &p, 42); + w32le(buf, &p, 8); + + w16le(buf, &p, 9); /* 9 IFD entries */ + ifd_le(buf, &p, 0x0100, 3, 1, 2); /* ImageWidth */ + ifd_le(buf, &p, 0x0101, 3, 1, 2); /* ImageLength */ + ifd_le(buf, &p, 0x0102, 3, 1, 8); /* BitsPerSample=8 */ + ifd_le(buf, &p, 0x0103, 3, 1, 1); /* Compression=RAW */ + ifd_le(buf, &p, 0x0106, 3, 1, 1); /* Photometric=BLACK_IS_ZERO */ + ifd_le(buf, &p, 0x0111, 4, 1, 122); /* StripOffsets */ + ifd_le(buf, &p, 0x0116, 3, 1, 2); /* RowsPerStrip */ + ifd_le(buf, &p, 0x0117, 4, 1, 4); /* StripByteCounts */ + ifd_le(buf, &p, 0x013D, 3, 1, 2); /* Predictor=2 */ + w32le(buf, &p, 0); + + buf[p++] = 0x80; buf[p++] = 0x10; /* row 0: base=0x80, delta=0x10 */ + buf[p++] = 0x40; buf[p++] = 0x20; /* row 1: base=0x40, delta=0x20 */ + + return decode_tiff(buf, sizeof(buf)); +} + +/* + * Little-endian 2x2 RGB24 image. + * Exercises: multi-component BitsPerSample ([8,8,8], count=3 — off-IFD + * sum loop at lines 1329-1334), SamplesPerPixel=3 handler (1340-1350), + * and init_image case 243 non-YCbCr path (AV_PIX_FMT_RGB24, line 1139). + * + * Layout: + * header(8) + IFD(2+9*12+4) + bps_data(6) + strip(12) = 140 bytes + * + * BitsPerSample=[8,8,8] at 122; RGB strip (4 pixels) at 128. + */ +static int test_rgb24(void) +{ + uint8_t buf[140]; + int p = 0; + + memset(buf, 0, sizeof(buf)); + + buf[p++] = 0x49; buf[p++] = 0x49; + w16le(buf, &p, 42); + w32le(buf, &p, 8); + + w16le(buf, &p, 9); /* 9 IFD entries */ + ifd_le(buf, &p, 0x0100, 3, 1, 2); /* ImageWidth */ + ifd_le(buf, &p, 0x0101, 3, 1, 2); /* ImageLength */ + ifd_le(buf, &p, 0x0102, 3, 3, 122); /* BitsPerSample=[8,8,8] at 122 */ + ifd_le(buf, &p, 0x0103, 3, 1, 1); /* Compression=RAW */ + ifd_le(buf, &p, 0x0106, 3, 1, 2); /* Photometric=RGB */ + ifd_le(buf, &p, 0x0111, 4, 1, 128); /* StripOffsets */ + ifd_le(buf, &p, 0x0115, 3, 1, 3); /* SamplesPerPixel=3 */ + ifd_le(buf, &p, 0x0116, 3, 1, 2); /* RowsPerStrip */ + ifd_le(buf, &p, 0x0117, 4, 1, 12); /* StripByteCounts=12 */ + w32le(buf, &p, 0); + + /* p == 122: BitsPerSample = [8, 8, 8] */ + w16le(buf, &p, 8); w16le(buf, &p, 8); w16le(buf, &p, 8); + + /* p == 128: RGB strip (4 pixels x 3 bytes) */ + buf[p++]=0xFF; buf[p++]=0x00; buf[p++]=0x00; /* red */ + buf[p++]=0x00; buf[p++]=0xFF; buf[p++]=0x00; /* green */ + buf[p++]=0x00; buf[p++]=0x00; buf[p++]=0xFF; /* blue */ + buf[p++]=0xFF; buf[p++]=0xFF; buf[p++]=0xFF; /* white */ + + return decode_tiff(buf, sizeof(buf)); +} + +/* + * Little-endian 2x2 GRAY8 DNG image (RAW compression). + * The DNG_VERSION tag (0xC612, type BYTE, count=4, value=[1,3,0,0]) + * triggers tiff_set_type(TIFF_TYPE_DNG), setting is_dng=1 in + * decode_frame. This exercises the DNG matrix/premultiply setup + * (lines 2071-2122), the dng_blit() call inside tiff_unpack_strip, + * and the dng_process_color8/dng_process_color16 functions. + * + * Layout: header(8) + IFD(2 + 9*12 + 4) + pixel_data(4) = 126 bytes + */ +static int test_dng(void) +{ + uint8_t buf[126]; + int p = 0; + + memset(buf, 0, sizeof(buf)); + + buf[p++] = 0x49; buf[p++] = 0x49; + w16le(buf, &p, 42); + w32le(buf, &p, 8); + + w16le(buf, &p, 9); /* 9 IFD entries */ + ifd_le(buf, &p, 0x0100, 3, 1, 2); /* ImageWidth */ + ifd_le(buf, &p, 0x0101, 3, 1, 2); /* ImageLength */ + ifd_le(buf, &p, 0x0102, 3, 1, 8); /* BitsPerSample */ + ifd_le(buf, &p, 0x0103, 3, 1, 1); /* Compression=RAW */ + ifd_le(buf, &p, 0x0106, 3, 1, 1); /* Photometric=BLACK_IS_ZERO */ + ifd_le(buf, &p, 0x0111, 3, 1, 122); /* StripOffsets */ + ifd_le(buf, &p, 0x0116, 3, 1, 2); /* RowsPerStrip */ + ifd_le(buf, &p, 0x0117, 3, 1, 4); /* StripByteCounts */ + /* DNG_VERSION = [1,3,0,0]: type BYTE(1), count=4, inline value 0x00000301 */ + ifd_le(buf, &p, 0xC612, 1, 4, 0x00000301); + w32le(buf, &p, 0); + + buf[p++] = 0xFF; buf[p++] = 0x80; + buf[p++] = 0x40; buf[p++] = 0x00; + + return decode_tiff(buf, sizeof(buf)); +} + +/* + * Little-endian 2x2 GRAY8 with PackBits compression (0x8005). + * Row 0 uses a literal run (code=1 → copy 2 bytes). + * Row 1 uses a repeat run (code=-1 → fill 2 pixels with same byte). + * Exercises both branches of the PackBits decoder loop in + * tiff_unpack_strip (lines 922-958). + * + * Strip data (5 bytes): [0x01, 0xFF, 0x80, 0xFF, 0x40] + * Row 0: 0x01 → literal 2 bytes [0xFF, 0x80] + * Row 1: 0xFF (-1 as int8_t) → repeat 2x [0x40] + * + * Layout: header(8) + IFD(2 + 8*12 + 4) + strip(5) = 115 bytes + */ +static int test_packbits(void) +{ + uint8_t buf[115]; + int p = 0; + + memset(buf, 0, sizeof(buf)); + + buf[p++] = 0x49; buf[p++] = 0x49; + w16le(buf, &p, 42); + w32le(buf, &p, 8); + + w16le(buf, &p, 8); /* 8 IFD entries */ + ifd_le(buf, &p, 0x0100, 3, 1, 2); /* ImageWidth */ + ifd_le(buf, &p, 0x0101, 3, 1, 2); /* ImageLength */ + ifd_le(buf, &p, 0x0102, 3, 1, 8); /* BitsPerSample */ + ifd_le(buf, &p, 0x0103, 3, 1, 32773); /* Compression=PACKBITS (0x8005) */ + ifd_le(buf, &p, 0x0106, 3, 1, 1); /* Photometric=BLACK_IS_ZERO */ + ifd_le(buf, &p, 0x0111, 4, 1, 110); /* StripOffsets */ + ifd_le(buf, &p, 0x0116, 3, 1, 2); /* RowsPerStrip */ + ifd_le(buf, &p, 0x0117, 4, 1, 5); /* StripByteCounts=5 */ + w32le(buf, &p, 0); + + /* PackBits stream: literal run then repeat run */ + buf[p++] = 0x01; /* copy next 2 bytes */ + buf[p++] = 0xFF; buf[p++] = 0x80; /* row 0 pixels */ + buf[p++] = 0xFF; /* -1 as int8_t: repeat next byte 2x */ + buf[p++] = 0x40; /* row 1 pixels */ + + return decode_tiff(buf, sizeof(buf)); +} + +/* + * Little-endian 2x2 GRAY8 with ADOBE_DEFLATE compression (type 8). + * The 4 raw pixels [0xFF,0x80,0x40,0x00] are stored as a zlib stream. + * Exercises tiff_uncompress() (inflate call) and tiff_unpack_zlib() + * (lines 501-564). + * + * Compressed bytes (zlib level 6, 12 bytes): + * 0x78 0x9C 0xFB 0xDF 0xE0 0xC0 0x00 0x00 0x06 0x00 0x01 0xC0 + * + * Layout: header(8) + IFD(2 + 8*12 + 4) + zlib_data(12) = 122 bytes + */ +static int test_deflate(void) +{ + static const uint8_t zdata[12] = { + 0x78, 0x9C, 0xFB, 0xDF, 0xE0, 0xC0, 0x00, 0x00, 0x06, 0x00, 0x01, 0xC0 + }; + uint8_t buf[122]; + int p = 0; + + memset(buf, 0, sizeof(buf)); + + buf[p++] = 0x49; buf[p++] = 0x49; + w16le(buf, &p, 42); + w32le(buf, &p, 8); + + w16le(buf, &p, 8); /* 8 IFD entries */ + ifd_le(buf, &p, 0x0100, 3, 1, 2); /* ImageWidth */ + ifd_le(buf, &p, 0x0101, 3, 1, 2); /* ImageLength */ + ifd_le(buf, &p, 0x0102, 3, 1, 8); /* BitsPerSample */ + ifd_le(buf, &p, 0x0103, 3, 1, 8); /* Compression=ADOBE_DEFLATE */ + ifd_le(buf, &p, 0x0106, 3, 1, 1); /* Photometric=BLACK_IS_ZERO */ + ifd_le(buf, &p, 0x0111, 4, 1, 110); /* StripOffsets */ + ifd_le(buf, &p, 0x0116, 3, 1, 2); /* RowsPerStrip */ + ifd_le(buf, &p, 0x0117, 4, 1, 12); /* StripByteCounts=12 */ + w32le(buf, &p, 0); + + memcpy(buf + p, zdata, sizeof(zdata)); /* zlib stream at offset 110 */ + + return decode_tiff(buf, sizeof(buf)); +} + +/* + * Little-endian 2x2 GRAY8 with TIFF_GRAY_RESPONSE_CURVE (tag 0x0122). + * Exercises the GRAY_RESPONSE_CURVE / DNG_LINEARIZATION_TABLE handler + * (tiff.c lines 1463-1470) which populates s->dng_lut and sets s->white_level. + * + * Layout: header(8) + IFD(2 + 9*12 + 4) + strip(4) = 126 bytes + */ +static int test_gray_lut(void) +{ + uint8_t buf[126]; + int p = 0; + + memset(buf, 0, sizeof(buf)); + + buf[p++] = 0x49; buf[p++] = 0x49; + w16le(buf, &p, 42); + w32le(buf, &p, 8); + + w16le(buf, &p, 9); /* 9 IFD entries */ + ifd_le(buf, &p, 0x0100, 3, 1, 2); /* ImageWidth */ + ifd_le(buf, &p, 0x0101, 3, 1, 2); /* ImageLength */ + ifd_le(buf, &p, 0x0102, 3, 1, 8); /* BitsPerSample */ + ifd_le(buf, &p, 0x0103, 3, 1, 1); /* Compression=RAW */ + ifd_le(buf, &p, 0x0106, 3, 1, 1); /* Photometric=BLACK_IS_ZERO */ + ifd_le(buf, &p, 0x0111, 4, 1, 122); /* StripOffsets */ + ifd_le(buf, &p, 0x0116, 3, 1, 2); /* RowsPerStrip */ + ifd_le(buf, &p, 0x0117, 4, 1, 4); /* StripByteCounts */ + ifd_le(buf, &p, 0x0123, 3, 1, 255); /* GRAY_RESPONSE_CURVE: count=1, value=255 */ + w32le(buf, &p, 0); + + buf[p++] = 0x10; buf[p++] = 0x20; + buf[p++] = 0x30; buf[p++] = 0x40; + + return decode_tiff(buf, sizeof(buf)); +} + +/* + * Little-endian 2x2 RGB48LE image (16 bits per channel, 3 channels). + * BitsPerSample=[16,16,16] stored off-IFD at offset 122 (3*2=6 > 4 bytes). + * Exercises init_image case 483 (tiff.c lines 1162-1164) → AV_PIX_FMT_RGB48LE. + * + * Layout: header(8) + IFD(2 + 9*12 + 4) + BPS(6) + strip(24) = 152 bytes + */ +static int test_rgb48(void) +{ + uint8_t buf[152]; + int p = 0; + + memset(buf, 0, sizeof(buf)); + + buf[p++] = 0x49; buf[p++] = 0x49; + w16le(buf, &p, 42); + w32le(buf, &p, 8); + + w16le(buf, &p, 9); /* 9 IFD entries */ + ifd_le(buf, &p, 0x0100, 3, 1, 2); /* ImageWidth */ + ifd_le(buf, &p, 0x0101, 3, 1, 2); /* ImageLength */ + ifd_le(buf, &p, 0x0102, 3, 3, 122); /* BitsPerSample: 3 SHORTs at offset 122 */ + ifd_le(buf, &p, 0x0103, 3, 1, 1); /* Compression=RAW */ + ifd_le(buf, &p, 0x0106, 3, 1, 2); /* Photometric=RGB */ + ifd_le(buf, &p, 0x0111, 4, 1, 128); /* StripOffsets */ + ifd_le(buf, &p, 0x0115, 3, 1, 3); /* SamplesPerPixel */ + ifd_le(buf, &p, 0x0116, 3, 1, 2); /* RowsPerStrip */ + ifd_le(buf, &p, 0x0117, 4, 1, 24); /* StripByteCounts */ + w32le(buf, &p, 0); + + /* BPS data at offset 122: [16, 16, 16] */ + w16le(buf, &p, 16); w16le(buf, &p, 16); w16le(buf, &p, 16); + + /* strip: 2*2*6 = 24 zero bytes (already zeroed by memset) */ + + return decode_tiff(buf, sizeof(buf)); +} + +/* + * Big-endian 2x2 CMYK 16-bit image (PhotometricInterpretation=SEPARATED). + * BitsPerSample=[16,16,16,16] stored off-IFD at offset 122 (4*2=8 > 4 bytes). + * Exercises init_image case 644 (→ AV_PIX_FMT_RGBA64BE, tiff.c lines 1165-1167) + * and the 16-bit CMYK-to-RGB conversion (tiff.c lines 2379-2394). + * + * Layout: header(8) + IFD(2 + 9*12 + 4) + BPS(8) + strip(32) = 162 bytes + */ +static int test_cmyk16be(void) +{ + uint8_t buf[162]; + int p = 0; + + memset(buf, 0, sizeof(buf)); + + buf[p++] = 0x4D; buf[p++] = 0x4D; /* 'MM' big-endian */ + w16be(buf, &p, 42); + w32be(buf, &p, 8); + + w16be(buf, &p, 9); /* 9 IFD entries */ + ifd_be(buf, &p, 0x0100, 3, 1, 2); /* ImageWidth */ + ifd_be(buf, &p, 0x0101, 3, 1, 2); /* ImageLength */ + ifd_be(buf, &p, 0x0102, 3, 4, 122); /* BitsPerSample: 4 SHORTs at offset 122 */ + ifd_be(buf, &p, 0x0103, 3, 1, 1); /* Compression=RAW */ + ifd_be(buf, &p, 0x0106, 3, 1, 5); /* Photometric=SEPARATED */ + ifd_be(buf, &p, 0x0111, 4, 1, 130); /* StripOffsets */ + ifd_be(buf, &p, 0x0115, 3, 1, 4); /* SamplesPerPixel */ + ifd_be(buf, &p, 0x0116, 3, 1, 2); /* RowsPerStrip */ + ifd_be(buf, &p, 0x0117, 4, 1, 32); /* StripByteCounts */ + w32be(buf, &p, 0); + + /* BPS data at offset 122: [16, 16, 16, 16] */ + w16be(buf, &p, 16); w16be(buf, &p, 16); w16be(buf, &p, 16); w16be(buf, &p, 16); + + /* strip: 2*2*8 = 32 zero bytes (already zeroed by memset) */ + + return decode_tiff(buf, sizeof(buf)); +} + +/* + * Little-endian 2x2 DNG Bayer RGGB 8-bit image. + * Exercises: CFA_PATTERN_DIM + CFA_PATTERN tag handlers, DNG_VERSION triggering + * TIFF_TYPE_DNG, DNG_BLACK_LEVEL, DNG_WHITE_LEVEL, DNG_COLOR_MATRIX1 (sets + * use_color_matrix=1), DNG_CAMERA_CALIBRATION1, init_image case 100081 + * (→ AV_PIX_FMT_BAYER_RGGB8), camera_xyz_coeff(), and the use_color_matrix + * else-branch in decode_frame (tiff.c lines 2087-2096). + * + * Buffer layout: + * header(8) + IFD count(2) + 16*12(192) + next_IFD(4) = 206 bytes + * DNG_COLOR_MATRIX1 data (9 SRATIONAL = 72 bytes) at offset 206 + * DNG_CAMERA_CALIBRATION1 data (72 bytes) at offset 278 + * strip (4 bytes) at offset 350 + * Total: 354 bytes + */ +static int test_bayer_rggb8(void) +{ + uint8_t buf[354]; + int p = 0; + int r, c; + + memset(buf, 0, sizeof(buf)); + + buf[p++] = 0x49; buf[p++] = 0x49; + w16le(buf, &p, 42); + w32le(buf, &p, 8); + + w16le(buf, &p, 16); /* 16 IFD entries */ + ifd_le(buf, &p, 0x0100, 3, 1, 2); /* ImageWidth */ + ifd_le(buf, &p, 0x0101, 3, 1, 2); /* ImageLength */ + ifd_le(buf, &p, 0x0102, 3, 1, 8); /* BitsPerSample */ + ifd_le(buf, &p, 0x0103, 3, 1, 1); /* Compression=RAW */ + ifd_le(buf, &p, 0x0106, 3, 1, 32803); /* Photometric=CFA */ + ifd_le(buf, &p, 0x0111, 4, 1, 350); /* StripOffsets */ + ifd_le(buf, &p, 0x0115, 3, 1, 1); /* SamplesPerPixel */ + ifd_le(buf, &p, 0x0116, 3, 1, 2); /* RowsPerStrip */ + ifd_le(buf, &p, 0x0117, 4, 1, 4); /* StripByteCounts */ + /* CFA_PATTERN_DIM: 2 inline SHORTs [2, 2] packed into the value field */ + ifd_le(buf, &p, 0x828D, 3, 2, 0x00020002); /* CFA pattern size 2x2 */ + /* CFA_PATTERN: 4 inline BYTEs [0,1,1,2] = RGGB */ + ifd_le(buf, &p, 0x828E, 1, 4, 0x02010100); /* RGGB Bayer pattern */ + /* DNG_VERSION: 4 inline BYTEs [1,3,0,0] */ + ifd_le(buf, &p, 0xC612, 1, 4, 0x00000301); /* DNG version 1.3.0.0 */ + ifd_le(buf, &p, 0xC61A, 3, 1, 0); /* DNG_BLACK_LEVEL=0 */ + ifd_le(buf, &p, 0xC61D, 3, 1, 255); /* DNG_WHITE_LEVEL=255 */ + /* DNG_COLOR_MATRIX1: 9 SRATIONAL values (9*8=72 bytes) at offset 206 */ + ifd_le(buf, &p, 0xC621, 10, 9, 206); /* DNG_COLOR_MATRIX1 */ + /* DNG_CAMERA_CALIBRATION1: 9 SRATIONAL values at offset 278 */ + ifd_le(buf, &p, 0xC623, 10, 9, 278); /* DNG_CAMERA_CALIBRATION1 */ + w32le(buf, &p, 0); + + /* DNG_COLOR_MATRIX1 at offset 206: identity 3x3 matrix as signed rationals */ + for (r = 0; r < 3; r++) + for (c = 0; c < 3; c++) { + w32le(buf, &p, r == c ? 1 : 0); /* numerator */ + w32le(buf, &p, 1); /* denominator */ + } + + /* DNG_CAMERA_CALIBRATION1 at offset 278: identity 3x3 matrix */ + for (r = 0; r < 3; r++) + for (c = 0; c < 3; c++) { + w32le(buf, &p, r == c ? 1 : 0); + w32le(buf, &p, 1); + } + + /* strip: 4 bytes at offset 350 (zero pixels, already set by memset) */ + + return decode_tiff(buf, sizeof(buf)); +} + +#define TEST(name) do { \ + int fmt = test_##name(); \ + const char *fn = fmt >= 0 ? av_get_pix_fmt_name(fmt) : NULL; \ + printf(#name ": %s\n", fn ? fn : "fail"); \ +} while (0) + +int main(void) +{ + TEST(be_gray8); + TEST(be_gray16); + TEST(miniswhite); + TEST(pal8); + TEST(fill_order); + TEST(gray12); + TEST(metadata); + TEST(geotiff); + TEST(dng); + TEST(packbits); + TEST(deflate); + TEST(ycbcr); + TEST(cmyk); + TEST(more_metadata); + TEST(predictor_gray16le); + TEST(predictor_gray8); + TEST(rgb24); + TEST(gray_lut); + TEST(rgb48); + TEST(cmyk16be); + TEST(bayer_rggb8); + return 0; +} diff --git a/tests/fate/libavcodec.mak b/tests/fate/libavcodec.mak index e2d616e307..f2c4d48558 100644 --- a/tests/fate/libavcodec.mak +++ b/tests/fate/libavcodec.mak @@ -114,5 +114,9 @@ FATE_LIBAVCODEC-yes += fate-libavcodec-htmlsubtitles fate-libavcodec-htmlsubtitles: libavcodec/tests/htmlsubtitles$(EXESUF) fate-libavcodec-htmlsubtitles: CMD = run libavcodec/tests/htmlsubtitles$(EXESUF) +FATE_LIBAVCODEC-$(CONFIG_TIFF_DECODER) += fate-tiffdec +fate-tiffdec: libavcodec/tests/tiffdec$(EXESUF) +fate-tiffdec: CMD = run libavcodec/tests/tiffdec$(EXESUF) + FATE-$(CONFIG_AVCODEC) += $(FATE_LIBAVCODEC-yes) fate-libavcodec: $(FATE_LIBAVCODEC-yes) diff --git a/tests/ref/fate/tiffdec b/tests/ref/fate/tiffdec new file mode 100644 index 0000000000..33bc20cf88 --- /dev/null +++ b/tests/ref/fate/tiffdec @@ -0,0 +1,21 @@ +be_gray8: gray +be_gray16: gray16be +miniswhite: gray +pal8: pal8 +fill_order: gray +gray12: gray12le +metadata: gray +geotiff: gray +dng: gray +packbits: gray +deflate: gray +ycbcr: yuv420p +cmyk: rgb0 +more_metadata: gray +predictor_gray16le: gray16le +predictor_gray8: gray +rgb24: rgb24 +gray_lut: gray +rgb48: rgb48le +cmyk16be: rgba64be +bayer_rggb8: bayer_rggb8 -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
