diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverter.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverter.cs index fa3f3d7e21..d0afec4ab2 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverter.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverter.cs @@ -12,7 +12,7 @@ public class ColorProfileConverter /// Initializes a new instance of the class. /// public ColorProfileConverter() - : this(new()) + : this(new()) { } diff --git a/src/ImageSharp/ColorProfiles/Icc/IccProfileConverter.cs b/src/ImageSharp/ColorProfiles/Icc/IccProfileConverter.cs deleted file mode 100644 index c8c4de4a60..0000000000 --- a/src/ImageSharp/ColorProfiles/Icc/IccProfileConverter.cs +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Buffers; -using System.Numerics; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.Metadata.Profiles.Icc; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.ColorProfiles.Icc; - -/// -/// Allows the conversion between ICC profiles. -/// -internal static class IccProfileConverter -{ - /// - /// Performs a conversion of the image pixels based on the input and output ICC profiles. - /// - /// The image to convert. - /// The input ICC profile. - /// The output ICC profile. - public static void Convert(Image image, IccProfile inputIccProfile, IccProfile outputIccProfile) - => image.AcceptVisitor(new IccProfileConverterVisitor(inputIccProfile, outputIccProfile)); - - /// - /// Performs a conversion of the image pixels based on the input and output ICC profiles. - /// - /// The type of pixel. - /// The image to convert. - /// The input ICC profile. - /// The output ICC profile. - public static void Convert(Image image, IccProfile inputIccProfile, IccProfile outputIccProfile) - where TPixel : unmanaged, IPixel - { - IccDataToPcsConverter converterDataToPcs = new(inputIccProfile); - IccPcsToDataConverter converterPcsToData = new(outputIccProfile); - Configuration configuration = image.Configuration; - - image.ProcessPixelRows(accessor => - { - ColorProfileConverter converter = new(new ColorConversionOptions() - { - SourceWhitePoint = new CieXyz(inputIccProfile.Header.PcsIlluminant), - TargetWhitePoint = new CieXyz(outputIccProfile.Header.PcsIlluminant), - }); - - // TODO: Our Xxy/Lab conversion are dependent on the version number. We are applying the conversion using V4 - // but we should use the correct algorithm per version. This includes Lab/Lab Xyz/Xyz. - using IMemoryOwner vectors = configuration.MemoryAllocator.Allocate(accessor.Width); - Span vectorsSpan = vectors.GetSpan(); - - // TODO: For debugging - remove. - // It appears we have a scaling problem. The pcs values differ by on average 0.000001. - Span temp = new Vector4[vectorsSpan.Length]; - - for (int y = 0; y < accessor.Height; y++) - { - Span row = accessor.GetRowSpan(y); - PixelOperations.Instance.ToVector4(configuration, row, vectorsSpan, PixelConversionModifiers.Scale); - - if (inputIccProfile.Header.ProfileConnectionSpace == IccColorSpaceType.CieLab && - outputIccProfile.Header.ProfileConnectionSpace == IccColorSpaceType.CieXyz) - { - for (int x = 0; x < vectorsSpan.Length; x++) - { - Vector4 pcs = converterDataToPcs.Calculate(vectorsSpan[x]); - temp[x] = pcs; - pcs = PcsToLab(pcs); - CieLab lab = new(pcs.X, pcs.Y, pcs.Z); - CieXyz xyz = converter.Convert(in lab); - pcs = XyzToPcs(pcs, xyz); - - vectorsSpan[x] = converterPcsToData.Calculate(pcs); - } - } - else if (inputIccProfile.Header.ProfileConnectionSpace == IccColorSpaceType.CieXyz && - outputIccProfile.Header.ProfileConnectionSpace == IccColorSpaceType.CieLab) - { - for (int x = 0; x < vectorsSpan.Length; x++) - { - Vector4 pcs = converterDataToPcs.Calculate(vectorsSpan[x]); - CieXyz xyz = new(pcs.X, pcs.Y, pcs.Z); - CieLab lab = converter.Convert(in xyz); - pcs = LabToPcs(pcs, lab); - vectorsSpan[x] = converterPcsToData.Calculate(pcs); - } - } - else - { - for (int x = 0; x < vectorsSpan.Length; x++) - { - Vector4 pcs = converterDataToPcs.Calculate(vectorsSpan[x]); - vectorsSpan[x] = converterPcsToData.Calculate(pcs); - } - } - - PixelOperations.Instance.FromVector4Destructive(configuration, vectorsSpan, row, PixelConversionModifiers.Scale); - } - }); - - image.Metadata.IccProfile = outputIccProfile; - } - - private static unsafe Vector4 PcsToLab(Vector4 input) - { - Vector3* v = (Vector3*)&input; - v[0] *= new Vector3(100f, 255, 255); - v[0] -= new Vector3(0, 128F, 128F); - return input; - } - - private static unsafe Vector4 LabToPcs(Vector4 input, CieLab lab) - { - Vector3* v = (Vector3*)&input; - v[0] = new Vector3(lab.L, lab.A + 128F, lab.B + 128F); - v[0] /= 100F; - return input; - } - - private static unsafe Vector4 XyzToPcs(Vector4 input, CieXyz xyz) - { - Vector3* v = (Vector3*)&input; - v[0] = xyz.ToVector3(); - v[0] *= 32768 / 65535f; - return input; - } - - private readonly struct IccProfileConverterVisitor : IImageVisitor - { - private readonly IccProfile inputIccProfile; - private readonly IccProfile outputIccProfile; - - public IccProfileConverterVisitor(IccProfile inputIccProfile, IccProfile outputIccProfile) - { - this.inputIccProfile = inputIccProfile; - this.outputIccProfile = outputIccProfile; - } - - public void Visit(Image image) - where TPixel : unmanaged, IPixel => Convert(image, this.inputIccProfile, this.outputIccProfile); - } -} diff --git a/src/ImageSharp/Formats/DecoderOptions.cs b/src/ImageSharp/Formats/DecoderOptions.cs index f606d2fff2..8c6b8fc225 100644 --- a/src/ImageSharp/Formats/DecoderOptions.cs +++ b/src/ImageSharp/Formats/DecoderOptions.cs @@ -62,8 +62,9 @@ public sealed class DecoderOptions /// /// Gets a value that controls how ICC profiles are handled during decode. + /// TODO: Implement this. /// - public ColorProfileHandling ColorProfileHandling { get; init; } + internal ColorProfileHandling ColorProfileHandling { get; init; } internal void SetConfiguration(Configuration configuration) => this.configuration = configuration; } diff --git a/src/ImageSharp/Formats/ImageDecoder.cs b/src/ImageSharp/Formats/ImageDecoder.cs index 0a8144712a..dd148dfedd 100644 --- a/src/ImageSharp/Formats/ImageDecoder.cs +++ b/src/ImageSharp/Formats/ImageDecoder.cs @@ -1,11 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.ColorProfiles.Icc; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Icc; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Metadata; -using SixLabors.ImageSharp.Metadata.Profiles.Icc; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; @@ -26,7 +23,6 @@ public abstract class ImageDecoder : IImageDecoder stream, s => this.Decode(options, s, default)); - TransformColorProfile(options, image); this.SetDecoderFormat(options.Configuration, image); return image; @@ -40,7 +36,6 @@ public abstract class ImageDecoder : IImageDecoder stream, s => this.Decode(options, s, default)); - TransformColorProfile(options, image); this.SetDecoderFormat(options.Configuration, image); return image; @@ -56,7 +51,6 @@ public abstract class ImageDecoder : IImageDecoder (s, ct) => this.Decode(options, s, ct), cancellationToken).ConfigureAwait(false); - TransformColorProfile(options, image); this.SetDecoderFormat(options.Configuration, image); return image; @@ -71,7 +65,6 @@ public abstract class ImageDecoder : IImageDecoder (s, ct) => this.Decode(options, s, ct), cancellationToken).ConfigureAwait(false); - TransformColorProfile(options, image); this.SetDecoderFormat(options.Configuration, image); return image; @@ -166,25 +159,6 @@ public abstract class ImageDecoder : IImageDecoder } } - /// - /// Converts the decoded image color profile if present to a V4 sRGB profile. - /// - /// The decoder options. - /// The image. - protected static void TransformColorProfile(DecoderOptions options, Image image) - { - if (options.ColorProfileHandling == ColorProfileHandling.Preserve) - { - return; - } - - IccProfile? profile = image.Metadata?.IccProfile; - if (profile is not null) - { - IccProfileConverter.Convert(image, profile, SrgbV4Profile.GetProfile()); - } - } - /// /// Determines whether the decoded image should be resized. /// diff --git a/src/ImageSharp/Formats/SpecializedImageDecoder{T}.cs b/src/ImageSharp/Formats/SpecializedImageDecoder{T}.cs index 2fb15aed29..38bfe817dd 100644 --- a/src/ImageSharp/Formats/SpecializedImageDecoder{T}.cs +++ b/src/ImageSharp/Formats/SpecializedImageDecoder{T}.cs @@ -23,7 +23,6 @@ public abstract class SpecializedImageDecoder : ImageDecoder, ISpecializedIma stream, s => this.Decode(options, s, default)); - TransformColorProfile(options.GeneralOptions, image); this.SetDecoderFormat(options.GeneralOptions.Configuration, image); return image; @@ -37,7 +36,6 @@ public abstract class SpecializedImageDecoder : ImageDecoder, ISpecializedIma stream, s => this.Decode(options, s, default)); - TransformColorProfile(options.GeneralOptions, image); this.SetDecoderFormat(options.GeneralOptions.Configuration, image); return image; @@ -53,7 +51,6 @@ public abstract class SpecializedImageDecoder : ImageDecoder, ISpecializedIma (s, ct) => this.Decode(options, s, ct), cancellationToken).ConfigureAwait(false); - TransformColorProfile(options.GeneralOptions, image); this.SetDecoderFormat(options.GeneralOptions.Configuration, image); return image; @@ -68,7 +65,6 @@ public abstract class SpecializedImageDecoder : ImageDecoder, ISpecializedIma (s, ct) => this.Decode(options, s, ct), cancellationToken).ConfigureAwait(false); - TransformColorProfile(options.GeneralOptions, image); this.SetDecoderFormat(options.GeneralOptions.Configuration, image); return image; diff --git a/tests/ImageSharp.Tests/ColorProfiles/Icc/IccProfileConverterTests.cs b/tests/ImageSharp.Tests/ColorProfiles/Icc/IccProfileConverterTests.cs deleted file mode 100644 index ad8193774d..0000000000 --- a/tests/ImageSharp.Tests/ColorProfiles/Icc/IccProfileConverterTests.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorProfiles.Icc; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Icc; -using SixLabors.ImageSharp.Formats.Png; -using SixLabors.ImageSharp.Metadata.Profiles.Icc; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; - -namespace SixLabors.ImageSharp.Tests.ColorProfiles.Icc; - -public class IccProfileConverterTests -{ - private static readonly PngEncoder Encoder = new(); - - [Theory(Skip = "Skip for now while we refactor the library")] - [WithFile(TestImages.Jpeg.ICC.AdobeRgb, PixelTypes.Rgb24)] - [WithFile(TestImages.Jpeg.ICC.AppleRGB, PixelTypes.Rgb24)] - [WithFile(TestImages.Jpeg.ICC.ColorMatch, PixelTypes.Rgb24)] - [WithFile(TestImages.Jpeg.ICC.WideRGB, PixelTypes.Rgb24)] - [WithFile(TestImages.Jpeg.ICC.SRgb, PixelTypes.Rgb24)] - [WithFile(TestImages.Jpeg.ICC.ProPhoto, PixelTypes.Rgb24)] - [WithFile(TestImages.Jpeg.ICC.CMYK, PixelTypes.Rgb24)] - public void CanRoundTripProfile(TestImageProvider provider) - where TPixel : unmanaged, IPixel - { - using Image image = provider.GetImage(); - - IccProfile profile = image.Metadata.IccProfile; - - TPixel expected = image[0, 0]; - - IccProfileConverter.Convert(image, profile, profile); - - image.DebugSave(provider, extension: "png", appendPixelTypeToFileName: false, appendSourceFileOrDescription: true, encoder: Encoder); - - TPixel actual = image[0, 0]; - - Assert.Equal(expected, actual); - } - - [Theory(Skip = "Skip for now while we refactor the library")] - [WithFile(TestImages.Jpeg.ICC.AdobeRgb, PixelTypes.Rgb24)] - [WithFile(TestImages.Jpeg.ICC.AppleRGB, PixelTypes.Rgb24)] - [WithFile(TestImages.Jpeg.ICC.ColorMatch, PixelTypes.Rgb24)] - [WithFile(TestImages.Jpeg.ICC.WideRGB, PixelTypes.Rgb24)] - [WithFile(TestImages.Jpeg.ICC.SRgb, PixelTypes.Rgb24)] - [WithFile(TestImages.Jpeg.ICC.ProPhoto, PixelTypes.Rgb24)] - [WithFile(TestImages.Jpeg.ICC.CMYK, PixelTypes.Rgb24)] - public void CanConvertToSRGB(TestImageProvider provider) - where TPixel : unmanaged, IPixel - { - using Image image = provider.GetImage(); - IccProfile profile = image.Metadata.IccProfile; - - IccProfile sRGBProfile = SrgbV4Profile.GetProfile(); - - IccProfileConverter.Convert(image, profile, sRGBProfile); - - Assert.Equal(image.Metadata.IccProfile, sRGBProfile); - - image.DebugSave(provider, extension: "png", appendPixelTypeToFileName: false, appendSourceFileOrDescription: true, encoder: Encoder); - - // Mac reports a difference of 0.0000% - image.CompareToReferenceOutput(ImageComparer.Tolerant(0.0001F), provider, appendPixelTypeToFileName: false); - } -}