diff --git a/src/ImageSharp/Formats/Jxl/Cms/TransferFunctions/JxlBt709TransferFunction.cs b/src/ImageSharp/Formats/Jxl/Cms/TransferFunctions/JxlBt709TransferFunction.cs
new file mode 100644
index 0000000000..99b6548b60
--- /dev/null
+++ b/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;
+
+///
+/// ITU-R BT.709 transfer function
+///
+internal static class JxlBt709TransferFunction
+{
+ public static double EncodedFromDisplay(double d)
+ {
+ if (d < Threshold)
+ {
+ return MulLow * d;
+ }
+
+ return (MulHi * Math.Pow(d, PowHi)) + Sub;
+ }
+}
diff --git a/src/ImageSharp/Formats/Jxl/Cms/TransferFunctions/JxlHybridLogGammaTransferFunction.cs b/src/ImageSharp/Formats/Jxl/Cms/TransferFunctions/JxlHybridLogGammaTransferFunction.cs
new file mode 100644
index 0000000000..3957c3ac7b
--- /dev/null
+++ b/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
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// Use static methods. Don't instantiate this class.
+ ///
+ private JxlHybridLogGammaTransferFunction()
+ {
+ }
+
+ public static Vector EncodedFromDisplay(Vector x)
+ {
+ Vector sign = Vector.Create(0x80000000u).As();
+ Vector originalSign = x & sign;
+ x = Vector.AndNot(sign, x);
+ Vector belowInverse12 = Vector.LessThan(x, Vector.Create(Inverse12));
+
+ Vector lo = Vector.SquareRoot(Vector.Create(3.0f) * x);
+ Vector hi = (Vector.Create(A * JxlMath.InverseLog2E) * Vector.Log2((Vector.Create(12f) * x) + Vector.Create(-B))) + Vector.Create(C);
+ Vector magnitude = Vector.ConditionalSelect(belowInverse12, lo, hi);
+ return Vector.AndNot(sign, magnitude) | originalSign;
+ }
+
+ public static Vector DisplayFromEncoded(Vector x)
+ {
+ Vector sign = Vector.Create(0x80000000u).As();
+ Vector originalSign = x & sign;
+ x = Vector.AndNot(sign, x);
+ Vector below05 = Vector.LessThan(x, Vector.Create(0.5f));
+
+ Vector lo = x * (x * Vector.Create(1f / 3f));
+ Vector hi = (Pow2(x * Vector.Create(HiPow)) * Vector.Create(HiMul)) + Vector.Create(HiAdd);
+ Vector magnitude = Vector.ConditionalSelect(below05, lo, hi);
+
+ return Vector.AndNot(sign, magnitude) | originalSign;
+ }
+
+ private static Vector Pow2(Vector x) => x * x;
+}
diff --git a/src/ImageSharp/Formats/Jxl/Cms/TransferFunctions/JxlHybridLogGammaTransferFunctionBase.cs b/src/ImageSharp/Formats/Jxl/Cms/TransferFunctions/JxlHybridLogGammaTransferFunctionBase.cs
new file mode 100644
index 0000000000..795db106c3
--- /dev/null
+++ b/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;
+
+///
+/// Base class for HLG transfer function.
+///
+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;
+
+ ///
+ /// Converts encoded signal to display signal.
+ ///
+ /// The encoded signal
+ /// The display signal
+ protected static double DisplayFromEncoded(double encoded) => Ootf(InverseOotf(encoded));
+
+ ///
+ /// Converts display signal to encoded signal.
+ ///
+ /// The display signal
+ /// The encoded signal
+ protected static double EncodedFromDisplay(double display) => Oetf(InverseOetf(display));
+
+ ///
+ /// Opto-Electronic Transfer Function - converts
+ /// real-world scene light (s) into a digital video
+ /// signal inside a camera.
+ ///
+ ///
+ ///
+ ///
+ /// Scene light
+ /// Digital video signal
+ 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);
+ }
+
+ ///
+ /// Inverse Opto-Electronic Transfer Function - converts
+ /// digital video signal into a real-world scene light.
+ ///
+ ///
+ ///
+ ///
+ /// Digital video signal
+ /// Scene light
+ 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);
+ }
+
+ ///
+ /// Opto-Optical Transfer Function - as-is.
+ ///
+ ///
+ ///
+ ///
+ /// Input signal
+ /// Digital video signal
+ private static double Ootf(double s) => s;
+
+ ///
+ /// Inverse Opto-Optical Transfer Function - as-is.
+ ///
+ ///
+ ///
+ ///
+ /// Digital video signal
+ /// Scene light
+ private static double InverseOotf(double s) => s;
+}
diff --git a/src/ImageSharp/Formats/Jxl/Cms/TransferFunctions/JxlPerceptualQuantizationTransferFunctionBase.cs b/src/ImageSharp/Formats/Jxl/Cms/TransferFunctions/JxlPerceptualQuantizationTransferFunctionBase.cs
new file mode 100644
index 0000000000..eea7b06560
--- /dev/null
+++ b/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;
+
+///
+/// Base class for PQ transfer function.
+///
+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);
+ }
+}
diff --git a/src/ImageSharp/Formats/Jxl/Processing/JxlSimdUtils.cs b/src/ImageSharp/Formats/Jxl/Processing/JxlSimdUtils.cs
index dd8e7314ee..8da98bbf4f 100644
--- a/src/ImageSharp/Formats/Jxl/Processing/JxlSimdUtils.cs
+++ b/src/ImageSharp/Formats/Jxl/Processing/JxlSimdUtils.cs
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
+using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
@@ -101,4 +102,11 @@ internal static partial class JxlSimdUtils
}
}
}
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Vector Pow(Vector @base, Vector exponent)
+ {
+ Vector vec = Vector.Log2(@base) * exponent;
+ return vec * vec;
+ }
}
diff --git a/src/ImageSharp/Formats/Jxl/Processing/RenderPipeline/PatchDictionaryStage.cs b/src/ImageSharp/Formats/Jxl/Processing/RenderPipeline/PatchDictionaryStage.cs
index 0e6bf82745..f2f2f0054f 100644
--- a/src/ImageSharp/Formats/Jxl/Processing/RenderPipeline/PatchDictionaryStage.cs
+++ b/src/ImageSharp/Formats/Jxl/Processing/RenderPipeline/PatchDictionaryStage.cs
@@ -1,7 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-using System.Drawing;
using SixLabors.ImageSharp.Formats.Jxl.IO.Metadata;
using SixLabors.ImageSharp.Formats.Jxl.Processing.Decoder;
using SixLabors.ImageSharp.Memory;
diff --git a/src/ImageSharp/Formats/Jxl/Processing/RenderPipeline/WriteToOutputStage.cs b/src/ImageSharp/Formats/Jxl/Processing/RenderPipeline/WriteToOutputStage.cs
index ad790ed10a..d442faeac6 100644
--- a/src/ImageSharp/Formats/Jxl/Processing/RenderPipeline/WriteToOutputStage.cs
+++ b/src/ImageSharp/Formats/Jxl/Processing/RenderPipeline/WriteToOutputStage.cs
@@ -1,10 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-using System.Numerics;
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;