mirror of https://github.com/SixLabors/ImageSharp
11 changed files with 1102 additions and 184 deletions
@ -0,0 +1,502 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.Intrinsics; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1; |
||||
|
|
||||
|
internal abstract partial class Av1ColorConverterBase |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Implements IPT-C2 conversion for scalar and SIMD lanes.
|
||||
|
/// </summary>
|
||||
|
internal readonly struct Av1IptC2ColorOperator : IAv1ColorOperator |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The linear red contribution to L.
|
||||
|
/// </summary>
|
||||
|
public const float RedToL = 1747F / 4096F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The linear green contribution to L.
|
||||
|
/// </summary>
|
||||
|
public const float GreenToL = 2169F / 4096F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The linear blue contribution to L.
|
||||
|
/// </summary>
|
||||
|
public const float BlueToL = 180F / 4096F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The linear red contribution to M.
|
||||
|
/// </summary>
|
||||
|
public const float RedToM = 673F / 4096F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The linear green contribution to M.
|
||||
|
/// </summary>
|
||||
|
public const float GreenToM = 3029F / 4096F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The linear blue contribution to M.
|
||||
|
/// </summary>
|
||||
|
public const float BlueToM = 394F / 4096F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The linear red contribution to S.
|
||||
|
/// </summary>
|
||||
|
public const float RedToS = 50F / 4096F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The linear green contribution to S.
|
||||
|
/// </summary>
|
||||
|
public const float GreenToS = 207F / 4096F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The linear blue contribution to S.
|
||||
|
/// </summary>
|
||||
|
public const float BlueToS = 3839F / 4096F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The nonlinear L contribution to intensity.
|
||||
|
/// </summary>
|
||||
|
public const float LToIntensity = 1638F / 4096F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The nonlinear M contribution to intensity.
|
||||
|
/// </summary>
|
||||
|
public const float MToIntensity = 1638F / 4096F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The nonlinear S contribution to intensity.
|
||||
|
/// </summary>
|
||||
|
public const float SToIntensity = 820F / 4096F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The nonlinear L contribution to the protan axis.
|
||||
|
/// </summary>
|
||||
|
public const float LToProtan = 18248F / 4096F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The nonlinear M contribution to the protan axis.
|
||||
|
/// </summary>
|
||||
|
public const float MToProtan = -19870F / 4096F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The nonlinear S contribution to the protan axis.
|
||||
|
/// </summary>
|
||||
|
public const float SToProtan = 1622F / 4096F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The nonlinear L contribution to the tritan axis.
|
||||
|
/// </summary>
|
||||
|
public const float LToTritan = 3300F / 4096F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The nonlinear M contribution to the tritan axis.
|
||||
|
/// </summary>
|
||||
|
public const float MToTritan = 1463F / 4096F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The nonlinear S contribution to the tritan axis.
|
||||
|
/// </summary>
|
||||
|
public const float SToTritan = -4763F / 4096F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The protan contribution to nonlinear L.
|
||||
|
/// </summary>
|
||||
|
public const float ProtanToL = 0.0975578875686935F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The tritan contribution to nonlinear L.
|
||||
|
/// </summary>
|
||||
|
public const float TritanToL = 0.20538292958984272F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The protan contribution to nonlinear M.
|
||||
|
/// </summary>
|
||||
|
public const float ProtanToM = -0.11388362209560723F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The tritan contribution to nonlinear M.
|
||||
|
/// </summary>
|
||||
|
public const float TritanToM = 0.13337828363655785F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The protan contribution to nonlinear S.
|
||||
|
/// </summary>
|
||||
|
public const float ProtanToS = 0.032611650189127685F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The tritan contribution to nonlinear S.
|
||||
|
/// </summary>
|
||||
|
public const float TritanToS = -0.6766961795912734F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The linear L contribution to red.
|
||||
|
/// </summary>
|
||||
|
public const float LToRed = 3.2374662424353895F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The linear M contribution to red.
|
||||
|
/// </summary>
|
||||
|
public const float MToRed = -2.324205800020636F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The linear S contribution to red.
|
||||
|
/// </summary>
|
||||
|
public const float SToRed = 0.08673955758524626F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The linear L contribution to green.
|
||||
|
/// </summary>
|
||||
|
public const float LToGreen = -0.7188754693535147F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The linear M contribution to green.
|
||||
|
/// </summary>
|
||||
|
public const float MToGreen = 1.877899954242238F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The linear S contribution to green.
|
||||
|
/// </summary>
|
||||
|
public const float SToGreen = -0.1590244848887234F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The linear L contribution to blue.
|
||||
|
/// </summary>
|
||||
|
public const float LToBlue = -0.003403513926958051F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The linear M contribution to blue.
|
||||
|
/// </summary>
|
||||
|
public const float MToBlue = -0.07098593397424108F; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The linear S contribution to blue.
|
||||
|
/// </summary>
|
||||
|
public const float SToBlue = 1.074389447901199F; |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public static bool ChromaUsesLumaRange => false; |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void ConvertToRgb(ref float intensity, ref float protan, ref float tritan, in Av1ColorConversionParameters parameters) |
||||
|
{ |
||||
|
// IPT-C2 stores opponent axes around intensity in nonlinear LMS. Undo both matrices around the
|
||||
|
// signaled transfer function so the final RGB values remain in the source signal domain.
|
||||
|
float nonlinearL = intensity + (ProtanToL * protan) + (TritanToL * tritan); |
||||
|
float nonlinearM = intensity + (ProtanToM * protan) + (TritanToM * tritan); |
||||
|
float nonlinearS = intensity + (ProtanToS * protan) + (TritanToS * tritan); |
||||
|
float linearL = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearL); |
||||
|
float linearM = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearM); |
||||
|
float linearS = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearS); |
||||
|
float linearRed = (LToRed * linearL) + (MToRed * linearM) + (SToRed * linearS); |
||||
|
float linearGreen = (LToGreen * linearL) + (MToGreen * linearM) + (SToGreen * linearS); |
||||
|
float linearBlue = (LToBlue * linearL) + (MToBlue * linearM) + (SToBlue * linearS); |
||||
|
|
||||
|
intensity = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearRed); |
||||
|
protan = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearGreen); |
||||
|
tritan = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearBlue); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void ConvertToRgb( |
||||
|
ref Vector128<float> intensity, |
||||
|
ref Vector128<float> protan, |
||||
|
ref Vector128<float> tritan, |
||||
|
in Av1ColorConversionParameters parameters) |
||||
|
{ |
||||
|
Vector128<float> nonlinearL = Vector128.MultiplyAddEstimate( |
||||
|
Vector128.Create(TritanToL), |
||||
|
tritan, |
||||
|
Vector128.MultiplyAddEstimate(Vector128.Create(ProtanToL), protan, intensity)); |
||||
|
|
||||
|
Vector128<float> nonlinearM = Vector128.MultiplyAddEstimate( |
||||
|
Vector128.Create(TritanToM), |
||||
|
tritan, |
||||
|
Vector128.MultiplyAddEstimate(Vector128.Create(ProtanToM), protan, intensity)); |
||||
|
|
||||
|
Vector128<float> nonlinearS = Vector128.MultiplyAddEstimate( |
||||
|
Vector128.Create(TritanToS), |
||||
|
tritan, |
||||
|
Vector128.MultiplyAddEstimate(Vector128.Create(ProtanToS), protan, intensity)); |
||||
|
|
||||
|
Vector128<float> linearL = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearL); |
||||
|
Vector128<float> linearM = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearM); |
||||
|
Vector128<float> linearS = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearS); |
||||
|
Vector128<float> linearRed = Vector128.MultiplyAddEstimate( |
||||
|
Vector128.Create(SToRed), linearS, Vector128.MultiplyAddEstimate(Vector128.Create(MToRed), linearM, Vector128.Create(LToRed) * linearL)); |
||||
|
|
||||
|
Vector128<float> linearGreen = Vector128.MultiplyAddEstimate( |
||||
|
Vector128.Create(SToGreen), linearS, Vector128.MultiplyAddEstimate(Vector128.Create(MToGreen), linearM, Vector128.Create(LToGreen) * linearL)); |
||||
|
|
||||
|
Vector128<float> linearBlue = Vector128.MultiplyAddEstimate( |
||||
|
Vector128.Create(SToBlue), linearS, Vector128.MultiplyAddEstimate(Vector128.Create(MToBlue), linearM, Vector128.Create(LToBlue) * linearL)); |
||||
|
|
||||
|
intensity = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearRed); |
||||
|
protan = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearGreen); |
||||
|
tritan = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearBlue); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void ConvertToRgb( |
||||
|
ref Vector256<float> intensity, |
||||
|
ref Vector256<float> protan, |
||||
|
ref Vector256<float> tritan, |
||||
|
in Av1ColorConversionParameters parameters) |
||||
|
{ |
||||
|
Vector256<float> nonlinearL = Vector256.MultiplyAddEstimate( |
||||
|
Vector256.Create(TritanToL), |
||||
|
tritan, |
||||
|
Vector256.MultiplyAddEstimate(Vector256.Create(ProtanToL), protan, intensity)); |
||||
|
|
||||
|
Vector256<float> nonlinearM = Vector256.MultiplyAddEstimate( |
||||
|
Vector256.Create(TritanToM), |
||||
|
tritan, |
||||
|
Vector256.MultiplyAddEstimate(Vector256.Create(ProtanToM), protan, intensity)); |
||||
|
|
||||
|
Vector256<float> nonlinearS = Vector256.MultiplyAddEstimate( |
||||
|
Vector256.Create(TritanToS), |
||||
|
tritan, |
||||
|
Vector256.MultiplyAddEstimate(Vector256.Create(ProtanToS), protan, intensity)); |
||||
|
|
||||
|
Vector256<float> linearL = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearL); |
||||
|
Vector256<float> linearM = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearM); |
||||
|
Vector256<float> linearS = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearS); |
||||
|
Vector256<float> linearRed = Vector256.MultiplyAddEstimate( |
||||
|
Vector256.Create(SToRed), linearS, Vector256.MultiplyAddEstimate(Vector256.Create(MToRed), linearM, Vector256.Create(LToRed) * linearL)); |
||||
|
|
||||
|
Vector256<float> linearGreen = Vector256.MultiplyAddEstimate( |
||||
|
Vector256.Create(SToGreen), linearS, Vector256.MultiplyAddEstimate(Vector256.Create(MToGreen), linearM, Vector256.Create(LToGreen) * linearL)); |
||||
|
|
||||
|
Vector256<float> linearBlue = Vector256.MultiplyAddEstimate( |
||||
|
Vector256.Create(SToBlue), linearS, Vector256.MultiplyAddEstimate(Vector256.Create(MToBlue), linearM, Vector256.Create(LToBlue) * linearL)); |
||||
|
|
||||
|
intensity = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearRed); |
||||
|
protan = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearGreen); |
||||
|
tritan = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearBlue); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void ConvertToRgb( |
||||
|
ref Vector512<float> intensity, |
||||
|
ref Vector512<float> protan, |
||||
|
ref Vector512<float> tritan, |
||||
|
in Av1ColorConversionParameters parameters) |
||||
|
{ |
||||
|
Vector512<float> nonlinearL = Vector512.MultiplyAddEstimate( |
||||
|
Vector512.Create(TritanToL), |
||||
|
tritan, |
||||
|
Vector512.MultiplyAddEstimate(Vector512.Create(ProtanToL), protan, intensity)); |
||||
|
|
||||
|
Vector512<float> nonlinearM = Vector512.MultiplyAddEstimate( |
||||
|
Vector512.Create(TritanToM), |
||||
|
tritan, |
||||
|
Vector512.MultiplyAddEstimate(Vector512.Create(ProtanToM), protan, intensity)); |
||||
|
|
||||
|
Vector512<float> nonlinearS = Vector512.MultiplyAddEstimate( |
||||
|
Vector512.Create(TritanToS), |
||||
|
tritan, |
||||
|
Vector512.MultiplyAddEstimate(Vector512.Create(ProtanToS), protan, intensity)); |
||||
|
|
||||
|
Vector512<float> linearL = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearL); |
||||
|
Vector512<float> linearM = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearM); |
||||
|
Vector512<float> linearS = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, nonlinearS); |
||||
|
Vector512<float> linearRed = Vector512.MultiplyAddEstimate( |
||||
|
Vector512.Create(SToRed), linearS, Vector512.MultiplyAddEstimate(Vector512.Create(MToRed), linearM, Vector512.Create(LToRed) * linearL)); |
||||
|
|
||||
|
Vector512<float> linearGreen = Vector512.MultiplyAddEstimate( |
||||
|
Vector512.Create(SToGreen), linearS, Vector512.MultiplyAddEstimate(Vector512.Create(MToGreen), linearM, Vector512.Create(LToGreen) * linearL)); |
||||
|
|
||||
|
Vector512<float> linearBlue = Vector512.MultiplyAddEstimate( |
||||
|
Vector512.Create(SToBlue), linearS, Vector512.MultiplyAddEstimate(Vector512.Create(MToBlue), linearM, Vector512.Create(LToBlue) * linearL)); |
||||
|
|
||||
|
intensity = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearRed); |
||||
|
protan = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearGreen); |
||||
|
tritan = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearBlue); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void ConvertFromRgb( |
||||
|
float red, |
||||
|
float green, |
||||
|
float blue, |
||||
|
in Av1ColorConversionParameters parameters, |
||||
|
out float intensity, |
||||
|
out float protan, |
||||
|
out float tritan) |
||||
|
{ |
||||
|
// The encoded RGB signal is linearized before the LMS matrix, then the signaled transfer function
|
||||
|
// is reapplied to each LMS component before the fixed IPT-C2 opponent matrix.
|
||||
|
float linearRed = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, red); |
||||
|
float linearGreen = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, green); |
||||
|
float linearBlue = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, blue); |
||||
|
float nonlinearL = Av1TransferFunctions.ToGamma( |
||||
|
parameters.TransferCharacteristics, |
||||
|
(RedToL * linearRed) + (GreenToL * linearGreen) + (BlueToL * linearBlue)); |
||||
|
|
||||
|
float nonlinearM = Av1TransferFunctions.ToGamma( |
||||
|
parameters.TransferCharacteristics, |
||||
|
(RedToM * linearRed) + (GreenToM * linearGreen) + (BlueToM * linearBlue)); |
||||
|
|
||||
|
float nonlinearS = Av1TransferFunctions.ToGamma( |
||||
|
parameters.TransferCharacteristics, |
||||
|
(RedToS * linearRed) + (GreenToS * linearGreen) + (BlueToS * linearBlue)); |
||||
|
|
||||
|
intensity = (LToIntensity * nonlinearL) + (MToIntensity * nonlinearM) + (SToIntensity * nonlinearS); |
||||
|
protan = (LToProtan * nonlinearL) + (MToProtan * nonlinearM) + (SToProtan * nonlinearS); |
||||
|
tritan = (LToTritan * nonlinearL) + (MToTritan * nonlinearM) + (SToTritan * nonlinearS); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void ConvertFromRgb( |
||||
|
Vector128<float> red, |
||||
|
Vector128<float> green, |
||||
|
Vector128<float> blue, |
||||
|
in Av1ColorConversionParameters parameters, |
||||
|
out Vector128<float> intensity, |
||||
|
out Vector128<float> protan, |
||||
|
out Vector128<float> tritan) |
||||
|
{ |
||||
|
Vector128<float> linearRed = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, red); |
||||
|
Vector128<float> linearGreen = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, green); |
||||
|
Vector128<float> linearBlue = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, blue); |
||||
|
Vector128<float> linearL = Vector128.MultiplyAddEstimate( |
||||
|
Vector128.Create(BlueToL), |
||||
|
linearBlue, |
||||
|
Vector128.MultiplyAddEstimate(Vector128.Create(GreenToL), linearGreen, Vector128.Create(RedToL) * linearRed)); |
||||
|
|
||||
|
Vector128<float> linearM = Vector128.MultiplyAddEstimate( |
||||
|
Vector128.Create(BlueToM), |
||||
|
linearBlue, |
||||
|
Vector128.MultiplyAddEstimate(Vector128.Create(GreenToM), linearGreen, Vector128.Create(RedToM) * linearRed)); |
||||
|
|
||||
|
Vector128<float> linearS = Vector128.MultiplyAddEstimate( |
||||
|
Vector128.Create(BlueToS), |
||||
|
linearBlue, |
||||
|
Vector128.MultiplyAddEstimate(Vector128.Create(GreenToS), linearGreen, Vector128.Create(RedToS) * linearRed)); |
||||
|
|
||||
|
Vector128<float> nonlinearL = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearL); |
||||
|
Vector128<float> nonlinearM = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearM); |
||||
|
Vector128<float> nonlinearS = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearS); |
||||
|
intensity = Vector128.MultiplyAddEstimate( |
||||
|
Vector128.Create(SToIntensity), |
||||
|
nonlinearS, |
||||
|
Vector128.MultiplyAddEstimate(Vector128.Create(MToIntensity), nonlinearM, Vector128.Create(LToIntensity) * nonlinearL)); |
||||
|
|
||||
|
protan = Vector128.MultiplyAddEstimate( |
||||
|
Vector128.Create(SToProtan), |
||||
|
nonlinearS, |
||||
|
Vector128.MultiplyAddEstimate(Vector128.Create(MToProtan), nonlinearM, Vector128.Create(LToProtan) * nonlinearL)); |
||||
|
|
||||
|
tritan = Vector128.MultiplyAddEstimate( |
||||
|
Vector128.Create(SToTritan), |
||||
|
nonlinearS, |
||||
|
Vector128.MultiplyAddEstimate(Vector128.Create(MToTritan), nonlinearM, Vector128.Create(LToTritan) * nonlinearL)); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void ConvertFromRgb( |
||||
|
Vector256<float> red, |
||||
|
Vector256<float> green, |
||||
|
Vector256<float> blue, |
||||
|
in Av1ColorConversionParameters parameters, |
||||
|
out Vector256<float> intensity, |
||||
|
out Vector256<float> protan, |
||||
|
out Vector256<float> tritan) |
||||
|
{ |
||||
|
Vector256<float> linearRed = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, red); |
||||
|
Vector256<float> linearGreen = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, green); |
||||
|
Vector256<float> linearBlue = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, blue); |
||||
|
Vector256<float> linearL = Vector256.MultiplyAddEstimate( |
||||
|
Vector256.Create(BlueToL), |
||||
|
linearBlue, |
||||
|
Vector256.MultiplyAddEstimate(Vector256.Create(GreenToL), linearGreen, Vector256.Create(RedToL) * linearRed)); |
||||
|
|
||||
|
Vector256<float> linearM = Vector256.MultiplyAddEstimate( |
||||
|
Vector256.Create(BlueToM), |
||||
|
linearBlue, |
||||
|
Vector256.MultiplyAddEstimate(Vector256.Create(GreenToM), linearGreen, Vector256.Create(RedToM) * linearRed)); |
||||
|
|
||||
|
Vector256<float> linearS = Vector256.MultiplyAddEstimate( |
||||
|
Vector256.Create(BlueToS), |
||||
|
linearBlue, |
||||
|
Vector256.MultiplyAddEstimate(Vector256.Create(GreenToS), linearGreen, Vector256.Create(RedToS) * linearRed)); |
||||
|
|
||||
|
Vector256<float> nonlinearL = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearL); |
||||
|
Vector256<float> nonlinearM = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearM); |
||||
|
Vector256<float> nonlinearS = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearS); |
||||
|
intensity = Vector256.MultiplyAddEstimate( |
||||
|
Vector256.Create(SToIntensity), |
||||
|
nonlinearS, |
||||
|
Vector256.MultiplyAddEstimate(Vector256.Create(MToIntensity), nonlinearM, Vector256.Create(LToIntensity) * nonlinearL)); |
||||
|
|
||||
|
protan = Vector256.MultiplyAddEstimate( |
||||
|
Vector256.Create(SToProtan), |
||||
|
nonlinearS, |
||||
|
Vector256.MultiplyAddEstimate(Vector256.Create(MToProtan), nonlinearM, Vector256.Create(LToProtan) * nonlinearL)); |
||||
|
|
||||
|
tritan = Vector256.MultiplyAddEstimate( |
||||
|
Vector256.Create(SToTritan), |
||||
|
nonlinearS, |
||||
|
Vector256.MultiplyAddEstimate(Vector256.Create(MToTritan), nonlinearM, Vector256.Create(LToTritan) * nonlinearL)); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void ConvertFromRgb( |
||||
|
Vector512<float> red, |
||||
|
Vector512<float> green, |
||||
|
Vector512<float> blue, |
||||
|
in Av1ColorConversionParameters parameters, |
||||
|
out Vector512<float> intensity, |
||||
|
out Vector512<float> protan, |
||||
|
out Vector512<float> tritan) |
||||
|
{ |
||||
|
Vector512<float> linearRed = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, red); |
||||
|
Vector512<float> linearGreen = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, green); |
||||
|
Vector512<float> linearBlue = Av1TransferFunctions.ToLinear(parameters.TransferCharacteristics, blue); |
||||
|
Vector512<float> linearL = Vector512.MultiplyAddEstimate( |
||||
|
Vector512.Create(BlueToL), |
||||
|
linearBlue, |
||||
|
Vector512.MultiplyAddEstimate(Vector512.Create(GreenToL), linearGreen, Vector512.Create(RedToL) * linearRed)); |
||||
|
|
||||
|
Vector512<float> linearM = Vector512.MultiplyAddEstimate( |
||||
|
Vector512.Create(BlueToM), |
||||
|
linearBlue, |
||||
|
Vector512.MultiplyAddEstimate(Vector512.Create(GreenToM), linearGreen, Vector512.Create(RedToM) * linearRed)); |
||||
|
|
||||
|
Vector512<float> linearS = Vector512.MultiplyAddEstimate( |
||||
|
Vector512.Create(BlueToS), |
||||
|
linearBlue, |
||||
|
Vector512.MultiplyAddEstimate(Vector512.Create(GreenToS), linearGreen, Vector512.Create(RedToS) * linearRed)); |
||||
|
|
||||
|
Vector512<float> nonlinearL = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearL); |
||||
|
Vector512<float> nonlinearM = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearM); |
||||
|
Vector512<float> nonlinearS = Av1TransferFunctions.ToGamma(parameters.TransferCharacteristics, linearS); |
||||
|
intensity = Vector512.MultiplyAddEstimate( |
||||
|
Vector512.Create(SToIntensity), |
||||
|
nonlinearS, |
||||
|
Vector512.MultiplyAddEstimate(Vector512.Create(MToIntensity), nonlinearM, Vector512.Create(LToIntensity) * nonlinearL)); |
||||
|
|
||||
|
protan = Vector512.MultiplyAddEstimate( |
||||
|
Vector512.Create(SToProtan), |
||||
|
nonlinearS, |
||||
|
Vector512.MultiplyAddEstimate(Vector512.Create(MToProtan), nonlinearM, Vector512.Create(LToProtan) * nonlinearL)); |
||||
|
|
||||
|
tritan = Vector512.MultiplyAddEstimate( |
||||
|
Vector512.Create(SToTritan), |
||||
|
nonlinearS, |
||||
|
Vector512.MultiplyAddEstimate(Vector512.Create(MToTritan), nonlinearM, Vector512.Create(LToTritan) * nonlinearL)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,222 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.Intrinsics; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1; |
||||
|
|
||||
|
internal abstract partial class Av1ColorConverterBase |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Implements the YCgCo-Re and YCgCo-Ro integer lifting transforms for scalar and SIMD lanes.
|
||||
|
/// </summary>
|
||||
|
internal readonly struct Av1YCgCoReversibleColorOperator : IAv1ColorOperator |
||||
|
{ |
||||
|
/// <inheritdoc/>
|
||||
|
public static bool ChromaUsesLumaRange => false; |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void ConvertToRgb(ref float y, ref float cg, ref float co, in Av1ColorConversionParameters parameters) |
||||
|
{ |
||||
|
int yCode = (int)MathF.Floor((y * parameters.EncodedSampleMaximum) + 0.5F); |
||||
|
int cgCode = (int)MathF.Floor((cg * parameters.EncodedSampleMaximum) + 0.5F); |
||||
|
int coCode = (int)MathF.Floor((co * parameters.EncodedSampleMaximum) + 0.5F); |
||||
|
|
||||
|
// Signed arithmetic shifts are part of the reversible lifting definition. In particular, they preserve
|
||||
|
// the specified floor division for negative odd Cg and Co values instead of truncating toward zero.
|
||||
|
int temporary = yCode - (cgCode >> 1); |
||||
|
int greenCode = Numerics.Clamp(temporary + cgCode, 0, (int)parameters.RgbSampleMaximum); |
||||
|
int blueCode = Numerics.Clamp(temporary - (coCode >> 1), 0, (int)parameters.RgbSampleMaximum); |
||||
|
int redCode = Numerics.Clamp(blueCode + coCode, 0, (int)parameters.RgbSampleMaximum); |
||||
|
float inverseRgbScale = 1F / parameters.RgbScale; |
||||
|
y = (redCode - parameters.RgbBias) * inverseRgbScale; |
||||
|
cg = (greenCode - parameters.RgbBias) * inverseRgbScale; |
||||
|
co = (blueCode - parameters.RgbBias) * inverseRgbScale; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void ConvertToRgb( |
||||
|
ref Vector128<float> y, |
||||
|
ref Vector128<float> cg, |
||||
|
ref Vector128<float> co, |
||||
|
in Av1ColorConversionParameters parameters) |
||||
|
{ |
||||
|
Vector128<float> encodedMaximum = Vector128.Create(parameters.EncodedSampleMaximum); |
||||
|
Vector128<float> half = Vector128.Create(0.5F); |
||||
|
Vector128<int> yCode = Vector128.ConvertToInt32(Vector128.Floor((y * encodedMaximum) + half)); |
||||
|
Vector128<int> cgCode = Vector128.ConvertToInt32(Vector128.Floor((cg * encodedMaximum) + half)); |
||||
|
Vector128<int> coCode = Vector128.ConvertToInt32(Vector128.Floor((co * encodedMaximum) + half)); |
||||
|
|
||||
|
// Integer lanes preserve the normative arithmetic shifts; converting the lifting stages back to
|
||||
|
// floating point would change negative odd Cg and Co values and break reversibility.
|
||||
|
Vector128<int> temporary = yCode - Vector128.ShiftRightArithmetic(cgCode, 1); |
||||
|
Vector128<int> zero = Vector128<int>.Zero; |
||||
|
Vector128<int> rgbMaximum = Vector128.Create((int)parameters.RgbSampleMaximum); |
||||
|
Vector128<int> greenCode = Vector128.Min(Vector128.Max(temporary + cgCode, zero), rgbMaximum); |
||||
|
Vector128<int> blueCode = Vector128.Min(Vector128.Max(temporary - Vector128.ShiftRightArithmetic(coCode, 1), zero), rgbMaximum); |
||||
|
Vector128<int> redCode = Vector128.Min(Vector128.Max(blueCode + coCode, zero), rgbMaximum); |
||||
|
Vector128<float> rgbBias = Vector128.Create(parameters.RgbBias); |
||||
|
Vector128<float> inverseRgbScale = Vector128.Create(1F / parameters.RgbScale); |
||||
|
y = (Vector128.ConvertToSingle(redCode) - rgbBias) * inverseRgbScale; |
||||
|
cg = (Vector128.ConvertToSingle(greenCode) - rgbBias) * inverseRgbScale; |
||||
|
co = (Vector128.ConvertToSingle(blueCode) - rgbBias) * inverseRgbScale; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void ConvertToRgb( |
||||
|
ref Vector256<float> y, |
||||
|
ref Vector256<float> cg, |
||||
|
ref Vector256<float> co, |
||||
|
in Av1ColorConversionParameters parameters) |
||||
|
{ |
||||
|
Vector256<float> encodedMaximum = Vector256.Create(parameters.EncodedSampleMaximum); |
||||
|
Vector256<float> half = Vector256.Create(0.5F); |
||||
|
Vector256<int> yCode = Vector256.ConvertToInt32(Vector256.Floor((y * encodedMaximum) + half)); |
||||
|
Vector256<int> cgCode = Vector256.ConvertToInt32(Vector256.Floor((cg * encodedMaximum) + half)); |
||||
|
Vector256<int> coCode = Vector256.ConvertToInt32(Vector256.Floor((co * encodedMaximum) + half)); |
||||
|
Vector256<int> temporary = yCode - Vector256.ShiftRightArithmetic(cgCode, 1); |
||||
|
Vector256<int> zero = Vector256<int>.Zero; |
||||
|
Vector256<int> rgbMaximum = Vector256.Create((int)parameters.RgbSampleMaximum); |
||||
|
Vector256<int> greenCode = Vector256.Min(Vector256.Max(temporary + cgCode, zero), rgbMaximum); |
||||
|
Vector256<int> blueCode = Vector256.Min(Vector256.Max(temporary - Vector256.ShiftRightArithmetic(coCode, 1), zero), rgbMaximum); |
||||
|
Vector256<int> redCode = Vector256.Min(Vector256.Max(blueCode + coCode, zero), rgbMaximum); |
||||
|
Vector256<float> rgbBias = Vector256.Create(parameters.RgbBias); |
||||
|
Vector256<float> inverseRgbScale = Vector256.Create(1F / parameters.RgbScale); |
||||
|
y = (Vector256.ConvertToSingle(redCode) - rgbBias) * inverseRgbScale; |
||||
|
cg = (Vector256.ConvertToSingle(greenCode) - rgbBias) * inverseRgbScale; |
||||
|
co = (Vector256.ConvertToSingle(blueCode) - rgbBias) * inverseRgbScale; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void ConvertToRgb( |
||||
|
ref Vector512<float> y, |
||||
|
ref Vector512<float> cg, |
||||
|
ref Vector512<float> co, |
||||
|
in Av1ColorConversionParameters parameters) |
||||
|
{ |
||||
|
Vector512<float> encodedMaximum = Vector512.Create(parameters.EncodedSampleMaximum); |
||||
|
Vector512<float> half = Vector512.Create(0.5F); |
||||
|
Vector512<int> yCode = Vector512.ConvertToInt32(Vector512.Floor((y * encodedMaximum) + half)); |
||||
|
Vector512<int> cgCode = Vector512.ConvertToInt32(Vector512.Floor((cg * encodedMaximum) + half)); |
||||
|
Vector512<int> coCode = Vector512.ConvertToInt32(Vector512.Floor((co * encodedMaximum) + half)); |
||||
|
Vector512<int> temporary = yCode - Vector512.ShiftRightArithmetic(cgCode, 1); |
||||
|
Vector512<int> zero = Vector512<int>.Zero; |
||||
|
Vector512<int> rgbMaximum = Vector512.Create((int)parameters.RgbSampleMaximum); |
||||
|
Vector512<int> greenCode = Vector512.Min(Vector512.Max(temporary + cgCode, zero), rgbMaximum); |
||||
|
Vector512<int> blueCode = Vector512.Min(Vector512.Max(temporary - Vector512.ShiftRightArithmetic(coCode, 1), zero), rgbMaximum); |
||||
|
Vector512<int> redCode = Vector512.Min(Vector512.Max(blueCode + coCode, zero), rgbMaximum); |
||||
|
Vector512<float> rgbBias = Vector512.Create(parameters.RgbBias); |
||||
|
Vector512<float> inverseRgbScale = Vector512.Create(1F / parameters.RgbScale); |
||||
|
y = (Vector512.ConvertToSingle(redCode) - rgbBias) * inverseRgbScale; |
||||
|
cg = (Vector512.ConvertToSingle(greenCode) - rgbBias) * inverseRgbScale; |
||||
|
co = (Vector512.ConvertToSingle(blueCode) - rgbBias) * inverseRgbScale; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void ConvertFromRgb( |
||||
|
float red, |
||||
|
float green, |
||||
|
float blue, |
||||
|
in Av1ColorConversionParameters parameters, |
||||
|
out float y, |
||||
|
out float cg, |
||||
|
out float co) |
||||
|
{ |
||||
|
int redCode = (int)MathF.Floor((red * parameters.RgbScale) + parameters.RgbBias + 0.5F); |
||||
|
int greenCode = (int)MathF.Floor((green * parameters.RgbScale) + parameters.RgbBias + 0.5F); |
||||
|
int blueCode = (int)MathF.Floor((blue * parameters.RgbScale) + parameters.RgbBias + 0.5F); |
||||
|
int coCode = redCode - blueCode; |
||||
|
int temporary = blueCode + (coCode >> 1); |
||||
|
int cgCode = greenCode - temporary; |
||||
|
int yCode = temporary + (cgCode >> 1); |
||||
|
float inverseEncodedMaximum = 1F / parameters.EncodedSampleMaximum; |
||||
|
y = yCode * inverseEncodedMaximum; |
||||
|
cg = cgCode * inverseEncodedMaximum; |
||||
|
co = coCode * inverseEncodedMaximum; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void ConvertFromRgb( |
||||
|
Vector128<float> red, |
||||
|
Vector128<float> green, |
||||
|
Vector128<float> blue, |
||||
|
in Av1ColorConversionParameters parameters, |
||||
|
out Vector128<float> y, |
||||
|
out Vector128<float> cg, |
||||
|
out Vector128<float> co) |
||||
|
{ |
||||
|
Vector128<float> rgbScale = Vector128.Create(parameters.RgbScale); |
||||
|
Vector128<float> quantizationOffset = Vector128.Create(parameters.RgbBias + 0.5F); |
||||
|
Vector128<int> redCode = Vector128.ConvertToInt32(Vector128.Floor((red * rgbScale) + quantizationOffset)); |
||||
|
Vector128<int> greenCode = Vector128.ConvertToInt32(Vector128.Floor((green * rgbScale) + quantizationOffset)); |
||||
|
Vector128<int> blueCode = Vector128.ConvertToInt32(Vector128.Floor((blue * rgbScale) + quantizationOffset)); |
||||
|
Vector128<int> coCode = redCode - blueCode; |
||||
|
Vector128<int> temporary = blueCode + Vector128.ShiftRightArithmetic(coCode, 1); |
||||
|
Vector128<int> cgCode = greenCode - temporary; |
||||
|
Vector128<int> yCode = temporary + Vector128.ShiftRightArithmetic(cgCode, 1); |
||||
|
Vector128<float> inverseEncodedMaximum = Vector128.Create(1F / parameters.EncodedSampleMaximum); |
||||
|
y = Vector128.ConvertToSingle(yCode) * inverseEncodedMaximum; |
||||
|
cg = Vector128.ConvertToSingle(cgCode) * inverseEncodedMaximum; |
||||
|
co = Vector128.ConvertToSingle(coCode) * inverseEncodedMaximum; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void ConvertFromRgb( |
||||
|
Vector256<float> red, |
||||
|
Vector256<float> green, |
||||
|
Vector256<float> blue, |
||||
|
in Av1ColorConversionParameters parameters, |
||||
|
out Vector256<float> y, |
||||
|
out Vector256<float> cg, |
||||
|
out Vector256<float> co) |
||||
|
{ |
||||
|
Vector256<float> rgbScale = Vector256.Create(parameters.RgbScale); |
||||
|
Vector256<float> quantizationOffset = Vector256.Create(parameters.RgbBias + 0.5F); |
||||
|
Vector256<int> redCode = Vector256.ConvertToInt32(Vector256.Floor((red * rgbScale) + quantizationOffset)); |
||||
|
Vector256<int> greenCode = Vector256.ConvertToInt32(Vector256.Floor((green * rgbScale) + quantizationOffset)); |
||||
|
Vector256<int> blueCode = Vector256.ConvertToInt32(Vector256.Floor((blue * rgbScale) + quantizationOffset)); |
||||
|
Vector256<int> coCode = redCode - blueCode; |
||||
|
Vector256<int> temporary = blueCode + Vector256.ShiftRightArithmetic(coCode, 1); |
||||
|
Vector256<int> cgCode = greenCode - temporary; |
||||
|
Vector256<int> yCode = temporary + Vector256.ShiftRightArithmetic(cgCode, 1); |
||||
|
Vector256<float> inverseEncodedMaximum = Vector256.Create(1F / parameters.EncodedSampleMaximum); |
||||
|
y = Vector256.ConvertToSingle(yCode) * inverseEncodedMaximum; |
||||
|
cg = Vector256.ConvertToSingle(cgCode) * inverseEncodedMaximum; |
||||
|
co = Vector256.ConvertToSingle(coCode) * inverseEncodedMaximum; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static void ConvertFromRgb( |
||||
|
Vector512<float> red, |
||||
|
Vector512<float> green, |
||||
|
Vector512<float> blue, |
||||
|
in Av1ColorConversionParameters parameters, |
||||
|
out Vector512<float> y, |
||||
|
out Vector512<float> cg, |
||||
|
out Vector512<float> co) |
||||
|
{ |
||||
|
Vector512<float> rgbScale = Vector512.Create(parameters.RgbScale); |
||||
|
Vector512<float> quantizationOffset = Vector512.Create(parameters.RgbBias + 0.5F); |
||||
|
Vector512<int> redCode = Vector512.ConvertToInt32(Vector512.Floor((red * rgbScale) + quantizationOffset)); |
||||
|
Vector512<int> greenCode = Vector512.ConvertToInt32(Vector512.Floor((green * rgbScale) + quantizationOffset)); |
||||
|
Vector512<int> blueCode = Vector512.ConvertToInt32(Vector512.Floor((blue * rgbScale) + quantizationOffset)); |
||||
|
Vector512<int> coCode = redCode - blueCode; |
||||
|
Vector512<int> temporary = blueCode + Vector512.ShiftRightArithmetic(coCode, 1); |
||||
|
Vector512<int> cgCode = greenCode - temporary; |
||||
|
Vector512<int> yCode = temporary + Vector512.ShiftRightArithmetic(cgCode, 1); |
||||
|
Vector512<float> inverseEncodedMaximum = Vector512.Create(1F / parameters.EncodedSampleMaximum); |
||||
|
y = Vector512.ConvertToSingle(yCode) * inverseEncodedMaximum; |
||||
|
cg = Vector512.ConvertToSingle(cgCode) * inverseEncodedMaximum; |
||||
|
co = Vector512.ConvertToSingle(coCode) * inverseEncodedMaximum; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue