-
-
Notifications
You must be signed in to change notification settings - Fork 891
Allow ICC conversion for CIE Lab and CMYK TIFF #3054
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Adds TIFF decoding improvements to support CIE Lab 16-bit (chunky + planar) and enables ICC-based color conversion for CMYK/Lab paths, plus updates wiring so decoders can access frame metadata and decoder options.
Changes:
- Add new 16-bit CIE Lab TIFF color decoders (chunky + planar) with endianness handling and ICC conversion support.
- Update CMYK TIFF decoding to support ICC conversion and use buffer/SIMD-based row processing.
- Pass
ImageFrameMetadata+DecoderOptionsinto the TIFF color decoder factory; add tests + new LFS test assets/reference outputs.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs | Passes frame metadata/options into color decoder creation for ICC-aware decoding. |
| src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs | Adjusts CieLab parsing to allow more than just 8-bit (but currently lacks bit-depth validation). |
| src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs | Selects new CIE Lab 8/16-bit decoders and passes ICC context to ICC-aware decoders. |
| src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CmykTiffColor{TPixel}.cs | Adds ICC conversion support and SIMD row conversion pipeline. |
| src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLab8TiffColor{TPixel}.cs | Renames 8-bit CieLab decoder for clarity. |
| src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLab8PlanarTiffColor{TPixel}.cs | Renames 8-bit planar CieLab decoder for clarity. |
| src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLab16TiffColor{TPixel}.cs | New 16-bit chunky CieLab decoder (endianness + ICC conversion). |
| src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLab16PlanarTiffColor{TPixel}.cs | New 16-bit planar CieLab decoder (endianness + ICC conversion). |
| src/ImageSharp/Formats/Tiff/PhotometricInterpretation/YCbCrConverter.cs | Minor default initialization simplification. |
| tests/ImageSharp.Tests/TestImages.cs | Adds TIFF ICC test image paths. |
| tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs | Adds ICC conversion test; currently also comments out an existing test case. |
| tests/Images/Input/Tiff/icc-profiles/* | Adds new LFS-backed TIFF ICC test assets. |
| tests/Images/External/ReferenceOutput/TiffDecoderTests/* | Adds new LFS-backed reference outputs for ICC conversion test. |
| .github/workflows/build-and-test.yml | Adjusts PR trigger types. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| [Theory] | ||
| [WithFile(MultiframeDifferentVariants, PixelTypes.Rgba32)] | ||
| // [WithFile(MultiframeDifferentVariants, PixelTypes.Rgba32)] | ||
| [WithFile(Cmyk64BitDeflate, PixelTypes.Rgba32)] |
Copilot
AI
Jan 29, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A decoder negative-coverage case was disabled by commenting out the [WithFile(MultiframeDifferentVariants, PixelTypes.Rgba32)] attribute. This reduces test coverage and can hide regressions. Either re-enable it, or replace it with a new/updated NotSupported assertion that reflects the intended behavior for that file (and add a short comment explaining why).
| @@ -452,13 +452,9 @@ private static void ParseColorType(this TiffDecoderCore options, ExifProfile exi | |||
| TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported for CieLab images."); | |||
| } | |||
|
|
|||
Copilot
AI
Jan 29, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CieLab parsing no longer validates BitsPerSample beyond the channel count. Since downstream decoding currently branches based on Channel0, inputs like 10/12/14-bit or mismatched per-channel bit depths would be accepted and then decoded incorrectly. Consider validating here that all 3 channels have the same supported bit depth (likely 8 or 16) and throw NotSupported otherwise.
| ushort bitsPerChannel = options.BitsPerSample.Channel0; | |
| if (bitsPerChannel != 8 && bitsPerChannel != 16) | |
| { | |
| TiffThrowHelper.ThrowNotSupported("Only 8 bits or 16 bits per channel are supported for CieLab images."); | |
| } |
| if (bitsPerSample.Channel0 == 8) | ||
| { | ||
| return new CieLab8TiffColor<TPixel>(); | ||
| } | ||
|
|
||
| return new CieLab16TiffColor<TPixel>( | ||
| configuration, | ||
| options, | ||
| metadata, | ||
| memoryAllocator, | ||
| byteOrder == ByteOrder.BigEndian); | ||
|
|
Copilot
AI
Jan 29, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CieLab decoder selection is based only on bitsPerSample.Channel0, and the previous per-channel validation was removed. If the TIFF has non-8/16 bit depths (eg 12-bit) or mismatched channel bit depths, this will incorrectly fall back to the 16-bit decoder instead of failing fast. Please validate that Channel0/1/2 are all equal and either 8 or 16; otherwise throw NotSupported (or keep a DebugGuard + runtime check).
| if (bitsPerSample.Channel0 == 8) | |
| { | |
| return new CieLab8TiffColor<TPixel>(); | |
| } | |
| return new CieLab16TiffColor<TPixel>( | |
| configuration, | |
| options, | |
| metadata, | |
| memoryAllocator, | |
| byteOrder == ByteOrder.BigEndian); | |
| bool all8Bit = | |
| bitsPerSample.Channel0 == 8 | |
| && bitsPerSample.Channel1 == 8 | |
| && bitsPerSample.Channel2 == 8; | |
| bool all16Bit = | |
| bitsPerSample.Channel0 == 16 | |
| && bitsPerSample.Channel1 == 16 | |
| && bitsPerSample.Channel2 == 16; | |
| DebugGuard.IsTrue(all8Bit || all16Bit, "bitsPerSample"); | |
| if (all8Bit) | |
| { | |
| return new CieLab8TiffColor<TPixel>(); | |
| } | |
| if (all16Bit) | |
| { | |
| return new CieLab16TiffColor<TPixel>( | |
| configuration, | |
| options, | |
| metadata, | |
| memoryAllocator, | |
| byteOrder == ByteOrder.BigEndian); | |
| } | |
| throw new System.NotSupportedException("CieLab color requires bitsPerSample of 8 or 16 bits for all three channels."); |
| case TiffColorType.CieLabPlanar: | ||
| return new CieLabPlanarTiffColor<TPixel>(); | ||
| return bitsPerSample.Channel0 == 8 | ||
| ? new CieLab8PlanarTiffColor<TPixel>() | ||
| : new CieLab16PlanarTiffColor<TPixel>( |
Copilot
AI
Jan 29, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CieLabPlanar selection also branches only on bitsPerSample.Channel0 and doesn't assert bitsPerSample.Channels == 3 or validate supported bit depths. Add validation that the sample count is 3 and that Channel0/1/2 are all equal and either 8 or 16, otherwise throw NotSupported to avoid silent mis-decoding.
| } | ||
| } | ||
|
|
||
| #pragma warning disable IDE0060 // Remove unused parameter |
Copilot
AI
Jan 29, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#pragma warning disable IDE0060 is introduced without a corresponding restore and appears unnecessary since the new parameters are used in the CieLabPlanar branch. Please remove the pragma (preferred) or scope it tightly with #pragma warning restore IDE0060 right after the affected code.
| #pragma warning disable IDE0060 // Remove unused parameter |
Prerequisites
Description
This pull request adds support for decoding TIFF images with CIE Lab color data in both 8-bit and 16-bit per channel formats, including both planar and interleaved storage. It also updates the CMYK TIFF decoder to support ICC color profile conversion and makes the decoder factory aware of these new types. The changes improve color accuracy and extensibility for TIFF decoding.
New TIFF color decoders and ICC profile support:
CieLab16TiffColor<TPixel>andCieLab16PlanarTiffColor<TPixel>classes to decode 16-bit per channel CIE Lab TIFF images, with proper ICC profile conversion and endianness handling. [1] [2]CmykTiffColor<TPixel>to support ICC profile conversion and use buffer-based SIMD processing for more efficient decoding. [1] [2]Refactoring and naming consistency:
CieLabTiffColor<TPixel>andCieLabPlanarTiffColor<TPixel>toCieLab8TiffColor<TPixel>andCieLab8PlanarTiffColor<TPixel>, respectively, to clarify that these handle 8-bit per channel data. [1] [2]Decoder factory enhancements:
TiffColorDecoderFactory<TPixel>to instantiate the correct decoder based on bits per sample and to pass necessary metadata, options, and allocator for ICC profile-aware decoders. [1] [2] [3] [4] [5]Minor improvements:
YCbCrToRgbConverter.