Browse Source

Cleanup and add conversion tests

pull/1567/head
James Jackson-South 4 years ago
parent
commit
daf366b374
  1. 4
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/MatrixCalculator.cs
  2. 61
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/ParametricCurveCalculator.cs
  3. 4
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccConverterBase.Checks.cs
  4. 5
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccConverterbase.cs
  5. 43
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccProfileConverter.cs
  6. 2
      src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs
  7. 6
      src/ImageSharp/Metadata/Profiles/ICC/IccTagDataEntry.cs
  8. 58
      tests/ImageSharp.Tests/Colorspaces/Icc/IccProfileConverterTests.cs
  9. 10
      tests/ImageSharp.Tests/TestImages.cs
  10. 3
      tests/Images/Input/Jpg/icc-profiles/Momiji-AdobeRGB-yes.jpg
  11. 3
      tests/Images/Input/Jpg/icc-profiles/Momiji-AppleRGB-yes.jpg
  12. 3
      tests/Images/Input/Jpg/icc-profiles/Momiji-ColorMatch-yes.jpg
  13. 3
      tests/Images/Input/Jpg/icc-profiles/Momiji-ProPhoto-yes.jpg
  14. 3
      tests/Images/Input/Jpg/icc-profiles/Momiji-WideRGB-yes.jpg
  15. 3
      tests/Images/Input/Jpg/icc-profiles/Momiji-sRGB-yes.jpg

4
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);
}
}

61
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)

4
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;

5
src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccConverterbase.cs

@ -29,8 +29,5 @@ internal abstract partial class IccConverterBase
/// <param name="value">The value to convert</param>
/// <returns>The converted value</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 Calculate(Vector4 value)
{
return this.calculator.Calculate(value);
}
public Vector4 Calculate(Vector4 value) => this.calculator.Calculate(value);
}

43
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<TPixel>(Image<TPixel> image, IccProfile inputIccProfile, IccProfile outputIccProfile)
where TPixel : unmanaged, IPixel<TPixel>
{
IccDataToPcsConverter converterDataToPcs = new(inputIccProfile);
IccPcsToDataConverter converterPcsToData = new(outputIccProfile);
Configuration configuration = image.GetConfiguration();
image.ProcessPixelRows(accessor =>
{
using IMemoryOwner<Vector4> vectors = configuration.MemoryAllocator.Allocate<Vector4>(accessor.Width);
Span<Vector4> vectorsSpan = vectors.GetSpan();
for (int y = 0; y < accessor.Height; y++)
{
Span<TPixel> row = accessor.GetRowSpan(y);
PixelOperations<TPixel>.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<TPixel>.Instance.FromVector4Destructive(configuration, vectorsSpan, row);
}
});
image.Metadata.IccProfile = outputIccProfile;
}
}

2
src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs

@ -174,7 +174,6 @@ public sealed class IccProfile : IDeepCloneable<IccProfile>
return copy;
}
IccWriter writer = new();
return IccWriter.Write(this);
}
@ -191,7 +190,6 @@ public sealed class IccProfile : IDeepCloneable<IccProfile>
return;
}
IccReader reader = new();
this.header = IccReader.ReadHeader(this.data);
}

6
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<IccTagDataEntry>
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is IccTagDataEntry entry && this.Equals(entry);
}
=> obj is IccTagDataEntry entry && this.Equals(entry);
/// <inheritdoc/>
public virtual bool Equals(IccTagDataEntry other)

58
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<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> 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<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> 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);
}
}

10
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";

3
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

3
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

3
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

3
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

3
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

3
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
Loading…
Cancel
Save