Browse Source

Refactor position inside transform block

pull/2633/head
Ynse Hoornenborg 2 years ago
parent
commit
41a11fd339
  1. 32
      src/ImageSharp/Formats/Heif/Av1/Entropy/Av1NzMap.cs
  2. 131
      src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolContextHelper.cs
  3. 57
      src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolDecoder.cs
  4. 7
      src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolEncoder.cs
  5. 17
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1LevelBuffer.cs
  6. 2
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileReader.cs
  7. 40
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformSize.cs
  8. 6
      tests/ImageSharp.Tests/Formats/Heif/Av1/Av1CoefficientsEntropyTests.cs
  9. 91
      tests/ImageSharp.Tests/Formats/Heif/Av1/Av1LevelBufferTests.cs

32
src/ImageSharp/Formats/Heif/Av1/Entropy/Av1NzMap.cs

@ -286,18 +286,12 @@ internal static class Av1NzMap
/// <summary>
/// SVT: get_nz_mag
/// </summary>
public static int GetNzMagnitude(Av1LevelBuffer levels, int index, int blockWidthLog2, Av1TransformClass transformClass)
=> GetNzMagnitude(levels, index >> blockWidthLog2, transformClass);
/// <summary>
/// SVT: get_nz_mag
/// </summary>
public static int GetNzMagnitude(Av1LevelBuffer levels, int y, Av1TransformClass transformClass)
public static int GetNzMagnitude(Av1LevelBuffer levels, Point position, Av1TransformClass transformClass)
{
int mag;
Span<byte> row0 = levels.GetRow(y);
Span<byte> row1 = levels.GetRow(y + 1);
Span<byte> row2 = levels.GetRow(y + 2);
Span<byte> row0 = levels.GetRow(position.Y);
Span<byte> row1 = levels.GetRow(position.Y + 1);
Span<byte> 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<byte> row3 = levels.GetRow(y + 3);
Span<byte> row4 = levels.GetRow(y + 4);
Span<byte> row3 = levels.GetRow(position.Y + 3);
Span<byte> 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;
}

131
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;
}
/// <summary>
/// SVT: get_lower_levels_ctx_eob
/// </summary>
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;
}
/// <summary>
/// SVT: get_lower_levels_ctx_eob
/// </summary>
@ -108,21 +134,17 @@ internal static class Av1SymbolContextHelper
/// <summary>
/// SVT: get_br_ctx_2d
/// </summary>
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<byte> row0 = levels.GetRow(y);
Span<byte> row1 = levels.GetRow(y + 1);
DebugGuard.MustBeGreaterThan(position.X + position.Y, 0, nameof(position));
Span<byte> row0 = levels.GetRow(position.Y);
Span<byte> 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
/// <summary>
/// SVT: get_lower_levels_ctx_2d
/// </summary>
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<byte> row0 = levelBuffer.GetRow(y);
Span<byte> row1 = levelBuffer.GetRow(y + 1);
Span<byte> row2 = levelBuffer.GetRow(y + 2);
Span<byte> row0 = levelBuffer.GetRow(pos.Y);
Span<byte> row1 = levelBuffer.GetRow(pos.Y + 1);
Span<byte> 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);
}
/// <summary>
/// SVT: get_br_ctx
/// </summary>
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<byte> row0 = levels.GetRow(y);
Span<byte> row1 = levels.GetRow(y + 1);
int mag = row0[x + 1];
mag += row1[x];
Span<byte> row0 = levels.GetRow(position.Y);
Span<byte> 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
/// </summary>
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);
}
/// <summary>
@ -297,12 +299,11 @@ internal static class Av1SymbolContextHelper
Av1TransformClass transformClass,
Span<sbyte> 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);
}
}

57
src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolDecoder.cs

@ -259,11 +259,8 @@ internal ref struct Av1SymbolDecoder
}
/// <summary>
/// 5.11.39. Coefficients syntax.
/// SVT: parse_coeffs
/// </summary>
/// <remarks>
/// The implementation is taken from SVT-AV1 library, which deviates from the code flow in the specification.
/// </remarks>
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<short> scan, int blockWidthLog2, Av1LevelBuffer levels, Av1TransformSize transformSizeContext, Av1PlaneType planeType)
public void ReadCoefficientsEndOfBlock(Av1TransformClass transformClass, int endOfBlock, ReadOnlySpan<short> 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<short> scan, int blockWidthLog2, Av1LevelBuffer levels, Av1TransformSize transformSizeContext, Av1PlaneType planeType)
public void ReadCoefficientsReverse2d(Av1TransformSize transformSize, int startScanIndex, int endScanIndex, ReadOnlySpan<short> 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<short> scan, int blockWidthLog2, Av1LevelBuffer levels, Av1TransformSize transformSizeContext, Av1PlaneType planeType)
public void ReadCoefficientsReverse(Av1TransformSize transformSize, Av1TransformClass transformClass, int startScanIndex, int endScanIndex, ReadOnlySpan<short> 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<int> coefficientBuffer, int endOfBlock, ReadOnlySpan<short> scan, int blockWidthLog2, Av1LevelBuffer levels, int dcSignContext, Av1PlaneType planeType)
public int ReadCoefficientsSign(Span<int> coefficientBuffer, int endOfBlock, ReadOnlySpan<short> 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)

7
src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolEncoder.cs

@ -98,12 +98,12 @@ internal class Av1SymbolEncoder : IDisposable
Av1ScanOrder scanOrder = Av1ScanOrderConstants.GetScanOrder(transformSize, transformType);
ReadOnlySpan<short> 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<sbyte> coefficientContexts = new sbyte[Av1Constants.MaxTransformSize * Av1Constants.MaxTransformSize];
Span<sbyte> 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);

17
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<byte> GetRow(Point pos)
=> this.GetRow(pos.Y);
public Span<byte> 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<byte> GetPaddedRow(int index, int blockWidthLog2)
{
int y = index >> blockWidthLog2;
return this.GetRow(y);
}
}

2
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileReader.cs

@ -545,7 +545,7 @@ internal class Av1TileReader : IAv1TileReader
Span<int> 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];

40
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,
}

6
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<int> coefficientsBuffer = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
Span<int> actuals = new int[16];
Span<int> 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..]);
}
}

91
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<byte> 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<byte> rowSpan = levels.GetRow(j);
for (int k = 0; k < width; k++)
{
Assert.Equal(0, rowSpan[k]);
}
}
}
}
Loading…
Cancel
Save