From fdda547aed735dd3d766b128505e7106989c8c7f Mon Sep 17 00:00:00 2001 From: lordnn Date: Fri, 5 Dec 2025 11:08:54 +0300 Subject: [PATCH 1/9] Changed IPTC profile handling. --- Source/Plugins/PluginTIFF.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Source/Plugins/PluginTIFF.cpp b/Source/Plugins/PluginTIFF.cpp index 1679df3..ab25767 100644 --- a/Source/Plugins/PluginTIFF.cpp +++ b/Source/Plugins/PluginTIFF.cpp @@ -721,12 +721,13 @@ tiff_read_iptc_profile(TIFF *tiff, FIBITMAP *dib) { uint8_t *profile{}; uint32_t profile_size = 0; - if (TIFFGetField(tiff,TIFFTAG_RICHTIFFIPTC, &profile_size, &profile) == 1) { - if (TIFFIsByteSwapped(tiff) != 0) { - TIFFSwabArrayOfLong((uint32_t *) profile, (unsigned long)profile_size); + if (TIFFGetField(tiff, TIFFTAG_RICHTIFFIPTC, &profile_size, &profile) == 1) { + assert(!(profile_size & 3)); + if (false && TIFFIsByteSwapped(tiff) != 0) { + TIFFSwabArrayOfLong((uint32_t *) profile, (unsigned long)profile_size / 4); } - return read_iptc_profile(dib, profile, 4 * profile_size); + return read_iptc_profile(dib, profile, profile_size); } return FALSE; @@ -826,19 +827,18 @@ tiff_write_iptc_profile(TIFF *tiff, FIBITMAP *dib) { uint32_t profile_size = 0; // create a binary profile if (write_iptc_profile(dib, &profile, &profile_size)) { - uint32_t iptc_size = profile_size; - iptc_size += (4-(iptc_size & 0x03)); // Round up for long word alignment + const uint32_t iptc_size = CalculatePitch(profile_size); // Round up for long word alignment auto *iptc_profile = static_cast(calloc(iptc_size, sizeof(uint8_t))); if (!iptc_profile) { free(profile); return FALSE; } memcpy(iptc_profile, profile, profile_size); - if (TIFFIsByteSwapped(tiff)) { - TIFFSwabArrayOfLong((uint32_t *) iptc_profile, (unsigned long)iptc_size/4); + if (false && TIFFIsByteSwapped(tiff)) { + TIFFSwabArrayOfLong((uint32_t *) iptc_profile, (unsigned long)iptc_size / 4); } // Tag is type TIFF_LONG so byte length is divided by four - TIFFSetField(tiff, TIFFTAG_RICHTIFFIPTC, iptc_size/4, iptc_profile); + TIFFSetField(tiff, TIFFTAG_RICHTIFFIPTC, iptc_size, iptc_profile); // release the profile data free(iptc_profile); free(profile); From 4d75ffbd59163e8cda8eb1d4d4f0009fffb0241b Mon Sep 17 00:00:00 2001 From: lordnn Date: Fri, 5 Dec 2025 11:17:30 +0300 Subject: [PATCH 2/9] Added support for some tiled images with different BPP. --- Source/Plugins/PluginTIFF.cpp | 67 ++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/Source/Plugins/PluginTIFF.cpp b/Source/Plugins/PluginTIFF.cpp index ab25767..279d788 100644 --- a/Source/Plugins/PluginTIFF.cpp +++ b/Source/Plugins/PluginTIFF.cpp @@ -723,7 +723,7 @@ tiff_read_iptc_profile(TIFF *tiff, FIBITMAP *dib) { if (TIFFGetField(tiff, TIFFTAG_RICHTIFFIPTC, &profile_size, &profile) == 1) { assert(!(profile_size & 3)); - if (false && TIFFIsByteSwapped(tiff) != 0) { + if (false && TIFFIsByteSwapped(tiff)) { TIFFSwabArrayOfLong((uint32_t *) profile, (unsigned long)profile_size / 4); } @@ -1286,25 +1286,27 @@ ReadThumbnail(FreeImageIO *io, fi_handle handle, void *data, TIFF *tiff, FIBITMA // -------------------------------------------------------------------------- -template static void DecodeStrip(const uint8_t *buf, const tmsize_t src_line, uint8_t *dst_line_begin, const unsigned dst_pitch, const uint32_t strips, const uint16_t sample, const uint16_t chCount, const uint16_t bitspersample) { +template static void DecodeStrip(const uint8_t *buf, const tmsize_t src_stride, uint8_t *dst_line_begin, const uint32_t dst_line, const unsigned dst_pitch, const uint32_t strips, const uint16_t sample, const uint16_t chCount, const uint16_t bitspersample) { const uint32_t bits_mask = (static_cast(1) << bitspersample) - 1; - const uint8_t *src_pixel = buf; for (uint32_t l{}; l < strips; ++l) { + const uint8_t* src_pixel = buf; auto *dst_pixel = reinterpret_cast(dst_line_begin) + sample; - uint32_t t{}; + uint32_t t{}, i{}; uint16_t stored_bits{}; - for (tmsize_t i{}; i < src_line; ++i) { + while (i < dst_line) { t <<= 8; t |= *src_pixel++; stored_bits += 8; - while (stored_bits >= bitspersample) { + while (stored_bits >= bitspersample && i < dst_line) { stored_bits -= bitspersample; *dst_pixel = static_cast
((t >> stored_bits) & bits_mask); dst_pixel += chCount; + ++i; } } dst_line_begin -= dst_pitch; + buf += src_stride; } } @@ -1880,10 +1882,10 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { } else { // not whole number of bytes if (bitspersample <= 8) { - DecodeStrip(buf.get(), src_line, bits, dst_pitch, rows, 0, 1, bitspersample); + DecodeStrip(buf.get(), src_line, bits, width * samplesperpixel, dst_pitch, rows, 0, 1, bitspersample); } else if (bitspersample <= 16) { - DecodeStrip(buf.get(), src_line, bits, dst_pitch, rows, 0, 1, bitspersample); + DecodeStrip(buf.get(), src_line, bits, width * samplesperpixel, dst_pitch, rows, 0, 1, bitspersample); } else { throw "Unsupported number of bits per sample"; @@ -1945,10 +1947,10 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { } else { // not whole number of bytes if (bitspersample <= 8) { - DecodeStrip(buf.get(), src_line, dib_strip, dst_pitch, strips, sample, chCount, bitspersample); + DecodeStrip(buf.get(), src_line, dib_strip, width, dst_pitch, strips, sample, chCount, bitspersample); } else if (bitspersample <= 16) { - DecodeStrip(buf.get(), src_line, dib_strip, dst_pitch, strips, sample, chCount, bitspersample); + DecodeStrip(buf.get(), src_line, dib_strip, width, dst_pitch, strips, sample, chCount, bitspersample); } else { throw "Unsupported number of bits per sample"; @@ -1977,7 +1979,6 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { // --------------------------------------------------------------------------------- uint32_t tileWidth, tileHeight; - uint32_t src_line = 0; // create a new DIB dib.reset(CreateImageType( header_only, image_type, width, height, bitspersample, samplesperpixel)); @@ -2012,6 +2013,7 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { const int dst_pitch = FreeImage_GetPitch(dib.get()); const uint32_t tileRowSize = (uint32_t)TIFFTileRowSize(tif); const uint32_t imageRowSize = (uint32_t)TIFFScanlineSize(tif); + const unsigned Bpp = FreeImage_GetBPP(dib.get()) / 8; // In the tiff file the lines are saved from up to down @@ -2022,7 +2024,7 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { for (uint32_t y = 0; y < height; y += tileHeight) { const uint32_t nrows = std::min(height - y, tileHeight); - for (uint32_t x = 0, rowSize = 0; x < width; x += tileWidth, rowSize += tileRowSize) { + for (uint32_t x = 0; x < width; x += tileWidth) { memset(tileBuffer.get(), 0, tileSize); // read one tile @@ -2030,17 +2032,26 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { throw "Corrupted tiled TIFF file"; } // convert to strip - if (x + tileWidth > width) { - src_line = imageRowSize - rowSize; - } else { - src_line = tileRowSize; + const uint32_t dst_line = std::min(tileWidth, width - x); + const uint8_t* src_bits = tileBuffer.get(); + uint8_t* dst_bits = bits + x * Bpp; + if (8 == bitspersample || 16 == bitspersample) { + for (uint32_t k = 0; k < nrows; k++) { + memcpy(dst_bits, src_bits, dst_line * samplesperpixel); + src_bits += tileRowSize; + dst_bits -= dst_pitch; + } } - const uint8_t *src_bits = tileBuffer.get(); - uint8_t *dst_bits = bits + rowSize; - for (uint32_t k = 0; k < nrows; k++) { - memcpy(dst_bits, src_bits, src_line); - src_bits += tileRowSize; - dst_bits -= dst_pitch; + else { + if (bitspersample <= 8) { + DecodeStrip(src_bits, tileRowSize, dst_bits, dst_line * samplesperpixel, dst_pitch, nrows, 0, 1, bitspersample); + } + else if (bitspersample <= 16) { + DecodeStrip(src_bits, tileRowSize, dst_bits, dst_line * samplesperpixel, dst_pitch, nrows, 0, 1, bitspersample); + } + else { + throw "Unsupported number of bits per sample"; + } } } @@ -2608,11 +2619,11 @@ InitTIFF(Plugin *plugin, int format_id) { } -std::unique_ptr MakeTiffDependencyInfo() { - auto info = std::make_unique(); - info->name = "LibTIFF"; - info->fullVersion = TIFFLIB_VERSION_STR_MAJ_MIN_MIC; - info->majorVersion = TIFFLIB_MAJOR_VERSION; - info->minorVersion = TIFFLIB_MINOR_VERSION; +FIDEPENDENCY MakeTiffDependencyInfo() { + FIDEPENDENCY info{}; + info.name = "LibTIFF"; + info.fullVersion = TIFFLIB_VERSION_STR_MAJ_MIN_MIC; + info.majorVersion = TIFFLIB_MAJOR_VERSION; + info.minorVersion = TIFFLIB_MINOR_VERSION; return info; } From 679dc8be6a0d23ef196c00c3cebe24b9aaf4e6bb Mon Sep 17 00:00:00 2001 From: lordnn Date: Fri, 5 Dec 2025 11:53:29 +0300 Subject: [PATCH 3/9] Reverted unwanted changes. --- Source/Plugins/PluginTIFF.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/Plugins/PluginTIFF.cpp b/Source/Plugins/PluginTIFF.cpp index 279d788..db05e2d 100644 --- a/Source/Plugins/PluginTIFF.cpp +++ b/Source/Plugins/PluginTIFF.cpp @@ -2619,11 +2619,11 @@ InitTIFF(Plugin *plugin, int format_id) { } -FIDEPENDENCY MakeTiffDependencyInfo() { - FIDEPENDENCY info{}; - info.name = "LibTIFF"; - info.fullVersion = TIFFLIB_VERSION_STR_MAJ_MIN_MIC; - info.majorVersion = TIFFLIB_MAJOR_VERSION; - info.minorVersion = TIFFLIB_MINOR_VERSION; +std::unique_ptr MakeTiffDependencyInfo() { + auto info = std::make_unique(); + info->name = "LibTIFF"; + info->fullVersion = TIFFLIB_VERSION_STR_MAJ_MIN_MIC; + info->majorVersion = TIFFLIB_MAJOR_VERSION; + info->minorVersion = TIFFLIB_MINOR_VERSION; return info; } From 851d2442d59e7ff5fc6c5aef0e7d6ce5636cd1b7 Mon Sep 17 00:00:00 2001 From: lordnn Date: Fri, 5 Dec 2025 12:04:13 +0300 Subject: [PATCH 4/9] Minor cleanup. --- Source/Plugins/PluginTIFF.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Plugins/PluginTIFF.cpp b/Source/Plugins/PluginTIFF.cpp index db05e2d..c468c6c 100644 --- a/Source/Plugins/PluginTIFF.cpp +++ b/Source/Plugins/PluginTIFF.cpp @@ -2032,9 +2032,9 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { throw "Corrupted tiled TIFF file"; } // convert to strip - const uint32_t dst_line = std::min(tileWidth, width - x); - const uint8_t* src_bits = tileBuffer.get(); - uint8_t* dst_bits = bits + x * Bpp; + const uint32_t dst_line = std::min(width - x, tileWidth); + const uint8_t *src_bits = tileBuffer.get(); + uint8_t *dst_bits = bits + x * Bpp; if (8 == bitspersample || 16 == bitspersample) { for (uint32_t k = 0; k < nrows; k++) { memcpy(dst_bits, src_bits, dst_line * samplesperpixel); From 6b59cc80c2e838751d9022e9e0694186507a3efd Mon Sep 17 00:00:00 2001 From: lordnn Date: Fri, 5 Dec 2025 21:09:20 +0300 Subject: [PATCH 5/9] Added support for some separated tiled TIFF images. --- Source/Plugins/PluginTIFF.cpp | 149 +++++++++++++++++++++------------- 1 file changed, 93 insertions(+), 56 deletions(-) diff --git a/Source/Plugins/PluginTIFF.cpp b/Source/Plugins/PluginTIFF.cpp index c468c6c..5ad53ee 100644 --- a/Source/Plugins/PluginTIFF.cpp +++ b/Source/Plugins/PluginTIFF.cpp @@ -1850,7 +1850,7 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { auto buf(std::make_unique(TIFFStripSize(tif))); - FIBOOL bThrowMessage = FALSE; + bool bThrowMessage{}; if (planar_config == PLANARCONFIG_CONTIG) { @@ -1859,7 +1859,7 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, y, 0), buf.get(), rows * src_line) == -1) { // ignore errors as they can be frequent and not really valid errors, especially with fax images - bThrowMessage = TRUE; + bThrowMessage = true; /* throw FI_MSG_ERROR_PARSING; */ @@ -1893,12 +1893,12 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { bits -= rows * dst_pitch; } } - } + } // height } else if (planar_config == PLANARCONFIG_SEPARATE) { const unsigned Bpc = bitspersample / 8; - uint8_t* dib_strip = bits; + uint8_t *dib_strip = bits; // - loop for strip blocks - for (uint32_t y = 0; y < height; y += rowsperstrip) { @@ -1910,7 +1910,7 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, y, sample), buf.get(), strips * src_line) == -1) { // ignore errors as they can be frequent and not really valid errors, especially with fax images - bThrowMessage = TRUE; + bThrowMessage = true; } if (sample >= chCount) { @@ -1919,27 +1919,27 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { } if (src_line == dst_line && 1 == samplesperpixel) { - uint8_t* dst_line_begin = dib_strip; + uint8_t *dst_line_begin = dib_strip; for (uint32_t l = 0; l < strips; l++) { memcpy(dst_line_begin, buf.get() + l * src_line, src_line); dst_line_begin -= dst_pitch; } } else { - if (0 == (bitspersample & 7)) { + if (!(bitspersample & 7)) { const unsigned channelOffset = sample * Bpc; // - loop for strips in block - - uint8_t* src_line_begin = buf.get(); - uint8_t* dst_line_begin = dib_strip; + uint8_t *src_line_begin = buf.get(); + uint8_t *dst_line_begin = dib_strip; for (uint32_t l = 0; l < strips; l++, src_line_begin += src_line, dst_line_begin -= dst_pitch) { // - loop for pixels in strip - - const uint8_t* const src_line_end = src_line_begin + src_line; + const uint8_t *const src_line_end = src_line_begin + src_line; - for (uint8_t* src_bits = src_line_begin, *dst_bits = dst_line_begin; src_bits < src_line_end; src_bits += Bpc, dst_bits += Bpp) { + for (uint8_t *src_bits = src_line_begin, *dst_bits = dst_line_begin; src_bits < src_line_end; src_bits += Bpc, dst_bits += Bpp) { // actually assigns channel AssignPixel(dst_bits + channelOffset, src_bits, Bpc); } // line @@ -1981,7 +1981,7 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { uint32_t tileWidth, tileHeight; // create a new DIB - dib.reset(CreateImageType( header_only, image_type, width, height, bitspersample, samplesperpixel)); + dib.reset(CreateImageType(header_only, image_type, width, height, bitspersample, samplesperpixel)); if (!dib) { throw FI_MSG_ERROR_DIB_MEMORY; } @@ -1994,15 +1994,12 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { ReadPalette(tif, photometric, bitspersample, dib.get()); - // get the tile geometry - if (!TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tileWidth) || !TIFFGetField(tif, TIFFTAG_TILELENGTH, &tileHeight)) { - throw "Invalid tiled TIFF image"; - } - - // read the tiff lines and save them in the DIB + if (!header_only) { + // get the tile geometry + if (!TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tileWidth) || !TIFFGetField(tif, TIFFTAG_TILELENGTH, &tileHeight)) { + throw "Invalid tiled TIFF image"; + } - if (planar_config == PLANARCONFIG_CONTIG && !header_only) { - // get the maximum number of bytes required to contain a tile const tmsize_t tileSize = TIFFTileSize(tif); @@ -2012,60 +2009,100 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { // calculate src line and dst pitch const int dst_pitch = FreeImage_GetPitch(dib.get()); const uint32_t tileRowSize = (uint32_t)TIFFTileRowSize(tif); - const uint32_t imageRowSize = (uint32_t)TIFFScanlineSize(tif); const unsigned Bpp = FreeImage_GetBPP(dib.get()) / 8; - // In the tiff file the lines are saved from up to down // In a DIB the lines must be saved from down to up - uint8_t *bits = FreeImage_GetScanLine(dib.get(), height - 1); - - for (uint32_t y = 0; y < height; y += tileHeight) { - const uint32_t nrows = std::min(height - y, tileHeight); - for (uint32_t x = 0; x < width; x += tileWidth) { - memset(tileBuffer.get(), 0, tileSize); + bool bThrowMessage{}; - // read one tile - if (TIFFReadTile(tif, tileBuffer.get(), x, y, 0, 0) < 0) { - throw "Corrupted tiled TIFF file"; - } - // convert to strip - const uint32_t dst_line = std::min(width - x, tileWidth); - const uint8_t *src_bits = tileBuffer.get(); - uint8_t *dst_bits = bits + x * Bpp; - if (8 == bitspersample || 16 == bitspersample) { - for (uint32_t k = 0; k < nrows; k++) { - memcpy(dst_bits, src_bits, dst_line * samplesperpixel); - src_bits += tileRowSize; - dst_bits -= dst_pitch; - } - } - else { - if (bitspersample <= 8) { - DecodeStrip(src_bits, tileRowSize, dst_bits, dst_line * samplesperpixel, dst_pitch, nrows, 0, 1, bitspersample); + if (planar_config == PLANARCONFIG_CONTIG) { + for (uint32_t y = 0; y < height; y += tileHeight) { + const uint32_t nrows = std::min(height - y, tileHeight); + + for (uint32_t x = 0; x < width; x += tileWidth) { + memset(tileBuffer.get(), 0xCD, tileSize); + + // read one tile + if (TIFFReadTile(tif, tileBuffer.get(), x, y, 0, 0) < 0) { + bThrowMessage = true; } - else if (bitspersample <= 16) { - DecodeStrip(src_bits, tileRowSize, dst_bits, dst_line * samplesperpixel, dst_pitch, nrows, 0, 1, bitspersample); + // convert to strip + const uint32_t dst_line = std::min(width - x, tileWidth); + const uint8_t *src_bits = tileBuffer.get(); + uint8_t *dst_bits = bits + x * Bpp; + if (8 == bitspersample || 16 == bitspersample) { + for (uint32_t k = 0; k < nrows; ++k) { + memcpy(dst_bits, src_bits, dst_line * samplesperpixel); + src_bits += tileRowSize; + dst_bits -= dst_pitch; + } } else { - throw "Unsupported number of bits per sample"; + if (bitspersample <= 8) { + DecodeStrip(src_bits, tileRowSize, dst_bits, dst_line * samplesperpixel, dst_pitch, nrows, 0, 1, bitspersample); + } + else if (bitspersample <= 16) { + DecodeStrip(src_bits, tileRowSize, dst_bits, dst_line * samplesperpixel, dst_pitch, nrows, 0, 1, bitspersample); + } + else { + throw "Unsupported number of bits per sample"; + } } } - } + bits -= nrows * dst_pitch; + } // height + } + else if (planar_config == PLANARCONFIG_SEPARATE) { + for (uint32_t y = 0; y < height; y += tileHeight) { + const uint32_t nrows = std::min(height - y, tileHeight); + for (uint32_t x = 0; x < width; x += tileWidth) { + const uint32_t dst_line = std::min(width - x, tileWidth); + uint8_t* dst_bits = bits + x * Bpp; + // - loop for channels (planes) - + for (uint16_t sample = 0; sample < samplesperpixel; ++sample) { + memset(tileBuffer.get(), 0xCD, tileSize); + + // read one tile + if (TIFFReadTile(tif, tileBuffer.get(), x, y, 0, sample) < 0) { + bThrowMessage = true; + } + // convert to strip + const uint8_t *src_bits = tileBuffer.get(); + + if ((8 == bitspersample || 16 == bitspersample) && 1 == samplesperpixel) { + for (uint32_t k = 0; k < nrows; ++k) { + memcpy(dst_bits, src_bits, dst_line * samplesperpixel); + src_bits += tileRowSize; + dst_bits -= dst_pitch; + } + } + else { + if (bitspersample <= 8) { + DecodeStrip(src_bits, tileRowSize, dst_bits, dst_line, dst_pitch, nrows, sample, samplesperpixel, bitspersample); + } + else if (bitspersample <= 16) { + DecodeStrip(src_bits, tileRowSize, dst_bits, dst_line, dst_pitch, nrows, sample, samplesperpixel, bitspersample); + } + else { + throw "Unsupported number of bits per sample"; + } + } + } // channels + } + bits -= nrows * dst_pitch; + } // height + } - bits -= nrows * dst_pitch; + if (bThrowMessage) { + FreeImage_OutputMessageProc(s_format_id, "Warning: parsing error. Image may be incomplete or contain invalid data !"); } #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR SwapRedBlue32(dib.get()); #endif - } - else if (planar_config == PLANARCONFIG_SEPARATE) { - throw "Separated tiled TIFF images are not supported"; - } - + } // !header only } else if (loadMethod == LoadAsLogLuv) { // --------------------------------------------------------------------------------- From 75d2b75b8917a44d4d6c68f64409941032ba5c7f Mon Sep 17 00:00:00 2001 From: lordnn Date: Sat, 6 Dec 2025 18:56:06 +0300 Subject: [PATCH 6/9] Changed single plane low-bits tile decoder. --- Source/Plugins/PluginTIFF.cpp | 51 ++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/Source/Plugins/PluginTIFF.cpp b/Source/Plugins/PluginTIFF.cpp index 5ad53ee..eed39f6 100644 --- a/Source/Plugins/PluginTIFF.cpp +++ b/Source/Plugins/PluginTIFF.cpp @@ -1286,6 +1286,44 @@ ReadThumbnail(FreeImageIO *io, fi_handle handle, void *data, TIFF *tiff, FIBITMA // -------------------------------------------------------------------------- +template static void DecodeMonoStrip(const uint8_t *buf, const tmsize_t src_stride, uint8_t *dst_line_begin, const uint32_t dst_line, const unsigned dst_pitch, const uint32_t strips, const uint16_t bitspersample, const uint8_t offset) { + const uint32_t bits_mask = (static_cast(1) << bitspersample) - 1; + + for (uint32_t l{}; l < strips; ++l) { + uint8_t poffset{ offset }; + const uint8_t *src_pixel = buf; + auto *dst_pixel = reinterpret_cast
(dst_line_begin); + uint32_t t{}, i{}; + uint16_t stored_bits{}; + while (i < dst_line) { + t <<= 8; + t |= *src_pixel++; + stored_bits += 8; + while (stored_bits >= bitspersample && i < dst_line) { + stored_bits -= bitspersample; + *dst_pixel |= (static_cast
(t & bits_mask)) << poffset; + t >>= bitspersample; + poffset += bitspersample; + if (poffset > 7) { + ++dst_pixel; + poffset -= 8; + } + ++i; + /*stored_bits -= bitspersample; + *dst_pixel |= (static_cast
((t >> stored_bits) & bits_mask)) << poffset; + poffset += bitspersample; + if (poffset > 7) { + ++dst_pixel; + poffset -= 8; + } + ++i;*/ + } + } + dst_line_begin -= dst_pitch; + buf += src_stride; + } +} + template static void DecodeStrip(const uint8_t *buf, const tmsize_t src_stride, uint8_t *dst_line_begin, const uint32_t dst_line, const unsigned dst_pitch, const uint32_t strips, const uint16_t sample, const uint16_t chCount, const uint16_t bitspersample) { const uint32_t bits_mask = (static_cast(1) << bitspersample) - 1; @@ -2009,7 +2047,8 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { // calculate src line and dst pitch const int dst_pitch = FreeImage_GetPitch(dib.get()); const uint32_t tileRowSize = (uint32_t)TIFFTileRowSize(tif); - const unsigned Bpp = FreeImage_GetBPP(dib.get()) / 8; + const unsigned Bipp = FreeImage_GetBPP(dib.get()); + const unsigned Bpp = Bipp / 8; // In the tiff file the lines are saved from up to down // In a DIB the lines must be saved from down to up @@ -2031,7 +2070,8 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { // convert to strip const uint32_t dst_line = std::min(width - x, tileWidth); const uint8_t *src_bits = tileBuffer.get(); - uint8_t *dst_bits = bits + x * Bpp; + uint8_t *dst_bits = bits + (x * Bipp) / 8; + const uint8_t dst_offset = (x * Bipp) & 0x7; if (8 == bitspersample || 16 == bitspersample) { for (uint32_t k = 0; k < nrows; ++k) { memcpy(dst_bits, src_bits, dst_line * samplesperpixel); @@ -2041,7 +2081,12 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { } else { if (bitspersample <= 8) { - DecodeStrip(src_bits, tileRowSize, dst_bits, dst_line * samplesperpixel, dst_pitch, nrows, 0, 1, bitspersample); + if (1 == samplesperpixel) { + DecodeMonoStrip(src_bits, tileRowSize, dst_bits, dst_line *samplesperpixel, dst_pitch, nrows, bitspersample, dst_offset); + } + else { + DecodeStrip(src_bits, tileRowSize, dst_bits, dst_line * samplesperpixel, dst_pitch, nrows, 0, 1, bitspersample); + } } else if (bitspersample <= 16) { DecodeStrip(src_bits, tileRowSize, dst_bits, dst_line * samplesperpixel, dst_pitch, nrows, 0, 1, bitspersample); From 19318bcb5b732ab14ca7433088c3cf98387ed23a Mon Sep 17 00:00:00 2001 From: lordnn Date: Sun, 7 Dec 2025 00:23:15 +0300 Subject: [PATCH 7/9] Support for more BPP images. --- Source/Plugins/PluginTIFF.cpp | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/Source/Plugins/PluginTIFF.cpp b/Source/Plugins/PluginTIFF.cpp index eed39f6..afa8345 100644 --- a/Source/Plugins/PluginTIFF.cpp +++ b/Source/Plugins/PluginTIFF.cpp @@ -463,6 +463,13 @@ ReadImageType(TIFF *tiff, uint16_t bitspersample, uint16_t samplesperpixel) { fit = FIT_UINT32; } break; + case 27: + case 30: + case 33: + case 36: + case 39: + case 42: + case 45: case 48: if (samplesperpixel == 3) { fit = FIT_RGB16; @@ -1309,14 +1316,6 @@ template static void DecodeMonoStrip(const uint8_t *buf, const tms poffset -= 8; } ++i; - /*stored_bits -= bitspersample; - *dst_pixel |= (static_cast
((t >> stored_bits) & bits_mask)) << poffset; - poffset += bitspersample; - if (poffset > 7) { - ++dst_pixel; - poffset -= 8; - } - ++i;*/ } } dst_line_begin -= dst_pitch; @@ -1324,18 +1323,18 @@ template static void DecodeMonoStrip(const uint8_t *buf, const tms } } -template static void DecodeStrip(const uint8_t *buf, const tmsize_t src_stride, uint8_t *dst_line_begin, const uint32_t dst_line, const unsigned dst_pitch, const uint32_t strips, const uint16_t sample, const uint16_t chCount, const uint16_t bitspersample) { +template static void DecodeStrip(const uint8_t *buf, const tmsize_t src_stride, uint8_t *dst_line_begin, const uint32_t dst_line, const unsigned dst_pitch, const uint32_t strips, const uint16_t sample, const uint16_t chCount, const uint16_t bitspersample) { const uint32_t bits_mask = (static_cast(1) << bitspersample) - 1; for (uint32_t l{}; l < strips; ++l) { - const uint8_t* src_pixel = buf; + const auto* src_pixel = reinterpret_cast(buf); auto *dst_pixel = reinterpret_cast(dst_line_begin) + sample; uint32_t t{}, i{}; uint16_t stored_bits{}; while (i < dst_line) { - t <<= 8; + t <<= sizeof(DS) * 8; t |= *src_pixel++; - stored_bits += 8; + stored_bits += sizeof(DS) * 8; while (stored_bits >= bitspersample && i < dst_line) { stored_bits -= bitspersample; *dst_pixel = static_cast
((t >> stored_bits) & bits_mask); @@ -2074,7 +2073,7 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { const uint8_t dst_offset = (x * Bipp) & 0x7; if (8 == bitspersample || 16 == bitspersample) { for (uint32_t k = 0; k < nrows; ++k) { - memcpy(dst_bits, src_bits, dst_line * samplesperpixel); + memcpy(dst_bits, src_bits, dst_line * Bpp); src_bits += tileRowSize; dst_bits -= dst_pitch; } @@ -2127,9 +2126,12 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { if (bitspersample <= 8) { DecodeStrip(src_bits, tileRowSize, dst_bits, dst_line, dst_pitch, nrows, sample, samplesperpixel, bitspersample); } - else if (bitspersample <= 16) { + else if (bitspersample < 16) { DecodeStrip(src_bits, tileRowSize, dst_bits, dst_line, dst_pitch, nrows, sample, samplesperpixel, bitspersample); } + else if (bitspersample == 16) { + DecodeStrip(src_bits, tileRowSize, dst_bits, dst_line, dst_pitch, nrows, sample, samplesperpixel, bitspersample); + } else { throw "Unsupported number of bits per sample"; } From ebc0a01f0649703a739cb513ed896e6726206f1b Mon Sep 17 00:00:00 2001 From: lordnn Date: Tue, 9 Dec 2025 01:21:27 +0300 Subject: [PATCH 8/9] Added some 4 planes cases to the `ReadImageType()`. --- Source/Plugins/PluginTIFF.cpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/Source/Plugins/PluginTIFF.cpp b/Source/Plugins/PluginTIFF.cpp index afa8345..9918092 100644 --- a/Source/Plugins/PluginTIFF.cpp +++ b/Source/Plugins/PluginTIFF.cpp @@ -466,15 +466,29 @@ ReadImageType(TIFF *tiff, uint16_t bitspersample, uint16_t samplesperpixel) { case 27: case 30: case 33: - case 36: case 39: case 42: case 45: - case 48: if (samplesperpixel == 3) { fit = FIT_RGB16; } break; + case 36: + if (samplesperpixel == 3) { + fit = FIT_RGB16; + } + else if (samplesperpixel == 4) { + fit = FIT_RGBA16; + } + break; + case 48: + if (samplesperpixel == 3) { + fit = FIT_RGB16; + } + else if (samplesperpixel == 4) { + fit = FIT_RGBA16; + } + break; case 64: if (samplesperpixel == 4) { fit = FIT_RGBA16; @@ -2282,7 +2296,7 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { ReadMetadata(io, handle, tif, dib.get()); // copy ICC profile data (must be done after FreeImage_Allocate) - + FreeImage_CreateICCProfile(dib.get(), iccBuf, iccSize); if (photometric == PHOTOMETRIC_SEPARATED) { if (asCMYK) { From 3265d68b7a27759d00eef9c3a42811283fa45fef Mon Sep 17 00:00:00 2001 From: lordnn Date: Fri, 12 Dec 2025 22:54:09 +0300 Subject: [PATCH 9/9] Prevent infinite recursion in Tiff `Load()`. --- Source/FreeImage.h | 1 + Source/Plugins/PluginTIFF.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Source/FreeImage.h b/Source/FreeImage.h index 22ec347..93120fc 100644 --- a/Source/FreeImage.h +++ b/Source/FreeImage.h @@ -733,6 +733,7 @@ FI_STRUCT (FIDEPENDENCY) { // Load / Save flag constants ----------------------------------------------- #define FIF_LOAD_NOPIXELS 0x8000 //! loading: load the image header only (not supported by all plugins, default to full loading) +#define FIF_LOAD_NOTHUMBNAIL 0x10000 #define BMP_DEFAULT 0 #define BMP_SAVE_RLE 1 diff --git a/Source/Plugins/PluginTIFF.cpp b/Source/Plugins/PluginTIFF.cpp index 9918092..74bee5d 100644 --- a/Source/Plugins/PluginTIFF.cpp +++ b/Source/Plugins/PluginTIFF.cpp @@ -1270,7 +1270,7 @@ ReadThumbnail(FreeImageIO *io, fi_handle handle, void *data, TIFF *tiff, FIBITMA if (TIFFSetSubDirectory(tiff, subIFD_offsets[0])) { // load the thumbnail int page = -1; - int flags = TIFF_DEFAULT; + int flags = TIFF_DEFAULT | FIF_LOAD_NOTHUMBNAIL; thumbnail.reset(Load(io, handle, page, flags, data)); // store the thumbnail (remember to release it before return) FreeImage_SetThumbnail(dib, thumbnail.get()); @@ -2311,7 +2311,9 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { // copy TIFF thumbnail (must be done after FreeImage_Allocate) - ReadThumbnail(io, handle, data, tif, dib.get()); + if (!(flags & FIF_LOAD_NOTHUMBNAIL)) { + ReadThumbnail(io, handle, data, tif, dib.get()); + } return dib.release();