From 8e92f2077e67d244baaebf62b09fc8a5c9d27dbe Mon Sep 17 00:00:00 2001 From: Wacton Date: Tue, 10 Dec 2024 23:57:48 +0000 Subject: [PATCH] Fix XYZ PCS conversions --- .../ColorProfileConverterExtensionsIcc.cs | 11 ++-- .../Icc/ColorProfileConverterTests.Icc.cs | 54 +++++++++---------- 2 files changed, 34 insertions(+), 31 deletions(-) diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsIcc.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsIcc.cs index fb4a2a9dc..ae97f34bc 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsIcc.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsIcc.cs @@ -63,8 +63,7 @@ internal static class ColorProfileConverterExtensionsIcc xyz = pcsConverter.Convert(in lab); break; case IccColorSpaceType.CieXyz: - xyz = new CieXyz(sourcePcs[0], sourcePcs[1], sourcePcs[2]); - xyz = pcsConverter.Convert(in xyz); + xyz = CieXyz.FromScaledVector4(sourcePcs); break; default: throw new ArgumentOutOfRangeException($"Source PCS {sourcePcsType} not supported"); @@ -113,11 +112,15 @@ internal static class ColorProfileConverterExtensionsIcc { targetPcs = LabToLabV2(targetPcs); } + else if (targetConverter.Is16BitLutEntry) + { + // TODO: find a way to test? needs a non-perceptual-intent v2 profile with 8-bit LUT... + targetPcs = LabToLabV2(targetPcs); + } break; case IccColorSpaceType.CieXyz: - CieXyz targetXyz = pcsConverter.Convert(in xyz); - targetPcs = targetXyz.ToScaledVector4(); + targetPcs = xyz.ToScaledVector4(); break; default: throw new ArgumentOutOfRangeException($"Target PCS {targetPcsType} not supported"); diff --git a/tests/ImageSharp.Tests/ColorProfiles/Icc/ColorProfileConverterTests.Icc.cs b/tests/ImageSharp.Tests/ColorProfiles/Icc/ColorProfileConverterTests.Icc.cs index 1fd76f40e..29426850d 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/Icc/ColorProfileConverterTests.Icc.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/Icc/ColorProfileConverterTests.Icc.cs @@ -3,6 +3,7 @@ using System.Numerics; using SixLabors.ImageSharp.ColorProfiles; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; using Wacton.Unicolour; using Wacton.Unicolour.Icc; using Rgb = SixLabors.ImageSharp.ColorProfiles.Rgb; @@ -12,20 +13,21 @@ namespace SixLabors.ImageSharp.Tests.ColorProfiles.Icc; public class ColorProfileConverterTests { [Theory] - [InlineData(TestIccProfiles.Fogra39, TestIccProfiles.Fogra39, IccConversion.CmykToCmyk)] // CMYK -> LAB -> CMYK (commonly used v2 profiles) - [InlineData(TestIccProfiles.Fogra39, TestIccProfiles.Swop2006, IccConversion.CmykToCmyk)] // CMYK -> LAB -> CMYK (commonly used v2 profiles) - [InlineData(TestIccProfiles.Swop2006, TestIccProfiles.Fogra39, IccConversion.CmykToCmyk)] // CMYK -> LAB -> CMYK (commonly used v2 profiles) - [InlineData(TestIccProfiles.Swop2006, TestIccProfiles.Swop2006, IccConversion.CmykToCmyk)] // CMYK -> LAB -> CMYK (commonly used v2 profiles) - [InlineData(TestIccProfiles.Fogra39, TestIccProfiles.JapanColor2011, IccConversion.CmykToCmyk)] // CMYK -> LAB -> CMYK (different bit depth v2 LUTs, 16-bit vs 8-bit) - [InlineData(TestIccProfiles.JapanColor2011, TestIccProfiles.Fogra39, IccConversion.CmykToCmyk)] // CMYK -> LAB -> CMYK (different bit depth v2 LUTs, 8-bit vs 16-bit) - [InlineData(TestIccProfiles.Fogra39, TestIccProfiles.Cgats21, IccConversion.CmykToCmyk)] // CMYK -> LAB -> CMYK (different LUT versions, v2 vs v4) - [InlineData(TestIccProfiles.Fogra39, TestIccProfiles.StandardRgbV4, IccConversion.CmykToRgb)] // CMYK -> LAB -> RGB (different LUT versions, v2 vs v4) - [InlineData(TestIccProfiles.StandardRgbV4, TestIccProfiles.Fogra39, IccConversion.RgbToCmyk)] // RGB -> LAB -> CMYK (different LUT versions, v4 vs v2) - [InlineData(TestIccProfiles.StandardRgbV4, TestIccProfiles.RommRgb, IccConversion.RgbToRgb)] // RGB -> LAB -> XYZ -> RGB (different LUT elements, B-Matrix-M-CLUT-A vs B-Matrix-M) + [InlineData(TestIccProfiles.Fogra39, TestIccProfiles.Fogra39)] // CMYK -> LAB -> CMYK (commonly used v2 profiles) + [InlineData(TestIccProfiles.Fogra39, TestIccProfiles.Swop2006)] // CMYK -> LAB -> CMYK (commonly used v2 profiles) + [InlineData(TestIccProfiles.Swop2006, TestIccProfiles.Fogra39)] // CMYK -> LAB -> CMYK (commonly used v2 profiles) + [InlineData(TestIccProfiles.Swop2006, TestIccProfiles.Swop2006)] // CMYK -> LAB -> CMYK (commonly used v2 profiles) + [InlineData(TestIccProfiles.Fogra39, TestIccProfiles.JapanColor2011)] // CMYK -> LAB -> CMYK (different bit depth v2 LUTs, 16-bit vs 8-bit) + [InlineData(TestIccProfiles.JapanColor2011, TestIccProfiles.Fogra39)] // CMYK -> LAB -> CMYK (different bit depth v2 LUTs, 8-bit vs 16-bit) + [InlineData(TestIccProfiles.Fogra39, TestIccProfiles.Cgats21)] // CMYK -> LAB -> CMYK (different LUT versions, v2 vs v4) + [InlineData(TestIccProfiles.Fogra39, TestIccProfiles.StandardRgbV4)] // CMYK -> LAB -> RGB (different LUT versions, v2 vs v4) + [InlineData(TestIccProfiles.StandardRgbV4, TestIccProfiles.Fogra39)] // RGB -> LAB -> CMYK (different LUT versions, v4 vs v2) + [InlineData(TestIccProfiles.StandardRgbV4, TestIccProfiles.RommRgb)] // RGB -> LAB -> XYZ -> RGB (different LUT elements, B-Matrix-M-CLUT-A vs B-Matrix-M) + [InlineData(TestIccProfiles.RommRgb, TestIccProfiles.StandardRgbV4)] // RGB -> XYZ -> LAB -> RGB (different LUT elements, B-Matrix-M vs B-Matrix-M-CLUT-A) // TODO: enable once supported by Unicolour - in the meantime, manually test known values - // [InlineData(TestIccProfiles.Fogra39, TestIccProfiles.StandardRgbV2, IccConversion.CmykToRgb)] // CMYK -> XYZ -> LAB -> RGB (different LUT tags, A2B vs TRC) - // [InlineData(TestIccProfiles.StandardRgbV2, TestIccProfiles.Fogra39, IccConversion.RgbToCmyk)] // RGB -> XYZ -> LAB -> CMYK (different LUT tags, TRC vs A2B) - public void CanConvertCmykIccProfiles(string sourceProfile, string targetProfile, IccConversion iccConversion) + // [InlineData(TestIccProfiles.Fogra39, TestIccProfiles.StandardRgbV2)] // CMYK -> XYZ -> LAB -> RGB (different LUT tags, A2B vs TRC) + // [InlineData(TestIccProfiles.StandardRgbV2, TestIccProfiles.Fogra39)] // RGB -> XYZ -> LAB -> CMYK (different LUT tags, TRC vs A2B) + public void CanConvertCmykIccProfiles(string sourceProfile, string targetProfile) { // TODO: delete after testing float[] input = [0.734798908f, 0.887050927f, 0.476583719f, 0.547810674f]; @@ -38,13 +40,19 @@ public class ColorProfileConverterTests TargetIccProfile = TestIccProfiles.GetProfile(targetProfile) }); - Vector4 actualTargetValues = iccConversion switch + IccColorSpaceType sourceDataSpace = converter.Options.SourceIccProfile!.Header.DataColorSpace; + IccColorSpaceType targetDataSpace = converter.Options.TargetIccProfile!.Header.DataColorSpace; + Vector4 actualTargetValues = sourceDataSpace switch { - IccConversion.CmykToCmyk => converter.Convert(new Cmyk(new Vector4(input))).ToScaledVector4(), - IccConversion.CmykToRgb => converter.Convert(new Cmyk(new Vector4(input))).ToScaledVector4(), - IccConversion.RgbToCmyk => converter.Convert(new Rgb(new Vector3(input))).ToScaledVector4(), - IccConversion.RgbToRgb => converter.Convert(new Rgb(new Vector3(input))).ToScaledVector4(), - _ => throw new ArgumentOutOfRangeException(nameof(iccConversion), iccConversion, null) + IccColorSpaceType.Cmyk when targetDataSpace == IccColorSpaceType.Cmyk + => converter.Convert(new Cmyk(new Vector4(input))).ToScaledVector4(), + IccColorSpaceType.Cmyk when targetDataSpace == IccColorSpaceType.Rgb + => converter.Convert(new Cmyk(new Vector4(input))).ToScaledVector4(), + IccColorSpaceType.Rgb when targetDataSpace == IccColorSpaceType.Cmyk + => converter.Convert(new Rgb(new Vector3(input))).ToScaledVector4(), + IccColorSpaceType.Rgb when targetDataSpace == IccColorSpaceType.Rgb + => converter.Convert(new Rgb(new Vector3(input))).ToScaledVector4(), + _ => throw new ArgumentOutOfRangeException("Unexpected ICC profile data colour spaces") }; const double tolerance = 0.000005; @@ -82,12 +90,4 @@ public class ColorProfileConverterTests // Clamp the result between 0 and 1 to ensure it does not exceed the bounds. return value == 0 ? 0F : Math.Clamp((float)value + 0.0000001F, 0, 1); } - - public enum IccConversion - { - CmykToCmyk, - CmykToRgb, - RgbToCmyk, - RgbToRgb - } }