diff --git a/src/ImageSharp/Common/InlineArray.cs b/src/ImageSharp/Common/InlineArray.cs
index 700551a8f3..12c12bd365 100644
--- a/src/ImageSharp/Common/InlineArray.cs
+++ b/src/ImageSharp/Common/InlineArray.cs
@@ -8,6 +8,24 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp;
+///
+/// Represents a safe, fixed sized buffer of 2 elements.
+///
+[InlineArray(2)]
+internal struct InlineArray2
+{
+ private T t;
+}
+
+///
+/// Represents a safe, fixed sized buffer of 3 elements.
+///
+[InlineArray(3)]
+internal struct InlineArray3
+{
+ private T t;
+}
+
///
/// Represents a safe, fixed sized buffer of 4 elements.
///
@@ -17,6 +35,24 @@ internal struct InlineArray4
private T t;
}
+///
+/// Represents a safe, fixed sized buffer of 6 elements.
+///
+[InlineArray(6)]
+internal struct InlineArray6
+{
+ private T t;
+}
+
+///
+/// Represents a safe, fixed sized buffer of 7 elements.
+///
+[InlineArray(7)]
+internal struct InlineArray7
+{
+ private T t;
+}
+
///
/// Represents a safe, fixed sized buffer of 8 elements.
///
@@ -26,6 +62,24 @@ internal struct InlineArray8
private T t;
}
+///
+/// Represents a safe, fixed sized buffer of 10 elements.
+///
+[InlineArray(10)]
+internal struct InlineArray10
+{
+ private T t;
+}
+
+///
+/// Represents a safe, fixed sized buffer of 12 elements.
+///
+[InlineArray(12)]
+internal struct InlineArray12
+{
+ private T t;
+}
+
///
/// Represents a safe, fixed sized buffer of 14 elements.
///
@@ -62,6 +116,24 @@ internal struct InlineArray19
private T t;
}
+///
+/// Represents a safe, fixed sized buffer of 24 elements.
+///
+[InlineArray(24)]
+internal struct InlineArray24
+{
+ private T t;
+}
+
+///
+/// Represents a safe, fixed sized buffer of 25 elements.
+///
+[InlineArray(25)]
+internal struct InlineArray25
+{
+ private T t;
+}
+
///
/// Represents a safe, fixed sized buffer of 26 elements.
///
@@ -80,6 +152,15 @@ internal struct InlineArray36
private T t;
}
+///
+/// Represents a safe, fixed sized buffer of 65 elements.
+///
+[InlineArray(65)]
+internal struct InlineArray65
+{
+ private T t;
+}
+
///
/// Represents a safe, fixed sized buffer of 256 elements.
///
diff --git a/src/ImageSharp/Common/InlineArray.tt b/src/ImageSharp/Common/InlineArray.tt
index d689b0469a..3869e42bb6 100644
--- a/src/ImageSharp/Common/InlineArray.tt
+++ b/src/ImageSharp/Common/InlineArray.tt
@@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp;
<#GenerateInlineArrays();#>
<#+
-private static int[] Lengths = [4, 8, 14, 16, 18, 19, 26, 36, 256];
+private static int[] Lengths = [2, 3, 4, 6, 7, 8, 10, 12, 14, 16, 18, 19, 24, 25, 26, 36, 65, 256];
void GenerateInlineArrays()
{
diff --git a/src/ImageSharp/Formats/Heif/Av1/Av1BitStreamWriter.cs b/src/ImageSharp/Formats/Heif/Av1/Av1BitStreamWriter.cs
index a7e193b11c..51ffdb432d 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Av1BitStreamWriter.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Av1BitStreamWriter.cs
@@ -1,12 +1,10 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-using SixLabors.ImageSharp.Memory;
-
namespace SixLabors.ImageSharp.Formats.Heif.Av1;
///
-/// Writes AV1 fixed-width and variable-length syntax to reusable expanding memory.
+/// Writes AV1 fixed-width and variable-length syntax to a caller-provided buffer.
///
internal ref struct Av1BitStreamWriter
{
@@ -16,19 +14,9 @@ internal ref struct Av1BitStreamWriter
private const int WordSize = 8;
///
- /// The expanding output allocation.
- ///
- private readonly AutoExpandingMemory memory;
-
- ///
- /// The current writable view over .
+ /// The writable output buffer.
///
- private Span span;
-
- ///
- /// The final byte index that can be written without expanding .
- ///
- private int capacityTrigger;
+ private readonly Span span;
///
/// The partially assembled output byte.
@@ -38,12 +26,10 @@ internal ref struct Av1BitStreamWriter
///
/// Initializes a new instance of the struct.
///
- /// The reusable expanding output allocation.
- public Av1BitStreamWriter(AutoExpandingMemory memory)
+ /// The preallocated output buffer.
+ public Av1BitStreamWriter(Span span)
{
- this.memory = memory;
- this.span = memory.GetEntireSpan();
- this.capacityTrigger = memory.Capacity - 1;
+ this.span = span;
}
///
@@ -54,7 +40,7 @@ internal ref struct Av1BitStreamWriter
///
/// Gets the current output capacity in bytes.
///
- public readonly int Capacity => this.memory.Capacity;
+ public readonly int Capacity => this.span.Length;
///
/// Encodes an unsigned 32-bit value using little-endian base-128 bytes.
@@ -155,14 +141,6 @@ internal ref struct Av1BitStreamWriter
public void WriteLittleEndianBytes128(uint value)
{
int wordPosition = this.BitPosition >> 3;
- const int maximumEncodedLength = 5;
- if (this.span.Length - wordPosition < maximumEncodedLength)
- {
- this.memory.GetSpan(wordPosition + maximumEncodedLength);
- this.span = this.memory.GetEntireSpan();
- this.capacityTrigger = this.span.Length - 1;
- }
-
int bytesWritten = GetLittleEndianBytes128(value, this.span[wordPosition..]);
this.BitPosition += bytesWritten << 3;
}
@@ -237,30 +215,16 @@ internal ref struct Av1BitStreamWriter
DebugGuard.IsTrue(Av1Math.Modulus8(this.BitPosition) == 0, "Writing of Tile Data only allowed on byte alignment");
int wordPosition = this.BitPosition >> 3;
- if (this.span.Length < wordPosition + tileData.Length)
- {
- this.memory.GetSpan(wordPosition + tileData.Length);
- this.span = this.memory.GetEntireSpan();
- }
-
tileData.CopyTo(this.span[wordPosition..]);
this.BitPosition += tileData.Length << 3;
}
///
- /// Stores the current output byte, expanding the allocation when necessary.
+ /// Stores the current output byte.
///
private void WriteBuffer()
{
int wordPosition = Av1Math.DivideBy8Floor(this.BitPosition);
- if (wordPosition > this.capacityTrigger)
- {
- // Expand the memory allocation.
- this.memory.GetSpan(wordPosition + 1);
- this.span = this.memory.GetEntireSpan();
- this.capacityTrigger = this.span.Length - 1;
- }
-
this.span[wordPosition] = this.buffer;
this.buffer = 0;
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1Distribution.cs b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1Distribution.cs
index c56744fb05..d962e5a605 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1Distribution.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1Distribution.cs
@@ -36,7 +36,7 @@ internal sealed class Av1Distribution
///
/// The inverse cumulative thresholds followed by the required zero sentinel.
///
- private readonly uint[] probabilities;
+ private InlineArray16 probabilities;
///
/// The symbol-count contribution to the adaptive update rate.
@@ -273,13 +273,13 @@ internal sealed class Av1Distribution
/// The symbol-count contribution to the update rate.
private Av1Distribution(ReadOnlySpan props, int speed)
{
- this.probabilities = new uint[props.Length];
+ Span probabilities = this.probabilities;
// AV1 range coding consumes inverse cumulative thresholds. The defaults are written in the more readable
// forward form, so convert every real threshold while leaving the final zero sentinel untouched.
for (int i = 0; i < props.Length - 1; i++)
{
- this.probabilities[i] = ProbabilityTop - props[i];
+ probabilities[i] = ProbabilityTop - props[i];
}
this.NumberOfSymbols = props.Length;
@@ -292,8 +292,9 @@ internal sealed class Av1Distribution
/// The distribution state to copy.
private Av1Distribution(Av1Distribution source)
{
- this.probabilities = new uint[source.probabilities.Length];
- source.probabilities.CopyTo(this.probabilities, 0);
+ ReadOnlySpan sourceProbabilities = source.probabilities;
+ Span probabilities = this.probabilities;
+ sourceProbabilities[..source.NumberOfSymbols].CopyTo(probabilities);
// The adaptation rate depends on both the alphabet size and prior update count, so copying only the
// thresholds would make the cloned frame context diverge after its next symbol.
@@ -328,7 +329,9 @@ internal sealed class Av1Distribution
{
// Entropy contexts are created from the same fixed default table shape. Copy only mutable state so resetting a
// working tile never allocates or replaces the distribution objects referenced by the symbol decoder.
- source.probabilities.AsSpan().CopyTo(this.probabilities);
+ ReadOnlySpan sourceProbabilities = source.probabilities;
+ Span probabilities = this.probabilities;
+ sourceProbabilities[..source.NumberOfSymbols].CopyTo(probabilities);
this.updateCount = source.updateCount;
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1FrameEntropyContexts.cs b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1FrameEntropyContexts.cs
index c180fad59f..4394aa06ea 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1FrameEntropyContexts.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1FrameEntropyContexts.cs
@@ -1,8 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-using System.Runtime.CompilerServices;
-
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Entropy;
///
@@ -139,17 +137,4 @@ internal sealed class Av1FrameEntropyContexts
}
}
}
-
- ///
- /// Provides inline storage for every entropy snapshot graph that one decoder session can allocate concurrently.
- ///
- /// The stored reference type.
- [InlineArray(MaximumSnapshotCount)]
- private struct InlineArray10
- {
- ///
- /// The first element in the compiler-expanded inline buffer.
- ///
- private T element;
- }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolEncoder.cs b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolEncoder.cs
index 9a02cb479e..87a3170933 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolEncoder.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolEncoder.cs
@@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Entropy;
///
/// Encodes AV1 tile syntax elements and transform coefficients with tile-local adaptive distributions.
///
-internal class Av1SymbolEncoder : IDisposable
+internal sealed class Av1SymbolEncoder : IDisposable
{
///
/// The largest coefficient-context plane required after AV1 removes the uncoded half of 64-point transforms.
@@ -186,10 +186,10 @@ internal class Av1SymbolEncoder : IDisposable
/// Initializes a new instance of the class for one AV1 tile.
///
/// The configuration providing output and temporary memory.
- /// The initial output buffer size in bytes.
+ /// The complete fixed output allocation length in bytes.
/// The frame base quantizer index.
/// A value indicating whether encoded symbols adapt their tile distributions.
- public Av1SymbolEncoder(Configuration configuration, int initialSize, int qIndex, bool updateCdf = true)
+ public Av1SymbolEncoder(Configuration configuration, int bufferLength, int qIndex, bool updateCdf)
{
this.configuration = configuration;
@@ -219,7 +219,7 @@ internal class Av1SymbolEncoder : IDisposable
this.coefficientsBaseEndOfBlock = Av1DefaultDistributions.GetBaseEndOfBlock(qIndex);
this.dcSign = Av1DefaultDistributions.GetDcSign(qIndex);
this.endOfBlockExtra = Av1DefaultDistributions.GetEndOfBlockExtra(qIndex);
- this.writer = new(configuration, initialSize, updateCdf);
+ this.writer = new(configuration, bufferLength, updateCdf);
this.baseQIndex = qIndex;
}
@@ -1310,7 +1310,7 @@ internal class Av1SymbolEncoder : IDisposable
}
///
- /// Finalizes the range-coded tile payload and transfers ownership of its memory.
+ /// Finalizes the range-coded tile payload and returns an owned exact-length copy.
///
/// The memory owner containing the encoded tile bytes.
public IMemoryOwner Exit()
@@ -1320,18 +1320,18 @@ internal class Av1SymbolEncoder : IDisposable
}
///
- /// Finalizes the range-coded tile payload and transfers its current allocation without copying.
+ /// Finalizes the range-coded tile payload and exposes its encoded prefix without copying.
///
- /// The number of encoded bytes at the beginning of the returned allocation.
- /// The complete allocation containing the encoded tile prefix.
- public IMemoryOwner Exit(out int length)
+ /// The number of encoded bytes in the returned memory.
+ /// The encoded prefix, valid until this encoder is disposed.
+ public ReadOnlyMemory Exit(out int length)
{
ref Av1SymbolWriter w = ref this.writer;
return w.Exit(out length);
}
///
- /// Releases output memory that has not been transferred by .
+ /// Releases the range-coder output buffer and coefficient scratch memory.
///
public void Dispose()
{
diff --git a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolWriter.cs b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolWriter.cs
index 522df8653e..fda8597fa2 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolWriter.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolWriter.cs
@@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Entropy;
///
/// Writes AV1 literals and adaptively coded symbols to a range-coded byte sequence.
///
-internal class Av1SymbolWriter : IDisposable
+internal sealed class Av1SymbolWriter : IDisposable
{
///
/// The lower endpoint of the current coding interval.
@@ -36,9 +36,14 @@ internal class Av1SymbolWriter : IDisposable
private readonly Configuration configuration;
///
- /// The output bytes accumulated during renormalization.
+ /// The owner of the fixed output buffer supplied for this tile.
///
- private readonly AutoExpandingMemory memory;
+ private readonly IMemoryOwner bufferOwner;
+
+ ///
+ /// The requested output range, excluding any excess capacity returned by a pooling allocator.
+ ///
+ private readonly Memory buffer;
///
/// Indicates whether encoded symbols adapt their distributions.
@@ -51,22 +56,23 @@ internal class Av1SymbolWriter : IDisposable
private int position;
///
- /// Initializes a new instance of the class with an estimated output size.
+ /// Initializes a new instance of the class with a bounded output size.
///
/// The configuration that supplies output allocation.
- /// The estimated encoded size in bytes.
+ /// The complete fixed output allocation length in bytes.
/// A value indicating whether encoded symbols adapt their distributions.
- public Av1SymbolWriter(Configuration configuration, int initialSize, bool updateCdf = true)
+ public Av1SymbolWriter(Configuration configuration, int bufferLength, bool updateCdf)
{
this.configuration = configuration;
- this.memory = new AutoExpandingMemory(configuration, initialSize);
+ this.bufferOwner = configuration.MemoryAllocator.Allocate(bufferLength);
+ this.buffer = this.bufferOwner.Memory[..bufferLength];
this.updateCdf = updateCdf;
}
///
- /// Releases the expandable pre-carry buffer.
+ /// Releases the tile output buffer.
///
- public void Dispose() => this.memory.Dispose();
+ public void Dispose() => this.bufferOwner.Dispose();
///
/// Writes one binary symbol and adapts its distribution when CDF updates are enabled.
@@ -132,20 +138,20 @@ internal class Av1SymbolWriter : IDisposable
{
int length = this.FinalizeRange();
IMemoryOwner output = this.configuration.MemoryAllocator.Allocate(length);
- this.memory.GetSpan(length).CopyTo(output.GetSpan()[..length]);
+ this.buffer.Span[..length].CopyTo(output.Memory.Span);
return output;
}
///
- /// Finalizes the range-coded sequence and transfers its current allocation without copying.
+ /// Finalizes the range-coded sequence and exposes its encoded prefix without copying.
///
- /// The number of encoded bytes at the beginning of the returned allocation.
- /// The complete allocation containing the encoded byte prefix.
- public IMemoryOwner Exit(out int length)
+ /// The number of encoded bytes in the returned memory.
+ /// The encoded prefix, valid until this writer is disposed.
+ public ReadOnlyMemory Exit(out int length)
{
length = this.FinalizeRange();
- return this.memory.Detach();
+ return this.buffer[..length];
}
///
@@ -164,7 +170,7 @@ internal class Av1SymbolWriter : IDisposable
ulong e = ((l + m) & ~m) | (m + 1);
s += c;
int pendingByteCount = Math.Max((s + 7) >> 3, 0);
- Span buffer = this.memory.GetSpan(pos + pendingByteCount);
+ Span buffer = this.buffer.Span[..(pos + pendingByteCount)];
if (s > 0)
{
ulong n = (1UL << (c + 16)) - 1;
@@ -289,7 +295,7 @@ internal class Av1SymbolWriter : IDisposable
// bytes together while preserving one carry bit.
if (s >= 40)
{
- Span buffer = this.memory.GetSpan(this.position + sizeof(ulong));
+ Span buffer = this.buffer.Span[..(this.position + sizeof(ulong))];
int readyByteCount = (s >> 3) + 1;
c += 24 - (readyByteCount << 3);
ulong output = low >> c;
diff --git a/src/ImageSharp/Formats/Heif/Av1/Motion/Av1GlobalMotionParameters.cs b/src/ImageSharp/Formats/Heif/Av1/Motion/Av1GlobalMotionParameters.cs
index ee1930f7a6..532b6f2e67 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Motion/Av1GlobalMotionParameters.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Motion/Av1GlobalMotionParameters.cs
@@ -2,8 +2,6 @@
// Licensed under the Six Labors Split License.
using System.Numerics;
-using System.Runtime.CompilerServices;
-using SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Motion;
@@ -484,17 +482,4 @@ internal struct Av1GlobalMotionParameters
=> value < 0
? -(((-value) + ((1L << bitCount) >> 1)) >> bitCount)
: (value + ((1L << bitCount) >> 1)) >> bitCount;
-
- ///
- /// Provides inline storage for the six parameters in an AV1 affine matrix.
- ///
- /// The stored parameter type.
- [InlineArray(6)]
- private struct InlineArray6
- {
- ///
- /// The first element in the compiler-expanded inline buffer.
- ///
- private T element;
- }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Motion/Av1MotionVariationCandidates.cs b/src/ImageSharp/Formats/Heif/Av1/Motion/Av1MotionVariationCandidates.cs
index 3987ecd902..aa89625234 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Motion/Av1MotionVariationCandidates.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Motion/Av1MotionVariationCandidates.cs
@@ -1,7 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
using SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
@@ -281,17 +280,4 @@ internal sealed class Av1MotionVariationCandidates
/// for inter prediction or intra-block copy; otherwise, .
private static bool IsOverlappable(Av1BlockModeInfo candidate)
=> candidate.UseIntraBlockCopy || candidate.ReferenceFrames[0] > Av1ReferenceFrameType.Intra;
-
- ///
- /// Provides fixed storage for AV1's eight local warped-motion projection samples.
- ///
- /// The source or reference point type stored in the inline buffer.
- [InlineArray(ProjectionSampleCapacity)]
- private struct InlineArray8
- {
- ///
- /// The first element in the compiler-expanded inline buffer.
- ///
- private T element;
- }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Motion/Av1ReferenceMotionVectors.cs b/src/ImageSharp/Formats/Heif/Av1/Motion/Av1ReferenceMotionVectors.cs
index 04dfb5060c..20209cc36b 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Motion/Av1ReferenceMotionVectors.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Motion/Av1ReferenceMotionVectors.cs
@@ -1,7 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
using SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
using SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
@@ -1365,30 +1364,4 @@ internal sealed class Av1ReferenceMotionVectors
Av1PredictionMode.NewNearestMotionVector or
Av1PredictionMode.NearNewMotionVector or
Av1PredictionMode.NewNearMotionVector;
-
- ///
- /// Provides fixed storage for AV1's eight reference-motion-vector candidates.
- ///
- /// The motion-vector or weight type stored in the inline buffer.
- [InlineArray(CandidateCapacity)]
- private struct InlineArray8
- {
- ///
- /// The first element in the compiler-expanded inline buffer.
- ///
- private T element;
- }
-
- ///
- /// Provides fixed storage for the nearest and near motion-vector references.
- ///
- /// The motion-vector type stored in the inline buffer.
- [InlineArray(2)]
- private struct InlineArray2
- {
- ///
- /// The first element in the compiler-expanded inline buffer.
- ///
- private T element;
- }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuFilmGrainParameters.cs b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuFilmGrainParameters.cs
index 8b9a5d7069..8838c28ddf 100644
--- a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuFilmGrainParameters.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuFilmGrainParameters.cs
@@ -1,8 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-using System.Runtime.CompilerServices;
-
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
///
@@ -250,43 +248,4 @@ internal sealed class ObuFilmGrainParameters
this.OverlapFlag = source.OverlapFlag;
this.ClipToRestrictedRange = source.ClipToRestrictedRange;
}
-
- ///
- /// Provides inline storage for the maximum luma autoregressive coefficient count.
- ///
- /// The stored value type.
- [InlineArray(24)]
- private struct InlineArray24
- {
- ///
- /// The first element in the compiler-expanded inline buffer.
- ///
- private T element;
- }
-
- ///
- /// Provides inline storage for the ten scaling points permitted on either chroma plane.
- ///
- /// The stored value type.
- [InlineArray(10)]
- private struct InlineArray10
- {
- ///
- /// The first element in the compiler-expanded inline buffer.
- ///
- private T element;
- }
-
- ///
- /// Provides inline storage for the maximum autoregressive coefficient count of either chroma plane.
- ///
- /// The stored value type.
- [InlineArray(25)]
- private struct InlineArray25
- {
- ///
- /// The first element in the compiler-expanded inline buffer.
- ///
- private T element;
- }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuFrameHeader.cs b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuFrameHeader.cs
index 99e62da161..ca496c9903 100644
--- a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuFrameHeader.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuFrameHeader.cs
@@ -1,7 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Formats.Heif.Av1.Motion;
using SixLabors.ImageSharp.Formats.Heif.Av1.Prediction.Inter;
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
@@ -341,17 +340,4 @@ internal sealed class ObuFrameHeader
}
}
}
-
- ///
- /// Provides inline storage for the seven canonical AV1 inter reference types.
- ///
- /// The stored parameter type.
- [InlineArray(Av1Constants.ReferencesPerFrame)]
- private struct InlineArray7
- {
- ///
- /// The first element in the compiler-expanded inline buffer.
- ///
- private T element;
- }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuTileGroupHeader.cs b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuTileGroupHeader.cs
index b508b1038e..99238b13fb 100644
--- a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuTileGroupHeader.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuTileGroupHeader.cs
@@ -1,8 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-using System.Runtime.CompilerServices;
-
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
///
@@ -10,8 +8,8 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
///
internal sealed class ObuTileGroupHeader
{
- private InlineTileColumnBoundaryArray tileColumnStartModeInfo;
- private InlineTileRowBoundaryArray tileRowStartModeInfo;
+ private InlineArray65 tileColumnStartModeInfo;
+ private InlineArray65 tileRowStartModeInfo;
///
/// Gets or sets the maximum tile width, in superblocks.
@@ -92,16 +90,4 @@ internal sealed class ObuTileGroupHeader
/// Gets or sets the number of bytes used to signal each tile size.
///
public int TileSizeBytes { get; set; }
-
- [InlineArray(Av1Constants.MaxTileColumnCount + 1)]
- private struct InlineTileColumnBoundaryArray
- {
- private int element;
- }
-
- [InlineArray(Av1Constants.MaxTileRowCount + 1)]
- private struct InlineTileRowBoundaryArray
- {
- private int element;
- }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuWriter.cs b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuWriter.cs
index 5d73612cdb..5b3e3ba5af 100644
--- a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuWriter.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuWriter.cs
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
+using System.Buffers;
using System.Buffers.Binary;
using SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.Quantizers;
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
@@ -11,8 +12,12 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
///
/// Writes the AV1 open bitstream units required for a single still-image frame.
///
-internal class ObuWriter
+internal sealed class ObuWriter
{
+ // Sequence and uncompressed-frame syntax have fixed field and array limits. A 512-byte owner covers their
+ // maximum supported representation without retaining any entropy-coded tile bytes in the header scratch.
+ private const int MaximumHeaderLength = 512;
+
///
/// Writes a temporal delimiter and the supplied sequence and frame OBUs.
///
@@ -27,11 +32,11 @@ internal class ObuWriter
Justification = "Preserves the existing writer instance contract.")]
public void WriteAll(Configuration configuration, Stream stream, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameHeader, IAv1TileWriter tileWriter)
{
- // The reusable scratch only contains headers. Entropy-coded tiles remain in their owning
- // buffers and are streamed directly so the complete compressed frame is never duplicated.
- int initialBufferSize = 2000;
- using AutoExpandingMemory buffer = new(configuration, initialBufferSize);
- Av1BitStreamWriter writer = new(buffer);
+ // The reusable scratch only contains headers. Entropy-coded tiles remain in their owning buffers and are
+ // streamed directly so the complete compressed frame is never duplicated.
+ using IMemoryOwner headerOwner = configuration.MemoryAllocator.Allocate(MaximumHeaderLength);
+ Span headerBuffer = headerOwner.Memory.Span[..MaximumHeaderLength];
+ Av1BitStreamWriter writer = new(headerBuffer);
WriteObuHeaderAndSize(stream, ObuType.TemporalDelimiter, []);
if (sequenceHeader != null)
@@ -39,7 +44,7 @@ internal class ObuWriter
WriteSequenceHeader(ref writer, sequenceHeader);
int bytesWritten = (writer.BitPosition + 7) >> 3;
writer.Flush();
- WriteObuHeaderAndSize(stream, ObuType.SequenceHeader, buffer.GetSpan(bytesWritten));
+ WriteObuHeaderAndSize(stream, ObuType.SequenceHeader, headerBuffer[..bytesWritten]);
}
if (frameHeader != null && sequenceHeader != null)
@@ -67,7 +72,7 @@ internal class ObuWriter
}
WriteObuHeaderAndSize(stream, ObuType.Frame, framePayloadSize);
- stream.Write(buffer.GetSpan(frameHeaderBytes));
+ stream.Write(headerBuffer[..frameHeaderBytes]);
if (tileInfo != null)
{
diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1FrameEncoder.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1FrameEncoder.cs
index 01e7b5fcb5..c8586d9d8e 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1FrameEncoder.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1FrameEncoder.cs
@@ -2,6 +2,7 @@
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1.Color;
+using SixLabors.ImageSharp.Formats.Heif.Av1.Entropy;
using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
using SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.Quantizers;
using SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
@@ -17,6 +18,29 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline;
///
internal static class Av1FrameEncoder
{
+ ///
+ /// The base-two exponent used to align each frame dimension for output sizing. Rounding to 32 samples accounts
+ /// for partial edge storage before the raw-plane size and all-intra expansion factor are calculated.
+ ///
+ private const int OutputAlignmentLog2 = 5;
+
+ ///
+ /// The lower bound, in bytes, for the bounded compressed-frame buffer. The raw-size ratio is too small for tiny
+ /// images to provide useful coder headroom, so the reference allocation retains an 8 KiB floor.
+ ///
+ private const int MinimumCompressedFrameBufferLength = 8 * 1024;
+
+ ///
+ /// The numerator of the all-intra output-capacity ratio. Together with the denominator, this reserves 2.5 times
+ /// the aligned uncompressed plane size because incompressible input can produce more output than its raw size.
+ ///
+ private const int AllIntraBufferScaleNumerator = 5;
+
+ ///
+ /// The denominator of the all-intra output-capacity ratio, completing the reference encoder's 5:2 sizing rule.
+ ///
+ private const int AllIntraBufferScaleDenominator = 2;
+
///
/// Encodes one reduced-still-picture AV1 frame into a low-overhead OBU stream.
///
@@ -141,8 +165,8 @@ internal static class Av1FrameEncoder
// Libaom reserves 2.5 times the 32-sample-aligned native input for an all-intra output packet.
// Counting the active planes directly retains that headroom without charging monochrome for unused chroma.
- int alignedWidth = Av1Math.AlignPowerOf2(width, 5);
- int alignedHeight = Av1Math.AlignPowerOf2(height, 5);
+ int alignedWidth = Av1Math.AlignPowerOf2(width, OutputAlignmentLog2);
+ int alignedHeight = Av1Math.AlignPowerOf2(height, OutputAlignmentLog2);
int subsamplingX = colorConfig.SubSamplingX ? 1 : 0;
int subsamplingY = colorConfig.SubSamplingY ? 1 : 0;
long sampleCount = (long)alignedWidth * alignedHeight;
@@ -152,14 +176,17 @@ internal static class Av1FrameEncoder
}
int sampleSize = colorConfig.BitDepth == Av1BitDepth.EightBit ? 1 : 2;
- int initialTileSize = checked((int)Math.Max(8192L, (sampleCount * sampleSize * 5) / 2));
+ long scaledInputLength = (sampleCount * sampleSize * AllIntraBufferScaleNumerator)
+ / AllIntraBufferScaleDenominator;
+
+ int tileBufferLength = checked((int)Math.Max(MinimumCompressedFrameBufferLength, scaledInputLength));
if (colorConfig.BitDepth == Av1BitDepth.EightBit)
{
- EncodeByte(configuration, image, stream, sequenceHeader, frameHeader, colorFormat, initialTileSize, effort, encodeAlpha);
+ EncodeByte(configuration, image, stream, sequenceHeader, frameHeader, colorFormat, tileBufferLength, effort, encodeAlpha);
}
else
{
- EncodeHighBitDepth(configuration, image, stream, sequenceHeader, frameHeader, colorFormat, initialTileSize, effort, encodeAlpha);
+ EncodeHighBitDepth(configuration, image, stream, sequenceHeader, frameHeader, colorFormat, tileBufferLength, effort, encodeAlpha);
}
return sequenceHeader;
@@ -204,7 +231,7 @@ internal static class Av1FrameEncoder
ObuSequenceHeader sequenceHeader,
ObuFrameHeader frameHeader,
Av1ColorFormat colorFormat,
- int initialTileSize,
+ int tileBufferLength,
int effort,
bool encodeAlpha)
where TPixel : unmanaged, IPixel
@@ -227,7 +254,7 @@ internal static class Av1FrameEncoder
chromaPositionX: 1,
chromaPositionY: 1);
- Encode(configuration, image, stream, sequenceHeader, frameHeader, source, reconstruction, initialTileSize, effort, encodeAlpha);
+ Encode(configuration, image, stream, sequenceHeader, frameHeader, source, reconstruction, tileBufferLength, effort, encodeAlpha);
}
private static void EncodeHighBitDepth(
@@ -237,7 +264,7 @@ internal static class Av1FrameEncoder
ObuSequenceHeader sequenceHeader,
ObuFrameHeader frameHeader,
Av1ColorFormat colorFormat,
- int initialTileSize,
+ int tileBufferLength,
int effort,
bool encodeAlpha)
where TPixel : unmanaged, IPixel
@@ -261,7 +288,7 @@ internal static class Av1FrameEncoder
chromaPositionX: 1,
chromaPositionY: 1);
- Encode(configuration, image, stream, sequenceHeader, frameHeader, source, reconstruction, initialTileSize, effort, encodeAlpha);
+ Encode(configuration, image, stream, sequenceHeader, frameHeader, source, reconstruction, tileBufferLength, effort, encodeAlpha);
}
private static void Encode(
@@ -272,7 +299,7 @@ internal static class Av1FrameEncoder
ObuFrameHeader frameHeader,
Av1EncoderFrameBuffer source,
Av1EncoderFrameBuffer reconstruction,
- int initialTileSize,
+ int tileBufferLength,
int effort,
bool encodeAlpha)
where TPixel : unmanaged, IPixel
@@ -316,15 +343,20 @@ internal static class Av1FrameEncoder
using Av1EncoderSuperblockWorkspace superblockWorkspace = new(configuration);
using Av1EncoderBlockWorkspace blockWorkspace = new(configuration);
- using Av1IntraTileWriter tileWriter = new(
+ using Av1SymbolEncoder symbolEncoder = new(
configuration,
+ tileBufferLength,
+ frameHeader.QuantizationParameters.BaseQIndex,
+ updateCdf: !frameHeader.DisableCdfUpdate);
+
+ Av1IntraTileWriter tileWriter = new(
+ symbolEncoder,
source.Frame,
reconstruction.Frame,
picture.Picture,
coefficients,
superblockWorkspace,
blockWorkspace,
- initialTileSize,
effort);
ObuWriter writer = new();
@@ -339,7 +371,7 @@ internal static class Av1FrameEncoder
ObuFrameHeader frameHeader,
Av1EncoderFrameBuffer source,
Av1EncoderFrameBuffer reconstruction,
- int initialTileSize,
+ int tileBufferLength,
int effort,
bool encodeAlpha)
where TPixel : unmanaged, IPixel
@@ -383,15 +415,20 @@ internal static class Av1FrameEncoder
using Av1EncoderSuperblockWorkspace superblockWorkspace = new(configuration);
using Av1EncoderBlockWorkspace blockWorkspace = new(configuration);
- using Av1IntraTileWriter tileWriter = new(
+ using Av1SymbolEncoder symbolEncoder = new(
configuration,
+ tileBufferLength,
+ frameHeader.QuantizationParameters.BaseQIndex,
+ updateCdf: !frameHeader.DisableCdfUpdate);
+
+ Av1IntraTileWriter tileWriter = new(
+ symbolEncoder,
source.Frame,
reconstruction.Frame,
picture.Picture,
coefficients,
superblockWorkspace,
blockWorkspace,
- initialTileSize,
effort);
ObuWriter writer = new();
diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1IntraTileWriter.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1IntraTileWriter.cs
index 139c9cf312..5fe0803227 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1IntraTileWriter.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1IntraTileWriter.cs
@@ -1,7 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-using System.Buffers;
using SixLabors.ImageSharp.Formats.Heif.Av1.Entropy;
using SixLabors.ImageSharp.Formats.Heif.Av1.Motion;
using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
@@ -10,38 +9,36 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline;
///
-/// Encodes and owns one range-coded all-intra tile payload.
+/// Encodes one range-coded all-intra tile payload.
///
-internal sealed partial class Av1IntraTileWriter : IAv1TileWriter, IDisposable
+internal sealed partial class Av1IntraTileWriter : IAv1TileWriter
{
- private IMemoryOwner? tileData;
+ private readonly ReadOnlyMemory tileData;
private readonly int tileDataLength;
///
/// Initializes a new instance of the class for eight-bit samples.
///
- /// The configuration providing tile output memory.
+ /// The operation-owned symbol encoder that retains the tile output memory.
/// The coded source frame.
/// The reconstructed frame updated during encoding.
/// The frame coding and mode-information state.
/// The frame-owned quantized coefficient and transform state.
/// The reusable partition and final-block decision workspace.
/// The reusable block arithmetic workspace.
- /// The estimated encoded tile size in bytes.
/// The mode-search effort in the inclusive range zero through ten.
public Av1IntraTileWriter(
- Configuration configuration,
+ Av1SymbolEncoder writer,
Av1EncoderFrame source,
Av1EncoderFrame reconstruction,
Av1PictureControlSet picture,
Av1EncoderCoefficientBuffer coefficientBuffer,
Av1EncoderSuperblockWorkspace superblockWorkspace,
Av1EncoderBlockWorkspace blockWorkspace,
- int initialSize,
int effort)
{
this.tileData = Encode(
- configuration,
+ writer,
source,
reconstruction,
picture,
@@ -49,35 +46,32 @@ internal sealed partial class Av1IntraTileWriter : IAv1TileWriter, IDisposable
superblockWorkspace,
blockWorkspace,
effort,
- initialSize,
out this.tileDataLength);
}
///
/// Initializes a new instance of the class for high-bit-depth samples.
///
- /// The configuration providing tile output memory.
+ /// The operation-owned symbol encoder that retains the tile output memory.
/// The coded source frame.
/// The reconstructed frame updated during encoding.
/// The frame coding and mode-information state.
/// The frame-owned quantized coefficient and transform state.
/// The reusable partition and final-block decision workspace.
/// The reusable block arithmetic workspace.
- /// The estimated encoded tile size in bytes.
/// The mode-search effort in the inclusive range zero through ten.
public Av1IntraTileWriter(
- Configuration configuration,
+ Av1SymbolEncoder writer,
Av1EncoderFrame source,
Av1EncoderFrame reconstruction,
Av1PictureControlSet picture,
Av1EncoderCoefficientBuffer coefficientBuffer,
Av1EncoderSuperblockWorkspace superblockWorkspace,
Av1EncoderBlockWorkspace blockWorkspace,
- int initialSize,
int effort)
{
this.tileData = Encode(
- configuration,
+ writer,
source,
reconstruction,
picture,
@@ -85,28 +79,14 @@ internal sealed partial class Av1IntraTileWriter : IAv1TileWriter, IDisposable
superblockWorkspace,
blockWorkspace,
effort,
- initialSize,
out this.tileDataLength);
}
///
- public ReadOnlySpan GetTileData(int tileNum)
- {
- ObjectDisposedException.ThrowIf(this.tileData is null, this);
- return this.tileData.Memory.Span[..this.tileDataLength];
- }
-
- ///
- /// Returns the detached range-coded tile allocation to the configured allocator.
- ///
- public void Dispose()
- {
- this.tileData?.Dispose();
- this.tileData = null;
- }
+ public ReadOnlySpan GetTileData(int tileNum) => this.tileData.Span[..this.tileDataLength];
- private static IMemoryOwner Encode(
- Configuration configuration,
+ private static ReadOnlyMemory Encode(
+ Av1SymbolEncoder writer,
Av1EncoderFrame source,
Av1EncoderFrame reconstruction,
Av1PictureControlSet picture,
@@ -114,7 +94,6 @@ internal sealed partial class Av1IntraTileWriter : IAv1TileWriter, IDisposable
Av1EncoderSuperblockWorkspace superblockWorkspace,
Av1EncoderBlockWorkspace blockWorkspace,
int effort,
- int initialSize,
out int tileDataLength)
where TSample : unmanaged
where TOperator : struct, Av1IntraSuperblockEncoder.IBlockEncodingOperator
@@ -136,12 +115,6 @@ internal sealed partial class Av1IntraTileWriter : IAv1TileWriter, IDisposable
MacroBlockModeInfo = picture.GetMacroBlockModeInfo(firstModeInfoPosition)
};
- using Av1SymbolEncoder writer = new(
- configuration,
- initialSize,
- frameHeader.QuantizationParameters.BaseQIndex,
- updateCdf: !frameHeader.DisableCdfUpdate);
-
int superblockModeInfoSize = sequenceHeader.SuperblockModeInfoSize;
int superblockShift = sequenceHeader.SuperblockSizeLog2 - Av1Constants.ModeInfoSizeLog2;
if (frameHeader.AllowIntraBlockCopy)
diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/Quantizers/Av1QuantizationLookup.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Quantizers/Av1QuantizationLookup.cs
index 0e2d30f297..ace7efab0f 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Pipeline/Quantizers/Av1QuantizationLookup.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Quantizers/Av1QuantizationLookup.cs
@@ -10,34 +10,20 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.Quantizers;
///
internal static class Av1QuantizationLookup
{
- // Coefficient scaling and quantization with AV1 TX are tailored to
- // the AV1 TX transforms. Regardless of the bit-depth of the input,
- // the transform stages scale the coefficient values up by a factor of
- // 8 (3 bits) over the scale of the pixel values. Thus, for 8-bit
- // input, the coefficients have effectively 11 bits of scale depth
- // (8+3), 10-bit input pixels result in 13-bit coefficient depth
- // (10+3) and 12-bit pixels yield 15-bit (12+3) coefficient depth.
- // All quantizers are built using this invariant of x8, 3-bit scaling,
- // thus the Q3 suffix.
+ private const int LinearQuantizerScale = 4;
+ private const int LastLinearQuantizer = 61;
+ private const int PenultimateQuantizer = 62;
+ private const int PenultimateQuantizerIndex = 249;
- // A partial exception to this rule is large transforms; to avoid
- // overflow, TX blocks with > 256 pels (>16x16) are scaled only
- // 4-times unity (2 bits) over the pixel depth, and TX blocks with
- // over 1024 pixels (>32x32) are scaled up only 2x unity (1 bit).
- // This descaling is found via av1_tx_get_scale(). Thus, 16x32, 32x16
- // and 32x32 transforms actually return Q2 coefficients, and 32x64,
- // 64x32 and 64x64 transforms return Q1 coefficients. However, the
- // quantizers are de-scaled down on-the-fly by the same amount
- // (av1_tx_get_scale()) during quantization, and as such the
- // dequantized/decoded coefficients, even for large TX blocks, are always
- // effectively Q3. Meanwhile, quantized/coded coefficients are Q0
- // because Qn quantizers are applied to Qn tx coefficients.
+ // AV1 transforms normally retain three fractional coefficient bits. The quantizer tables use the same Q3
+ // scale, leaving coded coefficients in Q0 and reconstructed coefficients in Q3.
- // Note that encoder decision making (which uses the quantizer to
- // generate several bespoke lamdas for RDO and other heuristics)
- // expects quantizers to be larger for higher-bitdepth input. In
- // addition, the minimum allowable quantizer is 4; smaller values will
- // underflow to 0 in the actual quantization routines.
+ // Transforms larger than 16x16 reduce coefficient scaling by one bit, and transforms larger than 32x32 reduce
+ // it by two bits to preserve numeric range. Quantization applies the same reduction to its step, so every
+ // reconstructed transform still reaches the inverse transform in Q3.
+
+ // Encoder rate decisions intentionally retain bit-depth-specific quantizer values. The minimum table value is
+ // four because a smaller step would round to zero during fixed-point quantization.
///
/// The Q3 AC dequantization values for 8-bit samples, indexed by quantizer index.
@@ -162,6 +148,23 @@ internal static class Av1QuantizationLookup
12750, 13118, 13501, 13913, 14343, 14807, 15290, 15812, 16356, 16943, 17575, 18237, 18949, 19718, 20521, 21387,
];
+ ///
+ /// Converts a quantizer on libaom's external zero-through-63 scale to an AV1 quantizer index.
+ ///
+ /// The external quantizer.
+ /// The corresponding AV1 quantizer index.
+ public static int GetQIndex(int quantizer)
+ {
+ // Four qindex steps separate the regular entries. The final two entries use 249 and 255 so the external
+ // scale reaches AV1's complete qindex range without changing the spacing of its first 62 entries.
+ if (quantizer <= LastLinearQuantizer)
+ {
+ return quantizer * LinearQuantizerScale;
+ }
+
+ return quantizer == PenultimateQuantizer ? PenultimateQuantizerIndex : Av1Constants.MaxQ;
+ }
+
///
/// Gets the DC dequantization value after applying a plane delta to the frame quantizer index.
///
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1BlockModeInfo.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1BlockModeInfo.cs
index f12492b3bd..ba9453aa29 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1BlockModeInfo.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1BlockModeInfo.cs
@@ -2,7 +2,6 @@
// Licensed under the Six Labors Split License.
using System.Diagnostics.CodeAnalysis;
-using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Formats.Heif.Av1.Motion;
using SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
using SixLabors.ImageSharp.Formats.Heif.Av1.Prediction.Inter;
@@ -463,17 +462,4 @@ internal struct Av1BlockModeInfo
this.chromaPaletteColorIndexBounds = bounds;
}
}
-
- ///
- /// Provides fixed storage for the two values associated with AV1's primary and secondary inter references.
- ///
- /// The stored reference label, motion vector, or interpolation-filter type.
- [InlineArray(2)]
- private struct InlineArray2
- {
- ///
- /// The first element in the compiler-expanded inline buffer.
- ///
- private T element;
- }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderPaletteInfo.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderPaletteInfo.cs
index 12ec0aa79d..db3dfca31a 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderPaletteInfo.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderPaletteInfo.cs
@@ -2,7 +2,6 @@
// Licensed under the Six Labors Split License.
using System.Diagnostics.CodeAnalysis;
-using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
@@ -52,24 +51,4 @@ internal struct Av1EncoderPaletteInfo
Span destination = this.paletteColors;
colors.CopyTo(destination[offset..]);
}
-
- ///
- /// Provides fixed storage for the luma and shared chroma palette sizes.
- ///
- /// The stored value type.
- [InlineArray(2)]
- private struct InlineArray2
- {
- private T element;
- }
-
- ///
- /// Provides fixed storage for all three eight-color palette planes.
- ///
- /// The stored value type.
- [InlineArray(3 * Av1Constants.PaletteMaxSize)]
- private struct InlineArray24
- {
- private T element;
- }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderPredictionUnit.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderPredictionUnit.cs
index 47865e43f1..eaf17068e1 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderPredictionUnit.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderPredictionUnit.cs
@@ -2,7 +2,6 @@
// Licensed under the Six Labors Split License.
using System.Diagnostics.CodeAnalysis;
-using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
@@ -33,13 +32,4 @@ internal struct Av1EncoderPredictionUnit
/// Gets or sets the packed chroma-from-luma alpha signs for the U and V planes.
///
public sbyte ChromaFromLumaSigns { get; set; }
-
- ///
- /// Stores the two signed angle deltas embedded by libaom in block mode information.
- ///
- [InlineArray(Av1Constants.PlaneTypeCount)]
- private struct InlineArray2
- {
- private T element;
- }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1LoopRestorationUnit.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1LoopRestorationUnit.cs
index c0765184f9..023d9dfd1e 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1LoopRestorationUnit.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1LoopRestorationUnit.cs
@@ -1,8 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-using System.Runtime.CompilerServices;
-
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
///
@@ -13,17 +11,17 @@ internal struct Av1LoopRestorationUnit
///
/// The three transmitted symmetric vertical Wiener coefficients.
///
- public WienerCoefficientBuffer WienerVertical;
+ public InlineArray3 WienerVertical;
///
/// The three transmitted symmetric horizontal Wiener coefficients.
///
- public WienerCoefficientBuffer WienerHorizontal;
+ public InlineArray3 WienerHorizontal;
///
/// The two self-guided projection coefficients.
///
- public SgrProjectionCoefficientBuffer SgrProjectionCoefficients;
+ public InlineArray2 SgrProjectionCoefficients;
///
/// Gets or sets the restoration filter selected for the unit.
@@ -34,22 +32,4 @@ internal struct Av1LoopRestorationUnit
/// Gets or sets the self-guided filter parameter-set index.
///
public int SgrParameterSet { get; set; }
-
- ///
- /// Stores the transmitted coefficients inline with the restoration unit.
- ///
- [InlineArray(Av1Constants.WienerCoefficientCount)]
- public struct WienerCoefficientBuffer
- {
- private int element0;
- }
-
- ///
- /// Stores the projection coefficients inline with the restoration unit.
- ///
- [InlineArray(2)]
- public struct SgrProjectionCoefficientBuffer
- {
- private int element0;
- }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileReader.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileReader.cs
index 339f6adcb0..8cece2ddb7 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileReader.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileReader.cs
@@ -3449,17 +3449,4 @@ internal sealed class Av1TileReader : IAv1TileReader, IDisposable
///
public Buffer2D Chroma { get; } = chroma;
}
-
- ///
- /// Provides inline storage for the two self-guided restoration coefficients of each of the three AV1 planes.
- ///
- /// The stored value type.
- [InlineArray(6)]
- private struct InlineArray6
- {
- ///
- /// The first element in the compiler-expanded inline buffer.
- ///
- private T element;
- }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1Transform2dFlipConfiguration.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1Transform2dFlipConfiguration.cs
index 3246bca19e..905136ea1d 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1Transform2dFlipConfiguration.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1Transform2dFlipConfiguration.cs
@@ -1,8 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-using System.Runtime.CompilerServices;
-
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
///
@@ -28,17 +26,17 @@ internal ref struct Av1Transform2dFlipConfiguration
///
/// The fixed-point shifts applied between successive stages of the configured transform pipeline.
///
- private ShiftBuffer shift;
+ private InlineArray3 shift;
///
/// The signed-bit ranges produced by the column transform stages.
///
- private Av1TransformStageRange stageRangeColumn;
+ private InlineArray12 stageRangeColumn;
///
/// The signed-bit ranges produced by the row transform stages.
///
- private Av1TransformStageRange stageRangeRow;
+ private InlineArray12 stageRangeRow;
///
/// Initializes a new instance of the struct.
@@ -337,12 +335,12 @@ internal ref struct Av1Transform2dFlipConfiguration
///
/// Gets the allowed signed-bit range after each column-transform stage.
///
- public readonly Av1TransformStageRange StageRangeColumn => this.stageRangeColumn;
+ public readonly InlineArray12 StageRangeColumn => this.stageRangeColumn;
///
/// Gets the allowed signed-bit range after each row-transform stage.
///
- public readonly Av1TransformStageRange StageRangeRow => this.stageRangeRow;
+ public readonly InlineArray12 StageRangeRow => this.stageRangeRow;
///
/// Creates the configuration used to transform spatial residuals into coefficients.
@@ -499,16 +497,4 @@ internal ref struct Av1Transform2dFlipConfiguration
this.stageRangeRow[i] = rowRange;
}
}
-
- ///
- /// Stores the three fixed-point shifts without allocating an array for each transform block.
- ///
- [InlineArray(3)]
- private struct ShiftBuffer
- {
- ///
- /// The first fixed-point shift.
- ///
- private int element0;
- }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformStageRange.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformStageRange.cs
deleted file mode 100644
index d4beaa42d4..0000000000
--- a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformStageRange.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (c) Six Labors.
-// Licensed under the Six Labors Split License.
-
-using System.Runtime.CompilerServices;
-
-namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
-
-///
-/// Stores the signed-bit ranges assigned to every stage of one AV1 transform axis.
-///
-[InlineArray(Av1Transform2dFlipConfiguration.MaxStageNumber)]
-internal struct Av1TransformStageRange
-{
- ///
- /// The signed-bit range assigned to the first transform stage.
- ///
- private byte element0;
-}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Adst16Operator.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Adst16Operator.cs
index eaaeb6590d..4c0a80f97a 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Adst16Operator.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Adst16Operator.cs
@@ -24,7 +24,7 @@ internal static partial class Av1Inverse2dTransformer
/// The sixteen-element stage buffer owned by the containing two-dimensional transform.
/// The fixed-point precision of the cosine constants.
/// The signed-bit range assigned to each transform stage.
- public static void Transform(ReadOnlySpan input, Span output, Span step, int cosBit, Av1TransformStageRange stageRange)
+ public static void Transform(ReadOnlySpan input, Span output, Span step, int cosBit, InlineArray12 stageRange)
{
ReadOnlySpan cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
@@ -205,7 +205,7 @@ internal static partial class Av1Inverse2dTransformer
ref Av1TransformVector> output,
ref Av1TransformVector> step,
int cosBit,
- Av1TransformStageRange stageRange)
+ InlineArray12 stageRange)
{
ReadOnlySpan cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
@@ -393,7 +393,7 @@ internal static partial class Av1Inverse2dTransformer
ref Av1TransformVector> output,
ref Av1TransformVector> step,
int cosBit,
- Av1TransformStageRange stageRange)
+ InlineArray12 stageRange)
{
ReadOnlySpan cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Adst4Operator.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Adst4Operator.cs
index ed04c36b20..ba1badf334 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Adst4Operator.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Adst4Operator.cs
@@ -24,7 +24,7 @@ internal static partial class Av1Inverse2dTransformer
/// The stage buffer owned by the containing two-dimensional transform.
/// The fixed-point precision of the sine constants.
/// The signed-bit range assigned to each transform stage.
- public static void Transform(ReadOnlySpan input, Span output, Span step, int cosBit, Av1TransformStageRange stageRange)
+ public static void Transform(ReadOnlySpan input, Span output, Span step, int cosBit, InlineArray12 stageRange)
{
ReadOnlySpan sinpi = Av1SinusConstants.SinusPi(cosBit);
@@ -79,7 +79,7 @@ internal static partial class Av1Inverse2dTransformer
ref Av1TransformVector> output,
ref Av1TransformVector> step,
int cosBit,
- Av1TransformStageRange stageRange)
+ InlineArray12 stageRange)
{
bool widenedRound = stageRange[0] >= Av1Transform1dMath.WidenedIntermediateBitCount;
@@ -115,7 +115,7 @@ internal static partial class Av1Inverse2dTransformer
ref Av1TransformVector> output,
ref Av1TransformVector> step,
int cosBit,
- Av1TransformStageRange stageRange)
+ InlineArray12 stageRange)
{
bool widenedRound = stageRange[0] >= Av1Transform1dMath.WidenedIntermediateBitCount;
diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Adst8Operator.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Adst8Operator.cs
index e9646e8e22..c741ec7dce 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Adst8Operator.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Adst8Operator.cs
@@ -24,7 +24,7 @@ internal static partial class Av1Inverse2dTransformer
/// The eight-element stage buffer owned by the containing two-dimensional transform.
/// The fixed-point precision of the cosine constants.
/// The signed-bit range assigned to each transform stage.
- public static void Transform(ReadOnlySpan input, Span output, Span step, int cosBit, Av1TransformStageRange stageRange)
+ public static void Transform(ReadOnlySpan input, Span output, Span step, int cosBit, InlineArray12 stageRange)
{
ReadOnlySpan cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
@@ -112,7 +112,7 @@ internal static partial class Av1Inverse2dTransformer
ref Av1TransformVector> output,
ref Av1TransformVector> step,
int cosBit,
- Av1TransformStageRange stageRange)
+ InlineArray12 stageRange)
{
ReadOnlySpan cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
@@ -207,7 +207,7 @@ internal static partial class Av1Inverse2dTransformer
ref Av1TransformVector> output,
ref Av1TransformVector> step,
int cosBit,
- Av1TransformStageRange stageRange)
+ InlineArray12 stageRange)
{
ReadOnlySpan cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Dct16Operator.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Dct16Operator.cs
index b23dcf616f..7db0baec48 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Dct16Operator.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Dct16Operator.cs
@@ -24,7 +24,7 @@ internal static partial class Av1Inverse2dTransformer
/// The sixteen-element stage buffer owned by the containing two-dimensional transform.
/// The fixed-point precision of the cosine constants.
/// The signed-bit range assigned to each transform stage.
- public static void Transform(ReadOnlySpan input, Span output, Span step, int cosBit, Av1TransformStageRange stageRange)
+ public static void Transform(ReadOnlySpan input, Span output, Span step, int cosBit, InlineArray12 stageRange)
{
ReadOnlySpan cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
@@ -174,7 +174,7 @@ internal static partial class Av1Inverse2dTransformer
ref Av1TransformVector> output,
ref Av1TransformVector> step,
int cosBit,
- Av1TransformStageRange stageRange)
+ InlineArray12 stageRange)
{
ReadOnlySpan cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
@@ -331,7 +331,7 @@ internal static partial class Av1Inverse2dTransformer
ref Av1TransformVector> output,
ref Av1TransformVector> step,
int cosBit,
- Av1TransformStageRange stageRange)
+ InlineArray12 stageRange)
{
ReadOnlySpan cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Dct32Operator.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Dct32Operator.cs
index 4f284da314..9808f02bf6 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Dct32Operator.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Dct32Operator.cs
@@ -24,7 +24,7 @@ internal static partial class Av1Inverse2dTransformer
/// The 32-element stage buffer owned by the containing two-dimensional transform.
/// The fixed-point precision of the cosine constants.
/// The signed-bit range assigned to each transform stage.
- public static void Transform(ReadOnlySpan input, Span output, Span step, int cosBit, Av1TransformStageRange stageRange)
+ public static void Transform(ReadOnlySpan input, Span output, Span step, int cosBit, InlineArray12 stageRange)
{
ReadOnlySpan cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
@@ -358,7 +358,7 @@ internal static partial class Av1Inverse2dTransformer
ref Av1TransformVector> output,
ref Av1TransformVector> step,
int cosBit,
- Av1TransformStageRange stageRange)
+ InlineArray12 stageRange)
{
ReadOnlySpan cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
@@ -699,7 +699,7 @@ internal static partial class Av1Inverse2dTransformer
ref Av1TransformVector> output,
ref Av1TransformVector> step,
int cosBit,
- Av1TransformStageRange stageRange)
+ InlineArray12 stageRange)
{
ReadOnlySpan cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Dct4Operator.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Dct4Operator.cs
index 149d646819..e9abc192fe 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Dct4Operator.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Dct4Operator.cs
@@ -24,7 +24,7 @@ internal static partial class Av1Inverse2dTransformer
/// The four-element stage buffer owned by the containing two-dimensional transform.
/// The fixed-point precision of the cosine constants.
/// The signed-bit range assigned to each transform stage.
- public static void Transform(ReadOnlySpan input, Span output, Span step, int cosBit, Av1TransformStageRange stageRange)
+ public static void Transform(ReadOnlySpan input, Span output, Span step, int cosBit, InlineArray12 stageRange)
{
// AV1 stores coefficients in frequency order; this permutation restores the order expected by the staged DCT.
output[0] = input[0];
@@ -53,7 +53,7 @@ internal static partial class Av1Inverse2dTransformer
ref Av1TransformVector> output,
ref Av1TransformVector> step,
int cosBit,
- Av1TransformStageRange stageRange)
+ InlineArray12 stageRange)
{
// AV1 stores coefficients in frequency order; this permutation restores the order expected by the staged DCT.
output.V0 = input.V0;
@@ -89,7 +89,7 @@ internal static partial class Av1Inverse2dTransformer
ref Av1TransformVector> output,
ref Av1TransformVector> step,
int cosBit,
- Av1TransformStageRange stageRange)
+ InlineArray12 stageRange)
{
// AV1 stores coefficients in frequency order; this permutation restores the order expected by the staged DCT.
output.V0 = input.V0;
diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Dct64Operator.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Dct64Operator.cs
index f3659699e0..020940df70 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Dct64Operator.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Dct64Operator.cs
@@ -24,7 +24,7 @@ internal static partial class Av1Inverse2dTransformer
/// The 64-element stage buffer owned by the containing two-dimensional transform.
/// The fixed-point precision of the cosine constants.
/// The signed-bit range assigned to each transform stage.
- public static void Transform(ReadOnlySpan input, Span output, Span step, int cosBit, Av1TransformStageRange stageRange)
+ public static void Transform(ReadOnlySpan input, Span output, Span step, int cosBit, InlineArray12 stageRange)
{
ReadOnlySpan cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
@@ -773,7 +773,7 @@ internal static partial class Av1Inverse2dTransformer
ref Av1TransformVector> output,
ref Av1TransformVector> step,
int cosBit,
- Av1TransformStageRange stageRange)
+ InlineArray12 stageRange)
{
ReadOnlySpan cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
@@ -1529,7 +1529,7 @@ internal static partial class Av1Inverse2dTransformer
ref Av1TransformVector> output,
ref Av1TransformVector> step,
int cosBit,
- Av1TransformStageRange stageRange)
+ InlineArray12 stageRange)
{
ReadOnlySpan cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Dct8Operator.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Dct8Operator.cs
index ada40dc907..409b973301 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Dct8Operator.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Dct8Operator.cs
@@ -24,7 +24,7 @@ internal static partial class Av1Inverse2dTransformer
/// The eight-element stage buffer owned by the containing two-dimensional transform.
/// The fixed-point precision of the cosine constants.
/// The signed-bit range assigned to each transform stage.
- public static void Transform(ReadOnlySpan input, Span output, Span step, int cosBit, Av1TransformStageRange stageRange)
+ public static void Transform(ReadOnlySpan input, Span output, Span step, int cosBit, InlineArray12 stageRange)
{
ReadOnlySpan cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
@@ -93,7 +93,7 @@ internal static partial class Av1Inverse2dTransformer
ref Av1TransformVector> output,
ref Av1TransformVector> step,
int cosBit,
- Av1TransformStageRange stageRange)
+ InlineArray12 stageRange)
{
ReadOnlySpan cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
@@ -169,7 +169,7 @@ internal static partial class Av1Inverse2dTransformer
ref Av1TransformVector> output,
ref Av1TransformVector> step,
int cosBit,
- Av1TransformStageRange stageRange)
+ InlineArray12 stageRange)
{
ReadOnlySpan cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Identity16Operator.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Identity16Operator.cs
index e9c435d9c8..3d2a7d8d0b 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Identity16Operator.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Identity16Operator.cs
@@ -24,7 +24,7 @@ internal static partial class Av1Inverse2dTransformer
/// Unused stage storage supplied by the common transform-kernel contract.
/// Unused cosine precision supplied by the common transform-kernel contract.
/// The signed-bit range assigned to the transform output.
- public static void Transform(ReadOnlySpan input, Span output, Span step, int cosBit, Av1TransformStageRange stageRange)
+ public static void Transform(ReadOnlySpan input, Span output, Span step, int cosBit, InlineArray12 stageRange)
{
_ = step;
_ = cosBit;
@@ -43,7 +43,7 @@ internal static partial class Av1Inverse2dTransformer
ref Av1TransformVector> output,
ref Av1TransformVector> step,
int cosBit,
- Av1TransformStageRange stageRange)
+ InlineArray12 stageRange)
{
// The doubled scale exceeds Int32 only for the 20-bit twelve-bit row range. Widen that exact product and
// rounding sequence, matching the reference decoder without changing the established lower-range SIMD path.
@@ -66,7 +66,7 @@ internal static partial class Av1Inverse2dTransformer
ref Av1TransformVector> output,
ref Av1TransformVector> step,
int cosBit,
- Av1TransformStageRange stageRange)
+ InlineArray12 stageRange)
{
if (stageRange[0] >= Av1Transform1dMath.WidenedIntermediateBitCount)
{
diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Identity32Operator.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Identity32Operator.cs
index bbed35d812..933fb8060b 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Identity32Operator.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Identity32Operator.cs
@@ -24,7 +24,7 @@ internal static partial class Av1Inverse2dTransformer
/// Unused stage storage supplied by the common transform-kernel contract.
/// Unused cosine precision supplied by the common transform-kernel contract.
/// The signed-bit range assigned to the transform output.
- public static void Transform(ReadOnlySpan input, Span output, Span step, int cosBit, Av1TransformStageRange stageRange)
+ public static void Transform(ReadOnlySpan input, Span output, Span step, int cosBit, InlineArray12 stageRange)
{
_ = step;
_ = cosBit;
@@ -43,7 +43,7 @@ internal static partial class Av1Inverse2dTransformer
ref Av1TransformVector> output,
ref Av1TransformVector> step,
int cosBit,
- Av1TransformStageRange stageRange)
+ InlineArray12 stageRange)
{
Av1IdentityTransform1d.Transform(ref input, ref output, 32, 4, 0);
_ = step;
@@ -57,7 +57,7 @@ internal static partial class Av1Inverse2dTransformer
ref Av1TransformVector> output,
ref Av1TransformVector> step,
int cosBit,
- Av1TransformStageRange stageRange)
+ InlineArray12 stageRange)
{
Av1IdentityTransform1d.Transform(ref input, ref output, 32, 4, 0);
_ = step;
diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Identity4Operator.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Identity4Operator.cs
index f952466283..5789e0e842 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Identity4Operator.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Identity4Operator.cs
@@ -24,7 +24,7 @@ internal static partial class Av1Inverse2dTransformer
/// Unused stage storage supplied by the common transform-kernel contract.
/// Unused cosine precision supplied by the common transform-kernel contract.
/// The signed-bit range assigned to the transform output.
- public static void Transform(ReadOnlySpan input, Span output, Span step, int cosBit, Av1TransformStageRange stageRange)
+ public static void Transform(ReadOnlySpan input, Span output, Span step, int cosBit, InlineArray12 stageRange)
{
_ = step;
_ = cosBit;
@@ -43,7 +43,7 @@ internal static partial class Av1Inverse2dTransformer
ref Av1TransformVector> output,
ref Av1TransformVector> step,
int cosBit,
- Av1TransformStageRange stageRange)
+ InlineArray12 stageRange)
{
// Only a twelve-bit row transform has the 20-bit input range that can overflow this fixed-point product.
// Match the reference decoder's high-bit-depth kernel there while retaining the compact Int32 path for narrower ranges.
@@ -66,7 +66,7 @@ internal static partial class Av1Inverse2dTransformer
ref Av1TransformVector> output,
ref Av1TransformVector> step,
int cosBit,
- Av1TransformStageRange stageRange)
+ InlineArray12 stageRange)
{
if (stageRange[0] >= Av1Transform1dMath.WidenedIntermediateBitCount)
{
diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Identity8Operator.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Identity8Operator.cs
index ca9f21f762..c4d6f36f7c 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Identity8Operator.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Identity8Operator.cs
@@ -24,7 +24,7 @@ internal static partial class Av1Inverse2dTransformer
/// Unused stage storage supplied by the common transform-kernel contract.
/// Unused cosine precision supplied by the common transform-kernel contract.
/// The signed-bit range assigned to the transform output.
- public static void Transform(ReadOnlySpan input, Span output, Span step, int cosBit, Av1TransformStageRange stageRange)
+ public static void Transform(ReadOnlySpan input, Span output, Span step, int cosBit, InlineArray12 stageRange)
{
_ = step;
_ = cosBit;
@@ -43,7 +43,7 @@ internal static partial class Av1Inverse2dTransformer
ref Av1TransformVector> output,
ref Av1TransformVector> step,
int cosBit,
- Av1TransformStageRange stageRange)
+ InlineArray12 stageRange)
{
Av1IdentityTransform1d.Transform(ref input, ref output, 8, 2, 0);
_ = step;
@@ -57,7 +57,7 @@ internal static partial class Av1Inverse2dTransformer
ref Av1TransformVector> output,
ref Av1TransformVector> step,
int cosBit,
- Av1TransformStageRange stageRange)
+ InlineArray12 stageRange)
{
Av1IdentityTransform1d.Transform(ref input, ref output, 8, 2, 0);
_ = step;
diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Operator.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Operator.cs
index 579524b3c9..d88e575d3d 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Operator.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Inverse2dTransformer.Operator.cs
@@ -27,7 +27,7 @@ internal static partial class Av1Inverse2dTransformer
/// The fixed stage storage for the transform axis.
/// The fixed-point precision of the cosine constants.
/// The signed-bit range assigned to each transform stage.
- public static abstract void Transform(ReadOnlySpan input, Span output, Span step, int cosBit, Av1TransformStageRange stageRange);
+ public static abstract void Transform(ReadOnlySpan input, Span output, Span step, int cosBit, InlineArray12 stageRange);
///
/// Transforms four independent axes in parallel.
@@ -42,7 +42,7 @@ internal static partial class Av1Inverse2dTransformer
ref Av1TransformVector> output,
ref Av1TransformVector> step,
int cosBit,
- Av1TransformStageRange stageRange);
+ InlineArray12 stageRange);
///
/// Transforms eight independent axes in parallel.
@@ -57,6 +57,6 @@ internal static partial class Av1Inverse2dTransformer
ref Av1TransformVector> output,
ref Av1TransformVector> step,
int cosBit,
- Av1TransformStageRange stageRange);
+ InlineArray12 stageRange);
}
}
diff --git a/src/ImageSharp/Formats/Heif/HeifConfigurationModule.cs b/src/ImageSharp/Formats/Heif/HeifConfigurationModule.cs
index f0ceb30e8d..c4077cb29b 100644
--- a/src/ImageSharp/Formats/Heif/HeifConfigurationModule.cs
+++ b/src/ImageSharp/Formats/Heif/HeifConfigurationModule.cs
@@ -11,6 +11,7 @@ public sealed class HeifConfigurationModule : IImageFormatConfigurationModule
///
public void Configure(Configuration configuration)
{
+ configuration.ImageFormatsManager.SetEncoder(HeifFormat.Instance, new HeifEncoder());
configuration.ImageFormatsManager.SetDecoder(HeifFormat.Instance, HeifDecoder.Instance);
configuration.ImageFormatsManager.AddImageFormatDetector(new HeifImageFormatDetector());
}
diff --git a/src/ImageSharp/Formats/Heif/HeifEncoder.cs b/src/ImageSharp/Formats/Heif/HeifEncoder.cs
index 8afae17803..db26aec078 100644
--- a/src/ImageSharp/Formats/Heif/HeifEncoder.cs
+++ b/src/ImageSharp/Formats/Heif/HeifEncoder.cs
@@ -25,9 +25,9 @@ public sealed class HeifEncoder : AnimatedImageEncoder
///
/// Gets the compression method used for the primary image item.
- /// The default is .
+ /// The default is .
///
- public HeifCompressionMethod CompressionMethod { get; init; } = HeifCompressionMethod.LegacyJpeg;
+ public HeifCompressionMethod CompressionMethod { get; init; } = HeifCompressionMethod.Av1;
///
/// Gets the lossy compression quality, or to use the compression method's default quality.
diff --git a/src/ImageSharp/Formats/Heif/HeifEncoderCore.Sequence.cs b/src/ImageSharp/Formats/Heif/HeifEncoderCore.Sequence.cs
index c6917403c2..1bf61d1904 100644
--- a/src/ImageSharp/Formats/Heif/HeifEncoderCore.Sequence.cs
+++ b/src/ImageSharp/Formats/Heif/HeifEncoderCore.Sequence.cs
@@ -1,13 +1,13 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
+using System.Buffers;
using System.Buffers.Binary;
using System.Text;
using SixLabors.ImageSharp.Formats.Heif.Av1;
using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
using SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline;
using SixLabors.ImageSharp.IO;
-using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata.Profiles.Cicp;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
using SixLabors.ImageSharp.PixelFormats;
@@ -22,6 +22,11 @@ internal sealed partial class HeifEncoderCore
private const uint UnityFixed2Point30 = 1U << 30;
private const ushort UnityFixed8Point8 = 1 << 8;
private const ushort PackedUndeterminedLanguage = 0x55C4;
+ private const uint AllReferencePicturesIntraMask = 1U << 31;
+ private const uint IntraPicturePredictionUsedMask = 1U << 30;
+ private const uint DefaultVisualSampleResolution = 72U << 16;
+ private const int VisualSampleCompressorNameLength = 32;
+ private const ushort VisualSampleDepth = 24;
private Av1EncodingSettings ResolveAv1Encoding(Image image)
where TPixel : unmanaged, IPixel
@@ -56,7 +61,11 @@ internal sealed partial class HeifEncoderCore
CicpProfile colorProfile;
if (sourceColorProfile is null)
{
- colorProfile = new CicpProfile(2, 2, 6, false);
+ colorProfile = new CicpProfile(
+ (byte)CicpColorPrimaries.Unspecified,
+ (byte)CicpTransferCharacteristics.Unspecified,
+ (byte)CicpMatrixCoefficients.ItuRBt601_7_525,
+ false);
}
else
{
@@ -133,6 +142,7 @@ internal sealed partial class HeifEncoderCore
Image image,
ChunkedMemoryStream stream,
Av1EncodingSettings settings,
+ Memory samples,
CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel
{
@@ -141,14 +151,25 @@ internal sealed partial class HeifEncoderCore
throw new NotSupportedException("AV1 image-sequence dimensions cannot exceed 65535 pixels.");
}
+ byte[]? exifData = null;
+ uint tiffHeaderOffset = 0;
+ byte[]? xmpData = null;
+ if (!this.encoder.SkipMetadata)
+ {
+ exifData = GetExifData(image.Metadata, out tiffHeaderOffset);
+ byte[]? sourceXmpData = image.Metadata.XmpProfile?.Data;
+ if (sourceXmpData is not null && sourceXmpData.Length > 0)
+ {
+ xmpData = sourceXmpData;
+ }
+ }
+
int frameCount = image.Frames.Count;
uint timescale = GetSequenceTimescale(image);
- int sampleCount = checked(frameCount * (settings.HasAlpha ? 2 : 1));
// The container needs only offset, length, and duration after each frame is streamed. Color and alpha
- // share one compact table, with each track occupying one contiguous slice for its complete operation lifetime.
- HeifSequenceSampleInfo[] samples = new HeifSequenceSampleInfo[sampleCount];
- Span