mirror of https://github.com/SixLabors/ImageSharp
40 changed files with 2088 additions and 30 deletions
@ -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]; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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, |
|||
} |
|||
} |
|||
} |
|||
@ -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"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
@ -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, |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -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, |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -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
|
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -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) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -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) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -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) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -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) }, |
|||
}; |
|||
} |
|||
} |
|||
@ -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 }, |
|||
}; |
|||
} |
|||
} |
|||
@ -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) }, |
|||
}; |
|||
} |
|||
} |
|||
@ -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) }, |
|||
}; |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -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 } } |
|||
}; |
|||
} |
|||
} |
|||
@ -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…
Reference in new issue