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 + }; +}