Browse Source

Copy original PR changes.

pull/1567/head
James Jackson-South 5 years ago
parent
commit
16179b3211
  1. 105
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/ClutCalculator.cs
  2. 54
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/ColorTrcCalculator.cs
  3. 15
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/CurveCalculator.CalculationType.cs
  4. 56
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/CurveCalculator.cs
  5. 25
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/GrayTrcCalculator.cs
  6. 18
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/ISingleCalculator.cs
  7. 20
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/IVector4Calculator.cs
  8. 19
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/LutABCalculator.CalculationType.cs
  9. 135
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/LutABCalculator.cs
  10. 73
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/LutCalculator.cs
  11. 75
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/LutEntryCalculator.cs
  12. 27
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/MatrixCalculator.cs
  13. 168
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/ParametricCurveCalculator.cs
  14. 47
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/TrcCalculator.cs
  15. 173
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccConverterBase.Checks.cs
  16. 67
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccConverterBase.ConversionMethod.cs
  17. 150
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccConverterbase.Conversions.cs
  18. 37
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccConverterbase.cs
  19. 22
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccDataToDataConverter.cs
  20. 22
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccDataToPcsConverter.cs
  21. 22
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccPcsToDataConverter.cs
  22. 22
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccPcsToPcsConverter.cs
  23. 27
      src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs
  24. 2
      src/ImageSharp/Metadata/Profiles/ICC/Enums/IccFormulaCurveType.cs
  25. 4
      src/ImageSharp/Metadata/Profiles/ICC/Exceptions/InvalidIccProfileException.cs
  26. 27
      tests/ImageSharp.Tests/Colorspaces/Icc/Calculators/ClutCalculatorTests.cs
  27. 26
      tests/ImageSharp.Tests/Colorspaces/Icc/Calculators/CurveCalculatorTests.cs
  28. 38
      tests/ImageSharp.Tests/Colorspaces/Icc/Calculators/LutABCalculatorTests.cs
  29. 25
      tests/ImageSharp.Tests/Colorspaces/Icc/Calculators/LutCalculatorTests.cs
  30. 38
      tests/ImageSharp.Tests/Colorspaces/Icc/Calculators/LutEntryCalculatorTests.cs
  31. 26
      tests/ImageSharp.Tests/Colorspaces/Icc/Calculators/MatrixCalculatorTests.cs
  32. 26
      tests/ImageSharp.Tests/Colorspaces/Icc/Calculators/ParametricCurveCalculatorTests.cs
  33. 27
      tests/ImageSharp.Tests/Colorspaces/Icc/Calculators/TrcCalculatorTests.cs
  34. 163
      tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionData.Clut.cs
  35. 30
      tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionData.Lut.cs
  36. 44
      tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionData.LutAB.cs
  37. 65
      tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionData.LutEntry.cs
  38. 45
      tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionData.Matrix.cs
  39. 76
      tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionData.MultiProcessElement.cs
  40. 77
      tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionData.Trc.cs

105
src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/ClutCalculator.cs

@ -0,0 +1,105 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Icc
{
internal class ClutCalculator : IVector4Calculator
{
private int inputCount;
private int outputCount;
private float[][] lut;
private byte[] gridPointCount;
private int[] indexFactor;
private int nodeCount;
public ClutCalculator(IccClut clut)
{
Guard.NotNull(clut, nameof(clut));
this.inputCount = clut.InputChannelCount;
this.outputCount = clut.OutputChannelCount;
this.lut = clut.Values;
this.gridPointCount = clut.GridPointCount;
this.indexFactor = this.CalculateIndexFactor(clut.InputChannelCount, clut.GridPointCount);
this.nodeCount = (int)Math.Pow(2, clut.InputChannelCount);
}
public unsafe Vector4 Calculate(Vector4 value)
{
value = Vector4.Clamp(value, Vector4.Zero, Vector4.One);
Vector4 result = default;
this.Interpolate((float*)&value, this.inputCount, (float*)&result, this.outputCount);
return result;
}
private int[] CalculateIndexFactor(int inputCount, byte[] gridPointCount)
{
int[] factors = new int[inputCount];
int gpc = 1;
for (int j = inputCount - 1; j >= 0; j--)
{
factors[j] = gpc * (gridPointCount[j] - 1);
gpc *= gridPointCount[j];
}
return factors;
}
private unsafe void Interpolate(float* values, int valueLength, float* result, int resultLength)
{
float[][] nodes = new float[this.nodeCount][];
for (int i = 0; i < nodes.Length; i++)
{
int index = 0;
for (int j = 0; j < valueLength; j++)
{
float fraction = 1f / (this.gridPointCount[j] - 1);
int position = (int)(values[j] / fraction) + ((i >> j) & 1);
index += (int)((this.indexFactor[j] * (position * fraction)) + 0.5f);
}
nodes[i] = this.lut[index];
}
Span<float> factors = stackalloc float[this.nodeCount];
for (int i = 0; i < factors.Length; i++)
{
float factor = 1;
for (int j = 0; j < valueLength; j++)
{
float fraction = 1f / (this.gridPointCount[j] - 1);
int position = (int)(values[j] / fraction);
float low = position * fraction;
float high = (position + 1) * fraction;
float percentage = (high - values[j]) / (high - low);
if (((i >> j) & 1) == 1)
{
factor *= percentage;
}
else
{
factor *= 1 - percentage;
}
}
factors[i] = factor;
}
for (int i = 0; i < resultLength; i++)
{
for (int j = 0; j < nodes.Length; j++)
{
result[i] += nodes[j][i] * factors[j];
}
}
}
}
}

54
src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/ColorTrcCalculator.cs

