diff --git a/src/ImageSharp/Formats/Jxl/InlineArrays.cs b/src/ImageSharp/Formats/Jxl/InlineArrays.cs
index 627605170..f006d2fd0 100644
--- a/src/ImageSharp/Formats/Jxl/InlineArrays.cs
+++ b/src/ImageSharp/Formats/Jxl/InlineArrays.cs
@@ -7,15 +7,6 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Jxl;
-///
-/// Used by Butteraugli
-///
-[InlineArray(2)]
-internal struct InlineArray2
-{
- private T first;
-}
-
[InlineArray(3)]
internal struct InlineArray3
{
diff --git a/src/ImageSharp/Formats/Jxl/Processing/Decoder/JxlBoxContentDecoder.cs b/src/ImageSharp/Formats/Jxl/Processing/Decoder/JxlBoxContentDecoder.cs
index 5d1beddf3..d339e6908 100644
--- a/src/ImageSharp/Formats/Jxl/Processing/Decoder/JxlBoxContentDecoder.cs
+++ b/src/ImageSharp/Formats/Jxl/Processing/Decoder/JxlBoxContentDecoder.cs
@@ -56,7 +56,7 @@ internal sealed class JxlBoxContentDecoder
isUnbounded,
size);
- public void Process(Stream stream, JxlMemoryWriter writer)
+ public void Process(Stream stream, Stream writer)
{
byte[] cache = ArrayPool.Shared.Rent(16384);
diff --git a/src/ImageSharp/Formats/Jxl/Processing/Decoder/JxlDecoderCore.cs b/src/ImageSharp/Formats/Jxl/Processing/Decoder/JxlDecoderCore.cs
index 818f1ec76..5578f4753 100644
--- a/src/ImageSharp/Formats/Jxl/Processing/Decoder/JxlDecoderCore.cs
+++ b/src/ImageSharp/Formats/Jxl/Processing/Decoder/JxlDecoderCore.cs
@@ -2138,11 +2138,8 @@ internal sealed class JxlDecoderCore : ImageDecoderCore, IDisposable
Span startSlice = bufferSpan[(int)this.boxOutBufferPos..];
int status = this.boxContentDecoder!.Process(
- this.nextInput,
- this.availableInput,
- this.filePosition - this.boxContentsBegin,
- nextOut,
- ref availOut);
+ stream,
+ this.boxOutputBuffer);
long produced = startSlice.Length - availOut;
this.boxOutBufferPos += produced;
@@ -2290,7 +2287,7 @@ internal sealed class JxlDecoderCore : ImageDecoderCore, IDisposable
ThrowNotEnoughData();
}
- if (this.inputClosed || (this.eventsWanted & JxlDecoderStatus.) != 0)
+ if (this.inputClosed || (this.eventsWanted & JxlDecoderStatus.Box) != 0)
{
return JxlDecoderStatus.Success;
}
@@ -2704,7 +2701,7 @@ internal sealed class JxlDecoderCore : ImageDecoderCore, IDisposable
int status = this.ProcessBoxes(stream);
- if (status == Success)
+ if (status == JxlDecoderStatus.Success)
{
if (this.CanUseMoreCodestreamInput())
{
diff --git a/src/ImageSharp/Formats/Jxl/Processing/Decoder/JxlFrameDecoder.cs b/src/ImageSharp/Formats/Jxl/Processing/Decoder/JxlFrameDecoder.cs
index b17aacd5c..e127354a7 100644
--- a/src/ImageSharp/Formats/Jxl/Processing/Decoder/JxlFrameDecoder.cs
+++ b/src/ImageSharp/Formats/Jxl/Processing/Decoder/JxlFrameDecoder.cs
@@ -32,6 +32,31 @@ internal sealed class JxlFrameDecoder
private JxlProgressiveDetail progressiveDetail = JxlProgressiveDetail.Frames;
private List passesToPause = [];
+ ///
+ /// Gets a value indicating whether there are any DC groups left to decode.
+ ///
+ private bool ContainsDcGroupToDecode => this.decodedDcGroups.Any(x => x == 0);
+
+ private static int GetStride(int width, JxlPixelFormat format)
+ {
+ if (!JxlMath.SafeMultiply(BytesPerChannel(format.DataType), format.Channels, out int xStride))
+ {
+ throw new InvalidOperationException("Image too large");
+ }
+
+ if (!JxlMath.SafeMultiply(xStride, width, out int yStride))
+ {
+ throw new InvalidOperationException("Image too large");
+ }
+
+ if (!JxlMath.SafeRoundUpTo(yStride, format.Align, yStride))
+ {
+ throw new InvalidOperationException("Image too large");
+ }
+
+ return yStride;
+ }
+
public static void DecodeGlobalDcInfo(Configuration configuration, JxlBitReader reader, bool isJpeg, JxlPassesDecoderState state)
{
state.SharedStorage.Quantizer.Decode(reader);
diff --git a/src/ImageSharp/Formats/Jxl/Processing/JxlDctQuantWeightParameters.cs b/src/ImageSharp/Formats/Jxl/Processing/JxlDctQuantWeightParameters.cs
index 288d12995..8cd8a16ab 100644
--- a/src/ImageSharp/Formats/Jxl/Processing/JxlDctQuantWeightParameters.cs
+++ b/src/ImageSharp/Formats/Jxl/Processing/JxlDctQuantWeightParameters.cs
@@ -8,21 +8,22 @@ internal sealed class JxlDctQuantWeightParameters
private const int Log2MaxDistanceBands = 4;
private const int MaxDistanceBands = 1 + (1 << Log2MaxDistanceBands);
- private int numDistanceBands;
- private readonly float[][] distanceBands;
-
public JxlDctQuantWeightParameters()
{
- this.distanceBands = new float[3][];
+ this.DistanceBands = new float[3][];
for (int i = 0; i < 3; i++)
{
- this.distanceBands[i] = new float[MaxDistanceBands];
+ this.DistanceBands[i] = new float[MaxDistanceBands];
}
}
public JxlDctQuantWeightParameters(float[][] distanceBands, int numDistanceBands)
{
- this.numDistanceBands = numDistanceBands;
- this.distanceBands = distanceBands;
+ this.NumDistanceBands = numDistanceBands;
+ this.DistanceBands = distanceBands;
}
+
+ public int NumDistanceBands { get; set; }
+
+ public float[][] DistanceBands { get; }
}
diff --git a/src/ImageSharp/Formats/Jxl/Processing/JxlQuantMode.cs b/src/ImageSharp/Formats/Jxl/Processing/JxlQuantMode.cs
index fa4dd2051..1b974343a 100644
--- a/src/ImageSharp/Formats/Jxl/Processing/JxlQuantMode.cs
+++ b/src/ImageSharp/Formats/Jxl/Processing/JxlQuantMode.cs
@@ -4,16 +4,48 @@
namespace SixLabors.ImageSharp.Formats.Jxl.Processing;
///
-/// Quantization mode.
+/// Specifies which algorithm should be used to quantize coefficients.
///
internal enum JxlQuantMode : byte
{
+ ///
+ /// The quantizer relies on predefined tables for quantization.
+ /// The idea is similar to Huffman coding.
+ ///
Library,
+
+ ///
+ /// The quantizer uses an Identity transform.
+ ///
Id,
+
+ ///
+ /// The quantizer uses a 2x2 Discrete Cosine Transform.
+ ///
Dct2,
+
+ ///
+ /// The quantizer uses a 4x4 Discrete Cosine Transform.
+ ///
Dct4,
+
+ ///
+ /// The quantizer uses a 4x8 Discrete Cosine Transform.
+ ///
Dct4x8,
+
+ ///
+ /// The quantizer uses the AFV transform.
+ ///
Afv,
+
+ ///
+ /// The quantizer uses a Discrete Cosine Transform with custom block size.
+ ///
Dct,
+
+ ///
+ /// No quantization is performed. Input data becomes the output as-is.
+ ///
Raw
}
diff --git a/src/ImageSharp/Formats/Jxl/Processing/JxlQuantizerEncoding.cs b/src/ImageSharp/Formats/Jxl/Processing/JxlQuantizerEncoding.cs
new file mode 100644
index 000000000..a50b7ad25
--- /dev/null
+++ b/src/ImageSharp/Formats/Jxl/Processing/JxlQuantizerEncoding.cs
@@ -0,0 +1,180 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System.Runtime.CompilerServices;
+
+namespace SixLabors.ImageSharp.Formats.Jxl.Processing;
+
+///
+/// Specifies weights and quantizer modes.
+///
+internal sealed class JxlQuantizerEncoding
+{
+ ///
+ /// Gets or sets the kind of transform used for this quantizer encoding.
+ ///
+ public JxlQuantMode Mode { get; set; }
+
+ ///
+ /// Gets or sets the weights for DCT4+ tables.
+ ///
+ public JxlDctQuantWeightParameters? DctParameters { get; set; }
+
+ ///
+ /// Gets or sets the weights for the 4x4 sub-block in AFV.
+ ///
+ public JxlDctQuantWeightParameters? DctParametersAfv4x4 { get; set; }
+
+ ///
+ /// Gets or sets the weights for the identity transform.
+ ///
+ public InlineArray3> IdWeights { get; set; }
+
+ ///
+ /// Gets or sets the weights for the DCT2 transform.
+ ///
+ public InlineArray3> Dct2Weights { get; set; }
+
+ ///
+ /// Gets or sets the multipliers for the DCT4 transform.
+ ///
+ public InlineArray3> Dct4Multipliers { get; set; }
+
+ ///
+ /// Gets or sets the weights for the AFV transform.
+ ///
+ public InlineArray3> AfvWeights { get; set; }
+
+ ///
+ /// Gets or sets the multipliers for the 4x8 DCT block-based transform.
+ ///
+ public InlineArray3 Dct4x8Multipliers { get; set; }
+
+ ///
+ /// Gets or sets the explicit quantization table (like in JPEG).
+ ///
+ ///
+ /// Only used when == .
+ ///
+ public int[]? QuantizationTable { get; set; }
+
+ ///
+ /// Gets or sets the denominator for each item in the explicit quantization table.
+ ///
+ ///
+ /// Only used when == .
+ ///
+ public float QuantizationTableDenominator { get; set; } = 1f / (8 * 255);
+
+ ///
+ /// Gets or sets a value indicating which predefined table to use. The value is
+ /// only used when == .
+ ///
+ public byte Predefined { get; set; }
+
+ ///
+ /// Creates a new quantizer encoding with the Library quantizer mode
+ /// and the specified library index.
+ ///
+ /// The library index (aka predefined table).
+ /// A new Library quantizer encoding.
+ public static JxlQuantizerEncoding Library(int libraryIndex)
+ {
+ DebugGuard.MustBeLessThan(libraryIndex, JxlQuantWeights.NumPredefinedTables, nameof(libraryIndex));
+
+ return new()
+ {
+ Mode = JxlQuantMode.Library,
+ Predefined = (byte)libraryIndex
+ };
+ }
+
+ ///
+ /// Creates a new quantizer encoding with the Identity quantizer mode
+ /// and the specified XYB/identity weights.
+ ///
+ /// Weights for the identity transform.
+ /// A new Identity quantizer encoding.
+ public static JxlQuantizerEncoding Identity(in InlineArray3> xybWeights)
+ => new()
+ {
+ Mode = JxlQuantMode.Id,
+ IdWeights = xybWeights
+ };
+
+ ///
+ /// Creates a new quantizer encoding with the DCT2x2 quantizer mode
+ /// and the specified XYB/DCT2x2 weights.
+ ///
+ /// Weights for the DCT2x2 transform.
+ /// A new DCT2x2 quantizer encoding.
+ public static JxlQuantizerEncoding Dct2(in InlineArray3> xybWeights)
+ => new()
+ {
+ Mode = JxlQuantMode.Dct2,
+ Dct2Weights = xybWeights
+ };
+
+ ///
+ /// Creates a new quantizer encoding with the DCT4x4 quantizer mode,
+ /// the specified XYB/DCT4x4 multipliers, and quantizer weight parameters.
+ ///
+ /// Quantizer weights for the DCT4x4 transform.
+ /// XYB multipliers for the DCT4x4 transform.
+ /// A new DCT4x4 quantizer encoding.
+ public static JxlQuantizerEncoding Dct4(JxlDctQuantWeightParameters parameters, in InlineArray3> xybMul)
+ => new()
+ {
+ Mode = JxlQuantMode.Dct4,
+ DctParameters = parameters,
+ Dct4Multipliers = xybMul
+ };
+
+ ///
+ /// Creates a new quantizer encoding with the DCT4x8 quantizer mode,
+ /// the specified XYB/DCT4x8 multipliers, and quantizer weight parameters.
+ ///
+ /// Quantizer weights for the DCT4x8 transform.
+ /// XYB multipliers for the DCT4x8 transform.
+ /// A new DCT4x8 quantizer encoding.
+ public static JxlQuantizerEncoding Dct4x8(JxlDctQuantWeightParameters parameters, in InlineArray3 xybMul)
+ => new()
+ {
+ Mode = JxlQuantMode.Dct4x8,
+ DctParameters = parameters,
+ Dct4x8Multipliers = xybMul
+ };
+
+ ///
+ /// Creates a new quantizer encoding with the DCT quantizer mode
+ /// and quantizer weight parameters.
+ ///
+ /// Quantizer weights for the DCT transform.
+ /// A new DCT quantizer encoding.
+ public static JxlQuantizerEncoding Dct(JxlDctQuantWeightParameters parameters)
+ => new()
+ {
+ Mode = JxlQuantMode.Dct,
+ DctParameters = parameters,
+ };
+
+ ///
+ /// Creates a new quantizer encoding with the AFV quantizer mode,
+ /// quantizer weight parameters for 4x8/4x4 blocks, and weights.
+ ///
+ /// Quantizer weights for the 4x8 sub-block for the AFV transform.
+ /// Quantizer weights for the 4x4 sub-block for the AFV transform.
+ /// Quantizer weights.
+ /// A new DCT quantizer encoding.
+ public static JxlQuantizerEncoding Afv(
+ JxlDctQuantWeightParameters params4x8,
+ JxlDctQuantWeightParameters params4x4,
+ in InlineArray3> weights)
+ => new()
+ {
+ Mode = JxlQuantMode.Afv,
+ DctParameters = params4x8,
+ AfvWeights = weights,
+ DctParametersAfv4x4 = params4x4
+ };
+}