Browse Source

Fix CMYK to RGB using TRCs

pull/1567/head
Wacton 1 year ago
parent
commit
369bf5ff82
  1. 6
      src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsIcc.cs
  2. 7
      src/ImageSharp/ColorProfiles/Icc/Calculators/ColorTrcCalculator.cs
  3. 9
      tests/ImageSharp.Tests/ColorProfiles/Icc/ColorProfileConverterTests.Icc.cs

6
src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsIcc.cs

@ -189,6 +189,9 @@ internal static class ColorProfileConverterExtensionsIcc
sourcePcs = sourceParams.Is16BitLutEntry ? LabV2ToLab(sourcePcs) : sourcePcs; sourcePcs = sourceParams.Is16BitLutEntry ? LabV2ToLab(sourcePcs) : sourcePcs;
CieLab lab = CieLab.FromScaledVector4(sourcePcs); CieLab lab = CieLab.FromScaledVector4(sourcePcs);
CieXyz xyz = pcsConverter.Convert<CieLab, CieXyz>(in lab); CieXyz xyz = pcsConverter.Convert<CieLab, CieXyz>(in lab);
// DemoMaxICC clips negatives as part of IccUtil.cpp : icLabToXYZ > icICubeth
xyz = new CieXyz(Vector3.Clamp(xyz.ToVector3(), Vector3.Zero, new Vector3(float.MaxValue, float.MaxValue, float.MaxValue)));
return xyz.ToScaledVector4(); return xyz.ToScaledVector4();
} }
@ -258,6 +261,9 @@ internal static class ColorProfileConverterExtensionsIcc
sourcePcs = sourceParams.Is16BitLutEntry ? LabV2ToLab(sourcePcs) : sourcePcs; sourcePcs = sourceParams.Is16BitLutEntry ? LabV2ToLab(sourcePcs) : sourcePcs;
CieLab lab = CieLab.FromScaledVector4(sourcePcs); CieLab lab = CieLab.FromScaledVector4(sourcePcs);
xyz = pcsConverter.Convert<CieLab, CieXyz>(in lab); xyz = pcsConverter.Convert<CieLab, CieXyz>(in lab);
// DemoMaxICC clips negatives as part of IccUtil.cpp : icLabToXYZ > icICubeth
xyz = new CieXyz(Vector3.Clamp(xyz.ToVector3(), Vector3.Zero, new Vector3(float.MaxValue, float.MaxValue, float.MaxValue)));
break; break;
case IccColorSpaceType.CieXyz: case IccColorSpaceType.CieXyz:
xyz = CieXyz.FromScaledVector4(sourcePcs); xyz = CieXyz.FromScaledVector4(sourcePcs);

7
src/ImageSharp/ColorProfiles/Icc/Calculators/ColorTrcCalculator.cs

@ -39,13 +39,16 @@ internal class ColorTrcCalculator : IVector4Calculator
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 Calculate(Vector4 value) public Vector4 Calculate(Vector4 value)
{ {
// uses the descaled XYZ as DemoMaxICC IccCmm.cpp : CIccXformMatrixTRC::Apply()
Vector4 xyz = new(CieXyz.FromScaledVector4(value).ToVector3(), 1);
if (this.toPcs) if (this.toPcs)
{ {
value = this.curveCalculator.Calculate(value); value = this.curveCalculator.Calculate(xyz);
return Vector4.Transform(value, this.matrix); return Vector4.Transform(value, this.matrix);
} }
value = Vector4.Transform(value, this.matrix); value = Vector4.Transform(xyz, this.matrix);
return this.curveCalculator.Calculate(value); return this.curveCalculator.Calculate(value);
} }
} }

9
tests/ImageSharp.Tests/ColorProfiles/Icc/ColorProfileConverterTests.Icc.cs

@ -80,9 +80,12 @@ public class ColorProfileConverterTests
Rgb actualRgb = converter.Convert<Cmyk, Rgb>(new Cmyk(new Vector4(input))); Rgb actualRgb = converter.Convert<Cmyk, Rgb>(new Cmyk(new Vector4(input)));
Assert.Equal(expectedR, actualRgb.R); // TODO: investigate lower tolerance than CanConvertCmykIccProfiles()
Assert.Equal(expectedG, actualRgb.G); // currently assuming it's a rounding error in the process of gathering test data manually
Assert.Equal(expectedB, actualRgb.B); const double tolerance = 0.0005;
Assert.Equal(expectedR, actualRgb.R, tolerance);
Assert.Equal(expectedG, actualRgb.G, tolerance);
Assert.Equal(expectedB, actualRgb.B, tolerance);
} }
private static double[] GetExpectedTargetValues(string sourceProfile, string targetProfile, float[] input) private static double[] GetExpectedTargetValues(string sourceProfile, string targetProfile, float[] input)

Loading…
Cancel
Save