Skip to content

Enable colorimetric normalization of JPEG image data during decode #2922

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

Open
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

JimBobSquarePants
Copy link
Member

Prerequisites

  • I have written a descriptive pull-request title
  • I have verified that there are no overlapping pull-requests open
  • I have verified that I am following the existing coding patterns and practice as demonstrated in the repository. These follow strict Stylecop rules 👮.
  • I have provided test coverage for my change (where applicable)

Description

This PR adds support for normalizing color data in JPEG files (and TIFF files with JPEG compression) to sRGB during decoding.

A new DecoderOptions.ColorProfileHandling property controls this behavior with the following options:

  • Preserve - Leaves any embedded ICC color profiles intact. (default, preserves existing behavior)
  • Compact - Removes any embedded Standard sRGB ICC color profiles without transforming pixel data.
  • Convert - Transforms pixels based on embedded ICC profiles into the sRGB v4 color space, then removes the original profile.

Several internal helper methods have been introduced to allow decoders to determine and apply the correct behavior.

All shared JPEG color converters now use a common scalar interleaving path to manipulate planar data for ColorProfileConverter. There's likely room for SIMD-based performance improvements here (AVX-512?).

@waacton - might be of interest to you since it introduces questions around deriving RGB working spaces from ICC profiles which you are likely to better understand than I.
@saucecontrol - tagging you since your compact sRGB v4 profile implementation was invaluable (and shamelessly borrowed 😁).

Copy link

@Copilot Copilot AI left a 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 colorimetric normalization support for JPEG (and JPEG-compressed TIFF) data by embedding ICC-based conversions into the decoding pipeline.

  • Introduces DecoderOptions.ColorProfileHandling to specify Preserve, Compact, or Convert behavior.
  • Extends grayscale and CMYK JPEG converters with ConvertToRgbInPlaceWithIcc paths and integrates ICC removal in ImageDecoder.
  • Renames and updates YCbCr types to YCbCrTransform, and adjusts color-profile classes for the new workflows.

Reviewed Changes

Copilot reviewed 59 out of 59 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector512.cs Add ICC-aware override and repeat grayscale channel in vectorized path
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector256.cs Add ICC-aware override and repeat grayscale channel in vectorized path
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector128.cs Add ICC-aware override and repeat grayscale channel in vectorized path
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleScalar.cs Implement scalar ICC conversion with buffer packing/unpacking
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector512.cs Add ICC-aware override for CMYK vectorized converter
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector256.cs Add ICC-aware override for CMYK vectorized converter
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector128.cs Add ICC-aware override for CMYK vectorized converter
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykScalar.cs Implement scalar ICC conversion for CMYK
src/ImageSharp/Formats/ImageDecoder.cs Invoke HandleIccProfile after decode/identify to strip profiles
src/ImageSharp/Formats/DecoderOptions.cs Expose ColorProfileHandling and add profile‐selection helpers
src/ImageSharp/Formats/ColorProfileHandling.cs Define Compact and update docs for enum
src/ImageSharp/ColorProfiles/YccK.cs Rename matrix references to YCbCrTransform
src/ImageSharp/ColorProfiles/YCbCrTransform.cs Rename YCbCrMatrix to YCbCrTransform and update ctor/docs
src/ImageSharp/ColorProfiles/YCbCr.cs Update property references to YCbCrTransform
src/ImageSharp/ColorProfiles/Y.cs Update transform references for Y channel
src/ImageSharp/ColorProfiles/Rgb.cs Replace ToScaledVector3 calls with AsVector3Unsafe
src/ImageSharp/ColorProfiles/KnownYCbCrMatrices.cs Rename constants to YCbCrTransform
src/ImageSharp/ColorProfiles/Icc/CompactSrgbV4Profile.cs Rename class to CompactSrgbV4Profile and adjust namespace
src/ImageSharp/ColorProfiles/ColorConversionOptions.cs Change YCbCrMatrix property to YCbCrTransform
src/ImageSharp/Advanced/AotCompilerTools.cs Update AOT stubs to match new GetPixelBuffer signatures
Comments suppressed due to low confidence (5)

src/ImageSharp/ColorProfiles/ColorConversionOptions.cs:55

  • Update this XML summary to reflect the renamed property (YCbCrTransform) and its semantics (e.g., "Gets the YCbCr transform used to perform conversions...").
/// Gets the YCbCr matrix to used to perform conversions from/to RGB.

src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector512.cs:22

  • The Configuration type isn’t imported in this file, causing a compilation error; add using SixLabors.ImageSharp; at the top so that Configuration is recognized.
public override void ConvertToRgbInPlaceWithIcc(Configuration configuration, in ComponentValues values, IccProfile profile)

src/ImageSharp/Formats/ImageDecoder.cs:325

  • [nitpick] This logic is duplicated in two overloads; consider consolidating into a single generic or helper method to reduce code duplication.
private static void HandleIccProfile(DecoderOptions options, Image image)

src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleScalar.cs:54

  • Allocating a new buffer for every decode may impact performance; consider reusing pooled buffers or using stackalloc for small images to reduce allocation overhead.
using IMemoryOwner<float> memoryOwner = configuration.MemoryAllocator.Allocate<float>(values.Component0.Length * 3);

src/ImageSharp/Formats/DecoderOptions.cs:72

  • The out parameter value is overly generic and nullable despite the NotNullWhen(true) annotation; rename it to a descriptive name (e.g. out IccProfile profileToConvert) and consider making it non-nullable when returned true.
internal bool TryGetIccProfileForColorConversion(IccProfile? profile, [NotNullWhen(true)] out IccProfile? value)

@waacton waacton closed this May 18, 2025
@waacton waacton reopened this May 18, 2025
@waacton
Copy link
Collaborator

waacton commented May 18, 2025

(Sorry, misclicking!) Happy to take a look but will be in a couple of weeks after my break

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

File conversion from jpg to webp changes colors of the image Color Changed When Resize
2 participants