diff --git a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1NzMap.cs b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1NzMap.cs
index da2c05f20a..e92a4a6507 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1NzMap.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1NzMap.cs
@@ -286,18 +286,12 @@ internal static class Av1NzMap
///
/// SVT: get_nz_mag
///
- public static int GetNzMagnitude(Av1LevelBuffer levels, int index, int blockWidthLog2, Av1TransformClass transformClass)
- => GetNzMagnitude(levels, index >> blockWidthLog2, transformClass);
-
- ///
- /// SVT: get_nz_mag
- ///
- public static int GetNzMagnitude(Av1LevelBuffer levels, int y, Av1TransformClass transformClass)
+ public static int GetNzMagnitude(Av1LevelBuffer levels, Point position, Av1TransformClass transformClass)
{
int mag;
- Span row0 = levels.GetRow(y);
- Span row1 = levels.GetRow(y + 1);
- Span row2 = levels.GetRow(y + 2);
+ Span row0 = levels.GetRow(position.Y);
+ Span row1 = levels.GetRow(position.Y + 1);
+ Span row2 = levels.GetRow(position.Y + 2);
// Note: AOMMIN(level, 3) is useless for decoder since level < 3.
mag = ClipMax3[row0[1]]; // { 0, 1 }
@@ -312,8 +306,8 @@ internal static class Av1NzMap
break;
case Av1TransformClass.ClassVertical:
- Span row3 = levels.GetRow(y + 3);
- Span row4 = levels.GetRow(y + 4);
+ Span row3 = levels.GetRow(position.Y + 3);
+ Span row4 = levels.GetRow(position.Y + 4);
mag += ClipMax3[row2[0]]; // { 2, 0 }
mag += ClipMax3[row3[0]]; // { 3, 0 }
mag += ClipMax3[row4[0]]; // { 4, 0 }
@@ -328,10 +322,10 @@ internal static class Av1NzMap
return mag;
}
- public static int GetNzMapContextFromStats(int stats, int pos, int bwl, Av1TransformSize transformSize, Av1TransformClass transformClass)
+ public static int GetNzMapContextFromStats(int stats, Av1LevelBuffer levels, Point position, Av1TransformSize transformSize, Av1TransformClass transformClass)
{
// tx_class == 0(TX_CLASS_2D)
- if (((int)transformClass | pos) == 0)
+ if (position.Y == 0 && ((int)transformClass | position.X) == 0)
{
return 0;
}
@@ -352,14 +346,12 @@ internal static class Av1NzMap
// if (row + col < 2) return ctx + 1;
// if (row + col < 4) return 5 + ctx + 1;
// return 21 + ctx;
- return ctx + NzMapContextOffset[(int)transformSize][pos];
+ int index = position.X + (levels.Size.Width * position.Y);
+ return ctx + NzMapContextOffset[(int)transformSize][index];
case Av1TransformClass.ClassHorizontal:
- int row = pos >> bwl;
- int col = pos - (row << bwl);
- return ctx + NzMapContextOffset1d[col];
+ return ctx + NzMapContextOffset1d[position.X];
case Av1TransformClass.ClassVertical:
- int row2 = pos >> bwl;
- return ctx + NzMapContextOffset1d[row2];
+ return ctx + NzMapContextOffset1d[position.Y];
default:
break;
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolContextHelper.cs b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolContextHelper.cs
index 6bcca093ac..6712036e76 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolContextHelper.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolContextHelper.cs
@@ -52,6 +52,9 @@ internal static class Av1SymbolContextHelper
// Maps tx set types to the indices. INTRA values only
private static readonly int[] ExtendedTransformSetToIndex = [0, -1, 2, 1, -1, -1];
+ internal static Av1TransformSize GetTransformSizeContext(Av1TransformSize originalSize)
+ => (Av1TransformSize)(((int)originalSize.GetSquareSize() + (int)originalSize.GetSquareUpSize() + 1) >> 1);
+
internal static int RecordEndOfBlockPosition(int endOfBlockPoint, int endOfBlockExtra)
{
int endOfBlock = EndOfBlockGroupStart[endOfBlockPoint];
@@ -63,18 +66,16 @@ internal static class Av1SymbolContextHelper
return endOfBlock;
}
- internal static int GetBaseRangeContextEndOfBlock(int index, int blockWidthLog2, Av1TransformClass transformClass)
+ internal static int GetBaseRangeContextEndOfBlock(Point pos, Av1TransformClass transformClass)
{
- int row = index >> blockWidthLog2;
- int col = index - (row << blockWidthLog2);
- if (index == 0)
+ if (pos.X == 0 && pos.Y == 0)
{
return 0;
}
- if ((transformClass == Av1TransformClass.Class2D && row < 2 && col < 2) ||
- (transformClass == Av1TransformClass.ClassHorizontal && col == 0) ||
- (transformClass == Av1TransformClass.ClassVertical && row == 0))
+ if ((transformClass == Av1TransformClass.Class2D && pos.Y < 2 && pos.X < 2) ||
+ (transformClass == Av1TransformClass.ClassHorizontal && pos.X == 0) ||
+ (transformClass == Av1TransformClass.ClassVertical && pos.Y == 0))
{
return 7;
}
@@ -82,6 +83,31 @@ internal static class Av1SymbolContextHelper
return 14;
}
+ ///
+ /// SVT: get_lower_levels_ctx_eob
+ ///
+ internal static int GetLowerLevelContextEndOfBlock(Av1LevelBuffer levels, Point position)
+ {
+ if (position.X == 0 && position.Y == 0)
+ {
+ return 0;
+ }
+
+ int total = levels.Size.Height * levels.Size.Width;
+ int index = position.X + (position.Y * levels.Size.Width);
+ if (index <= total >> 3)
+ {
+ return 1;
+ }
+
+ if (index <= total >> 2)
+ {
+ return 2;
+ }
+
+ return 3;
+ }
+
///
/// SVT: get_lower_levels_ctx_eob
///
@@ -108,21 +134,17 @@ internal static class Av1SymbolContextHelper
///
/// SVT: get_br_ctx_2d
///
- internal static int GetBaseRangeContext2d(Av1LevelBuffer levels, int index, int blockWidthLog2)
+ internal static int GetBaseRangeContext2d(Av1LevelBuffer levels, Point position)
{
- DebugGuard.MustBeGreaterThan(index, 0, nameof(index));
- int y = index >> blockWidthLog2;
- int x = index - (y << blockWidthLog2);
- int stride = (1 << blockWidthLog2) + Av1Constants.TransformPadHorizontal;
- int pos = (y * stride) + x;
- Span row0 = levels.GetRow(y);
- Span row1 = levels.GetRow(y + 1);
+ DebugGuard.MustBeGreaterThan(position.X + position.Y, 0, nameof(position));
+ Span row0 = levels.GetRow(position.Y);
+ Span row1 = levels.GetRow(position.Y + 1);
int mag =
Math.Min((int)row0[1], Av1Constants.MaxBaseRange) +
Math.Min((int)row1[0], Av1Constants.MaxBaseRange) +
Math.Min((int)row1[1], Av1Constants.MaxBaseRange);
mag = Math.Min((mag + 1) >> 1, 6);
- if ((y | x) < 2)
+ if ((position.Y | position.X) < 2)
{
return mag + 7;
}
@@ -133,15 +155,13 @@ internal static class Av1SymbolContextHelper
///
/// SVT: get_lower_levels_ctx_2d
///
- internal static int GetLowerLevelsContext2d(Av1LevelBuffer levelBuffer, int index, int blockWidthLog2, Av1TransformSize transformSize)
+ internal static int GetLowerLevelsContext2d(Av1LevelBuffer levelBuffer, Point pos, Av1TransformSize transformSize)
{
- DebugGuard.MustBeGreaterThan(index, 0, nameof(index));
- int y = index >> blockWidthLog2;
- int x = index - (y << blockWidthLog2);
+ DebugGuard.MustBeGreaterThan(pos.X + pos.Y, 0, nameof(pos));
int mag;
- Span row0 = levelBuffer.GetRow(y);
- Span row1 = levelBuffer.GetRow(y + 1);
- Span row2 = levelBuffer.GetRow(y + 2);
+ Span row0 = levelBuffer.GetRow(pos.Y);
+ Span row1 = levelBuffer.GetRow(pos.Y + 1);
+ Span row2 = levelBuffer.GetRow(pos.Y + 2);
mag = Math.Min((int)row0[1], 3); // { 0, 1 }
mag += Math.Min((int)row1[0], 3); // { 1, 0 }
mag += Math.Min((int)row1[1], 3); // { 1, 1 }
@@ -149,31 +169,30 @@ internal static class Av1SymbolContextHelper
mag += Math.Min((int)row2[0], 3); // { 2, 0 }
int ctx = Math.Min((mag + 1) >> 1, 4);
+ int index = pos.X + (pos.Y * levelBuffer.Size.Width);
return ctx + Av1NzMap.GetNzMapContext(transformSize, index);
}
///
/// SVT: get_br_ctx
///
- internal static int GetBaseRangeContext(Av1LevelBuffer levels, int index, int blockWidthLog2, Av1TransformClass transformClass)
+ internal static int GetBaseRangeContext(Av1LevelBuffer levels, Point position, Av1TransformClass transformClass)
{
- int y = index >> blockWidthLog2;
- int x = index - (y << blockWidthLog2);
- Span row0 = levels.GetRow(y);
- Span row1 = levels.GetRow(y + 1);
- int mag = row0[x + 1];
- mag += row1[x];
+ Span row0 = levels.GetRow(position.Y);
+ Span row1 = levels.GetRow(position.Y + 1);
+ int mag = row0[position.X + 1];
+ mag += row1[position.X];
switch (transformClass)
{
case Av1TransformClass.Class2D:
- mag += row1[x + 1];
+ mag += row1[position.X + 1];
mag = Math.Min((mag + 1) >> 1, 6);
- if (index == 0)
+ if ((position.X + position.Y) == 0)
{
return mag;
}
- if (y < 2 && x < 2)
+ if (position.Y < 2 && position.X < 2)
{
return mag + 7;
}
@@ -182,26 +201,26 @@ internal static class Av1SymbolContextHelper
case Av1TransformClass.ClassHorizontal:
mag += row0[2];
mag = Math.Min((mag + 1) >> 1, 6);
- if (index == 0)
+ if ((position.X + position.Y) == 0)
{
return mag;
}
- if (x == 0)
+ if (position.X == 0)
{
return mag + 7;
}
break;
case Av1TransformClass.ClassVertical:
- mag += levels.GetRow(y + 2)[0];
+ mag += levels.GetRow(position.Y + 2)[0];
mag = Math.Min((mag + 1) >> 1, 6);
- if (index == 0)
+ if ((position.X + position.Y) == 0)
{
return mag;
}
- if (y == 0)
+ if (position.Y == 0)
{
return mag + 7;
}
@@ -214,10 +233,10 @@ internal static class Av1SymbolContextHelper
return mag + 14;
}
- internal static int GetLowerLevelsContext(Av1LevelBuffer levels, int pos, int bwl, Av1TransformSize transformSize, Av1TransformClass transformClass)
+ internal static int GetLowerLevelsContext(Av1LevelBuffer levels, Point position, Av1TransformSize transformSize, Av1TransformClass transformClass)
{
- int stats = Av1NzMap.GetNzMagnitude(levels, pos >> bwl, transformClass);
- return Av1NzMap.GetNzMapContextFromStats(stats, pos, bwl, transformSize, transformClass);
+ int stats = Av1NzMap.GetNzMagnitude(levels, position, transformClass);
+ return Av1NzMap.GetNzMapContextFromStats(stats, levels, position, transformSize, transformClass);
}
internal static Av1TransformSetType GetExtendedTransformSetType(Av1TransformSize transformSize, bool useReducedSet)
@@ -254,9 +273,7 @@ internal static class Av1SymbolContextHelper
///
internal static sbyte GetNzMapContext(
Av1LevelBuffer levels,
- int index,
- int blockWidthLog2,
- int height,
+ Point position,
int scan_idx,
bool is_eob,
Av1TransformSize transformSize,
@@ -264,26 +281,11 @@ internal static class Av1SymbolContextHelper
{
if (is_eob)
{
- if (scan_idx == 0)
- {
- return 0;
- }
-
- if (scan_idx <= (height << blockWidthLog2) / 8)
- {
- return 1;
- }
-
- if (scan_idx <= (height << blockWidthLog2) / 4)
- {
- return 2;
- }
-
- return 3;
+ return (sbyte)GetLowerLevelContextEndOfBlock(levels, position);
}
- int stats = Av1NzMap.GetNzMagnitude(levels, index, blockWidthLog2, transformClass);
- return (sbyte)Av1NzMap.GetNzMapContextFromStats(stats, index, blockWidthLog2, transformSize, transformClass);
+ int stats = Av1NzMap.GetNzMagnitude(levels, position, transformClass);
+ return (sbyte)Av1NzMap.GetNzMapContextFromStats(stats, levels, position, transformSize, transformClass);
}
///
@@ -297,12 +299,11 @@ internal static class Av1SymbolContextHelper
Av1TransformClass transformClass,
Span coefficientContexts)
{
- int blockWidthLog2 = transformSize.GetBlockWidthLog2();
- int height = transformSize.GetHeight();
for (int i = 0; i < eob; ++i)
{
int pos = scan[i];
- coefficientContexts[pos] = GetNzMapContext(levels, pos, blockWidthLog2, height, i, i == eob - 1, transformSize, transformClass);
+ Point position = levels.GetPosition(pos);
+ coefficientContexts[pos] = GetNzMapContext(levels, position, i, i == eob - 1, transformSize, transformClass);
}
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolDecoder.cs b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolDecoder.cs
index 21c6299a99..b421676885 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolDecoder.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolDecoder.cs
@@ -259,11 +259,8 @@ internal ref struct Av1SymbolDecoder
}
///
- /// 5.11.39. Coefficients syntax.
+ /// SVT: parse_coeffs
///
- ///
- /// The implementation is taken from SVT-AV1 library, which deviates from the code flow in the specification.
- ///
public int ReadCoefficients(
Av1BlockModeInfo modeInfo,
Point blockPosition,
@@ -285,14 +282,13 @@ internal ref struct Av1SymbolDecoder
{
int width = transformSize.GetWidth();
int height = transformSize.GetHeight();
- Av1TransformSize transformSizeContext = (Av1TransformSize)(((int)transformSize.GetSquareSize() + (int)transformSize.GetSquareUpSize() + 1) >> 1);
+ Av1TransformSize transformSizeContext = Av1SymbolContextHelper.GetTransformSizeContext(transformSize);
Av1PlaneType planeType = (Av1PlaneType)Math.Min(plane, 1);
int culLevel = 0;
Av1LevelBuffer levels = new(this.configuration, new Size(width, height));
bool allZero = this.ReadTransformBlockSkip(transformSizeContext, transformBlockContext.SkipContext);
- int bwl = transformSize.GetBlockWidthLog2();
int endOfBlock;
if (allZero)
{
@@ -322,22 +318,24 @@ internal ref struct Av1SymbolDecoder
levels.Clear();
}
- this.ReadCoefficientsEndOfBlock(transformClass, endOfBlock, height, scan, bwl, levels, transformSizeContext, planeType);
+ this.ReadCoefficientsEndOfBlock(transformClass, endOfBlock, scan, levels, transformSizeContext, planeType);
if (endOfBlock > 1)
{
if (transformClass == Av1TransformClass.Class2D)
{
- this.ReadCoefficientsReverse2d(transformSize, 1, endOfBlock - 1 - 1, scan, bwl, levels, transformSizeContext, planeType);
- this.ReadCoefficientsReverse(transformSize, transformClass, 0, 0, scan, bwl, levels, transformSizeContext, planeType);
+ this.ReadCoefficientsReverse2d(transformSize, 1, endOfBlock - 1 - 1, scan, levels, transformSizeContext, planeType);
+ this.ReadCoefficientsReverse(transformSize, transformClass, 0, 0, scan, levels, transformSizeContext, planeType);
}
else
{
- this.ReadCoefficientsReverse(transformSize, transformClass, 0, endOfBlock - 1 - 1, scan, bwl, levels, transformSizeContext, planeType);
+ this.ReadCoefficientsReverse(transformSize, transformClass, 0, endOfBlock - 1 - 1, scan, levels, transformSizeContext, planeType);
}
}
+ coefficientBuffer[0] = endOfBlock;
+
DebugGuard.MustBeGreaterThan(scan.Length, 0, nameof(scan));
- culLevel = this.ReadCoefficientsSign(coefficientBuffer, endOfBlock, scan, bwl, levels, transformBlockContext.DcSignContext, planeType);
+ culLevel = this.ReadCoefficientsSign(coefficientBuffer, endOfBlock, scan, levels, transformBlockContext.DcSignContext, planeType);
UpdateCoefficientContext(modeInfo, aboveContexts, leftContexts, blocksWide, blocksHigh, transformSize, blockPosition, aboveOffset, leftOffset, culLevel, modeBlocksToRightEdge, modeBlocksToBottomEdge);
transformInfo.CodeBlockFlag = true;
@@ -372,15 +370,15 @@ internal ref struct Av1SymbolDecoder
return Av1SymbolContextHelper.RecordEndOfBlockPosition(endOfBlockPoint, endOfBlockExtra);
}
- public void ReadCoefficientsEndOfBlock(Av1TransformClass transformClass, int endOfBlock, int height, ReadOnlySpan scan, int blockWidthLog2, Av1LevelBuffer levels, Av1TransformSize transformSizeContext, Av1PlaneType planeType)
+ public void ReadCoefficientsEndOfBlock(Av1TransformClass transformClass, int endOfBlock, ReadOnlySpan scan, Av1LevelBuffer levels, Av1TransformSize transformSizeContext, Av1PlaneType planeType)
{
int i = endOfBlock - 1;
- int pos = scan[i];
- int coefficientContext = Av1SymbolContextHelper.GetLowerLevelContextEndOfBlock(blockWidthLog2, height, i);
+ Point position = levels.GetPosition(scan[i]);
+ int coefficientContext = Av1SymbolContextHelper.GetLowerLevelContextEndOfBlock(levels, position);
int level = this.ReadBaseEndOfBlock(transformSizeContext, planeType, coefficientContext);
if (level > Av1Constants.BaseLevelsCount)
{
- int baseRangeContext = Av1SymbolContextHelper.GetBaseRangeContextEndOfBlock(pos, blockWidthLog2, transformClass);
+ int baseRangeContext = Av1SymbolContextHelper.GetBaseRangeContextEndOfBlock(position, transformClass);
for (int idx = 0; idx < Av1Constants.CoefficientBaseRange / Av1Constants.BaseRangeSizeMinus1; idx++)
{
int coefficinetBaseRange = this.ReadCoefficientsBaseRange(transformSizeContext, planeType, baseRangeContext);
@@ -392,19 +390,19 @@ internal ref struct Av1SymbolDecoder
}
}
- levels.GetPaddedRow(pos, blockWidthLog2)[0] = (byte)level;
+ levels.GetRow(position)[0] = (byte)level;
}
- public void ReadCoefficientsReverse2d(Av1TransformSize transformSize, int startScanIndex, int endScanIndex, ReadOnlySpan scan, int blockWidthLog2, Av1LevelBuffer levels, Av1TransformSize transformSizeContext, Av1PlaneType planeType)
+ public void ReadCoefficientsReverse2d(Av1TransformSize transformSize, int startScanIndex, int endScanIndex, ReadOnlySpan scan, Av1LevelBuffer levels, Av1TransformSize transformSizeContext, Av1PlaneType planeType)
{
for (int c = endScanIndex; c >= startScanIndex; --c)
{
- int pos = scan[c];
- int coefficientContext = Av1SymbolContextHelper.GetLowerLevelsContext2d(levels, pos, blockWidthLog2, transformSize);
+ Point position = levels.GetPosition(scan[c]);
+ int coefficientContext = Av1SymbolContextHelper.GetLowerLevelsContext2d(levels, position, transformSize);
int level = this.ReadCoefficientsBase(coefficientContext, transformSizeContext, planeType);
if (level > Av1Constants.BaseLevelsCount)
{
- int baseRangeContext = Av1SymbolContextHelper.GetBaseRangeContext2d(levels, pos, blockWidthLog2);
+ int baseRangeContext = Av1SymbolContextHelper.GetBaseRangeContext2d(levels, position);
for (int idx = 0; idx < Av1Constants.CoefficientBaseRange; idx += Av1Constants.BaseRangeSizeMinus1)
{
int k = this.ReadCoefficientsBaseRange(transformSizeContext, planeType, baseRangeContext);
@@ -416,20 +414,21 @@ internal ref struct Av1SymbolDecoder
}
}
- levels.GetPaddedRow(pos, blockWidthLog2)[0] = (byte)level;
+ levels.GetRow(position)[0] = (byte)level;
}
}
- public void ReadCoefficientsReverse(Av1TransformSize transformSize, Av1TransformClass transformClass, int startScanIndex, int endScanIndex, ReadOnlySpan scan, int blockWidthLog2, Av1LevelBuffer levels, Av1TransformSize transformSizeContext, Av1PlaneType planeType)
+ public void ReadCoefficientsReverse(Av1TransformSize transformSize, Av1TransformClass transformClass, int startScanIndex, int endScanIndex, ReadOnlySpan scan, Av1LevelBuffer levels, Av1TransformSize transformSizeContext, Av1PlaneType planeType)
{
for (int c = endScanIndex; c >= startScanIndex; --c)
{
int pos = scan[c];
- int coefficientContext = Av1SymbolContextHelper.GetLowerLevelsContext(levels, pos, blockWidthLog2, transformSize, transformClass);
+ Point position = levels.GetPosition(pos);
+ int coefficientContext = Av1SymbolContextHelper.GetLowerLevelsContext(levels, position, transformSize, transformClass);
int level = this.ReadCoefficientsBase(coefficientContext, transformSizeContext, planeType);
if (level > Av1Constants.BaseLevelsCount)
{
- int baseRangeContext = Av1SymbolContextHelper.GetBaseRangeContext(levels, pos, blockWidthLog2, transformClass);
+ int baseRangeContext = Av1SymbolContextHelper.GetBaseRangeContext(levels, position, transformClass);
for (int idx = 0; idx < Av1Constants.CoefficientBaseRange; idx += Av1Constants.BaseRangeSizeMinus1)
{
int k = this.ReadCoefficientsBaseRange(transformSizeContext, planeType, baseRangeContext);
@@ -441,11 +440,11 @@ internal ref struct Av1SymbolDecoder
}
}
- levels.GetPaddedRow(pos, blockWidthLog2)[0] = (byte)level;
+ levels.GetRow(position)[0] = (byte)level;
}
}
- public int ReadCoefficientsSign(Span coefficientBuffer, int endOfBlock, ReadOnlySpan scan, int blockWidthLog2, Av1LevelBuffer levels, int dcSignContext, Av1PlaneType planeType)
+ public int ReadCoefficientsSign(Span coefficientBuffer, int endOfBlock, ReadOnlySpan scan, Av1LevelBuffer levels, int dcSignContext, Av1PlaneType planeType)
{
int maxScanLine = 0;
int culLevel = 0;
@@ -454,7 +453,8 @@ internal ref struct Av1SymbolDecoder
for (int c = 0; c < endOfBlock; c++)
{
int sign = 0;
- int level = levels.GetPaddedRow(scan[c], blockWidthLog2)[0];
+ Point position = levels.GetPosition(scan[c]);
+ int level = levels.GetRow(position)[0];
if (level != 0)
{
maxScanLine = Math.Max(maxScanLine, scan[c]);
@@ -507,7 +507,8 @@ internal ref struct Av1SymbolDecoder
private int ReadCoefficientsBaseRange(Av1TransformSize transformSizeContext, Av1PlaneType planeType, int baseRangeContext)
{
ref Av1SymbolReader r = ref this.reader;
- return r.ReadSymbol(this.coefficientsBaseRange[(int)transformSizeContext][(int)planeType][baseRangeContext]);
+ int transformContext = Math.Min((int)transformSizeContext, (int)Av1TransformSize.Size32x32);
+ return r.ReadSymbol(this.coefficientsBaseRange[transformContext][(int)planeType][baseRangeContext]);
}
private int ReadDcSign(Av1PlaneType planeType, int dcSignContext)
diff --git a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolEncoder.cs b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolEncoder.cs
index d0e669bfba..ab12516e17 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolEncoder.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolEncoder.cs
@@ -98,12 +98,12 @@ internal class Av1SymbolEncoder : IDisposable
Av1ScanOrder scanOrder = Av1ScanOrderConstants.GetScanOrder(transformSize, transformType);
ReadOnlySpan scan = scanOrder.Scan;
int blockWidthLog2 = transformSize.GetBlockWidthLog2();
- Av1TransformSize transformSizeContext = (Av1TransformSize)(((int)transformSize.GetSquareSize() + (int)transformSize.GetSquareUpSize() + 1) >> 1);
+ Av1TransformSize transformSizeContext = Av1SymbolContextHelper.GetTransformSizeContext(transformSize);
ref Av1SymbolWriter w = ref this.writer;
Av1LevelBuffer levels = new(this.configuration, new Size(width, height));
- Span coefficientContexts = new sbyte[Av1Constants.MaxTransformSize * Av1Constants.MaxTransformSize];
+ Span coefficientContexts = new sbyte[width * height];
Guard.MustBeLessThan((int)transformSizeContext, (int)Av1TransformSize.AllSizes, nameof(transformSizeContext));
@@ -129,6 +129,7 @@ internal class Av1SymbolEncoder : IDisposable
short pos = scan[c];
int v = coefficientBuffer[pos];
short coeffContext = coefficientContexts[pos];
+ Point position = levels.GetPosition(pos);
int level = Math.Abs(v);
if (c == endOfBlock - 1)
@@ -144,7 +145,7 @@ internal class Av1SymbolEncoder : IDisposable
{
// level is above 1.
int baseRange = level - 1 - Av1Constants.BaseLevelsCount;
- int baseRangeContext = Av1SymbolContextHelper.GetBaseRangeContext(levels, pos, blockWidthLog2, transformClass);
+ int baseRangeContext = Av1SymbolContextHelper.GetBaseRangeContext(levels, position, transformClass);
for (int idx = 0; idx < Av1Constants.CoefficientBaseRange; idx += Av1Constants.BaseRangeSizeMinus1)
{
int k = Math.Min(baseRange - idx, Av1Constants.BaseRangeSizeMinus1);
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1LevelBuffer.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1LevelBuffer.cs
index 2fa4c03f03..77fd0cc8ba 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1LevelBuffer.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1LevelBuffer.cs
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
+using System;
using System.Buffers;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;
@@ -45,6 +46,16 @@ internal sealed class Av1LevelBuffer : IDisposable
}
}
+ public Point GetPosition(int index)
+ {
+ int x = index % this.Size.Width;
+ int y = index / this.Size.Width;
+ return new Point(x, y);
+ }
+
+ public Span GetRow(Point pos)
+ => this.GetRow(pos.Y);
+
public Span GetRow(int y)
{
ObjectDisposedException.ThrowIf(this.memory == null, this);
@@ -64,10 +75,4 @@ internal sealed class Av1LevelBuffer : IDisposable
ObjectDisposedException.ThrowIf(this.memory == null, this);
this.memory.Memory.Span.Clear();
}
-
- internal Span GetPaddedRow(int index, int blockWidthLog2)
- {
- int y = index >> blockWidthLog2;
- return this.GetRow(y);
- }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileReader.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileReader.cs
index 63827c5e98..d217ff4451 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileReader.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileReader.cs
@@ -545,7 +545,7 @@ internal class Av1TileReader : IAv1TileReader
Span coefficientBuffer = this.FrameInfo.GetCoefficients(plane);
int width = transformSize.GetWidth();
int height = transformSize.GetHeight();
- Av1TransformSize transformSizeContext = (Av1TransformSize)(((int)transformSize.GetSquareSize() + ((int)transformSize.GetSquareUpSize() + 1)) >> 1);
+ Av1TransformSize transformSizeContext = Av1SymbolContextHelper.GetTransformSizeContext(transformSize);
Av1PlaneType planeType = (Av1PlaneType)Math.Min(plane, 1);
Point blockPosition = new(blockColumn, blockRow);
bool isLossless = this.FrameHeader.LosslessArray[partitionInfo.ModeInfo.SegmentId];
diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformSize.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformSize.cs
index e812778335..f4af96c5bc 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformSize.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformSize.cs
@@ -5,26 +5,26 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
internal enum Av1TransformSize : byte
{
- Size4x4,
- Size8x8,
- Size16x16,
- Size32x32,
- Size64x64,
- Size4x8,
- Size8x4,
- Size8x16,
- Size16x8,
- Size16x32,
- Size32x16,
- Size32x64,
- Size64x32,
- Size4x16,
- Size16x4,
- Size8x32,
- Size32x8,
- Size16x64,
- Size64x16,
- AllSizes,
+ Size4x4 = 0,
+ Size8x8 = 1,
+ Size16x16 = 2,
+ Size32x32 = 3,
+ Size64x64 = 4,
+ Size4x8 = 5,
+ Size8x4 = 6,
+ Size8x16 = 7,
+ Size16x8 = 8,
+ Size16x32 = 9,
+ Size32x16 = 10,
+ Size32x64 = 11,
+ Size64x32 = 12,
+ Size4x16 = 13,
+ Size16x4 = 14,
+ Size8x32 = 15,
+ Size32x8 = 16,
+ Size16x64 = 17,
+ Size64x16 = 18,
+ AllSizes = 19,
SquareSizes = Size4x8,
Invalid = 255,
}
diff --git a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1CoefficientsEntropyTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1CoefficientsEntropyTests.cs
index 43baea8c46..add6619cf7 100644
--- a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1CoefficientsEntropyTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1CoefficientsEntropyTests.cs
@@ -48,6 +48,7 @@ public class Av1CoefficientsEntropyTests
decoder.ReadCoefficients(modeInfo, new Point(0, 0), aboveContexts, leftContexts, 0, 0, 0, 1, 1, transformBlockContext, transformSize, false, true, transformInfo, 0, 0, actuals);
// Assert
+ Assert.Equal(endOfBlock, actuals[0]);
Assert.Equal(expected, actuals);
}
@@ -70,7 +71,7 @@ public class Av1CoefficientsEntropyTests
Configuration configuration = Configuration.Default;
Av1SymbolEncoder encoder = new(configuration, 100 / 8, BaseQIndex);
Span coefficientsBuffer = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
- Span actuals = new int[16];
+ Span actuals = new int[16 + 1];
// Act
encoder.WriteCoefficients(transformSize, transformType, intraDirection, coefficientsBuffer, componentType, transformBlockContext, endOfBlock, true, filterIntraMode);
@@ -83,6 +84,7 @@ public class Av1CoefficientsEntropyTests
decoder.ReadCoefficients(modeInfo, new Point(0, 0), aboveContexts, leftContexts, 0, 0, plane, 1, 1, transformBlockContext, transformSize, false, true, transformInfo, 0, 0, actuals);
// Assert
- Assert.Equal(coefficientsBuffer, actuals);
+ Assert.Equal(endOfBlock, actuals[0]);
+ Assert.Equal(coefficientsBuffer, actuals[1..]);
}
}
diff --git a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1LevelBufferTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1LevelBufferTests.cs
new file mode 100644
index 0000000000..6eabd6c7e2
--- /dev/null
+++ b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1LevelBufferTests.cs
@@ -0,0 +1,91 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+
+namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1;
+
+[Trait("Format", "Avif")]
+public class Av1LevelBufferTests
+{
+ [Theory]
+ [InlineData(4, 4, 4, 1)]
+ [InlineData(4, 4, 5, 1)]
+ [InlineData(4, 4, 6, 1)]
+ [InlineData(4, 4, 7, 1)]
+ [InlineData(8, 4, 7, 0)]
+ [InlineData(8, 8, 16, 2)]
+ [InlineData(8, 4, 16, 2)]
+ public void TestGetPaddedRow(int width, int height, int index, byte expected)
+ {
+ // Arrange
+ Size size = new(width, height);
+ Av1LevelBuffer levels = new(Configuration.Default, size);
+ for (byte i = 0; i < 4; i++)
+ {
+ levels.GetRow(i).Fill(i);
+ }
+
+ // Act
+ Point pos = levels.GetPosition(index);
+
+ // Assert
+ Assert.Equal(expected, pos.Y);
+ Assert.Equal(expected, levels.GetRow(pos)[0]);
+ }
+
+ [Theory]
+ [InlineData(4, 4)]
+ [InlineData(8, 4)]
+ [InlineData(8, 8)]
+ [InlineData(16, 4)]
+ public void TestGetRow(int width, int height)
+ {
+ // Arrange
+ Size size = new(width, height);
+ Av1LevelBuffer levels = new(Configuration.Default, size);
+ for (byte i = 0; i < height; i++)
+ {
+ levels.GetRow(i).Fill(i);
+ }
+
+ for (int j = 0; j < height; j++)
+ {
+ // Act
+ Span actual = levels.GetRow(j);
+
+ // Assert
+ Assert.Equal(j, actual[0]);
+ Assert.True(actual.Length >= width);
+ }
+ }
+
+ [Theory]
+ [InlineData(4, 4)]
+ [InlineData(8, 4)]
+ [InlineData(8, 8)]
+ [InlineData(16, 4)]
+ public void TestClear(int width, int height)
+ {
+ // Arrange
+ Size size = new(width, height);
+ Av1LevelBuffer levels = new(Configuration.Default, size);
+ for (byte i = 0; i < height; i++)
+ {
+ levels.GetRow(i).Fill(i);
+ }
+
+ // Act
+ levels.Clear();
+
+ // Assert
+ for (int j = 0; j < height; j++)
+ {
+ Span rowSpan = levels.GetRow(j);
+ for (int k = 0; k < width; k++)
+ {
+ Assert.Equal(0, rowSpan[k]);
+ }
+ }
+ }
+}