Browse Source

Add a few CMS transfer functions (incomplete)

pull/3153/head
winscripter 4 weeks ago
parent
commit
5739218437
  1. 20
      src/ImageSharp/Formats/Jxl/Cms/TransferFunctions/JxlBt709TransferFunction.cs
  2. 53
      src/ImageSharp/Formats/Jxl/Cms/TransferFunctions/JxlHybridLogGammaTransferFunction.cs
  3. 114
      src/ImageSharp/Formats/Jxl/Cms/TransferFunctions/JxlHybridLogGammaTransferFunctionBase.cs
  4. 54
      src/ImageSharp/Formats/Jxl/Cms/TransferFunctions/JxlPerceptualQuantizationTransferFunctionBase.cs
  5. 8
      src/ImageSharp/Formats/Jxl/Processing/JxlSimdUtils.cs
  6. 1
      src/ImageSharp/Formats/Jxl/Processing/RenderPipeline/PatchDictionaryStage.cs
  7. 3
      src/ImageSharp/Formats/Jxl/Processing/RenderPipeline/WriteToOutputStage.cs

20
src/ImageSharp/Formats/Jxl/Cms/TransferFunctions/JxlBt709TransferFunction.cs

@ -0,0 +1,20 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Jxl.Cms.TransferFunctions;
/// <summary>
/// ITU-R BT.709 transfer function
/// </summary>
internal static class JxlBt709TransferFunction
{
public static double EncodedFromDisplay(double d)
{
if (d < Threshold)
{
return MulLow * d;
}
return (MulHi * Math.Pow(d, PowHi)) + Sub;
}
}

53
src/ImageSharp/Formats/Jxl/Cms/TransferFunctions/JxlHybridLogGammaTransferFunction.cs

@ -0,0 +1,53 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Numerics;
using SixLabors.ImageSharp.Formats.Jxl.Processing;
namespace SixLabors.ImageSharp.Formats.Jxl.Cms.TransferFunctions;
internal sealed class JxlHybridLogGammaTransferFunction : JxlHybridLogGammaTransferFunctionBase
{
private const float HiAdd = B * Inverse12;
private const float HiMul = 0.003639807079052639f; // MathF.Exp(-C * RA) * Inverse12
private const float HiPow = 8.067285659607931f; // RA * JxlMath.InverseLog2E
/// <summary>
/// Initializes a new instance of the <see cref="JxlHybridLogGammaTransferFunction"/> class.
/// </summary>
/// <remarks>
/// Use static methods. Don't instantiate this class.
/// </remarks>
private JxlHybridLogGammaTransferFunction()
{
}
public static Vector<float> EncodedFromDisplay(Vector<float> x)
{
Vector<float> sign = Vector.Create(0x80000000u).As<uint, float>();
Vector<float> originalSign = x & sign;
x = Vector.AndNot(sign, x);
Vector<int> belowInverse12 = Vector.LessThan(x, Vector.Create(Inverse12));
Vector<float> lo = Vector.SquareRoot(Vector.Create(3.0f) * x);
Vector<float> hi = (Vector.Create(A * JxlMath.InverseLog2E) * Vector.Log2((Vector.Create(12f) * x) + Vector.Create(-B))) + Vector.Create(C);
Vector<float> magnitude = Vector.ConditionalSelect(belowInverse12, lo, hi);
return Vector.AndNot(sign, magnitude) | originalSign;
}
public static Vector<float> DisplayFromEncoded(Vector<float> x)
{
Vector<float> sign = Vector.Create(0x80000000u).As<uint, float>();
Vector<float> originalSign = x & sign;
x = Vector.AndNot(sign, x);
Vector<int> below05 = Vector.LessThan(x, Vector.Create(0.5f));
Vector<float> lo = x * (x * Vector.Create(1f / 3f));
Vector<float> hi = (Pow2(x * Vector.Create(HiPow)) * Vector.Create(HiMul)) + Vector.Create(HiAdd);
Vector<float> magnitude = Vector.ConditionalSelect(below05, lo, hi);
return Vector.AndNot(sign, magnitude) | originalSign;
}
private static Vector<float> Pow2(Vector<float> x) => x * x;
}

114
src/ImageSharp/Formats/Jxl/Cms/TransferFunctions/JxlHybridLogGammaTransferFunctionBase.cs