@ -0,0 +1,54 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Icc
{
internal class ColorTrcCalculator : IVector4Calculator
{
private TrcCalculator curveCalculator;
private Matrix4x4 matrix;
private bool toPcs;
public ColorTrcCalculator(
IccXyzTagDataEntry redMatrixColumn,
IccXyzTagDataEntry greenMatrixColumn,
IccXyzTagDataEntry blueMatrixColumn,
IccTagDataEntry redTrc,
IccTagDataEntry greenTrc,
IccTagDataEntry blueTrc,
bool toPcs)
{
this.toPcs = toPcs;
this.curveCalculator = new TrcCalculator(new IccTagDataEntry[] { redTrc, greenTrc, blueTrc }, !toPcs);
Vector3 mr = redMatrixColumn.Data[0];
Vector3 mg = greenMatrixColumn.Data[0];
Vector3 mb = blueMatrixColumn.Data[0];
this.matrix = new Matrix4x4(mr.X, mr.Y, mr.Z, 0, mg.X, mg.Y, mg.Z, 0, mb.X, mb.Y, mb.Z, 0, 0, 0, 0, 1);
if (!toPcs)
{
Matrix4x4.Invert(this.matrix, out this.matrix);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 Calculate(Vector4 value)
{
if (this.toPcs)
{
value = this.curveCalculator.Calculate(value);
return Vector4.Transform(value, this.matrix);
}
else
{
value = Vector4.Transform(value, this.matrix);
return this.curveCalculator.Calculate(value);
}
}
}
}

15
src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/CurveCalculator.CalculationType.cs

@ -0,0 +1,15 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Icc
{
internal partial class CurveCalculator
{
private enum CalculationType
{
Identity,
Gamma,
Lut,
}
}
}

56
src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/CurveCalculator.cs

@ -0,0 +1,56 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Icc
{
internal partial class CurveCalculator : ISingleCalculator
{
private LutCalculator lutCalculator;
private float gamma;
private CalculationType type;
public CurveCalculator(IccCurveTagDataEntry entry, bool inverted)
{
if (entry.IsIdentityResponse)
{
this.type = CalculationType.Identity;
}
else if (entry.IsGamma)
{
this.gamma = entry.Gamma;
if (inverted)
{
this.gamma = 1f / this.gamma;
}
this.type = CalculationType.Gamma;
}
else
{
this.lutCalculator = new LutCalculator(entry.CurveData, inverted);
this.type = CalculationType.Lut;
}
}
public float Calculate(float value)
{
switch (this.type)
{
case CalculationType.Identity:
return value;
case CalculationType.Gamma:
return MathF.Pow(value, this.gamma);
case CalculationType.Lut:
return this.lutCalculator.Calculate(value);
default:
throw new InvalidOperationException("Invalid calculation type");
}
}
}
}

25
src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/GrayTrcCalculator.cs

@ -0,0 +1,25 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Icc
{
internal class GrayTrcCalculator : IVector4Calculator
{
private TrcCalculator calculator;
public GrayTrcCalculator(IccTagDataEntry grayTrc, bool toPcs)
{
this.calculator = new TrcCalculator(new IccTagDataEntry[] { grayTrc }, !toPcs);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 Calculate(Vector4 value)
{
return this.calculator.Calculate(value);
}
}
}

18
src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/ISingleCalculator.cs

@ -0,0 +1,18 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Icc
{
/// <summary>
/// Represents an ICC calculator with a single floating point value and result
/// </summary>
internal interface ISingleCalculator
{
/// <summary>
/// Calculates a result from the given value
/// </summary>
/// <param name="value">The input value</param>
/// <returns>The calculated result</returns>
float Calculate(float value);
}
}

20
src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/IVector4Calculator.cs

@ -0,0 +1,20 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Icc
{
/// <summary>
/// Represents an ICC calculator with <see cref="Vector4"/> values and results
/// </summary>
internal interface IVector4Calculator
{
/// <summary>
/// Calculates a result from the given values
/// </summary>
/// <param name="value">The input values</param>
/// <returns>The calculated result</returns>
Vector4 Calculate(Vector4 value);
}
}

19
src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/LutABCalculator.CalculationType.cs

@ -0,0 +1,19 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Icc
{
internal partial class LutABCalculator
{
private enum CalculationType
{
AtoB = 1 << 3,
BtoA = 1 << 4,
SingleCurve = 1,
CurveMatrix = 2,
CurveClut = 3,
Full = 4,
}
}
}

135
src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/LutABCalculator.cs

@ -0,0 +1,135 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Icc
{
internal partial class LutABCalculator : IVector4Calculator
{
private CalculationType type;
private TrcCalculator curveACalculator;
private TrcCalculator curveBCalculator;
private TrcCalculator curveMCalculator;
private MatrixCalculator matrixCalculator;
private ClutCalculator clutCalculator;
public LutABCalculator(IccLutAToBTagDataEntry entry)
{
Guard.NotNull(entry, nameof(entry));
this.Init(entry.CurveA, entry.CurveB, entry.CurveM, entry.Matrix3x1, entry.Matrix3x3, entry.ClutValues);
this.type |= CalculationType.AtoB;
}
public LutABCalculator(IccLutBToATagDataEntry entry)
{
Guard.NotNull(entry, nameof(entry));
this.Init(entry.CurveA, entry.CurveB, entry.CurveM, entry.Matrix3x1, entry.Matrix3x3, entry.ClutValues);
this.type |= CalculationType.BtoA;
}
public Vector4 Calculate(Vector4 value)
{
switch (this.type)
{
case CalculationType.Full | CalculationType.AtoB:
value = this.curveACalculator.Calculate(value);
value = this.clutCalculator.Calculate(value);
value = this.curveMCalculator.Calculate(value);
value = this.matrixCalculator.Calculate(value);
return this.curveBCalculator.Calculate(value);
case CalculationType.Full | CalculationType.BtoA:
value = this.curveBCalculator.Calculate(value);
value = this.matrixCalculator.Calculate(value);
value = this.curveMCalculator.Calculate(value);
value = this.clutCalculator.Calculate(value);
return this.curveACalculator.Calculate(value);
case CalculationType.CurveClut | CalculationType.AtoB:
value = this.curveACalculator.Calculate(value);
value = this.clutCalculator.Calculate(value);
return this.curveBCalculator.Calculate(value);
case CalculationType.CurveClut | CalculationType.BtoA:
value = this.curveBCalculator.Calculate(value);
value = this.clutCalculator.Calculate(value);
return this.curveACalculator.Calculate(value);
case CalculationType.CurveMatrix | CalculationType.AtoB:
value = this.curveMCalculator.Calculate(value);
value = this.matrixCalculator.Calculate(value);
return this.curveBCalculator.Calculate(value);
case CalculationType.CurveMatrix | CalculationType.BtoA:
value = this.curveBCalculator.Calculate(value);
value = this.matrixCalculator.Calculate(value);
return this.curveMCalculator.Calculate(value);
case CalculationType.SingleCurve | CalculationType.AtoB:
case CalculationType.SingleCurve | CalculationType.BtoA:
return this.curveBCalculator.Calculate(value);
default:
throw new InvalidOperationException("Invalid calculation type");
}
}
private void Init(IccTagDataEntry[] curveA, IccTagDataEntry[] curveB, IccTagDataEntry[] curveM, Vector3? matrix3x1, Matrix4x4? matrix3x3, IccClut clut)
{
bool hasACurve = curveA != null;
bool hasBCurve = curveB != null;
bool hasMCurve = curveM != null;
bool hasMatrix = matrix3x1 != null && matrix3x3 != null;
bool hasClut = clut != null;
if (hasBCurve && hasMatrix && hasMCurve && hasClut && hasACurve)
{
this.type = CalculationType.Full;
}
else if (hasBCurve && hasClut && hasACurve)
{
this.type = CalculationType.CurveClut;
}
else if (hasBCurve && hasMatrix && hasMCurve)
{
this.type = CalculationType.CurveMatrix;
}
else if (hasBCurve)
{
this.type = CalculationType.SingleCurve;
}
else
{
throw new InvalidIccProfileException("AToB or BToA tag has an invalid configuration");
}
if (hasACurve)
{
this.curveACalculator = new TrcCalculator(curveA, false);
}
if (hasBCurve)
{
this.curveBCalculator = new TrcCalculator(curveB, false);
}
if (hasMCurve)
{
this.curveMCalculator = new TrcCalculator(curveM, false);
}
if (hasMatrix)
{
this.matrixCalculator = new MatrixCalculator(matrix3x3.Value, matrix3x1.Value);
}
if (hasClut)
{
this.clutCalculator = new ClutCalculator(clut);
}
}
}
}

73
src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/LutCalculator.cs

@ -0,0 +1,73 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Icc
{
internal class LutCalculator : ISingleCalculator
{
private float[] lut;
private bool inverse;
public LutCalculator(float[] lut, bool inverse)
{
Guard.NotNull(lut, nameof(lut));
this.lut = lut;
this.inverse = inverse;
}
public float Calculate(float value)
{
if (this.inverse)
{
return this.LookupInverse(value);
}
else
{
return this.Lookup(value);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private float Lookup(float value)
{
float factor = value * (this.lut.Length - 1);
int index = (int)factor;
float low = this.lut[index];
float high = this.lut[index + 1];
return low + ((high - low) * (factor - index));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private float LookupInverse(float value)
{
int index = Array.BinarySearch(this.lut, value);
if (index >= 0)
{
return index / (float)(this.lut.Length - 1);
}
index = ~index;
if (index == 0)
{
return 0;
}
else if (index == this.lut.Length)
{
return 1;
}
float high = this.lut[index];
float low = this.lut[index - 1];
float valuePercent = (value - low) / (high - low);
float lutRange = 1 / (float)(this.lut.Length - 1);
float lutLow = (index - 1) / (float)(this.lut.Length - 1);
return lutLow + (valuePercent * lutRange);
}
}
}

75
src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/LutEntryCalculator.cs

@ -0,0 +1,75 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Icc
{
internal class LutEntryCalculator : IVector4Calculator
{
private LutCalculator[] inputCurve;
private LutCalculator[] outputCurve;
private ClutCalculator clutCalculator;
private Matrix4x4 matrix;
private bool doTransform;
public LutEntryCalculator(IccLut8TagDataEntry lut)
{
Guard.NotNull(lut, nameof(lut));
this.Init(lut.InputValues, lut.OutputValues, lut.ClutValues, lut.Matrix);
}
public LutEntryCalculator(IccLut16TagDataEntry lut)
{
Guard.NotNull(lut, nameof(lut));
this.Init(lut.InputValues, lut.OutputValues, lut.ClutValues, lut.Matrix);
}
public Vector4 Calculate(Vector4 value)
{
if (this.doTransform)
{
value = Vector4.Transform(value, this.matrix);
}
value = this.CalculateLut(this.inputCurve, value);
value = this.clutCalculator.Calculate(value);
return this.CalculateLut(this.outputCurve, value);
}
private unsafe Vector4 CalculateLut(LutCalculator[] lut, Vector4 value)
{
value = Vector4.Clamp(value, Vector4.Zero, Vector4.One);
float* valuePointer = (float*)&value;
for (int i = 0; i < lut.Length; i++)
{
valuePointer[i] = lut[i].Calculate(valuePointer[i]);
}
return value;
}
private void Init(IccLut[] inputCurve, IccLut[] outputCurve, IccClut clut, Matrix4x4 matrix)
{
this.inputCurve = this.InitLut(inputCurve);
this.outputCurve = this.InitLut(outputCurve);
this.clutCalculator = new ClutCalculator(clut);
this.matrix = matrix;
this.doTransform = !matrix.IsIdentity && inputCurve.Length == 3;
}
private LutCalculator[] InitLut(IccLut[] curves)
{
var calculators = new LutCalculator[curves.Length];
for (int i = 0; i < curves.Length; i++)
{
calculators[i] = new LutCalculator(curves[i].Values, false);
}
return calculators;
}
}
}

27
src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/MatrixCalculator.cs

@ -0,0 +1,27 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Icc
{
internal class MatrixCalculator : IVector4Calculator
{
private Matrix4x4 matrix2D;
private Vector4 matrix1D;
public MatrixCalculator(Matrix4x4 matrix3x3, Vector3 matrix3x1)
{
this.matrix2D = matrix3x3;
this.matrix1D = new Vector4(matrix3x1, 0);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 Calculate(Vector4 value)
{
var transformed = Vector4.Transform(value, this.matrix2D);
return Vector4.Add(this.matrix1D, transformed);
}
}
}

168
src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/ParametricCurveCalculator.cs

@ -0,0 +1,168 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Icc
{
internal class ParametricCurveCalculator : ISingleCalculator
{
private IccParametricCurve curve;
private IccParametricCurveType type;
private const IccParametricCurveType InvertedFlag = (IccParametricCurveType)(1 << 3);
public ParametricCurveCalculator(IccParametricCurveTagDataEntry entry, bool inverted)
{
Guard.NotNull(entry, nameof(entry));
this.curve = entry.Curve;
this.type = entry.Curve.Type;
if (inverted)
{
this.type |= InvertedFlag;
}
}
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");
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private float CalculateGamma(float value)
{
return MathF.Pow(value, this.curve.G);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private float CalculateCie122(float value)
{
if (value >= -this.curve.B / this.curve.A)
{
return MathF.Pow((this.curve.A * value) + this.curve.B, this.curve.G);
}
else
{
return 0;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private float CalculateIec61966(float value)
{
if (value >= -this.curve.B / this.curve.A)
{
return MathF.Pow((this.curve.A * value) + this.curve.B, this.curve.G) + this.curve.C;
}
else
{
return this.curve.C;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private float CalculateSRgb(float value)
{
if (value >= this.curve.D)
{
return MathF.Pow((this.curve.A * value) + this.curve.B, this.curve.G);
}
else
{
return this.curve.C * value;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private float CalculateType5(float value)
{
if (value >= this.curve.D)
{
return MathF.Pow((this.curve.A * value) + this.curve.B, this.curve.G) + this.curve.E;
}
else
{
return (this.curve.C * value) + this.curve.F;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private float CalculateInvertedGamma(float value)
{
return 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;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private float CalculateInvertedIec61966(float value)
{
if (value >= this.curve.C)
{
return (MathF.Pow(value - this.curve.C, 1 / this.curve.G) - this.curve.B) / this.curve.A;
}
else
{
return -this.curve.B / this.curve.A;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private float CalculateInvertedSRgb(float value)
{
if (value >= MathF.Pow((this.curve.A * this.curve.D) + this.curve.B, this.curve.G))
{
return (MathF.Pow(value, 1 / this.curve.G) - this.curve.B) / this.curve.A;
}
else
{
return value / this.curve.C;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private float CalculateInvertedType5(float value)
{
if (value >= (this.curve.C * this.curve.D) + this.curve.F)
{
return (MathF.Pow(value - this.curve.E, 1 / this.curve.G) - this.curve.B) / this.curve.A;
}
else
{
return (value - this.curve.F) / this.curve.C;
}
}
}
}

47
src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/Calculators/TrcCalculator.cs

@ -0,0 +1,47 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Icc
{
internal class TrcCalculator : IVector4Calculator
{
private ISingleCalculator[] calculators;
public TrcCalculator(IccTagDataEntry[] entries, bool inverted)
{
Guard.NotNull(entries, nameof(entries));
this.calculators = new ISingleCalculator[entries.Length];
for (int i = 0; i < entries.Length; i++)
{
switch (entries[i])
{
case IccCurveTagDataEntry curve:
this.calculators[i] = new CurveCalculator(curve, inverted);
break;
case IccParametricCurveTagDataEntry parametricCurve:
this.calculators[i] = new ParametricCurveCalculator(parametricCurve, inverted);
break;
default:
throw new InvalidIccProfileException("Invalid Entry.");
}
}
}
public unsafe Vector4 Calculate(Vector4 value)
{
float* valuePointer = (float*)&value;
for (int i = 0; i < this.calculators.Length; i++)
{
valuePointer[i] = this.calculators[i].Calculate(valuePointer[i]);
}
return value;
}
}
}

173
src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccConverterBase.Checks.cs

@ -0,0 +1,173 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Linq;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Icc
{
/// <summary>
/// Color converter for ICC profiles
/// </summary>
internal abstract partial class IccConverterBase
{
private ConversionMethod GetConversionMethod(IccProfile profile, IccRenderingIntent renderingIntent)
{
switch (profile.Header.Class)
{
case IccProfileClass.InputDevice:
case IccProfileClass.DisplayDevice:
case IccProfileClass.OutputDevice:
case IccProfileClass.ColorSpace:
return this.CheckMethod1(profile, renderingIntent);
case IccProfileClass.DeviceLink:
case IccProfileClass.Abstract:
return this.CheckMethod2(profile);
default:
return ConversionMethod.Invalid;
}
}
private ConversionMethod CheckMethod1(IccProfile profile, IccRenderingIntent renderingIntent)
{
ConversionMethod method = ConversionMethod.Invalid;
method = this.CheckMethodD(profile, renderingIntent);
if (method != ConversionMethod.Invalid)
{
return method;
}
method = this.CheckMethodA(profile, renderingIntent);
if (method != ConversionMethod.Invalid)
{
return method;
}
method = this.CheckMethodA0(profile);
if (method != ConversionMethod.Invalid)
{
return method;
}
method = this.CheckMethodTrc(profile);
if (method != ConversionMethod.Invalid)
{
return method;
}
return ConversionMethod.Invalid;
}
private ConversionMethod CheckMethodD(IccProfile profile, IccRenderingIntent renderingIntent)
{
if ((this.HasTag(profile, IccProfileTag.DToB0) || this.HasTag(profile, IccProfileTag.BToD0))
&& renderingIntent == IccRenderingIntent.Perceptual)
{
return ConversionMethod.D0;
}
if ((this.HasTag(profile, IccProfileTag.DToB1) || this.HasTag(profile, IccProfileTag.BToD1))
&& renderingIntent == IccRenderingIntent.MediaRelativeColorimetric)
{
return ConversionMethod.D1;
}
if ((this.HasTag(profile, IccProfileTag.DToB2) || this.HasTag(profile, IccProfileTag.BToD2))
&& renderingIntent == IccRenderingIntent.Saturation)
{
return ConversionMethod.D2;
}
if ((this.HasTag(profile, IccProfileTag.DToB3) || this.HasTag(profile, IccProfileTag.BToD3))
&& renderingIntent == IccRenderingIntent.AbsoluteColorimetric)
{
return ConversionMethod.D3;
}
return ConversionMethod.Invalid;
}
private ConversionMethod CheckMethodA(IccProfile profile, IccRenderingIntent renderingIntent)
{
if ((this.HasTag(profile, IccProfileTag.AToB0) || this.HasTag(profile, IccProfileTag.BToA0))
&& renderingIntent == IccRenderingIntent.Perceptual)
{
return ConversionMethod.A0;
}
if ((this.HasTag(profile, IccProfileTag.AToB1) || this.HasTag(profile, IccProfileTag.BToA1))
&& renderingIntent == IccRenderingIntent.MediaRelativeColorimetric)
{
return ConversionMethod.A1;
}
if ((this.HasTag(profile, IccProfileTag.AToB2) || this.HasTag(profile, IccProfileTag.BToA2))
&& renderingIntent == IccRenderingIntent.Saturation)
{
return ConversionMethod.A2;
}
return ConversionMethod.Invalid;
}
private ConversionMethod CheckMethodA0(IccProfile profile)
{
bool valid = this.HasTag(profile, IccProfileTag.AToB0) || this.HasTag(profile, IccProfileTag.BToA0);
return valid ? ConversionMethod.A0 : ConversionMethod.Invalid;
}
private ConversionMethod CheckMethodTrc(IccProfile profile)
{
if (this.HasTag(profile, IccProfileTag.RedMatrixColumn)
&& this.HasTag(profile, IccProfileTag.GreenMatrixColumn)
&& this.HasTag(profile, IccProfileTag.BlueMatrixColumn)
&& this.HasTag(profile, IccProfileTag.RedTrc)
&& this.HasTag(profile, IccProfileTag.GreenTrc)
&& this.HasTag(profile, IccProfileTag.BlueTrc))
{
return ConversionMethod.ColorTrc;
}
if (this.HasTag(profile, IccProfileTag.GrayTrc))
{
return ConversionMethod.GrayTrc;
}
return ConversionMethod.Invalid;
}
private ConversionMethod CheckMethod2(IccProfile profile)
{
if (this.HasTag(profile, IccProfileTag.DToB0) || this.HasTag(profile, IccProfileTag.BToD0))
{
return ConversionMethod.D0;
}
if (this.HasTag(profile, IccProfileTag.AToB0) || this.HasTag(profile, IccProfileTag.AToB0))
{
return ConversionMethod.A0;
}
return ConversionMethod.Invalid;
}
private bool HasTag(IccProfile profile, IccProfileTag tag)
{
return profile.Entries.Any(t => t.TagSignature == tag);
}
private IccTagDataEntry GetTag(IccProfile profile, IccProfileTag tag)
{
return profile.Entries.FirstOrDefault(t => t.TagSignature == tag);
}
private T GetTag<T>(IccProfile profile, IccProfileTag tag)
where T : IccTagDataEntry
{
return profile.Entries.OfType<T>().FirstOrDefault(t => t.TagSignature == tag);
}
}
}

67
src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccConverterBase.ConversionMethod.cs

@ -0,0 +1,67 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Icc
{
/// <summary>
/// Color converter for ICC profiles
/// </summary>
internal abstract partial class IccConverterBase
{
/// <summary>
/// Conversion methods with ICC profiles
/// </summary>
private enum ConversionMethod
{
/// <summary>
/// Conversion using anything but Multi Process Elements with perceptual rendering intent
/// </summary>
A0,
/// <summary>
/// Conversion using anything but Multi Process Elements with relative colorimetric rendering intent
/// </summary>
A1,
/// <summary>
/// Conversion using anything but Multi Process Elements with saturation rendering intent
/// </summary>
A2,
/// <summary>
/// Conversion using Multi Process Elements with perceptual rendering intent
/// </summary>
D0,
/// <summary>
/// Conversion using Multi Process Elements with relative colorimetric rendering intent
/// </summary>
D1,
/// <summary>
/// Conversion using Multi Process Elements with saturation rendering intent
/// </summary>
D2,
/// <summary>
/// Conversion using Multi Process Elements with absolute colorimetric rendering intent
/// </summary>
D3,
/// <summary>
/// Conversion of more than one channel using tone reproduction curves
/// </summary>
ColorTrc,
/// <summary>
/// Conversion of exactly one channel using a tone reproduction curve
/// </summary>
GrayTrc,
/// <summary>
/// No valid conversion method available or found
/// </summary>
Invalid,
}
}
}

150
src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccConverterbase.Conversions.cs

@ -0,0 +1,150 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Icc;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Icc
{
/// <summary>
/// Color converter for ICC profiles
/// </summary>
internal abstract partial class IccConverterBase
{
private IVector4Calculator calculator;
/// <summary>
/// Checks the profile for available conversion methods and gathers all the informations necessary for it
/// </summary>
/// <param name="profile">The profile to use for the conversion</param>
/// <param name="toPcs">True if the conversion is to the Profile Connection Space</param>
/// <param name="renderingIntent">The wanted rendering intent. Can be ignored if not available</param>
protected void Init(IccProfile profile, bool toPcs, IccRenderingIntent renderingIntent)
{
ConversionMethod method = this.GetConversionMethod(profile, renderingIntent);
switch (method)
{
case ConversionMethod.D0:
this.calculator = toPcs ?
this.InitD(profile, IccProfileTag.DToB0) :
this.InitD(profile, IccProfileTag.BToD0);
break;
case ConversionMethod.D1:
this.calculator = toPcs ?
this.InitD(profile, IccProfileTag.DToB1) :
this.InitD(profile, IccProfileTag.BToD1);
break;
case ConversionMethod.D2:
this.calculator = toPcs ?
this.InitD(profile, IccProfileTag.DToB2) :
this.InitD(profile, IccProfileTag.BToD2);
break;
case ConversionMethod.D3:
this.calculator = toPcs ?
this.InitD(profile, IccProfileTag.DToB3) :
this.InitD(profile, IccProfileTag.BToD3);
break;
case ConversionMethod.A0:
this.calculator = toPcs ?
this.InitA(profile, IccProfileTag.AToB0) :
this.InitA(profile, IccProfileTag.BToA0);
break;
case ConversionMethod.A1:
this.calculator = toPcs ?
this.InitA(profile, IccProfileTag.AToB1) :
this.InitA(profile, IccProfileTag.BToA1);
break;
case ConversionMethod.A2:
this.calculator = toPcs ?
this.InitA(profile, IccProfileTag.AToB2) :
this.InitA(profile, IccProfileTag.BToA2);
break;
case ConversionMethod.ColorTrc:
this.calculator = this.InitColorTrc(profile, toPcs);
break;
case ConversionMethod.GrayTrc:
this.calculator = this.InitGrayTrc(profile, toPcs);
break;
case ConversionMethod.Invalid:
default:
throw new InvalidIccProfileException("Invalid conversion method.");
}
}
private IVector4Calculator InitA(IccProfile profile, IccProfileTag tag)
{
IccTagDataEntry entry = this.GetTag(profile, tag);
switch (entry)
{
case IccLut8TagDataEntry lut8:
return new LutEntryCalculator(lut8);
case IccLut16TagDataEntry lut16:
return new LutEntryCalculator(lut16);
case IccLutAToBTagDataEntry lutAtoB:
return new LutABCalculator(lutAtoB);
case IccLutBToATagDataEntry lutBtoA:
return new LutABCalculator(lutBtoA);
default:
throw new InvalidIccProfileException("Invalid entry.");
}
}
private IVector4Calculator InitD(IccProfile profile, IccProfileTag tag)
{
IccMultiProcessElementsTagDataEntry entry = this.GetTag<IccMultiProcessElementsTagDataEntry>(profile, tag);
if (entry == null)
{
throw new InvalidIccProfileException("Entry is null.");
}
throw new NotImplementedException("Multi process elements are not supported");
}
private IVector4Calculator InitColorTrc(IccProfile profile, bool toPcs)
{
IccXyzTagDataEntry redMatrixColumn = this.GetTag<IccXyzTagDataEntry>(profile, IccProfileTag.RedMatrixColumn);
IccXyzTagDataEntry greenMatrixColumn = this.GetTag<IccXyzTagDataEntry>(profile, IccProfileTag.GreenMatrixColumn);
IccXyzTagDataEntry blueMatrixColumn = this.GetTag<IccXyzTagDataEntry>(profile, IccProfileTag.BlueMatrixColumn);
IccTagDataEntry redTrc = this.GetTag(profile, IccProfileTag.RedTrc);
IccTagDataEntry greenTrc = this.GetTag(profile, IccProfileTag.GreenTrc);
IccTagDataEntry blueTrc = this.GetTag(profile, IccProfileTag.BlueTrc);
if (redMatrixColumn == null ||
greenMatrixColumn == null ||
blueMatrixColumn == null ||
redTrc == null ||
greenTrc == null ||
blueTrc == null)
{
throw new InvalidIccProfileException("Missing matrix column or channel.");
}
return new ColorTrcCalculator(
redMatrixColumn,
greenMatrixColumn,
blueMatrixColumn,
redTrc,
greenTrc,
blueTrc,
toPcs);
}
private IVector4Calculator InitGrayTrc(IccProfile profile, bool toPcs)
{
IccTagDataEntry entry = this.GetTag(profile, IccProfileTag.GrayTrc);
return new GrayTrcCalculator(entry, toPcs);
}
}
}

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

@ -0,0 +1,37 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Icc
{
/// <summary>
/// Color converter for ICC profiles
/// </summary>
internal abstract partial class IccConverterBase
{
/// <summary>
/// Initializes a new instance of the <see cref="IccConverterBase"/> class.
/// </summary>
/// <param name="profile">The ICC profile to use for the conversions</param>
/// <param name="toPcs">True if the conversion is to the profile connection space (PCS); False if the conversion is to the data space</param>
protected IccConverterBase(IccProfile profile, bool toPcs)
{
Guard.NotNull(profile, nameof(profile));
this.Init(profile, toPcs, profile.Header.RenderingIntent);
}
/// <summary>
/// Converts colors with the initially provided ICC profile
/// </summary>
/// <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);
}
}
}

22
src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccDataToDataConverter.cs

@ -0,0 +1,22 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Icc
{
/// <summary>
/// Color converter for ICC profiles
/// </summary>
internal class IccDataToDataConverter : IccConverterBase
{
/// <summary>
/// Initializes a new instance of the <see cref="IccDataToDataConverter"/> class.
/// </summary>
/// <param name="profile">The ICC profile to use for the conversions</param>
public IccDataToDataConverter(IccProfile profile)
: base(profile, true) // toPCS is true because in this case the PCS space is also a data space
{
}
}
}

22
src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccDataToPcsConverter.cs

@ -0,0 +1,22 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Icc
{
/// <summary>
/// Color converter for ICC profiles
/// </summary>
internal class IccDataToPcsConverter : IccConverterBase
{
/// <summary>
/// Initializes a new instance of the <see cref="IccDataToPcsConverter"/> class.
/// </summary>
/// <param name="profile">The ICC profile to use for the conversions</param>
public IccDataToPcsConverter(IccProfile profile)
: base(profile, true)
{
}
}
}

22
src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccPcsToDataConverter.cs

@ -0,0 +1,22 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Icc
{
/// <summary>
/// Color converter for ICC profiles
/// </summary>
internal class IccPcsToDataConverter : IccConverterBase
{
/// <summary>
/// Initializes a new instance of the <see cref="IccPcsToDataConverter"/> class.
/// </summary>
/// <param name="profile">The ICC profile to use for the conversions</param>
public IccPcsToDataConverter(IccProfile profile)
: base(profile, false)
{
}
}
}

22
src/ImageSharp/ColorSpaces/Conversion/Implementation/Icc/IccPcsToPcsConverter.cs

@ -0,0 +1,22 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Icc
{
/// <summary>
/// Color converter for ICC profiles
/// </summary>
internal class IccPcsToPcsConverter : IccConverterBase
{
/// <summary>
/// Initializes a new instance of the <see cref="IccPcsToPcsConverter"/> class.
/// </summary>
/// <param name="profile">The ICC profile to use for the conversions</param>
public IccPcsToPcsConverter(IccProfile profile)
: base(profile, true)
{
}
}
}

27
src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs

@ -79,33 +79,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc
return count;
}
/// <summary>
/// Writes a two dimensional matrix
/// </summary>
/// <param name="value">The matrix to write</param>
/// <param name="isSingle">True if the values are encoded as Single; false if encoded as Fix16</param>
/// <returns>The number of bytes written</returns>
public int WriteMatrix(float[,] value, bool isSingle)
{
int count = 0;
for (int y = 0; y < value.GetLength(1); y++)
{
for (int x = 0; x < value.GetLength(0); x++)
{
if (isSingle)
{
count += this.WriteSingle(value[x, y]);
}
else
{
count += this.WriteFix16(value[x, y]);
}
}
}
return count;
}
/// <summary>
/// Writes a one dimensional matrix
/// </summary>

2
src/ImageSharp/Metadata/Profiles/ICC/Enums/IccFormulaCurveType.cs

@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc
Type1 = 0,
/// <summary>
/// Type 1: Y = a * log10 (b * X^γ + c) + d
/// Type 2: Y = a * log10 (b * X^γ + c) + d
/// </summary>
Type2 = 1,

4
src/ImageSharp/Metadata/Profiles/ICC/Exceptions/InvalidIccProfileException.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -30,4 +30,4 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Icc
{
}
}
}
}

27
tests/ImageSharp.Tests/Colorspaces/Icc/Calculators/ClutCalculatorTests.cs

@ -0,0 +1,27 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Icc;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Colorspaces.Icc.Calculators
{
/// <summary>
/// Tests ICC <see cref="ClutCalculator"/>
/// </summary>
public class ClutCalculatorTests
{
[Theory]
[MemberData(nameof(IccConversionDataClut.ClutConversionTestData), MemberType = typeof(IccConversionDataClut))]
internal void ClutCalculator_WithClut_ReturnsResult(IccClut lut, Vector4 input, Vector4 expected)
{
var calculator = new ClutCalculator(lut);
Vector4 result = calculator.Calculate(input);
VectorAssert.Equal(expected, result, 4);
}
}
}

26
tests/ImageSharp.Tests/Colorspaces/Icc/Calculators/CurveCalculatorTests.cs

@ -0,0 +1,26 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces.Conversion.Icc;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Colorspaces.Icc.Calculators
{
/// <summary>
/// Tests ICC <see cref="CurveCalculator"/>
/// </summary>
public class CurveCalculatorTests
{
[Theory]
[MemberData(nameof(IccConversionDataTrc.CurveConversionTestData), MemberType = typeof(IccConversionDataTrc))]
internal void CurveCalculator_WithCurveEntry_ReturnsResult(IccCurveTagDataEntry curve, bool inverted, float input, float expected)
{
var calculator = new CurveCalculator(curve, inverted);
float result = calculator.Calculate(input);
Assert.Equal(expected, result, 4);
}
}
}

38
tests/ImageSharp.Tests/Colorspaces/Icc/Calculators/LutABCalculatorTests.cs

@ -0,0 +1,38 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Icc;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Colorspaces.Icc.Calculators
{
/// <summary>
/// Tests ICC <see cref="LutABCalculator"/>
/// </summary>
public class LutABCalculatorTests
{
[Theory]
[MemberData(nameof(IccConversionDataLutAB.LutAToBConversionTestData), MemberType = typeof(IccConversionDataLutAB))]
internal void LutABCalculator_WithLutAToB_ReturnsResult(IccLutAToBTagDataEntry lut, Vector4 input, Vector4 expected)
{
var calculator = new LutABCalculator(lut);
Vector4 result = calculator.Calculate(input);
VectorAssert.Equal(expected, result, 4);
}
[Theory]
[MemberData(nameof(IccConversionDataLutAB.LutBToAConversionTestData), MemberType = typeof(IccConversionDataLutAB))]
internal void LutABCalculator_WithLutBToA_ReturnsResult(IccLutBToATagDataEntry lut, Vector4 input, Vector4 expected)
{
var calculator = new LutABCalculator(lut);
Vector4 result = calculator.Calculate(input);
VectorAssert.Equal(expected, result, 4);
}
}
}

25
tests/ImageSharp.Tests/Colorspaces/Icc/Calculators/LutCalculatorTests.cs

@ -0,0 +1,25 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces.Conversion.Icc;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Colorspaces.Icc.Calculators
{
/// <summary>
/// Tests ICC <see cref="LutCalculator"/>
/// </summary>
public class LutCalculatorTests
{
[Theory]
[MemberData(nameof(IccConversionDataLut.LutConversionTestData), MemberType = typeof(IccConversionDataLut))]
internal void LutCalculator_WithLut_ReturnsResult(float[] lut, bool inverted, float input, float expected)
{
var calculator = new LutCalculator(lut, inverted);
float result = calculator.Calculate(input);
Assert.Equal(expected, result, 4);
}
}
}

38
tests/ImageSharp.Tests/Colorspaces/Icc/Calculators/LutEntryCalculatorTests.cs

@ -0,0 +1,38 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Icc;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Colorspaces.Icc.Calculators
{
/// <summary>
/// Tests ICC <see cref="LutEntryCalculator"/>
/// </summary>
public class LutEntryCalculatorTests
{
[Theory]
[MemberData(nameof(IccConversionDataLutEntry.Lut8ConversionTestData), MemberType = typeof(IccConversionDataLutEntry))]
internal void LutEntryCalculator_WithLut8_ReturnsResult(IccLut8TagDataEntry lut, Vector4 input, Vector4 expected)
{
var calculator = new LutEntryCalculator(lut);
Vector4 result = calculator.Calculate(input);
VectorAssert.Equal(expected, result, 4);
}
[Theory]
[MemberData(nameof(IccConversionDataLutEntry.Lut16ConversionTestData), MemberType = typeof(IccConversionDataLutEntry))]
internal void LutEntryCalculator_WithLut16_ReturnsResult(IccLut16TagDataEntry lut, Vector4 input, Vector4 expected)
{
var calculator = new LutEntryCalculator(lut);
Vector4 result = calculator.Calculate(input);
VectorAssert.Equal(expected, result, 4);
}
}
}

26
tests/ImageSharp.Tests/Colorspaces/Icc/Calculators/MatrixCalculatorTests.cs

@ -0,0 +1,26 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Icc;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Colorspaces.Icc.Calculators
{
/// <summary>
/// Tests ICC <see cref="MatrixCalculator"/>
/// </summary>
public class MatrixCalculatorTests
{
[Theory]
[MemberData(nameof(IccConversionDataMatrix.MatrixConversionTestData), MemberType = typeof(IccConversionDataMatrix))]
internal void MatrixCalculator_WithMatrix_ReturnsResult(Matrix4x4 matrix2D, Vector3 matrix1D, Vector4 input, Vector4 expected)
{
var calculator = new MatrixCalculator(matrix2D, matrix1D);
Vector4 result = calculator.Calculate(input);
VectorAssert.Equal(expected, result, 4);
}
}
}

26
tests/ImageSharp.Tests/Colorspaces/Icc/Calculators/ParametricCurveCalculatorTests.cs

@ -0,0 +1,26 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.ColorSpaces.Conversion.Icc;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Colorspaces.Icc.Calculators
{
/// <summary>
/// Tests ICC <see cref="ParametricCurveCalculator"/>
/// </summary>
public class ParametricCurveCalculatorTests
{
[Theory]
[MemberData(nameof(IccConversionDataTrc.ParametricCurveConversionTestData), MemberType = typeof(IccConversionDataTrc))]
internal void ParametricCurveCalculator_WithCurveEntry_ReturnsResult(IccParametricCurveTagDataEntry curve, bool inverted, float input, float expected)
{
var calculator = new ParametricCurveCalculator(curve, inverted);
float result = calculator.Calculate(input);
Assert.Equal(expected, result, 4);
}
}
}

27
tests/ImageSharp.Tests/Colorspaces/Icc/Calculators/TrcCalculatorTests.cs

@ -0,0 +1,27 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.ColorSpaces.Conversion.Icc;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Colorspaces.Icc.Calculators
{
/// <summary>
/// Tests ICC <see cref="TrcCalculator"/>
/// </summary>
public class TrcCalculatorTests
{
[Theory]
[MemberData(nameof(IccConversionDataTrc.TrcArrayConversionTestData), MemberType = typeof(IccConversionDataTrc))]
internal void TrcCalculator_WithCurvesArray_ReturnsResult(IccTagDataEntry[] entries, bool inverted, Vector4 input, Vector4 expected)
{
var calculator = new TrcCalculator(entries, inverted);
Vector4 result = calculator.Calculate(input);
VectorAssert.Equal(expected, result, 4);
}
}
}

163
tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionData.Clut.cs

@ -0,0 +1,163 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
namespace SixLabors.ImageSharp.Tests.Colorspaces.Icc
{
public class IccConversionDataClut
{
internal static IccClut Clut3x2 = new IccClut(
new float[][]
{
new float[] { 0.1f, 0.1f },
new float[] { 0.2f, 0.2f },
new float[] { 0.3f, 0.3f },
new float[] { 0.11f, 0.11f },
new float[] { 0.21f, 0.21f },
new float[] { 0.31f, 0.31f },
new float[] { 0.12f, 0.12f },
new float[] { 0.22f, 0.22f },
new float[] { 0.32f, 0.32f },
new float[] { 0.13f, 0.13f },
new float[] { 0.23f, 0.23f },
new float[] { 0.33f, 0.33f },
new float[] { 0.14f, 0.14f },
new float[] { 0.24f, 0.24f },
new float[] { 0.34f, 0.34f },
new float[] { 0.15f, 0.15f },
new float[] { 0.25f, 0.25f },
new float[] { 0.35f, 0.35f },
new float[] { 0.16f, 0.16f },
new float[] { 0.26f, 0.26f },
new float[] { 0.36f, 0.36f },
new float[] { 0.17f, 0.17f },
new float[] { 0.27f, 0.27f },
new float[] { 0.37f, 0.37f },
new float[] { 0.18f, 0.18f },
new float[] { 0.28f, 0.28f },
new float[] { 0.38f, 0.38f },
},
new byte[] { 3, 3, 3 },
IccClutDataType.Float);
internal static IccClut Clut3x1 = new IccClut(
new float[][]
{
new float[] { 0.10f },
new float[] { 0.20f },
new float[] { 0.30f },
new float[] { 0.11f },
new float[] { 0.21f },
new float[] { 0.31f },
new float[] { 0.12f },
new float[] { 0.22f },
new float[] { 0.32f },
new float[] { 0.13f },
new float[] { 0.23f },
new float[] { 0.33f },
new float[] { 0.14f },
new float[] { 0.24f },
new float[] { 0.34f },
new float[] { 0.15f },
new float[] { 0.25f },
new float[] { 0.35f },
new float[] { 0.16f },
new float[] { 0.26f },
new float[] { 0.36f },
new float[] { 0.17f },
new float[] { 0.27f },
new float[] { 0.37f },
new float[] { 0.18f },
new float[] { 0.28f },
new float[] { 0.38f },
},
new byte[] { 3, 3, 3 },
IccClutDataType.Float);
internal static IccClut Clut2x2 = new IccClut(
new float[][]
{
new float[] { 0.1f, 0.9f },
new float[] { 0.2f, 0.8f },
new float[] { 0.3f, 0.7f },
new float[] { 0.4f, 0.6f },
new float[] { 0.5f, 0.5f },
new float[] { 0.6f, 0.4f },
new float[] { 0.7f, 0.3f },
new float[] { 0.8f, 0.2f },
new float[] { 0.9f, 0.1f },
},
new byte[] { 3, 3 },
IccClutDataType.Float);
internal static IccClut Clut2x1 = new IccClut(
new float[][]
{
new float[] { 0.1f },
new float[] { 0.2f },
new float[] { 0.3f },
new float[] { 0.4f },
new float[] { 0.5f },
new float[] { 0.6f },
new float[] { 0.7f },
new float[] { 0.8f },
new float[] { 0.9f },
},
new byte[] { 3, 3 },
IccClutDataType.Float);
internal static IccClut Clut1x2 = new IccClut(
new float[][]
{
new float[] { 0f, 0.5f },
new float[] { 0.25f, 0.75f, },
new float[] { 0.5f, 1f },
},
new byte[] { 3 },
IccClutDataType.Float);
internal static IccClut Clut1x1 = new IccClut(
new float[][]
{
new float[] { 0f },
new float[] { 0.5f },
new float[] { 1f },
},
new byte[] { 3 },
IccClutDataType.Float);
public static object[][] ClutConversionTestData =
{
new object[] { Clut3x2, new Vector4(0.75f, 0.75f, 0.75f, 0), new Vector4(0.31f, 0.31f, 0, 0) },
new object[] { Clut3x1, new Vector4(0.2f, 0.6f, 0.8f, 0), new Vector4(0.276f, 0, 0, 0) },
new object[] { Clut3x1, new Vector4(0.75f, 0.75f, 0.75f, 0), new Vector4(0.31f, 0, 0, 0) },
new object[] { Clut2x2, new Vector4(0.2f, 0.6f, 0, 0), new Vector4(0.46f, 0.54f, 0, 0) },
new object[] { Clut2x2, new Vector4(0.25f, 0.75f, 0, 0), new Vector4(0.4f, 0.6f, 0, 0) },
new object[] { Clut2x1, new Vector4(0.25f, 0.75f, 0, 0), new Vector4(0.4f, 0, 0, 0) },
new object[] { Clut1x2, new Vector4(0.25f, 0, 0, 0), new Vector4(0.125f, 0.625f, 0, 0) },
new object[] { Clut1x1, new Vector4(0.25f, 0, 0, 0), new Vector4(0.25f, 0, 0, 0) },
};
}
}

30
tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionData.Lut.cs

@ -0,0 +1,30 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Tests.Colorspaces.Icc
{
public class IccConversionDataLut
{
private static float[] LutEven = { 0, 0.5f, 1 };
private static float[] LutUneven = { 0, 0.7f, 1 };
public static object[][] LutConversionTestData =
{
new object[] { LutEven, false, 0.5f, 0.5f },
new object[] { LutEven, false, 0.25f, 0.25f },
new object[] { LutEven, false, 0.75f, 0.75f },
new object[] { LutEven, true, 0.5f, 0.5f },
new object[] { LutEven, true, 0.25f, 0.25f },
new object[] { LutEven, true, 0.75f, 0.75f },
new object[] { LutUneven, false, 0.1, 0.14 },
new object[] { LutUneven, false, 0.5, 0.7 },
new object[] { LutUneven, false, 0.75, 0.85 },
new object[] { LutUneven, true, 0.14, 0.1 },
new object[] { LutUneven, true, 0.7, 0.5 },
new object[] { LutUneven, true, 0.85, 0.75 },
};
}
}

44
tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionData.LutAB.cs

@ -0,0 +1,44 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
namespace SixLabors.ImageSharp.Tests.Colorspaces.Icc
{
public class IccConversionDataLutAB
{
private static IccLutAToBTagDataEntry lutAtoB_SingleCurve = new IccLutAToBTagDataEntry(
new IccTagDataEntry[]
{
IccConversionDataTrc.IdentityCurve,
IccConversionDataTrc.IdentityCurve,
IccConversionDataTrc.IdentityCurve
},
null, null, null, null, null);
// also need:
// # CurveM + matrix
// # CurveA + CLUT + CurveB
// # CurveA + CLUT + CurveM + Matrix + CurveB
private static IccLutBToATagDataEntry lutBtoA_SingleCurve = new IccLutBToATagDataEntry(
new IccTagDataEntry[]
{
IccConversionDataTrc.IdentityCurve,
IccConversionDataTrc.IdentityCurve,
IccConversionDataTrc.IdentityCurve
},
null, null, null, null, null);
public static object[][] LutAToBConversionTestData =
{
new object[] { lutAtoB_SingleCurve, new Vector4(0.2f, 0.3f, 0.4f, 0), new Vector4(0.2f, 0.3f, 0.4f, 0) },
};
public static object[][] LutBToAConversionTestData =
{
new object[] { lutBtoA_SingleCurve, new Vector4(0.2f, 0.3f, 0.4f, 0), new Vector4(0.2f, 0.3f, 0.4f, 0) },
};
}
}

65
tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionData.LutEntry.cs

@ -0,0 +1,65 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
namespace SixLabors.ImageSharp.Tests.Colorspaces.Icc
{
public class IccConversionDataLutEntry
{
private static readonly IccLut Lut256 = CreateLut(256);
private static readonly IccLut Lut32 = CreateLut(32);
private static readonly IccLut LutIdentity = CreateIdentityLut(0, 1);
private static readonly IccLut8TagDataEntry Lut8 = new IccLut8TagDataEntry(
new IccLut[] { Lut256, Lut256 },
IccConversionDataClut.Clut2x1,
new IccLut[] { Lut256 });
private static readonly IccLut16TagDataEntry Lut16 = new IccLut16TagDataEntry(
new IccLut[] { Lut32, Lut32 },
IccConversionDataClut.Clut2x1,
new IccLut[] { LutIdentity });
private static readonly IccLut8TagDataEntry Lut8Matrix = new IccLut8TagDataEntry(
IccConversionDataMatrix.Matrix3x3Random,
new IccLut[] { Lut256, Lut256, Lut256 },
IccConversionDataClut.Clut3x1,
new IccLut[] { Lut256 });
private static readonly IccLut16TagDataEntry Lut16Matrix = new IccLut16TagDataEntry(
IccConversionDataMatrix.Matrix3x3Random,
new IccLut[] { Lut32, Lut32, Lut32 },
IccConversionDataClut.Clut3x1,
new IccLut[] { LutIdentity });
private static IccLut CreateLut(int length)
{
float[] values = new float[length];
for (int i = 0; i < values.Length; i++)
{
values[i] = 0.1f + (i / (float)length);
}
return new IccLut(values);
}
private static IccLut CreateIdentityLut(float min, float max)
{
return new IccLut(new float[] { min, max });
}
public static object[][] Lut8ConversionTestData =
{
new object[] { Lut8, new Vector4(0.2f, 0.3f, 0, 0), new Vector4(0.339762866f, 0, 0, 0) },
new object[] { Lut8Matrix, new Vector4(0.21f, 0.31f, 0.41f, 0), new Vector4(0.431305826f, 0, 0, 0) },
};
public static object[][] Lut16ConversionTestData =
{
new object[] { Lut16, new Vector4(0.2f, 0.3f, 0, 0), new Vector4(0.245625019f, 0, 0, 0) },
new object[] { Lut16Matrix, new Vector4(0.21f, 0.31f, 0.41f, 0), new Vector4(0.336980581f, 0, 0, 0) },
};
}
}

45
tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionData.Matrix.cs

@ -0,0 +1,45 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
namespace SixLabors.ImageSharp.Tests.Colorspaces.Icc
{
public class IccConversionDataMatrix
{
public static float[,] Matrix3x3Random = { { 0.1f, 0.2f, 0.3f }, { 0.4f, 0.5f, 0.6f }, { 0.7f, 0.8f, 0.9f } };
public static float[,] Matrix3x3Identity = { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } };
public static object[][] MatrixConversionTestData =
{
new object[] { CreateMatrix(Matrix3x3Identity), Vector3.Zero, new Vector4(0.5f, 0.5f, 0.5f, 0), new Vector4(0.5f, 0.5f, 0.5f, 0) },
new object[] { CreateMatrix(Matrix3x3Identity), new Vector3(0.2f, 0.2f, 0.2f), new Vector4(0.5f, 0.5f, 0.5f, 0), new Vector4(0.7f, 0.7f, 0.7f, 0) },
new object[] { CreateMatrix(Matrix3x3Random), Vector3.Zero, new Vector4(0.5f, 0.5f, 0.5f, 0), new Vector4(0.6f, 0.75f, 0.9f, 0) },
new object[] { CreateMatrix(Matrix3x3Random), new Vector3(0.1f, 0.2f, 0.3f), new Vector4(0.5f, 0.5f, 0.5f, 0), new Vector4(0.7f, 0.95f, 1.2f, 0) },
new object[] { CreateMatrix(Matrix3x3Random), Vector3.Zero, new Vector4(0.2f, 0.4f, 0.7f, 0), new Vector4(0.67f, 0.8f, 0.93f, 0) },
new object[] { CreateMatrix(Matrix3x3Random), new Vector3(0.1f, 0.2f, 0.3f), new Vector4(0.2f, 0.4f, 0.7f, 0), new Vector4(0.77f, 1, 1.23f, 0) },
};
private static Matrix4x4 CreateMatrix(float[,] matrix)
{
return new Matrix4x4(
matrix[0, 0],
matrix[0, 1],
matrix[0, 2],
0,
matrix[1, 0],
matrix[1, 1],
matrix[1, 2],
0,
matrix[2, 0],
matrix[2, 1],
matrix[2, 2],
0,
0,
0,
0,
1);
}
}
}

76
tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionData.MultiProcessElement.cs

@ -0,0 +1,76 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
namespace SixLabors.ImageSharp.Tests.Colorspaces.Icc
{
public class IccConversionDataMultiProcessElement
{
private static IccMatrixProcessElement Matrix = new IccMatrixProcessElement(new float[,]
{
{ 2, 4, 6 },
{ 3, 5, 7 },
}, new float[] { 3, 4, 5 });
private static IccClut Clut = new IccClut(new float[][]
{
new float[] { 0.2f, 0.3f },
new float[] { 0.4f, 0.5f },
new float[] { 0.21f, 0.31f },
new float[] { 0.41f, 0.51f },
new float[] { 0.22f, 0.32f },
new float[] { 0.42f, 0.52f },
new float[] { 0.23f, 0.33f },
new float[] { 0.43f, 0.53f },
}, new byte[] { 2, 2, 2 }, IccClutDataType.Float);
private static IccFormulaCurveElement FormulaCurveElement1 = new IccFormulaCurveElement(IccFormulaCurveType.Type1, 2.2f, 0.7f, 0.2f, 0.3f, 0, 0);
private static IccFormulaCurveElement FormulaCurveElement2 = new IccFormulaCurveElement(IccFormulaCurveType.Type2, 2.2f, 0.9f, 0.9f, 0.02f, 0.1f, 0);
private static IccFormulaCurveElement FormulaCurveElement3 = new IccFormulaCurveElement(IccFormulaCurveType.Type3, 0, 0.9f, 0.9f, 1.02f, 0.1f, 0.02f);
private static IccCurveSetProcessElement CurveSet1DFormula1 = Create1DSingleCurveSet(FormulaCurveElement1);
private static IccCurveSetProcessElement CurveSet1DFormula2 = Create1DSingleCurveSet(FormulaCurveElement2);
private static IccCurveSetProcessElement CurveSet1DFormula3 = Create1DSingleCurveSet(FormulaCurveElement3);
private static IccCurveSetProcessElement CurveSet1DFormula1And2 = Create1DMultiCurveSet(new float[] { 0.5f }, FormulaCurveElement1, FormulaCurveElement2);
private static IccClutProcessElement ClutElement = new IccClutProcessElement(Clut);
private static IccCurveSetProcessElement Create1DSingleCurveSet(IccCurveSegment segment)
{
var curve = new IccOneDimensionalCurve(new float[0], new IccCurveSegment[] { segment });
return new IccCurveSetProcessElement(new IccOneDimensionalCurve[] { curve });
}
private static IccCurveSetProcessElement Create1DMultiCurveSet(float[] breakPoints, params IccCurveSegment[] segments)
{
var curve = new IccOneDimensionalCurve(breakPoints, segments);
return new IccCurveSetProcessElement(new IccOneDimensionalCurve[] { curve });
}
public static object[][] MpeCurveConversionTestData =
{
new object[] { CurveSet1DFormula1, new float[] { 0.51f }, new float[] { 0.575982451f } },
new object[] { CurveSet1DFormula2, new float[] { 0.52f }, new float[] { -0.4684991f } },
new object[] { CurveSet1DFormula3, new float[] { 0.53f }, new float[] { 0.86126f } },
new object[] { CurveSet1DFormula1And2, new float[] { 0.31f }, new float[] { 0.445982f } },
new object[] { CurveSet1DFormula1And2, new float[] { 0.61f }, new float[] { -0.341274023f } },
};
public static object[][] MpeMatrixConversionTestData =
{
new object[] { Matrix, new float[] { 2, 4 }, new float[] { 19, 32, 45 } }
};
public static object[][] MpeClutConversionTestData =
{
new object[] { ClutElement, new float[] { 0.5f, 0.5f, 0.5f }, new float[] { 0.5f, 0.5f } }
};
}
}

77
tests/ImageSharp.Tests/TestDataIcc/Conversion/IccConversionData.Trc.cs

@ -0,0 +1,77 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
namespace SixLabors.ImageSharp.Tests.Colorspaces.Icc
{
public class IccConversionDataTrc
{
internal static IccCurveTagDataEntry IdentityCurve = new IccCurveTagDataEntry();
internal static IccCurveTagDataEntry Gamma2Curve = new IccCurveTagDataEntry(2);
internal static IccCurveTagDataEntry LutCurve = new IccCurveTagDataEntry(new float[] { 0, 0.7f, 1 });
internal static IccParametricCurveTagDataEntry ParamCurve1 = new IccParametricCurveTagDataEntry(new IccParametricCurve(2.2f));
internal static IccParametricCurveTagDataEntry ParamCurve2 = new IccParametricCurveTagDataEntry(new IccParametricCurve(2.2f, 1.5f, -0.5f));
internal static IccParametricCurveTagDataEntry ParamCurve3 = new IccParametricCurveTagDataEntry(new IccParametricCurve(2.2f, 1.5f, -0.5f, 0.3f));
internal static IccParametricCurveTagDataEntry ParamCurve4 = new IccParametricCurveTagDataEntry(new IccParametricCurve(2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f));
internal static IccParametricCurveTagDataEntry ParamCurve5 = new IccParametricCurveTagDataEntry(new IccParametricCurve(2.2f, 0.7f, 0.2f, 0.3f, 0.1f, 0.5f, 0.2f));
public static object[][] TrcArrayConversionTestData =
{
new object[]
{
new IccTagDataEntry[] { IdentityCurve, Gamma2Curve, ParamCurve1 },
false,
new Vector4(2, 2, 0.5f, 0),
new Vector4(2, 4, 0.217637628f, 0),
},
new object[]
{
new IccTagDataEntry[] { IdentityCurve, Gamma2Curve, ParamCurve1 },
true,
new Vector4(1, 4, 0.217637628f, 0),
new Vector4(1, 2, 0.5f, 0),
},
};
public static object[][] CurveConversionTestData =
{
new object[] { IdentityCurve, false, 2, 2 },
new object[] { Gamma2Curve, false, 2, 4 },
new object[] { LutCurve, false, 0.1, 0.14 },
new object[] { LutCurve, false, 0.5, 0.7 },
new object[] { LutCurve, false, 0.75, 0.85 },
new object[] { IdentityCurve, true, 2, 2 },
new object[] { Gamma2Curve, true, 4, 2 },
new object[] { LutCurve, true, 0.14, 0.1 },
new object[] { LutCurve, true, 0.7, 0.5 },
new object[] { LutCurve, true, 0.85, 0.75 },
};
public static object[][] ParametricCurveConversionTestData =
{
new object[] { ParamCurve1, false, 0.5f, 0.217637628f },
new object[] { ParamCurve2, false, 0.6f, 0.133208528f },
new object[] { ParamCurve2, false, 0.21f, 0 },
new object[] { ParamCurve3, false, 0.61f, 0.444446117f },
new object[] { ParamCurve3, false, 0.22f, 0.3f },
new object[] { ParamCurve4, false, 0.3f, 0.0732389539f },
new object[] { ParamCurve4, false, 0.03f, 0.00232198136f },
new object[] { ParamCurve5, false, 0.2f, 0.593165159f },
new object[] { ParamCurve5, false, 0.05f, 0.215f },
new object[] { ParamCurve1, true, 0.217637628f, 0.5f },
new object[] { ParamCurve2, true, 0.133208528f, 0.6f },
new object[] { ParamCurve2, true, 0, 1 / 3f },
new object[] { ParamCurve3, true, 0.444446117f, 0.61f },
new object[] { ParamCurve3, true, 0.3f, 1 / 3f },
new object[] { ParamCurve4, true, 0.0732389539f, 0.3f },
new object[] { ParamCurve4, true, 0.00232198136f, 0.03f },
new object[] { ParamCurve5, true, 0.593165159f, 0.2f },
new object[] { ParamCurve5, true, 0.215f, 0.05f },
};
}
}
Loading…
Cancel
Save