diff --git a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1DefaultDistributions.cs b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1DefaultDistributions.cs
index cc08556fd..09efc4ba9 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1DefaultDistributions.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1DefaultDistributions.cs
@@ -135,7 +135,16 @@ internal static class Av1DefaultDistributions
public static Av1Distribution DeltaQuantizerAbsolute => new(28160, 32120, 32677);
- public static Av1Distribution[] SegmentId => [new(128 * 128), new(128 * 128), new(128 * 128)];
+ ///
+ /// Gets the Segment identifier .
+ ///
+ /// SVT: default_spatial_pred_seg_tree_cdf
+ public static Av1Distribution[] SegmentId =>
+ [
+ new(5622, 7893, 16093, 18233, 27809, 28373, 32533),
+ new(14274, 18230, 22557, 24935, 29980, 30851, 32344),
+ new(27527, 28487, 28723, 28890, 32397, 32647, 32679),
+ ];
public static Av1Distribution[][] KeyFrameYMode =>
[
diff --git a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolDecoder.cs b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolDecoder.cs
index f265156a7..ca46def22 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolDecoder.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolDecoder.cs
@@ -53,7 +53,7 @@ internal ref struct Av1SymbolDecoder
this.endOfBlockExtra = Av1DefaultDistributions.GetEndOfBlockExtra(qIndex);
}
- public int ReadLiteral(int bitCount)
+ public int ReadCdfStrength(int bitCount)
{
ref Av1SymbolReader r = ref this.reader;
return r.ReadLiteral(bitCount);
@@ -130,22 +130,53 @@ internal ref struct Av1SymbolDecoder
return r.ReadSymbol(this.skipMode[(int)blockSize]) > 0;
}
- public int ReadDeltaLoopFilterAbsolute()
+ public int ReadDeltaLoopFilter()
{
ref Av1SymbolReader r = ref this.reader;
- return r.ReadSymbol(this.deltaLoopFilterAbsolute);
+ int deltaLoopFilterAbsolute = r.ReadSymbol(this.deltaLoopFilterAbsolute);
+ if (deltaLoopFilterAbsolute == Av1Constants.DeltaLoopFilterSmall)
+ {
+ int deltaLoopFilterRemainingBits = r.ReadLiteral(3) + 1;
+ int deltaLoopFilterAbsoluteBitCount = r.ReadLiteral(deltaLoopFilterRemainingBits);
+ deltaLoopFilterAbsolute = deltaLoopFilterAbsoluteBitCount + (1 << deltaLoopFilterRemainingBits) + 1;
+ }
+
+ bool deltaLoopFilterSign = true;
+ if (deltaLoopFilterAbsolute != 0)
+ {
+ deltaLoopFilterSign = r.ReadLiteral(1) > 0;
+ }
+
+ return deltaLoopFilterSign ? -deltaLoopFilterAbsolute : deltaLoopFilterAbsolute;
}
- public int ReadDeltaQuantizerAbsolute()
+ ///
+ /// SVT: read_delta_qindex
+ ///
+ public int ReadDeltaQuantizerIndex()
{
ref Av1SymbolReader r = ref this.reader;
- return r.ReadSymbol(this.deltaQuantizerAbsolute);
+ int deltaQuantizerAbsolute = r.ReadSymbol(this.deltaQuantizerAbsolute);
+ if (deltaQuantizerAbsolute == Av1Constants.DeltaQuantizerSmall)
+ {
+ int deltaQuantizerRemainingBits = r.ReadLiteral(3) + 1;
+ int deltaQuantizerAbsoluteBase = r.ReadLiteral(deltaQuantizerRemainingBits);
+ deltaQuantizerAbsolute = deltaQuantizerAbsoluteBase + (1 << deltaQuantizerRemainingBits) + 1;
+ }
+
+ bool deltaQuantizerSignBit = true;
+ if (deltaQuantizerAbsolute != 0)
+ {
+ deltaQuantizerSignBit = r.ReadLiteral(1) > 0;
+ }
+
+ return deltaQuantizerSignBit ? -deltaQuantizerAbsolute : deltaQuantizerAbsolute;
}
- public int ReadSegmentId(int ctx)
+ public int ReadSegmentId(int context)
{
ref Av1SymbolReader r = ref this.reader;
- return r.ReadSymbol(this.segmentId[ctx]);
+ return r.ReadSymbol(this.segmentId[context]);
}
public int ReadAngleDelta(Av1PredictionMode mode)
@@ -154,16 +185,17 @@ internal ref struct Av1SymbolDecoder
return r.ReadSymbol(this.angleDelta[(int)mode - 1]);
}
- public bool ReadUseFilterUltra(Av1BlockSize blockSize)
+ public Av1FilterIntraMode ReadFilterUltraMode(Av1BlockSize blockSize)
{
ref Av1SymbolReader r = ref this.reader;
- return r.ReadSymbol(this.filterIntra[(int)blockSize]) > 0;
- }
+ Av1FilterIntraMode filterIntraMode = Av1FilterIntraMode.AllFilterIntraModes;
+ bool useFilterIntra = r.ReadSymbol(this.filterIntra[(int)blockSize]) > 0;
+ if (useFilterIntra)
+ {
+ filterIntraMode = (Av1FilterIntraMode)r.ReadSymbol(this.filterIntraMode);
+ }
- public Av1FilterIntraMode ReadFilterUltraMode()
- {
- ref Av1SymbolReader r = ref this.reader;
- return (Av1FilterIntraMode)r.ReadSymbol(this.filterIntraMode);
+ return filterIntraMode;
}
public Av1TransformSize ReadTransformSize(Av1BlockSize blockSize, int context)
@@ -348,6 +380,7 @@ internal ref struct Av1SymbolDecoder
public int ReadEndOfBlockPosition(Av1TransformSize transformSize, Av1TransformClass transformClass, Av1TransformSize transformSizeContext, Av1PlaneType planeType)
{
+ ref Av1SymbolReader r = ref this.reader;
int endOfBlockExtra = 0;
int endOfBlockPoint = this.ReadEndOfBlockFlag(planeType, transformClass, transformSize);
int endOfBlockShift = Av1SymbolContextHelper.EndOfBlockOffsetBits[endOfBlockPoint];
@@ -362,7 +395,7 @@ internal ref struct Av1SymbolDecoder
for (int j = 1; j < endOfBlockShift; j++)
{
- if (this.ReadLiteral(1) != 0)
+ if (r.ReadLiteral(1) != 0)
{
Av1Math.SetBit(ref endOfBlockExtra, endOfBlockShift - 1 - j);
}
@@ -451,6 +484,7 @@ internal ref struct Av1SymbolDecoder
public int ReadCoefficientsSign(Span coefficientBuffer, int endOfBlock, ReadOnlySpan scan, Av1LevelBuffer levels, int dcSignContext, Av1PlaneType planeType)
{
+ ref Av1SymbolReader r = ref this.reader;
int maxScanLine = 0;
int culLevel = 0;
int dcValue = 0;
@@ -469,7 +503,7 @@ internal ref struct Av1SymbolDecoder
}
else
{
- sign = this.ReadLiteral(1);
+ sign = r.ReadLiteral(1);
}
if (level >= Av1Constants.CoefficientBaseRange + Av1Constants.BaseLevelsCount + 1)
@@ -535,13 +569,14 @@ internal ref struct Av1SymbolDecoder
internal int ReadGolomb()
{
+ ref Av1SymbolReader r = ref this.reader;
int x = 1;
int length = 0;
int i = 0;
while (i == 0)
{
- i = this.ReadLiteral(1);
+ i = r.ReadLiteral(1);
++length;
if (length > 20)
{
@@ -553,7 +588,7 @@ internal ref struct Av1SymbolDecoder
for (i = 0; i < length - 1; ++i)
{
x <<= 1;
- x += this.ReadLiteral(1);
+ x += r.ReadLiteral(1);
}
return x - 1;
diff --git a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolEncoder.cs b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolEncoder.cs
index 1c9ef131c..4f98f446f 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolEncoder.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolEncoder.cs
@@ -330,19 +330,21 @@ internal class Av1SymbolEncoder : IDisposable
w.WriteSymbol(skip, this.skipMode[context]);
}
- internal void WriteFilterIntra(Av1FilterIntraMode filterIntraMode, Av1BlockSize blockSize)
+ internal void WriteFilterIntraMode(Av1FilterIntraMode filterIntraMode, Av1BlockSize blockSize)
{
ref Av1SymbolWriter w = ref this.writer;
- w.WriteSymbol(filterIntraMode != Av1FilterIntraMode.AllFilterIntraModes, this.filterIntra[(int)blockSize]);
- }
-
- internal void WriteFilterIntraMode(Av1FilterIntraMode filterIntraMode)
- {
- ref Av1SymbolWriter w = ref this.writer;
- w.WriteSymbol((int)filterIntraMode, this.filterIntraMode);
+ bool useFilter = filterIntraMode != Av1FilterIntraMode.AllFilterIntraModes;
+ w.WriteSymbol(useFilter, this.filterIntra[(int)blockSize]);
+ if (useFilter)
+ {
+ w.WriteSymbol((int)filterIntraMode, this.filterIntraMode);
+ }
}
- internal void WriteDeltaQIndex(int deltaQindex)
+ ///
+ /// SVT: av1_write_delta_q_index
+ ///
+ internal void WriteDeltaQuantizerIndex(int deltaQindex)
{
ref Av1SymbolWriter w = ref this.writer;
bool sign = deltaQindex < 0;
@@ -353,7 +355,7 @@ internal class Av1SymbolEncoder : IDisposable
if (!smallval)
{
- int rem_bits = Av1Math.MostSignificantBit((uint)(abs - 1)) - 1;
+ int rem_bits = Av1Math.MostSignificantBit((uint)(abs - 1));
int threshold = (1 << rem_bits) + 1;
w.WriteLiteral((uint)(rem_bits - 1), 3);
w.WriteLiteral((uint)(abs - threshold), rem_bits);
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileReader.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileReader.cs
index e4cf2f5fe..f6aad1019 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileReader.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileReader.cs
@@ -1051,21 +1051,19 @@ internal class Av1TileReader : IAv1TileReader
private void FilterIntraModeInfo(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo)
{
+ partitionInfo.ModeInfo.FilterIntraModeInfo.UseFilterIntra = false;
if (this.SequenceHeader.EnableFilterIntra &&
partitionInfo.ModeInfo.YMode == Av1PredictionMode.DC &&
partitionInfo.ModeInfo.GetPaletteSize(Av1PlaneType.Y) == 0 &&
Math.Max(partitionInfo.ModeInfo.BlockSize.GetWidth(), partitionInfo.ModeInfo.BlockSize.GetHeight()) <= 32)
{
- partitionInfo.ModeInfo.FilterIntraModeInfo.UseFilterIntra = reader.ReadUseFilterUltra(partitionInfo.ModeInfo.BlockSize);
- if (partitionInfo.ModeInfo.FilterIntraModeInfo.UseFilterIntra)
+ Av1FilterIntraMode filterIntraMode = reader.ReadFilterUltraMode(partitionInfo.ModeInfo.BlockSize);
+ if (filterIntraMode != Av1FilterIntraMode.AllFilterIntraModes)
{
- partitionInfo.ModeInfo.FilterIntraModeInfo.Mode = reader.ReadFilterUltraMode();
+ partitionInfo.ModeInfo.FilterIntraModeInfo.UseFilterIntra = true;
+ partitionInfo.ModeInfo.FilterIntraModeInfo.Mode = filterIntraMode;
}
}
- else
- {
- partitionInfo.ModeInfo.FilterIntraModeInfo.UseFilterIntra = false;
- }
}
///
@@ -1209,7 +1207,8 @@ internal class Av1TileReader : IAv1TileReader
int c = partitionInfo.ColumnIndex & cdefMask4;
if (partitionInfo.CdefStrength[r][c] == -1)
{
- partitionInfo.CdefStrength[r][c] = reader.ReadLiteral(this.FrameHeader.CdefParameters.BitCount);
+ int cdfStrength = reader.ReadCdfStrength(this.FrameHeader.CdefParameters.BitCount);
+ partitionInfo.CdefStrength[r][c] = cdfStrength;
if (this.SequenceHeader.SuperblockSize == Av1BlockSize.Block128x128)
{
int w4 = partitionInfo.ModeInfo.BlockSize.Get4x4WideCount();
@@ -1218,7 +1217,7 @@ internal class Av1TileReader : IAv1TileReader
{
for (int j = c; j < c + w4; j += cdefSize4)
{
- partitionInfo.CdefStrength[i & cdefMask4][j & cdefMask4] = partitionInfo.CdefStrength[r][c];
+ partitionInfo.CdefStrength[i & cdefMask4][j & cdefMask4] = cdfStrength;
}
}
}
@@ -1245,21 +1244,9 @@ internal class Av1TileReader : IAv1TileReader
Span currentDeltaLoopFilter = partitionInfo.SuperblockInfo.SuperblockDeltaLoopFilter;
for (int i = 0; i < frameLoopFilterCount; i++)
{
- int deltaLoopFilterAbsolute = reader.ReadDeltaLoopFilterAbsolute();
- if (deltaLoopFilterAbsolute == Av1Constants.DeltaLoopFilterSmall)
- {
- int deltaLoopFilterRemainingBits = reader.ReadLiteral(3) + 1;
- int deltaLoopFilterAbsoluteBitCount = reader.ReadLiteral(deltaLoopFilterRemainingBits);
- deltaLoopFilterAbsolute = deltaLoopFilterAbsoluteBitCount + (1 << deltaLoopFilterRemainingBits) + 1;
- }
-
- if (deltaLoopFilterAbsolute != 0)
- {
- bool deltaLoopFilterSign = reader.ReadLiteral(1) > 0;
- int reducedDeltaLoopFilterLevel = deltaLoopFilterSign ? -deltaLoopFilterAbsolute : deltaLoopFilterAbsolute;
- int deltaLoopFilterResolution = this.FrameHeader.DeltaLoopFilterParameters.Resolution;
- currentDeltaLoopFilter[i] = Av1Math.Clip3(-Av1Constants.MaxLoopFilter, Av1Constants.MaxLoopFilter, currentDeltaLoopFilter[i] + (reducedDeltaLoopFilterLevel << deltaLoopFilterResolution));
- }
+ int reducedDeltaLoopFilterLevel = reader.ReadDeltaLoopFilter();
+ int deltaLoopFilterResolution = this.FrameHeader.DeltaLoopFilterParameters.Resolution;
+ currentDeltaLoopFilter[i] = Av1Math.Clip3(-Av1Constants.MaxLoopFilter, Av1Constants.MaxLoopFilter, currentDeltaLoopFilter[i] + (reducedDeltaLoopFilterLevel << deltaLoopFilterResolution));
}
}
}
@@ -1280,6 +1267,9 @@ internal class Av1TileReader : IAv1TileReader
}
}
+ ///
+ /// SVT: read_delta_qindex
+ ///
private void ReadDeltaQuantizerIndex(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo)
{
Av1BlockSize superBlockSize = this.SequenceHeader.Use128x128Superblock ? Av1BlockSize.Block128x128 : Av1BlockSize.Block64x64;
@@ -1291,22 +1281,10 @@ internal class Av1TileReader : IAv1TileReader
if (partitionInfo.ModeInfo.BlockSize != this.SequenceHeader.SuperblockSize || !partitionInfo.ModeInfo.Skip)
{
- int deltaQuantizerAbsolute = reader.ReadDeltaQuantizerAbsolute();
- if (deltaQuantizerAbsolute == Av1Constants.DeltaQuantizerSmall)
- {
- int deltaQuantizerRemainingBits = reader.ReadLiteral(3) + 1;
- int deltaQuantizerAbsoluteBitCount = reader.ReadLiteral(deltaQuantizerRemainingBits);
- deltaQuantizerAbsolute = deltaQuantizerRemainingBits + (1 << deltaQuantizerRemainingBits) + 1;
- }
-
- if (deltaQuantizerAbsolute != 0)
- {
- bool deltaQuantizerSignBit = reader.ReadLiteral(1) > 0;
- int reducedDeltaQuantizerIndex = deltaQuantizerSignBit ? -deltaQuantizerAbsolute : deltaQuantizerAbsolute;
- int deltaQuantizerResolution = this.FrameHeader.DeltaQParameters.Resolution;
- this.currentQuantizerIndex = Av1Math.Clip3(1, 255, this.currentQuantizerIndex + (reducedDeltaQuantizerIndex << deltaQuantizerResolution));
- partitionInfo.SuperblockInfo.SuperblockDeltaQ = this.currentQuantizerIndex;
- }
+ int reducedDeltaQuantizerIndex = reader.ReadDeltaQuantizerIndex();
+ int deltaQuantizerResolution = this.FrameHeader.DeltaQParameters.Resolution;
+ this.currentQuantizerIndex = Av1Math.Clip3(1, 255, this.currentQuantizerIndex + (reducedDeltaQuantizerIndex << deltaQuantizerResolution));
+ partitionInfo.SuperblockInfo.SuperblockDeltaQ = this.currentQuantizerIndex;
}
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileWriter.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileWriter.cs
index c405cbd05..2ee25e9bd 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileWriter.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileWriter.cs
@@ -449,7 +449,7 @@ internal partial class Av1TileWriter
int reduced_delta_qindex = (current_q_index - pcs.Parent.PreviousQIndex[tile_idx]) /
frm_hdr.DeltaQParameters.Resolution;
- writer.WriteDeltaQIndex(reduced_delta_qindex);
+ writer.WriteDeltaQuantizerIndex(reduced_delta_qindex);
pcs.Parent.PreviousQIndex[tile_idx] = current_q_index;
}
}
@@ -495,11 +495,7 @@ internal partial class Av1TileWriter
if (!macroBlockModeInfo.Block.UseIntraBlockCopy &&
IsFilterIntraAllowed(scs.SequenceHeader.FilterIntraLevel > 0, blockSize, blk_ptr.PaletteSize[0], intra_luma_mode))
{
- writer.WriteFilterIntra(blk_ptr.FilterIntraMode, blockSize);
- if (blk_ptr.FilterIntraMode != Av1FilterIntraMode.AllFilterIntraModes)
- {
- writer.WriteFilterIntraMode(blk_ptr.FilterIntraMode);
- }
+ writer.WriteFilterIntraMode(blk_ptr.FilterIntraMode, blockSize);
}
if (!macroBlockModeInfo.Block.UseIntraBlockCopy)
diff --git a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1EntropyTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1EntropyTests.cs
index cabcc7ae4..cfe14009d 100644
--- a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1EntropyTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1EntropyTests.cs
@@ -191,11 +191,11 @@ public class Av1EntropyTests
Assert.Equal(expectedValues, values);
}
- [Fact]
- public void RoundTripPartitionType()
+ [Theory]
+ [MemberData(nameof(GetRangeData), 20)]
+ public void RoundTripPartitionType(int context)
{
// Assign
- int ctx = 7;
Configuration configuration = Configuration.Default;
Av1SymbolEncoder encoder = new(configuration, 100 / 8, BaseQIndex);
Av1PartitionType[] values = [
@@ -206,7 +206,7 @@ public class Av1EntropyTests
// Act
foreach (Av1PartitionType value in values)
{
- encoder.WritePartitionType(value, 7);
+ encoder.WritePartitionType(value, context);
}
using IMemoryOwner encoded = encoder.Exit();
@@ -214,7 +214,7 @@ public class Av1EntropyTests
Av1SymbolDecoder decoder = new(Configuration.Default, encoded.GetSpan(), BaseQIndex);
for (int i = 0; i < values.Length; i++)
{
- actuals[i] = decoder.ReadPartitionType(ctx);
+ actuals[i] = decoder.ReadPartitionType(context);
}
// Assert
@@ -222,16 +222,11 @@ public class Av1EntropyTests
}
[Theory]
- [InlineData((int)Av1BlockSize.Block4x4, 7)]
- [InlineData((int)Av1BlockSize.Block4x4, 5)]
- [InlineData((int)Av1BlockSize.Block8x4, 7)]
- [InlineData((int)Av1BlockSize.Block4x8, 7)]
- [InlineData((int)Av1BlockSize.Block32x64, 7)]
- [InlineData((int)Av1BlockSize.Block64x32, 7)]
- [InlineData((int)Av1BlockSize.Block64x64, 7)]
- public void RoundTripSplitOrHorizontalPartitionType(int blockSize, int context)
+ [MemberData(nameof(GetSplitPartitionTypeData))]
+ public void RoundTripSplitOrHorizontalPartitionType(int size, int context)
{
// Assign
+ Av1BlockSize blockSize = (Av1BlockSize)size;
Configuration configuration = Configuration.Default;
Av1SymbolEncoder encoder = new(configuration, 100 / 8, BaseQIndex);
Av1PartitionType[] values = [
@@ -242,7 +237,7 @@ public class Av1EntropyTests
// Act
foreach (Av1PartitionType value in values)
{
- encoder.WriteSplitOrHorizontal(value, (Av1BlockSize)blockSize, context);
+ encoder.WriteSplitOrHorizontal(value, blockSize, context);
}
using IMemoryOwner encoded = encoder.Exit();
@@ -250,7 +245,7 @@ public class Av1EntropyTests
Av1SymbolDecoder decoder = new(Configuration.Default, encoded.GetSpan(), BaseQIndex);
for (int i = 0; i < values.Length; i++)
{
- actuals[i] = decoder.ReadSplitOrHorizontal((Av1BlockSize)blockSize, context);
+ actuals[i] = decoder.ReadSplitOrHorizontal(blockSize, context);
}
// Assert
@@ -258,16 +253,11 @@ public class Av1EntropyTests
}
[Theory]
- [InlineData((int)Av1BlockSize.Block4x4, 7)]
- [InlineData((int)Av1BlockSize.Block4x4, 5)]
- [InlineData((int)Av1BlockSize.Block8x4, 7)]
- [InlineData((int)Av1BlockSize.Block4x8, 7)]
- [InlineData((int)Av1BlockSize.Block32x64, 7)]
- [InlineData((int)Av1BlockSize.Block64x32, 7)]
- [InlineData((int)Av1BlockSize.Block64x64, 7)]
- public void RoundTripSplitOrVerticalPartitionType(int blockSize, int context)
+ [MemberData(nameof(GetSplitPartitionTypeData))]
+ public void RoundTripSplitOrVerticalPartitionType(int size, int context)
{
// Assign
+ Av1BlockSize blockSize = (Av1BlockSize)size;
Configuration configuration = Configuration.Default;
Av1SymbolEncoder encoder = new(configuration, 100 / 8, BaseQIndex);
Av1PartitionType[] values = [
@@ -278,7 +268,7 @@ public class Av1EntropyTests
// Act
foreach (Av1PartitionType value in values)
{
- encoder.WriteSplitOrVertical(value, (Av1BlockSize)blockSize, context);
+ encoder.WriteSplitOrVertical(value, blockSize, context);
}
using IMemoryOwner encoded = encoder.Exit();
@@ -286,19 +276,49 @@ public class Av1EntropyTests
Av1SymbolDecoder decoder = new(Configuration.Default, encoded.GetSpan(), BaseQIndex);
for (int i = 0; i < values.Length; i++)
{
- actuals[i] = decoder.ReadSplitOrVertical((Av1BlockSize)blockSize, context);
+ actuals[i] = decoder.ReadSplitOrVertical(blockSize, context);
}
// Assert
Assert.Equal(values, actuals);
}
- [Fact]
- public void RoundTripTransformBlockSkip()
+ [Theory]
+ [InlineData(0)]
+ [InlineData(1)]
+ [InlineData(2)]
+ public void RoundTripSkip(int context)
{
// Assign
- const Av1TransformSize transformSizeContext = Av1TransformSize.Size4x4;
- const int skipContext = 0;
+ Configuration configuration = Configuration.Default;
+ Av1SymbolEncoder encoder = new(configuration, 100 / 8, BaseQIndex);
+ bool[] values = [true, true, false, false, false, false, false, false, true];
+ bool[] actuals = new bool[values.Length];
+
+ // Act
+ foreach (bool value in values)
+ {
+ encoder.WriteSkip(value, context);
+ }
+
+ using IMemoryOwner encoded = encoder.Exit();
+
+ Av1SymbolDecoder decoder = new(Configuration.Default, encoded.GetSpan(), BaseQIndex);
+ for (int i = 0; i < values.Length; i++)
+ {
+ actuals[i] = decoder.ReadSkip(context);
+ }
+
+ // Assert
+ Assert.Equal(values, actuals);
+ }
+
+ [Theory]
+ [MemberData(nameof(GetTransformBlockSkipData))]
+ internal void RoundTripTransformBlockSkip(int transformContext, int skipContext)
+ {
+ // Assign
+ Av1TransformSize transformSizeContext = (Av1TransformSize)transformContext;
Configuration configuration = Configuration.Default;
Av1SymbolEncoder encoder = new(configuration, 100 / 8, BaseQIndex);
bool[] values = [true, true, false, false, false, false, false, false, true];
@@ -322,13 +342,14 @@ public class Av1EntropyTests
Assert.Equal(values, actuals);
}
- [Fact]
- public void RoundTripTransformType()
+ // [Theory]
+ [MemberData(nameof(GetTransformTypeData))]
+ public void RoundTripTransformType(int txSizeContext, int intraMode, int intraDir)
{
// Assign
- const Av1TransformSize transformSizeContext = Av1TransformSize.Size4x4;
- const Av1FilterIntraMode filterIntraMode = Av1FilterIntraMode.DC;
- const Av1PredictionMode intraDirection = Av1PredictionMode.DC;
+ Av1TransformSize transformSizeContext = (Av1TransformSize)txSizeContext;
+ Av1FilterIntraMode filterIntraMode = (Av1FilterIntraMode)intraMode;
+ Av1PredictionMode intraDirection = (Av1PredictionMode)intraDir;
Configuration configuration = Configuration.Default;
Av1SymbolEncoder encoder = new(configuration, 100 / 8, BaseQIndex);
@@ -336,7 +357,7 @@ public class Av1EntropyTests
Av1TransformType[] values = [
Av1TransformType.DctDct, Av1TransformType.DctDct, Av1TransformType.Identity, Av1TransformType.AdstDct,
Av1TransformType.DctDct, Av1TransformType.AdstAdst, Av1TransformType.Identity, Av1TransformType.DctAdst
- ];
+ ];
Av1TransformType[] actuals = new Av1TransformType[values.Length];
// Act
@@ -357,25 +378,26 @@ public class Av1EntropyTests
Assert.Equal(values, actuals);
}
- [Fact]
- public void RoundTripEndOfBlockPosition()
+ [Theory]
+ [MemberData(nameof(GetEndOfBlockPositionData))]
+ public void RoundTripEndOfBlockPosition(int txSize, int txSizeContext, int plane, int txClass)
{
// Assign
- const Av1TransformSize transformSize = Av1TransformSize.Size4x4;
- const Av1TransformSize transformSizeContext = Av1TransformSize.Size4x4;
- const Av1ComponentType componentType = Av1ComponentType.Luminance;
- const Av1PlaneType planeType = Av1PlaneType.Y;
- const Av1TransformClass transformClass = Av1TransformClass.Class2D;
+ Av1TransformSize transformSize = (Av1TransformSize)txSize;
+ Av1TransformSize transformSizeContext = (Av1TransformSize)txSizeContext;
+ Av1ComponentType componentType = (Av1ComponentType)plane;
+ Av1PlaneType planeType = (Av1PlaneType)plane;
+ Av1TransformClass transformClass = (Av1TransformClass)txClass;
Configuration configuration = Configuration.Default;
Av1SymbolEncoder encoder = new(configuration, 100 / 8, BaseQIndex);
- ushort[] values = [1, 2, 3, 4, 5];
+ int[] values = [1, 2, 3, 4, 5];
int[] actuals = new int[values.Length];
// Act
- foreach (ushort value in values)
+ foreach (int value in values)
{
- encoder.WriteEndOfBlockPosition(value, componentType, transformClass, transformSize, transformSizeContext);
+ encoder.WriteEndOfBlockPosition((ushort)value, componentType, transformClass, transformSize, transformSizeContext);
}
using IMemoryOwner encoded = encoder.Exit();
@@ -387,7 +409,7 @@ public class Av1EntropyTests
}
// Assert
- Assert.Equal(values.Select(x => (int)x).ToArray(), actuals);
+ Assert.Equal(values, actuals);
}
[Fact]
@@ -418,6 +440,94 @@ public class Av1EntropyTests
Assert.Equal(values, actuals);
}
+ [Theory]
+ [InlineData(0)]
+ [InlineData(1)]
+ [InlineData(2)]
+ public void RoundTripSegmentId(int context)
+ {
+ // Assign
+ int[] values = [3, 6, 7, 0, 2, 0, 2, 1, 1];
+ Configuration configuration = Configuration.Default;
+ Av1SymbolEncoder encoder = new(configuration, 100 / 8, BaseQIndex);
+ int[] actuals = new int[values.Length];
+
+ // Act
+ foreach (int value in values)
+ {
+ encoder.WriteSegmentId(value, context);
+ }
+
+ using IMemoryOwner encoded = encoder.Exit();
+
+ Av1SymbolDecoder decoder = new(Configuration.Default, encoded.GetSpan(), BaseQIndex);
+ for (int i = 0; i < values.Length; i++)
+ {
+ actuals[i] = decoder.ReadSegmentId(context);
+ }
+
+ // Assert
+ Assert.Equal(values, actuals);
+ }
+
+ [Fact]
+ public void RoundTripDeltaQuantizerIndex()
+ {
+ // Assign
+ int[] values = [3, 6, -7, -8, -2, 0, 2, 1, -1];
+ Configuration configuration = Configuration.Default;
+ Av1SymbolEncoder encoder = new(configuration, 100 / 8, BaseQIndex);
+ int[] actuals = new int[values.Length];
+
+ // Act
+ foreach (int value in values)
+ {
+ encoder.WriteDeltaQuantizerIndex(value);
+ }
+
+ using IMemoryOwner encoded = encoder.Exit();
+
+ Av1SymbolDecoder decoder = new(Configuration.Default, encoded.GetSpan(), BaseQIndex);
+ for (int i = 0; i < values.Length; i++)
+ {
+ actuals[i] = decoder.ReadDeltaQuantizerIndex();
+ }
+
+ // Assert
+ Assert.Equal(values, actuals);
+ }
+
+ [Theory]
+ [MemberData(nameof(GetRangeData), (int)Av1BlockSize.AllSizes)]
+ public void RoundTripFilterIntraMode(int bSize)
+ {
+ // Assign
+ Av1BlockSize blockSize = (Av1BlockSize)bSize;
+ Av1FilterIntraMode[] values = [
+ Av1FilterIntraMode.DC, Av1FilterIntraMode.Vertical, Av1FilterIntraMode.DC, Av1FilterIntraMode.Paeth,
+ Av1FilterIntraMode.AllFilterIntraModes, Av1FilterIntraMode.Directional157, Av1FilterIntraMode.DC, Av1FilterIntraMode.Directional157];
+ Configuration configuration = Configuration.Default;
+ Av1SymbolEncoder encoder = new(configuration, 100 / 8, BaseQIndex);
+ Av1FilterIntraMode[] actuals = new Av1FilterIntraMode[values.Length];
+
+ // Act
+ foreach (Av1FilterIntraMode value in values)
+ {
+ encoder.WriteFilterIntraMode(value, blockSize);
+ }
+
+ using IMemoryOwner encoded = encoder.Exit();
+
+ Av1SymbolDecoder decoder = new(Configuration.Default, encoded.GetSpan(), BaseQIndex);
+ for (int i = 0; i < values.Length; i++)
+ {
+ actuals[i] = decoder.ReadFilterUltraMode(blockSize);
+ }
+
+ // Assert
+ Assert.Equal(values, actuals);
+ }
+
[Fact]
public void RoundTripUseIntraBlockCopy()
{
@@ -444,4 +554,80 @@ public class Av1EntropyTests
// Assert
Assert.Equal(values, actuals);
}
+
+ public static TheoryData GetRangeData(int count)
+ {
+ TheoryData result = [];
+ for (int i = 0; i < count; i++)
+ {
+ result.Add(i);
+ }
+
+ return result;
+ }
+
+ public static TheoryData GetTransformBlockSkipData()
+ {
+ TheoryData result = [];
+ for (Av1TransformSize transformSizeContext = Av1TransformSize.Size4x4; transformSizeContext <= Av1TransformSize.Size64x64; transformSizeContext++)
+ {
+ for (int skipContext = 0; skipContext < 5; skipContext++)
+ {
+ result.Add((int)transformSizeContext, skipContext);
+ }
+ }
+
+ return result;
+ }
+
+ public static TheoryData GetSplitPartitionTypeData()
+ {
+ TheoryData result = [];
+ for (Av1BlockSize blockSize = Av1BlockSize.Block4x4; blockSize < Av1BlockSize.AllSizes; blockSize++)
+ {
+ for (int context = 4; context < 16; context++)
+ {
+ result.Add((int)blockSize, context);
+ }
+ }
+
+ return result;
+ }
+
+ public static TheoryData GetTransformTypeData()
+ {
+ TheoryData result = [];
+ for (Av1TransformSize transformSize = Av1TransformSize.Size4x4; transformSize < Av1TransformSize.AllSizes; transformSize++)
+ {
+ for (Av1FilterIntraMode filterIntraMode = Av1FilterIntraMode.DC; filterIntraMode <= Av1FilterIntraMode.AllFilterIntraModes; filterIntraMode++)
+ {
+ for (Av1PredictionMode intraDirection = Av1PredictionMode.IntraModeStart; intraDirection < Av1PredictionMode.IntraModeEnd; intraDirection++)
+ {
+ result.Add((int)transformSize, (int)filterIntraMode, (int)intraDirection);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ public static TheoryData GetEndOfBlockPositionData()
+ {
+ TheoryData result = [];
+ for (Av1TransformSize transformSize = Av1TransformSize.Size4x4; transformSize < Av1TransformSize.AllSizes; transformSize++)
+ {
+ for (Av1TransformSize transformSizeContext = Av1TransformSize.Size4x4; transformSizeContext <= Av1TransformSize.Size64x64; transformSizeContext++)
+ {
+ for (int componentType = 0; componentType < 2; componentType++)
+ {
+ for (Av1TransformClass transformClass = Av1TransformClass.Class2D; transformClass <= Av1TransformClass.ClassVertical; transformClass++)
+ {
+ result.Add((int)transformSize, (int)transformSizeContext, componentType, (int)transformClass);
+ }
+ }
+ }
+ }
+
+ return result;
+ }
}
diff --git a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1SymbolContextTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1SymbolContextTests.cs
index f21aa5715..2a7bf4e68 100644
--- a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1SymbolContextTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1SymbolContextTests.cs
@@ -69,9 +69,9 @@ public class Av1SymbolContextTests
for (Av1TransformSetType setType = Av1TransformSetType.DctOnly; setType <= Av1TransformSetType.All16; setType++)
{
int count = Av1SymbolContextHelper.GetExtendedTransformTypeCount(setType);
- for (int type = 1; type < count; type++)
+ for (int index = 1; index < count; index++)
{
- result.Add((int)setType, type);
+ result.Add((int)setType, index);
}
}