diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/MatrixCalculator.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/MatrixCalculator.cs
index f479d186a8..5997a9cdc8 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/MatrixCalculator.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/MatrixCalculator.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors.
+// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Numerics;
@@ -20,7 +20,7 @@ internal class MatrixCalculator : IVector4Calculator
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 Calculate(Vector4 value)
{
- var transformed = Vector4.Transform(value, this.matrix2D);
+ Vector4 transformed = Vector4.Transform(value, this.matrix2D);
return Vector4.Add(this.matrix1D, transformed);
}
}
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/ParametricCurveCalculator.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/ParametricCurveCalculator.cs
index 5a3bab6e86..9312326830 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/ParametricCurveCalculator.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/ParametricCurveCalculator.cs
@@ -1,7 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
@@ -9,8 +8,8 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Icc;
internal class ParametricCurveCalculator : ISingleCalculator
{
- private IccParametricCurve curve;
- private IccParametricCurveType type;
+ private readonly IccParametricCurve curve;
+ private readonly IccParametricCurveType type;
private const IccParametricCurveType InvertedFlag = (IccParametricCurveType)(1 << 3);
public ParametricCurveCalculator(IccParametricCurveTagDataEntry entry, bool inverted)
@@ -26,41 +25,23 @@ internal class ParametricCurveCalculator : ISingleCalculator
}
public float Calculate(float value)
- {
- switch (this.type)
- {
- case IccParametricCurveType.Type1:
- return this.CalculateGamma(value);
- case IccParametricCurveType.Cie122_1996:
- return this.CalculateCie122(value);
- case IccParametricCurveType.Iec61966_3:
- return this.CalculateIec61966(value);
- case IccParametricCurveType.SRgb:
- return this.CalculateSRgb(value);
- case IccParametricCurveType.Type5:
- return this.CalculateType5(value);
-
- case IccParametricCurveType.Type1 | InvertedFlag:
- return this.CalculateInvertedGamma(value);
- case IccParametricCurveType.Cie122_1996 | InvertedFlag:
- return this.CalculateInvertedCie122(value);
- case IccParametricCurveType.Iec61966_3 | InvertedFlag:
- return this.CalculateInvertedIec61966(value);
- case IccParametricCurveType.SRgb | InvertedFlag:
- return this.CalculateInvertedSRgb(value);
- case IccParametricCurveType.Type5 | InvertedFlag:
- return this.CalculateInvertedType5(value);
-
- default:
- throw new InvalidIccProfileException("ParametricCurve");
- }
- }
+ => this.type switch
+ {
+ IccParametricCurveType.Type1 => this.CalculateGamma(value),
+ IccParametricCurveType.Cie122_1996 => this.CalculateCie122(value),
+ IccParametricCurveType.Iec61966_3 => this.CalculateIec61966(value),
+ IccParametricCurveType.SRgb => this.CalculateSRgb(value),
+ IccParametricCurveType.Type5 => this.CalculateType5(value),
+ IccParametricCurveType.Type1 | InvertedFlag => this.CalculateInvertedGamma(value),
+ IccParametricCurveType.Cie122_1996 | InvertedFlag => this.CalculateInvertedCie122(value),
+ IccParametricCurveType.Iec61966_3 | InvertedFlag => this.CalculateInvertedIec61966(value),
+ IccParametricCurveType.SRgb | InvertedFlag => this.CalculateInvertedSRgb(value),
+ IccParametricCurveType.Type5 | InvertedFlag => this.CalculateInvertedType5(value),
+ _ => throw new InvalidIccProfileException("ParametricCurve"),
+ };
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private float CalculateGamma(float value)
- {
- return MathF.Pow(value, this.curve.G);
- }
+ private float CalculateGamma(float value) => MathF.Pow(value, this.curve.G);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private float CalculateCie122(float value)
@@ -116,15 +97,11 @@ internal class ParametricCurveCalculator : ISingleCalculator
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private float CalculateInvertedGamma(float value)
- {
- return MathF.Pow(value, 1 / this.curve.G);
- }
+ => MathF.Pow(value, 1 / this.curve.G);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private float CalculateInvertedCie122(float value)
- {
- return (MathF.Pow(value, 1 / this.curve.G) - this.curve.B) / this.curve.A;
- }
+ => (MathF.Pow(value, 1 / this.curve.G) - this.curve.B) / this.curve.A;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private float CalculateInvertedIec61966(float value)
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccConverterBase.Checks.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccConverterBase.Checks.cs
index fac56c3b5d..344efddad2 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccConverterBase.Checks.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccConverterBase.Checks.cs
@@ -31,9 +31,7 @@ internal abstract partial class IccConverterBase
private static ConversionMethod CheckMethod1(IccProfile profile, IccRenderingIntent renderingIntent)
{
- ConversionMethod method = ConversionMethod.Invalid;
-
- method = CheckMethodD(profile, renderingIntent);
+ ConversionMethod method = CheckMethodD(profile, renderingIntent);
if (method != ConversionMethod.Invalid)
{
return method;
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccConverterbase.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccConverterbase.cs
index 6e9c8aa829..79f04229e1 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccConverterbase.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccConverterbase.cs
@@ -29,8 +29,5 @@ internal abstract partial class IccConverterBase
/// The value to convert
/// The converted value
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Vector4 Calculate(Vector4 value)
- {
- return this.calculator.Calculate(value);
- }
+ public Vector4 Calculate(Vector4 value) => this.calculator.Calculate(value);
}
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccProfileConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccProfileConverter.cs
new file mode 100644
index 0000000000..ef7be7d1ab
--- /dev/null
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccProfileConverter.cs
@@ -0,0 +1,43 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System.Buffers;
+using System.Numerics;
+using SixLabors.ImageSharp.Advanced;
+using SixLabors.ImageSharp.ColorSpaces.Conversion.Icc;
+using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.Metadata.Profiles.Icc;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.Icc;
+internal static class IccProfileConverter
+{
+ 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.GetConfiguration();
+
+ image.ProcessPixelRows(accessor =>
+ {
+ using IMemoryOwner vectors = configuration.MemoryAllocator.Allocate(accessor.Width);
+ Span vectorsSpan = vectors.GetSpan();
+ for (int y = 0; y < accessor.Height; y++)
+ {
+ Span row = accessor.GetRowSpan(y);
+ PixelOperations.Instance.ToVector4(configuration, row, vectorsSpan, PixelConversionModifiers.Scale);
+
+ 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);
+ }
+ });
+
+ image.Metadata.IccProfile = outputIccProfile;
+ }
+}
diff --git a/src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs b/src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs
index e4403a47e2..00c545c88a 100644
--- a/src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs
+++ b/src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs
@@ -174,7 +174,6 @@ public sealed class IccProfile : IDeepCloneable
return copy;
}
- IccWriter writer = new();
return IccWriter.Write(this);
}
@@ -191,7 +190,6 @@ public sealed class IccProfile : IDeepCloneable
return;
}
- IccReader reader = new();
this.header = IccReader.ReadHeader(this.data);
}
diff --git a/src/ImageSharp/Metadata/Profiles/ICC/IccTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/IccTagDataEntry.cs
index 56d620ec32..428dabf37b 100644
--- a/src/ImageSharp/Metadata/Profiles/ICC/IccTagDataEntry.cs
+++ b/src/ImageSharp/Metadata/Profiles/ICC/IccTagDataEntry.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors.
+// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Metadata.Profiles.Icc;
@@ -41,9 +41,7 @@ public abstract class IccTagDataEntry : IEquatable
///
public override bool Equals(object obj)
- {
- return obj is IccTagDataEntry entry && this.Equals(entry);
- }
+ => obj is IccTagDataEntry entry && this.Equals(entry);
///
public virtual bool Equals(IccTagDataEntry other)
diff --git a/tests/ImageSharp.Tests/Colorspaces/Icc/IccProfileConverterTests.cs b/tests/ImageSharp.Tests/Colorspaces/Icc/IccProfileConverterTests.cs
new file mode 100644
index 0000000000..272219a5cf
--- /dev/null
+++ b/tests/ImageSharp.Tests/Colorspaces/Icc/IccProfileConverterTests.cs
@@ -0,0 +1,58 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.Icc;
+using SixLabors.ImageSharp.Formats.Png;
+using SixLabors.ImageSharp.Metadata.Profiles.Icc;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Tests.Colorspaces.Icc;
+public class IccProfileConverterTests
+{
+ private static PngEncoder Encoder = new PngEncoder();
+
+ [Theory]
+ [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)] ConverterBase says this is invalid.
+ // [WithFile(TestImages.Jpeg.ICC.ProPhoto, PixelTypes.Rgb24)] ConverterBase says this is invalid.
+ 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, Encoder);
+
+ TPixel actual = image[0, 0];
+
+ Assert.Equal(expected, actual);
+ }
+
+ // TODO: This fails as the base calculator says sRGB is invalid.
+ [Theory]
+ [WithFile(TestImages.Jpeg.ICC.AdobeRgb, PixelTypes.Rgb24)]
+ public void CanConvertTosRGB(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
+ {
+ using Image image = provider.GetImage();
+ IccProfile profile = image.Metadata.IccProfile;
+
+ string file = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Jpeg.ICC.SRgb);
+ IImageInfo i = Image.Identify(file);
+ IccProfile sRGBProfile = i.Metadata.IccProfile;
+
+ IccProfileConverter.Convert(image, profile, sRGBProfile);
+
+ // TODO: Compare.
+ image.DebugSave(provider, Encoder);
+ }
+}
diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs
index 638f7dfb71..39e4feab32 100644
--- a/tests/ImageSharp.Tests/TestImages.cs
+++ b/tests/ImageSharp.Tests/TestImages.cs
@@ -169,6 +169,16 @@ public static class TestImages
public static class Jpeg
{
+ public static class ICC
+ {
+ public const string SRgb = "Jpg/icc-profiles/Momiji-sRGB-yes.jpg";
+ public const string AdobeRgb = "Jpg/icc-profiles/Momiji-AdobeRGB-yes.jpg";
+ public const string ColorMatch = "Jpg/icc-profiles/Momiji-ColorMatch-yes.jpg";
+ public const string ProPhoto = "Jpg/icc-profiles/Momiji-ProPhoto-yes.jpg";
+ public const string WideRGB = "Jpg/icc-profiles/Momiji-WideRGB-yes.jpg";
+ public const string AppleRGB = "Jpg/icc-profiles/Momiji-AppleRGB-yes.jpg";
+ }
+
public static class Progressive
{
public const string Fb = "Jpg/progressive/fb.jpg";
diff --git a/tests/Images/Input/Jpg/icc-profiles/Momiji-AdobeRGB-yes.jpg b/tests/Images/Input/Jpg/icc-profiles/Momiji-AdobeRGB-yes.jpg
new file mode 100644
index 0000000000..077ee22beb
--- /dev/null
+++ b/tests/Images/Input/Jpg/icc-profiles/Momiji-AdobeRGB-yes.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cb8bdcc137efa3e28db69e48612230b3a9fec17267de9ce29757d9bacc181d28
+size 42001
diff --git a/tests/Images/Input/Jpg/icc-profiles/Momiji-AppleRGB-yes.jpg b/tests/Images/Input/Jpg/icc-profiles/Momiji-AppleRGB-yes.jpg
new file mode 100644
index 0000000000..188faa2bdd
--- /dev/null
+++ b/tests/Images/Input/Jpg/icc-profiles/Momiji-AppleRGB-yes.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7129f5485e997b75cff143021522cc8ab94e2c3c1912689bc765ce2b3b937441
+size 72150
diff --git a/tests/Images/Input/Jpg/icc-profiles/Momiji-ColorMatch-yes.jpg b/tests/Images/Input/Jpg/icc-profiles/Momiji-ColorMatch-yes.jpg
new file mode 100644
index 0000000000..befc3d1170
--- /dev/null
+++ b/tests/Images/Input/Jpg/icc-profiles/Momiji-ColorMatch-yes.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fe7fa60a53893836200c62f34492c7a0c931692dd073dffa4afc49fe3826e433
+size 44446
diff --git a/tests/Images/Input/Jpg/icc-profiles/Momiji-ProPhoto-yes.jpg b/tests/Images/Input/Jpg/icc-profiles/Momiji-ProPhoto-yes.jpg
new file mode 100644
index 0000000000..645ad2869a
--- /dev/null
+++ b/tests/Images/Input/Jpg/icc-profiles/Momiji-ProPhoto-yes.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bb686b44e3253143a32db890823f63c79026c9ac9badc4ad9de21f6cb2fa2f2a
+size 40703
diff --git a/tests/Images/Input/Jpg/icc-profiles/Momiji-WideRGB-yes.jpg b/tests/Images/Input/Jpg/icc-profiles/Momiji-WideRGB-yes.jpg
new file mode 100644
index 0000000000..57727aaa29
--- /dev/null
+++ b/tests/Images/Input/Jpg/icc-profiles/Momiji-WideRGB-yes.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:928b854a9629d1532d37095c4744da6bc2fc986f878a76aea373f69490f4b586
+size 40505
diff --git a/tests/Images/Input/Jpg/icc-profiles/Momiji-sRGB-yes.jpg b/tests/Images/Input/Jpg/icc-profiles/Momiji-sRGB-yes.jpg
new file mode 100644
index 0000000000..4b7b612be0
--- /dev/null
+++ b/tests/Images/Input/Jpg/icc-profiles/Momiji-sRGB-yes.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:faf67048c2b7bd3fb5fa9b69bd53943d63a216ef371c5dc9d062ac443c9d2d34
+size 47434