@ -0,0 +1,114 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Jxl.Cms.TransferFunctions;
/// <summary>
/// Base class for HLG transfer function.
/// </summary>
internal abstract class JxlHybridLogGammaTransferFunctionBase
{
// Shared constants used by transfer functions
protected const float A = 0.17883277f;
protected const float RA = 1.0f / A;
protected const float B = 1 - (4 * A);
protected const float C = 0.5599107295f;
protected const float Inverse12 = 1.0f / 12.0f;
/// <summary>
/// Converts encoded signal to display signal.
/// </summary>
/// <param name="encoded">The encoded signal</param>
/// <returns>The display signal</returns>
protected static double DisplayFromEncoded(double encoded) => Ootf(InverseOotf(encoded));
/// <summary>
/// Converts display signal to encoded signal.
/// </summary>
/// <param name="display">The display signal</param>
/// <returns>The encoded signal</returns>
protected static double EncodedFromDisplay(double display) => Oetf(InverseOetf(display));
/// <summary>
/// Opto-Electronic Transfer Function - converts
/// real-world scene light (s) into a digital video
/// signal inside a camera.
/// </summary>
/// <remarks>
/// <seealso href="https://en.wikipedia.org/wiki/Transfer_functions_in_imaging#Definition"/>
/// </remarks>
/// <param name="s">Scene light</param>
/// <returns>Digital video signal</returns>
private static double Oetf(double s)
{
if (s == 0)
{
return 0;
}
double originalSign = s;
s = Math.Abs(s);
if (s <= Inverse12)
{
return Math.CopySign(Math.Sqrt(3.0 * s), originalSign);
}
double e = (A * Math.Log((12 * s) - B)) + C;
DebugGuard.MustBeGreaterThan(e, 0.0, nameof(e));
return Math.CopySign(e, originalSign);
}
/// <summary>
/// Inverse Opto-Electronic Transfer Function - converts
/// digital video signal into a real-world scene light.
/// </summary>
/// <remarks>
/// <seealso href="https://en.wikipedia.org/wiki/Transfer_functions_in_imaging#Definition"/>
/// </remarks>
/// <param name="e">Digital video signal</param>
/// <returns>Scene light</returns>
private static double InverseOetf(double e)
{
if (e == 0)
{
return 0;
}
double originalSign = e;
e = Math.Abs(e);
if (e <= 0.5)
{
return Math.CopySign(e * e * (1.0 / 3), originalSign);
}
double s = (Math.Exp((e - C) * RA) + B) * Inverse12;
DebugGuard.MustBeGreaterThan(s, 0.0, nameof(s));
return Math.CopySign(s, originalSign);
}
/// <summary>
/// Opto-Optical Transfer Function - as-is.
/// </summary>
/// <remarks>
/// <seealso href="https://en.wikipedia.org/wiki/Transfer_functions_in_imaging#Definition"/>
/// </remarks>
/// <param name="s">Input signal</param>
/// <returns>Digital video signal</returns>
private static double Ootf(double s) => s;
/// <summary>
/// Inverse Opto-Optical Transfer Function - as-is.
/// </summary>
/// <remarks>
/// <seealso href="https://en.wikipedia.org/wiki/Transfer_functions_in_imaging#Definition"/>
/// </remarks>
/// <param name="s">Digital video signal</param>
/// <returns>Scene light</returns>
private static double InverseOotf(double s) => s;
}

54
src/ImageSharp/Formats/Jxl/Cms/TransferFunctions/JxlPerceptualQuantizationTransferFunctionBase.cs

@ -0,0 +1,54 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Jxl.Cms.TransferFunctions;
/// <summary>
/// Base class for PQ transfer function.
/// </summary>
internal abstract class JxlPerceptualQuantizationTransferFunctionBase
{
private const double M1 = 2610.0 / 16384;
private const double M2 = (2523.0 / 4096) * 128;
private const double C1 = 3424.0 / 4096;
private const double C2 = (2413.0 / 4096) * 32;
private const double C3 = (2392.0 / 4096) * 32;
protected static double DisplayFromEncoded(float displayIntensityTarget, double e)
{
if (e == 0.0)
{
return 0.0;
}
double originalSign = e;
e = Math.Abs(e);
double xp = Math.Pow(e, 1.0 / M2);
double num = Math.Max(xp - C1, 0.0);
double den = C2 - (C3 * xp);
double d = Math.Pow(num / den, 1.0 / M1);
return Math.CopySign(d * (10000.0 / displayIntensityTarget), originalSign);
}
protected static double EncodedFromDisplay(float displayIntensityTarget, double d)
{
if (d == 0.0)
{
return 0.0;
}
double originalSign = d;
d = Math.Abs(d);
double xp = Math.Pow(d * (displayIntensityTarget * (1 / 10000)), M1);
double num = C1 + (xp * C2);
double den = 1.0 + (xp * C3);
double e = Math.Pow(num / den, M2);
return Math.CopySign(e, originalSign);
}
}

8
src/ImageSharp/Formats/Jxl/Processing/JxlSimdUtils.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
@ -101,4 +102,11 @@ internal static partial class JxlSimdUtils
} }
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector<float> Pow(Vector<float> @base, Vector<float> exponent)
{
Vector<float> vec = Vector.Log2(@base) * exponent;
return vec * vec;
}
} }

1
src/ImageSharp/Formats/Jxl/Processing/RenderPipeline/PatchDictionaryStage.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Drawing;
using SixLabors.ImageSharp.Formats.Jxl.IO.Metadata; using SixLabors.ImageSharp.Formats.Jxl.IO.Metadata;
using SixLabors.ImageSharp.Formats.Jxl.Processing.Decoder; using SixLabors.ImageSharp.Formats.Jxl.Processing.Decoder;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;

3
src/ImageSharp/Formats/Jxl/Processing/RenderPipeline/WriteToOutputStage.cs

@ -1,10 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Numerics;
using SixLabors.ImageSharp.Formats.Jxl.IO.Metadata; using SixLabors.ImageSharp.Formats.Jxl.IO.Metadata;
using SixLabors.ImageSharp.Formats.Jxl.Processing.Image;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Jxl.Processing.RenderPipeline; namespace SixLabors.ImageSharp.Formats.Jxl.Processing.RenderPipeline;

Loading…
Cancel
Save