Browse Source

First prototype of JxlDecoderCore

- Implemented decode.cc
- Added prototype of JXL image info
- Made JxlBitReader use ReadOnlyMemory
- Made changes to ICC codecs, changing accessibility of a few methods from private to internal and exposing IccDataReader's index
- Added JxlMemoryWriter as a MemoryAllocator alternative to MemoryStream
pull/3153/head
winscripter 3 weeks ago
parent
commit
8b4ff41b9a
  1. 2
      src/ImageSharp/Formats/Jxl/Fields/JxlBundle.cs
  2. 82
      src/ImageSharp/Formats/Jxl/IO/JxlMemoryWriter.cs
  3. 22
      src/ImageSharp/Formats/Jxl/JxlImageInfo.cs
  4. 14
      src/ImageSharp/Formats/Jxl/Processing/Decoder/JxlBitReader.cs
  5. 2938
      src/ImageSharp/Formats/Jxl/Processing/Decoder/JxlDecoderCore.cs
  6. 20
      src/ImageSharp/Formats/Jxl/Processing/JxlBoxCodingMode.cs
  7. 7
      src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.cs
  8. 4
      src/ImageSharp/Metadata/Profiles/ICC/IccReader.cs

2
src/ImageSharp/Formats/Jxl/Fields/JxlBundle.cs

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

82
src/ImageSharp/Formats/Jxl/IO/JxlMemoryWriter.cs

@ -0,0 +1,82 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Buffers;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Jxl.IO;
/// <summary>
/// A disposable writer for bytes in memory. It is highly similar to
/// <see cref="MemoryStream"/>, but its buffer relies on
/// <see cref="MemoryAllocator"/>.
/// </summary>
internal sealed class JxlMemoryWriter(MemoryAllocator allocator) : IDisposable
{
/// <summary>
/// The initial capacity in bytes.
/// </summary>
private const int InitialCapacity = 1024;
/// <summary>
/// Core buffer.
/// </summary>
private IMemoryOwner<byte> buffer = allocator.Allocate<byte>(InitialCapacity);
/// <summary>
/// Gets the length of the written data in bytes.
/// </summary>
public int Length { get; private set; }
/// <summary>
/// Gets the capacity of the buffer in bytes.
/// </summary>
public int Capacity => this.buffer.Memory.Length;
/// <summary>
/// Releases the underlying buffer.
/// </summary>
public void Dispose() => this.buffer.Dispose();
/// <summary>
/// Writes the specified bytes into the writer.
/// </summary>
/// <param name="bytes">The bytes to write.</param>
public void Write(ReadOnlySpan<byte> bytes)
{
int requiredCapacity = checked(this.Length + bytes.Length);
this.EnsureCapacity(requiredCapacity);
bytes.CopyTo(this.buffer.Memory.Span[this.Length..]);
this.Length = requiredCapacity;
}
/// <summary>
/// Returns a span containing the bytes written to the writer.
/// </summary>
/// <returns>A span containing the written bytes.</returns>
public Span<byte> AsSpan() => this.buffer.Memory.Span[..this.Length];
/// <summary>
/// Returns memory containing the bytes written to the writer.
/// </summary>
/// <returns>Memory containing the written bytes.</returns>
public Memory<byte> AsMemory() => this.buffer.Memory[..this.Length];
private void EnsureCapacity(int requiredCapacity)
{
if (requiredCapacity <= this.Capacity)
{
return;
}
int newCapacity = Math.Max(requiredCapacity, checked(this.Capacity * 2));
IMemoryOwner<byte> previousBuffer = this.buffer;
this.buffer = allocator.Allocate<byte>(newCapacity);
previousBuffer.Memory.Span[..this.Length].CopyTo(this.buffer.Memory.Span);
previousBuffer.Dispose();
}
}

22
src/ImageSharp/Formats/Jxl/JxlImageInfo.cs

@ -0,0 +1,22 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Metadata;
namespace SixLabors.ImageSharp.Formats.Jxl;
/// <summary>
/// Image information specific to the JPEG XL format.
/// </summary>
public class JxlImageInfo : ImageInfo
{
/// <summary>
/// Initializes a new instance of the <see cref="JxlImageInfo"/> class.
/// </summary>
/// <param name="size">Image size</param>
/// <param name="metadata">Image metadata</param>
public JxlImageInfo(Size size, ImageMetadata metadata)
: base(size, metadata)
{
}
}

