Browse Source

Add box content decoder with Brotli compression & reduce errors

pull/3153/head
winscripter 1 week ago
parent
commit
9f8eb4a434
  1. 108
      src/ImageSharp/Formats/Jxl/Processing/Decoder/JxlBoxContentDecoder.cs
  2. 10
      src/ImageSharp/Formats/Jxl/Processing/Decoder/JxlDecoderCore.cs

108
src/ImageSharp/Formats/Jxl/Processing/Decoder/JxlBoxContentDecoder.cs

@ -0,0 +1,108 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Buffers;
using System.IO.Compression;
using SixLabors.ImageSharp.Formats.Jxl.IO;
namespace SixLabors.ImageSharp.Formats.Jxl.Processing.Decoder;
/// <summary>
/// Allows decoding and decompressing box data.
/// </summary>
internal sealed class JxlBoxContentDecoder
{
/// <summary>
/// Specifies how many bytes to read to fetch box data. This is ignored
/// if the box extends till EOF.
/// </summary>
private ulong boxSize;
/// <summary>
/// When true the box size is ignored and is unbounded - that is, keeps going
/// till the end of the file or stream.
/// </summary>
private bool boxExtendsTillEnd;
/// <summary>
/// This contains flags that determine whether the box is Brotli-compressed or
/// not.
/// </summary>
private JxlBoxCodingMode codingMode;
/// <summary>
/// Prepares parsing the box.
/// </summary>
/// <param name="codingMode">Specifies box compression.</param>
/// <param name="isUnbounded">Specifies whether or not the box size keeps going till the end of stream.</param>
/// <param name="size">Specifies the fixed size of the box when it is not unbounded.</param>
public void Initialize(JxlBoxCodingMode codingMode, bool isUnbounded, ulong size)
{
this.boxSize = size;
this.codingMode = codingMode;
this.boxExtendsTillEnd = isUnbounded;
}
/// <summary>
/// Prepares parsing the box.
/// </summary>
/// <param name="isBrotliCompressed">True if the box is compressed with Brotli. If uncompressed - false.</param>
/// <param name="isUnbounded">Specifies whether or not the box size keeps going till the end of stream.</param>
/// <param name="size">Specifies the fixed size of the box when it is not unbounded.</param>
public void Initialize(bool isBrotliCompressed, bool isUnbounded, ulong size)
=> this.Initialize(
isBrotliCompressed ? JxlBoxCodingMode.Brotli : JxlBoxCodingMode.Uncompressed,
isUnbounded,
size);
public void Process(Stream stream, JxlMemoryWriter writer)
{
byte[] cache = ArrayPool<byte>.Shared.Rent(16384);
try
{
if (this.codingMode == JxlBoxCodingMode.Brotli)
{
using BrotliStream brotli = new(stream, CompressionMode.Decompress, leaveOpen: true);
int bytesRead;
while ((bytesRead = brotli.Read(cache, 0, cache.Length)) > 0)
{
writer.Write(cache.AsSpan(0, bytesRead));
}
}
else
{
if (this.boxExtendsTillEnd)
{
int bytesRead;
while ((bytesRead = stream.Read(cache, 0, cache.Length)) > 0)
{
writer.Write(cache.AsSpan(0, bytesRead));
}
}
else
{
ulong bytesLeft = this.boxSize;
while (bytesLeft > 0)
{
int toRead = (int)Math.Min((ulong)cache.Length, bytesLeft);
int bytesRead = stream.Read(cache, 0, toRead);
if (bytesRead == 0)
{
throw new EndOfStreamException("Unexpected EOF while reading box content");
}
writer.Write(cache.AsSpan(0, bytesRead));
bytesLeft -= (ulong)bytesRead;
}
}
}
}
finally
{
ArrayPool<byte>.Shared.Return(cache);
}
}
}

10
src/ImageSharp/Formats/Jxl/Processing/Decoder/JxlDecoderCore.cs

@ -1508,7 +1508,7 @@ internal sealed class JxlDecoderCore : ImageDecoderCore, IDisposable
Span<byte> fileSignature = stackalloc byte[2];
stream.ReadExactly(fileSignature);
if (fileSignature[0] != 0xFF || fileSignature[1] != CodestreamMarker)
if (fileSignature[0] != 0xFF || fileSignature[1] != JxlShared.CodestreamMarker)
{
throw new InvalidOperationException("The file signature is invalid");
}
@ -1553,7 +1553,7 @@ internal sealed class JxlDecoderCore : ImageDecoderCore, IDisposable
{
Span<byte> span = this.GetCodeStreamSpan();
JxlBitReader reader = new(span);
JxlBitReader reader = new(this.stream);
reader.SkipBits64((ulong)this.codestreamBitsAhead);
this.metadata!.CustomTransformData!.NonserializedXybEncoded = this.metadata.ImageMetadata!.XybEncoded;
@ -2286,7 +2286,7 @@ internal sealed class JxlDecoderCore : ImageDecoderCore, IDisposable
{
if (this.decoderStage != JxlDecoderStage.CodeStreamFinished || this.JbrdNeedsMoreBoxes())
{
return NeedMoreInput;
ThrowNotEnoughData();
}
if (this.inputClosed || (this.eventsWanted & Box) != 0)
@ -2294,7 +2294,7 @@ internal sealed class JxlDecoderCore : ImageDecoderCore, IDisposable
return Success;
}
return NeedMoreInput;
ThrowNotEnoughData();
}
bool boxedCodestreamDone = ((this.eventsWanted & Box) != 0)
@ -2556,7 +2556,7 @@ internal sealed class JxlDecoderCore : ImageDecoderCore, IDisposable
if (!boxDone)
{
return NeedMoreInput;
ThrowNotEnoughData();
}
this.boxStage = JxlBoxStage.Header;

Loading…
Cancel
Save