From c3dad1a3a927846529082cc18cb28df99312554a Mon Sep 17 00:00:00 2001 From: Lior Lahav Date: Tue, 5 May 2026 09:16:26 +0300 Subject: [PATCH 1/3] Improve TIFF extra sample handling Adds TIFF texel format detection for 16-bit RGB and RGBA data, 64-bit floating grayscale data, 4-bit grayscale data, and signed integer grayscale samples. Handles unsupported extra-sample layouts by extracting separate channel images or returning format-not-supported instead of continuing with unknown formats; fixes extra sample extraction to use the correct per-sample source offset and neutral photometric interpretation for extracted auxiliary channels. --- Codecs/CodecTiff/Source/CodecTiff.h | 125 ++++++++++++++++++++-------- 1 file changed, 90 insertions(+), 35 deletions(-) diff --git a/Codecs/CodecTiff/Source/CodecTiff.h b/Codecs/CodecTiff/Source/CodecTiff.h index a888cfd..2c83eb9 100644 --- a/Codecs/CodecTiff/Source/CodecTiff.h +++ b/Codecs/CodecTiff/Source/CodecTiff.h @@ -9,6 +9,8 @@ #include #include +#include + namespace IMCodec { class CodecTiff : public IImagePlugin @@ -66,20 +68,15 @@ namespace IMCodec case SAMPLEFORMAT_UINT: switch (samplesPerPixel) { - case 1: - switch (bitsPerSample) - { - case 8: - texelFormat = TexelFormat::I_A8; - break; - } - break; case 3: switch (bitsPerSample) { case 8: texelFormat = TexelFormat::I_R8_G8_B8; break; + case 16: + texelFormat = TexelFormat::I_R16_G16_B16; + break; } break; case 4: @@ -88,6 +85,9 @@ namespace IMCodec case 8: texelFormat = TexelFormat::I_R8_G8_B8_A8; break; + case 16: + texelFormat = TexelFormat::I_R16_G16_B16_A16; + break; } break; } @@ -112,6 +112,9 @@ namespace IMCodec case 32: texelFormat = TexelFormat::F_X32; break; + case 64: + texelFormat = TexelFormat::F_X64; + break; default: break; } @@ -127,37 +130,42 @@ namespace IMCodec case 1: texelFormat = TexelFormat::I_X1; break; + case 4: + texelFormat = TexelFormat::I_X4; + break; case 8: texelFormat = TexelFormat::I_X8; break; case 16: texelFormat = TexelFormat::I_X16; break; - case SAMPLEFORMAT_INT: - switch (bitsPerSample) - { - case 8: - texelFormat = TexelFormat::S_X8; - break; - case 16: - texelFormat = TexelFormat::S_X16; - break; - default: - break; - } + } + break; + } + break; + case SAMPLEFORMAT_INT: + switch (samplesPerPixel) + { + case 1: + switch (bitsPerSample) + { + case 8: + texelFormat = TexelFormat::S_X8; + break; + case 16: + texelFormat = TexelFormat::S_X16; + break; + default: break; } break; } + break; } } - - if (texelFormat == TexelFormat::UNKNOWN) - LL_ERROR(LLUtils::Exception::ErrorCode::NotImplemented, "CodecTiff: unsupported floating point format."); - - return texelFormat; - } + return texelFormat; + } std::vector GetImage(TIFF* tiff) { @@ -201,6 +209,9 @@ namespace IMCodec TIFFGetField(tiff, TIFFTAG_MAXSAMPLEVALUE, &maxSampleValue);*/ TIFFGetField(tiff, TIFFTAG_EXTRASAMPLES, &extraSamples, &sampleTypes); + if (samplesPerPixel <= extraSamples) + return {}; + const uint32_t bytesPerSample = bitsPerSample / CHAR_BIT; uint32_t numberOfStripts = TIFFNumberOfStrips(tiff); @@ -229,17 +240,38 @@ namespace IMCodec //If there's an alpha channel and it's interleaved in the image data, return as single image with alpha channel imageItem->descriptor.texelFormatDecompressed = imageItem->descriptor.texelFormatStorage = mainChannelTexelFormat; - if (planarConfig == PLANARCONFIG_CONTIG && extraSamples == 1 && sampleTypes[0] > 0) + auto extractExtraSampleImages = [&]() { - imageItem->descriptor.texelFormatStorage = imageItem->descriptor.texelFormatDecompressed = GetTexelFormat(sampleFormat, bitsPerSample, samplesPerPixel, photoMetric); + return ExtactExtraSamples(width, height, rowPitch, samplesPerPixel, extraSamples, planarConfig + , bytesPerSample, imageItem->data, photoMetric, bitsPerSample, sampleFormat); + }; + + if (planarConfig == PLANARCONFIG_CONTIG && extraSamples > 0) + { + const auto interleavedTexelFormat = GetTexelFormat(sampleFormat, bitsPerSample, samplesPerPixel, photoMetric); + + if (interleavedTexelFormat != TexelFormat::UNKNOWN) + { + imageItem->descriptor.texelFormatStorage = imageItem->descriptor.texelFormatDecompressed = interleavedTexelFormat; + } + else + { + auto extraChannelImages = extractExtraSampleImages(); + + if (extraChannelImages.empty() == false) + imageItems = extraChannelImages; + else + return {}; + } } - else + else if (extraSamples > 0) { - auto extraChannelImages = ExtactExtraSamples(width, height, rowPitch, samplesPerPixel, extraSamples, planarConfig - , bytesPerSample, imageItem->data, photoMetric, bitsPerSample, sampleFormat); + auto extraChannelImages = extractExtraSampleImages(); if (extraChannelImages.empty() == false) imageItems = extraChannelImages; + else + return {}; } } else @@ -274,9 +306,13 @@ namespace IMCodec { const auto sampleSize = sampleCount * bytesPerSample; + const auto samplePhotometric = sampleOffset == 0 ? photometric : PHOTOMETRIC_MINISBLACK; auto imageItem = std::make_shared(); - imageItem->descriptor.texelFormatStorage = GetTexelFormat(sampleFormat, bitsPerSample , sampleCount, photometric); + imageItem->descriptor.texelFormatStorage = GetTexelFormat(sampleFormat, bitsPerSample , sampleCount, samplePhotometric); + if (imageItem->descriptor.texelFormatStorage == TexelFormat::UNKNOWN) + return {}; + imageItem->descriptor.texelFormatDecompressed = imageItem->descriptor.texelFormatStorage; imageItem->descriptor.height = height; imageItem->descriptor.width = width; @@ -293,7 +329,7 @@ namespace IMCodec { for (size_t x = 0; x < width; x++) { - const size_t sourceRowIndex = sampleOffset * sampleSize + x * samplesPerPixel * bytesPerSample; + const size_t sourceRowIndex = sampleOffset * bytesPerSample + x * samplesPerPixel * bytesPerSample; const size_t destRowIndex = x * sampleSize; memcpy(image.data() + destOffset + destRowIndex, sourceBuffer.data() + sourceOffset + sourceRowIndex, sampleSize); } @@ -318,12 +354,25 @@ namespace IMCodec if (extraSmaples > 0) { + if (bytesPerSample == 0 || bitsPerSample % CHAR_BIT != 0 || samplesPerPixel <= extraSmaples) + return {}; + const auto mainImageSampleCount = samplesPerPixel - extraSmaples; - subChannels.push_back(extractSamples(0, mainImageSampleCount)); + auto mainImage = extractSamples(0, mainImageSampleCount); + if (mainImage == nullptr) + return {}; + + subChannels.push_back(std::move(mainImage)); for (int i = 0; i < extraSmaples; i++) - subChannels.push_back(extractSamples(mainImageSampleCount + i, 1)); + { + auto extraImage = extractSamples(mainImageSampleCount + i, 1); + if (extraImage == nullptr) + return {}; + + subChannels.push_back(std::move(extraImage)); + } } @@ -344,6 +393,9 @@ namespace IMCodec int currentSubImage = 0; auto firstImageItemChannles = GetImage(tiff); + if (firstImageItemChannles.empty()) + return ImageResult::FormatNotSupported; + firstImageItem = firstImageItemChannles.at(0); if (firstImageItemChannles.size() > 1) @@ -365,6 +417,9 @@ namespace IMCodec } auto nextImages = GetImage(tiff); + if (nextImages.empty()) + return ImageResult::FormatNotSupported; + for (size_t i = 0; i < nextImages.size(); i++) { subImages.push_back(std::make_shared(nextImages.at(i), ImageItemType::Pages)); From 550553b0237790d23cb2c9971482f91a09de02ae Mon Sep 17 00:00:00 2001 From: Lior Lahav Date: Tue, 5 May 2026 09:48:22 +0300 Subject: [PATCH 2/3] Gate delayimp to Windows MSVC builds Restricts the ImageCodec delayimp link dependency to Windows MSVC-style toolchains and excludes MinGW. This keeps Linux and Clang builds from receiving an unsupported -ldelayimp linker input while preserving delay-load support for the Windows configuration that needs it. --- ImageCodec/CMakeLists.txt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ImageCodec/CMakeLists.txt b/ImageCodec/CMakeLists.txt index b2a3867..3230ea0 100644 --- a/ImageCodec/CMakeLists.txt +++ b/ImageCodec/CMakeLists.txt @@ -47,6 +47,8 @@ if ( ${IMCODEC_BUILD_CODEC_FREEIMAGE}) endif() target_link_libraries(${TargetName} PRIVATE TinyEXIFstatic) -target_link_libraries(${TargetName} PUBLIC delayimp) - - +if (${CMAKE_SYSTEM_NAME} MATCHES "Windows" AND + (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" OR CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") AND + NOT MINGW) + target_link_libraries(${TargetName} PUBLIC delayimp) +endif() From 43847dfd15c7702a48baebd46ad0dfc6219d44f0 Mon Sep 17 00:00:00 2001 From: Lior Lahav Date: Wed, 6 May 2026 01:08:54 +0300 Subject: [PATCH 3/3] Fix delay-load propagation from ImageCodec ImageCodec now publishes the MSVC delay-load requirements for the DLL-backed codecs it enables, so final consumers inherit the correct link behavior while codec implementation libraries stay private. The codec-local delay-load blocks were removed, and zlib delay loading is applied to png_shared where zlib is actually imported. --- Codecs/CodecJPG/CMakeLists.txt | 4 ---- Codecs/CodecPNG/CMakeLists.txt | 5 ----- Codecs/CodecTiff/CMakeLists.txt | 3 --- ImageCodec/CMakeLists.txt | 11 +++++++++++ 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/Codecs/CodecJPG/CMakeLists.txt b/Codecs/CodecJPG/CMakeLists.txt index 744bae1..d9750a6 100644 --- a/Codecs/CodecJPG/CMakeLists.txt +++ b/Codecs/CodecJPG/CMakeLists.txt @@ -149,7 +149,3 @@ set(TargetName CodecJPG) add_library (${TargetName} ${sourceFiles} ) target_link_libraries(${TargetName} PRIVATE libjpeg-turbo::libjpeg-turbo) # adjust to match built target name - -if (${CMAKE_SYSTEM_NAME} MATCHES "Windows" AND (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" OR CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") AND NOT MINGW) - target_link_options(${TargetName} PUBLIC "/DELAYLOAD:turbojpeg.dll") -endif() \ No newline at end of file diff --git a/Codecs/CodecPNG/CMakeLists.txt b/Codecs/CodecPNG/CMakeLists.txt index d35b375..70fd797 100644 --- a/Codecs/CodecPNG/CMakeLists.txt +++ b/Codecs/CodecPNG/CMakeLists.txt @@ -61,8 +61,3 @@ target_include_directories(${TargetName} PRIVATE ${LibPNGFolder}) target_include_directories(${TargetName} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/external/libpng) target_link_libraries(${TargetName} png_shared ) target_include_directories(png_shared PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/external/zlib) - -if (${CMAKE_SYSTEM_NAME} MATCHES "Windows" AND (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" OR CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")) - target_link_options(${TargetName} PUBLIC "/DELAYLOAD:$.dll") - target_link_options(${TargetName} PUBLIC "/DELAYLOAD:$.dll") -endif() \ No newline at end of file diff --git a/Codecs/CodecTiff/CMakeLists.txt b/Codecs/CodecTiff/CMakeLists.txt index cb1dec7..6a6a5ba 100644 --- a/Codecs/CodecTiff/CMakeLists.txt +++ b/Codecs/CodecTiff/CMakeLists.txt @@ -83,6 +83,3 @@ target_include_directories(${TargetName} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/ext target_link_libraries(${TargetName} tiff) -if (${CMAKE_SYSTEM_NAME} MATCHES "Windows" AND (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" OR CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") AND NOT MINGW) - target_link_options(${TargetName} PUBLIC "/DELAYLOAD:$.dll") -endif() diff --git a/ImageCodec/CMakeLists.txt b/ImageCodec/CMakeLists.txt index 3230ea0..df2e40c 100644 --- a/ImageCodec/CMakeLists.txt +++ b/ImageCodec/CMakeLists.txt @@ -51,4 +51,15 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Windows" AND (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" OR CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") AND NOT MINGW) target_link_libraries(${TargetName} PUBLIC delayimp) + if ( ${IMCODEC_BUILD_CODEC_JPG}) + target_link_options(${TargetName} PUBLIC "/DELAYLOAD:turbojpeg.dll") + endif() + if ( ${IMCODEC_BUILD_CODEC_PNG}) + target_link_options(${TargetName} PUBLIC "/DELAYLOAD:$.dll") + set_property(TARGET png_shared APPEND PROPERTY LINK_LIBRARIES delayimp) + set_property(TARGET png_shared APPEND PROPERTY LINK_OPTIONS "/DELAYLOAD:$.dll") + endif() + if ( ${IMCODEC_BUILD_CODEC_TIFF}) + target_link_options(${TargetName} PUBLIC "/DELAYLOAD:$.dll") + endif() endif()