14
src/ImageSharp/Formats/Jxl/Processing/Decoder/JxlBitReader.cs

@ -8,8 +8,10 @@ namespace SixLabors.ImageSharp.Formats.Jxl.Processing.Decoder;
/// <summary>
/// Represents a bitstream reader.
/// </summary>
internal sealed class JxlBitReader(ReadOnlyMemory<byte> bytes)
internal ref struct JxlBitReader(ReadOnlySpan<byte> bytes)
{
private readonly ReadOnlySpan<byte> data = bytes;
private ulong buffer;
private uint bufferRemainingBits;
private int pointer;
@ -22,16 +24,14 @@ internal sealed class JxlBitReader(ReadOnlyMemory<byte> bytes)
/// <summary>
/// Gets the total number of bits consumed.
/// </summary>
public long TotalBitsConsumed => ((long)this.pointer * 8) + (64 - this.bufferRemainingBits);
public readonly long TotalBitsConsumed => ((long)this.pointer * 8) + (64 - this.bufferRemainingBits);
/// <summary>
/// Fetches a new buffer.
/// </summary>
private void RefillCore()
{
ReadOnlySpan<byte> samplesSpan = bytes.Span;
int remaining = samplesSpan.Length - this.pointer;
int remaining = this.data.Length - this.pointer;
if (remaining <= 0)
{
// we don't have any more data... mark an end of stream
@ -43,7 +43,7 @@ internal sealed class JxlBitReader(ReadOnlyMemory<byte> bytes)
if (remaining >= 8)
{
this.buffer = BinaryPrimitives.ReadUInt64LittleEndian(samplesSpan[this.pointer..]);
this.buffer = BinaryPrimitives.ReadUInt64LittleEndian(this.data[this.pointer..]);
this.bufferRemainingBits = 64u;
this.pointer += 8;
}
@ -52,7 +52,7 @@ internal sealed class JxlBitReader(ReadOnlyMemory<byte> bytes)
ulong value = 0;
for (int i = 0; i < remaining; i++)
{
value |= (ulong)samplesSpan[this.pointer + i] << (8 * i);
value |= (ulong)this.data[this.pointer + i] << (8 * i);
}
this.buffer = value;

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

File diff suppressed because it is too large

20
src/ImageSharp/Formats/Jxl/Processing/JxlBoxCodingMode.cs

@ -0,0 +1,20 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Jxl.Processing;
/// <summary>
/// Specifies the type of box content.
/// </summary>
internal enum JxlBoxCodingMode : byte
{
/// <summary>
/// Compress using Brotli codec.
/// </summary>
Brotli,
/// <summary>
/// No compression (raw contents).
/// </summary>
Uncompressed
}

7
src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.cs

@ -3,6 +3,8 @@
namespace SixLabors.ImageSharp.Metadata.Profiles.Icc;
#pragma warning disable IDE0032 // Use auto property
/// <summary>
/// Provides methods to read ICC data types
/// </summary>
@ -25,6 +27,11 @@ internal sealed partial class IccDataReader
public IccDataReader(byte[] data)
=> this.data = data ?? throw new ArgumentNullException(nameof(data));
/// <summary>
/// Gets the reading position.
/// </summary>
public int Index => this.currentIndex;
/// <summary>
/// Gets the length in bytes of the raw data
/// </summary>

4
src/ImageSharp/Metadata/Profiles/ICC/IccReader.cs

@ -53,7 +53,7 @@ internal sealed class IccReader
return ReadTagData(reader);
}
private static IccProfileHeader ReadHeader(IccDataReader reader)
internal static IccProfileHeader ReadHeader(IccDataReader reader)
{
reader.SetIndex(0);
@ -79,7 +79,7 @@ internal sealed class IccReader
};
}
private static IccTagDataEntry[] ReadTagData(IccDataReader reader)
internal static IccTagDataEntry[] ReadTagData(IccDataReader reader)
{
IccTagTableEntry[] tagTable = ReadTagTable(reader);
List<IccTagDataEntry> entries = new(tagTable.Length);

Loading…
Cancel
Save