Browse Source

Introduce Av1LevelBuffer class

pull/2633/head
Ynse Hoornenborg 2 years ago
parent
commit
e592ef6881
  1. 39
      src/ImageSharp/Formats/Heif/Av1/Entropy/Av1NzMap.cs
  2. 382
      src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolContextHelper.cs
  3. 244
      src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolDecoder.cs
  4. 313
      src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolEncoder.cs
  5. 73
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1LevelBuffer.cs
  6. 2
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileReader.cs
  7. 2
      tests/ImageSharp.Tests/Formats/Heif/Av1/Av1CoefficientsEntropyTests.cs
  8. 8
      tests/ImageSharp.Tests/Formats/Heif/Av1/Av1EntropyTests.cs

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

@ -1,6 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Entropy;
@ -282,31 +283,45 @@ internal static class Av1NzMap
NzMapContextOffset64x32, // TX_64x16
];
public static int GetNzMagnitude(ReadOnlySpan<byte> levels, int bwl, Av1TransformClass transformClass)
/// <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)
{
int mag;
Span<byte> row0 = levels.GetRow(y);
Span<byte> row1 = levels.GetRow(y + 1);
Span<byte> row2 = levels.GetRow(y + 2);
// Note: AOMMIN(level, 3) is useless for decoder since level < 3.
mag = ClipMax3[levels[1]]; // { 0, 1 }
mag += ClipMax3[levels[(1 << bwl) + Av1Constants.TransformPadHorizontal]]; // { 1, 0 }
mag = ClipMax3[row0[1]]; // { 0, 1 }
mag += ClipMax3[row1[0]]; // { 1, 0 }
switch (transformClass)
{
case Av1TransformClass.Class2D:
mag += ClipMax3[levels[(1 << bwl) + Av1Constants.TransformPadHorizontal + 1]]; // { 1, 1 }
mag += ClipMax3[levels[2]]; // { 0, 2 }
mag += ClipMax3[levels[(2 << bwl) + (2 << Av1Constants.TransformPadHorizontalLog2)]]; // { 2, 0 }
mag += ClipMax3[row1[1]]; // { 1, 1 }
mag += ClipMax3[row0[2]]; // { 0, 2 }
mag += ClipMax3[row2[0]]; // { 2, 0 }
break;
case Av1TransformClass.ClassVertical:
mag += ClipMax3[levels[(2 << bwl) + (2 << Av1Constants.TransformPadHorizontalLog2)]]; // { 2, 0 }
mag += ClipMax3[levels[(3 << bwl) + (3 << Av1Constants.TransformPadHorizontalLog2)]]; // { 3, 0 }
mag += ClipMax3[levels[(4 << bwl) + (4 << Av1Constants.TransformPadHorizontalLog2)]]; // { 4, 0 }
Span<byte> row3 = levels.GetRow(y + 3);
Span<byte> row4 = levels.GetRow(y + 4);
mag += ClipMax3[row2[0]]; // { 2, 0 }
mag += ClipMax3[row3[0]]; // { 3, 0 }
mag += ClipMax3[row4[0]]; // { 4, 0 }
break;
case Av1TransformClass.ClassHorizontal:
mag += ClipMax3[levels[2]]; // { 0, 2 }
mag += ClipMax3[levels[3]]; // { 0, 3 }
mag += ClipMax3[levels[4]]; // { 0, 4 }
mag += ClipMax3[row0[2]]; // { 0, 2 }
mag += ClipMax3[row0[3]]; // { 0, 3 }
mag += ClipMax3[row0[4]]; // { 0, 4 }
break;
}

382
src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolContextHelper.cs

@ -0,0 +1,382 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
using SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Entropy;
internal static class Av1SymbolContextHelper
{
public static readonly int[] EndOfBlockOffsetBits = [0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
public static readonly int[] EndOfBlockGroupStart = [0, 1, 2, 3, 5, 9, 17, 33, 65, 129, 257, 513];
private static readonly int[] TransformCountInSet = [1, 2, 5, 7, 12, 16];
private static readonly byte[] EndOfBlockToPositionSmall = [
0, 1, 2, // 0-2
3, 3, // 3-4
4, 4, 4, 4, // 5-8
5, 5, 5, 5, 5, 5, 5, 5, // 9-16
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 // 17-32
];
private static readonly byte[] EndOfBlockToPositionLarge = [
6, // place holder
7, // 33-64
8,
8, // 65-128
9,
9,
9,
9, // 129-256
10,
10,
10,
10,
10,
10,
10,
10, // 257-512
11 // 513-
];
// Maps tx set types to the indices. INTRA values only
private static readonly int[] ExtendedTransformSetToIndex = [0, -1, 2, 1, -1, -1];
internal static int RecordEndOfBlockPosition(int endOfBlockPoint, int endOfBlockExtra)
{
int endOfBlock = EndOfBlockGroupStart[endOfBlockPoint];
if (endOfBlock > 2)
{
endOfBlock += endOfBlockExtra;
}
return endOfBlock;
}
internal static int GetBaseRangeContextEndOfBlock(int index, int blockWidthLog2, Av1TransformClass transformClass)
{
int row = index >> blockWidthLog2;
int col = index - (row << blockWidthLog2);
if (index == 0)
{
return 0;
}
if ((transformClass == Av1TransformClass.Class2D && row < 2 && col < 2) ||
(transformClass == Av1TransformClass.ClassHorizontal && col == 0) ||
(transformClass == Av1TransformClass.ClassVertical && row == 0))
{
return 7;
}
return 14;
}
/// <summary>
/// SVT: get_lower_levels_ctx_eob
/// </summary>
internal static int GetLowerLevelContextEndOfBlock(int blockWidthLog2, int height, int scanIndex)
{
if (scanIndex == 0)
{
return 0;
}
if (scanIndex <= height << blockWidthLog2 >> 3)
{
return 1;
}
if (scanIndex <= height << blockWidthLog2 >> 2)
{
return 2;
}
return 3;
}
/// <summary>
/// SVT: get_br_ctx_2d
/// </summary>
internal static int GetBaseRangeContext2d(Av1LevelBuffer levels, int index, int blockWidthLog2)
{
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);
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)
{
return mag + 7;
}
return mag + 14;
}
/// <summary>
/// SVT: get_lower_levels_ctx_2d
/// </summary>
internal static int GetLowerLevelsContext2d(Av1LevelBuffer levelBuffer, int index, int blockWidthLog2, Av1TransformSize transformSize)
{
DebugGuard.MustBeGreaterThan(index, 0, nameof(index));
int y = index >> blockWidthLog2;
int x = index - (y << blockWidthLog2);
int mag;
Span<byte> row0 = levelBuffer.GetRow(y);
Span<byte> row1 = levelBuffer.GetRow(y + 1);
Span<byte> row2 = levelBuffer.GetRow(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 }
mag += Math.Min((int)row0[2], 3); // { 0, 2 }
mag += Math.Min((int)row2[0], 3); // { 2, 0 }
int ctx = Math.Min((mag + 1) >> 1, 4);
return ctx + Av1NzMap.GetNzMapContext(transformSize, index);
}
/// <summary>
/// SVT: get_br_ctx
/// </summary>
internal static int GetBaseRangeContext(Av1LevelBuffer levels, int index, int blockWidthLog2, 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];
switch (transformClass)
{
case Av1TransformClass.Class2D:
mag += row1[x + 1];
mag = Math.Min((mag + 1) >> 1, 6);
if (index == 0)
{
return mag;
}
if (y < 2 && x < 2)
{
return mag + 7;
}
break;
case Av1TransformClass.ClassHorizontal:
mag += row0[2];
mag = Math.Min((mag + 1) >> 1, 6);
if (index == 0)
{
return mag;
}
if (x == 0)
{
return mag + 7;
}
break;
case Av1TransformClass.ClassVertical:
mag += levels.GetRow(y + 2)[0];
mag = Math.Min((mag + 1) >> 1, 6);
if (index == 0)
{
return mag;
}
if (y == 0)
{
return mag + 7;
}
break;
default:
break;
}
return mag + 14;
}
internal static int GetLowerLevelsContext(Av1LevelBuffer levels, int pos, int bwl, Av1TransformSize transformSize, Av1TransformClass transformClass)
{
int stats = Av1NzMap.GetNzMagnitude(levels, pos >> bwl, transformClass);
return Av1NzMap.GetNzMapContextFromStats(stats, pos, bwl, transformSize, transformClass);
}
internal static Av1TransformSetType GetExtendedTransformSetType(Av1TransformSize transformSize, bool useReducedSet)
{
Av1TransformSize squareUpSize = transformSize.GetSquareUpSize();
if (squareUpSize >= Av1TransformSize.Size32x32)
{
return Av1TransformSetType.DctOnly;
}
if (useReducedSet)
{
return Av1TransformSetType.Dtt4Identity;
}
Av1TransformSize squareSize = transformSize.GetSquareSize();
return squareSize == Av1TransformSize.Size16x16 ? Av1TransformSetType.Dtt4Identity : Av1TransformSetType.Dtt4Identity1dDct;
}
internal static Av1TransformType ConvertIntraModeToTransformType(Av1BlockModeInfo modeInfo, Av1PlaneType planeType)
{
Av1PredictionMode mode = (planeType == Av1PlaneType.Y) ? modeInfo.YMode : modeInfo.UvMode;
if (mode == Av1PredictionMode.UvChromaFromLuma)
{
mode = Av1PredictionMode.DC;
}
return mode.ToTransformType();
}
/// <summary>
/// SVT: get_nz_map_ctx
/// </summary>
internal static sbyte GetNzMapContext(
Av1LevelBuffer levels,
int index,
int blockWidthLog2,
int height,
int scan_idx,
bool is_eob,
Av1TransformSize transformSize,
Av1TransformClass transformClass)
{
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;
}
int stats = Av1NzMap.GetNzMagnitude(levels, index, blockWidthLog2, transformClass);
return (sbyte)Av1NzMap.GetNzMapContextFromStats(stats, index, blockWidthLog2, transformSize, transformClass);
}
/// <summary>
/// SVT: svt_av1_get_nz_map_contexts_c
/// </summary>
internal static void GetNzMapContexts(
Av1LevelBuffer levels,
ReadOnlySpan<short> scan,
ushort eob,
Av1TransformSize transformSize,
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);
}
}
/// <summary>
/// SVT: get_ext_tx_set_type
/// </summary>
internal static Av1TransformSetType GetExtendedTransformSetType(Av1TransformSize transformSize, bool isInter, bool useReducedTransformSet)
{
Av1TransformSize transformSizeSquareUp = transformSize.GetSquareUpSize();
if (transformSizeSquareUp > Av1TransformSize.Size32x32)
{
return Av1TransformSetType.DctOnly;
}
if (transformSizeSquareUp == Av1TransformSize.Size32x32)
{
return isInter ? Av1TransformSetType.DctIdentity : Av1TransformSetType.DctOnly;
}
if (useReducedTransformSet)
{
return isInter ? Av1TransformSetType.DctIdentity : Av1TransformSetType.Dtt4Identity;
}
Av1TransformSize transformSizeSquare = transformSize.GetSquareSize();
if (isInter)
{
return transformSizeSquare == Av1TransformSize.Size16x16 ? Av1TransformSetType.Dtt9Identity1dDct : Av1TransformSetType.All16;
}
else
{
return transformSizeSquare == Av1TransformSize.Size16x16 ? Av1TransformSetType.Dtt4Identity : Av1TransformSetType.Dtt4Identity1dDct;
}
}
internal static int GetExtendedTransformTypeCount(Av1TransformSize transformSize, bool useReducedTransformSet)
{
int setType = (int)GetExtendedTransformSetType(transformSize, useReducedTransformSet);
return TransformCountInSet[setType];
}
/// <summary>
/// SVT: get_ext_tx_set
/// </summary>
internal static int GetExtendedTransformSet(Av1TransformSize transformSize, bool useReducedTransformSet)
{
int setType = (int)GetExtendedTransformSetType(transformSize, useReducedTransformSet);
return ExtendedTransformSetToIndex[setType];
}
/// <summary>
/// SVT: set_dc_sign
/// </summary>
internal static void SetDcSign(ref int culLevel, int dcValue)
{
if (dcValue < 0)
{
culLevel |= 1 << Av1Constants.CoefficientContextBitCount;
}
else if (dcValue > 0)
{
culLevel += 2 << Av1Constants.CoefficientContextBitCount;
}
}
/// <summary>
/// SVT: get_eob_pos_token
/// </summary>
internal static short GetEndOfBlockPosition(ushort endOfBlock, out int extra)
{
short t;
if (endOfBlock < 33)
{
t = EndOfBlockToPositionSmall[endOfBlock];
}
else
{
int e = Math.Min((endOfBlock - 1) >> 5, 16);
t = EndOfBlockToPositionLarge[e];
}
extra = endOfBlock - EndOfBlockGroupStart[t];
return t;
}
}

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

@ -11,8 +11,6 @@ internal ref struct Av1SymbolDecoder
{
private static readonly int[] IntraModeContext = [0, 1, 2, 3, 4, 4, 4, 4, 3, 0, 1, 2, 0];
private static readonly int[] AlphaVContexts = [-1, 0, 3, -1, 1, 4, -1, 2, 5];
public static readonly int[] EndOfBlockOffsetBits = [0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
public static readonly int[] EndOfBlockGroupStart = [0, 1, 2, 3, 5, 9, 17, 33, 65, 129, 257, 513];
private readonly Av1Distribution tileIntraBlockCopy = Av1DefaultDistributions.IntraBlockCopy;
private readonly Av1Distribution[] tilePartitionTypes = Av1DefaultDistributions.PartitionTypes;
@ -35,10 +33,12 @@ internal ref struct Av1SymbolDecoder
private readonly Av1Distribution[][][] endOfBlockExtra;
private readonly Av1Distribution chromeForLumaSign = Av1DefaultDistributions.ChromeForLumaSign;
private readonly Av1Distribution[] chromeForLumaAlpha = Av1DefaultDistributions.ChromeForLumaAlpha;
private Configuration configuration;
private Av1SymbolReader reader;
public Av1SymbolDecoder(Span<byte> tileData, int qIndex)
public Av1SymbolDecoder(Configuration configuration, Span<byte> tileData, int qIndex)
{
this.configuration = configuration;
this.reader = new Av1SymbolReader(tileData);
this.endOfBlockFlag = Av1DefaultDistributions.GetEndOfBlockFlag(qIndex);
this.coefficientsBase = Av1DefaultDistributions.GetCoefficientsBase(qIndex);
@ -237,8 +237,7 @@ internal ref struct Av1SymbolDecoder
Av1PlaneType planeType = (Av1PlaneType)Math.Min(plane, 1);
int culLevel = 0;
byte[] levelsBuffer = new byte[Av1Constants.TransformPad2d];
Span<byte> levels = levelsBuffer.AsSpan()[(Av1Constants.TransformPadTop * (width + Av1Constants.TransformPadHorizontal))..];
Av1LevelBuffer levels = new(this.configuration, new Size(width, height));
bool allZero = this.ReadTransformBlockSkip(transformSizeContext, transformBlockContext.SkipContext);
int bwl = transformSize.GetBlockWidthLog2();
@ -263,7 +262,7 @@ internal ref struct Av1SymbolDecoder
endOfBlock = this.ReadEndOfBlockPosition(transformSize, transformClass, transformSizeContext, planeType);
if (endOfBlock > 1)
{
Array.Fill(levelsBuffer, (byte)0, 0, ((width + Av1Constants.TransformPadHorizontal) * (height + Av1Constants.TransformPadVertical)) + Av1Constants.TransformPadEnd);
levels.Clear();
}
this.ReadCoefficientsEndOfBlock(transformClass, endOfBlock, height, scan, bwl, levels, transformSizeContext, planeType);
@ -292,7 +291,7 @@ internal ref struct Av1SymbolDecoder
{
int endOfBlockExtra = 0;
int endOfBlockPoint = this.ReadEndOfBlockFlag(planeType, transformClass, transformSize);
int endOfBlockShift = EndOfBlockOffsetBits[endOfBlockPoint];
int endOfBlockShift = Av1SymbolContextHelper.EndOfBlockOffsetBits[endOfBlockPoint];
if (endOfBlockShift > 0)
{
int endOfBlockContext = endOfBlockPoint;
@ -313,18 +312,18 @@ internal ref struct Av1SymbolDecoder
}
}
return RecordEndOfBlockPosition(endOfBlockPoint, endOfBlockExtra);
return Av1SymbolContextHelper.RecordEndOfBlockPosition(endOfBlockPoint, endOfBlockExtra);
}
public void ReadCoefficientsEndOfBlock(Av1TransformClass transformClass, int endOfBlock, int height, ReadOnlySpan<short> scan, int bwl, Span<byte> levels, Av1TransformSize transformSizeContext, Av1PlaneType planeType)
public void ReadCoefficientsEndOfBlock(Av1TransformClass transformClass, int endOfBlock, int height, ReadOnlySpan<short> scan, int blockWidthLog2, Av1LevelBuffer levels, Av1TransformSize transformSizeContext, Av1PlaneType planeType)
{
int i = endOfBlock - 1;
int pos = scan[i];
int coefficientContext = GetLowerLevelContextEndOfBlock(bwl, height, i);
int coefficientContext = Av1SymbolContextHelper.GetLowerLevelContextEndOfBlock(blockWidthLog2, height, i);
int level = this.ReadBaseEndOfBlock(transformSizeContext, planeType, coefficientContext);
if (level > Av1Constants.BaseLevelsCount)
{
int baseRangeContext = GetBaseRangeContextEndOfBlock(pos, bwl, transformClass);
int baseRangeContext = Av1SymbolContextHelper.GetBaseRangeContextEndOfBlock(pos, blockWidthLog2, transformClass);
for (int idx = 0; idx < Av1Constants.CoefficientBaseRange / Av1Constants.BaseRangeSizeMinus1; idx++)
{
int coefficinetBaseRange = this.ReadCoefficientsBaseRange(transformSizeContext, planeType, baseRangeContext);
@ -336,19 +335,19 @@ internal ref struct Av1SymbolDecoder
}
}
levels[GetPaddedIndex(pos, bwl)] = (byte)level;
levels.GetPaddedRow(pos, blockWidthLog2)[0] = (byte)level;
}
public void ReadCoefficientsReverse2d(Av1TransformSize transformSize, int startScanIndex, int endScanIndex, ReadOnlySpan<short> scan, int bwl, Span<byte> levels, Av1TransformSize transformSizeContext, Av1PlaneType planeType)
public void ReadCoefficientsReverse2d(Av1TransformSize transformSize, int startScanIndex, int endScanIndex, ReadOnlySpan<short> scan, int blockWidthLog2, Av1LevelBuffer levels, Av1TransformSize transformSizeContext, Av1PlaneType planeType)
{
for (int c = endScanIndex; c >= startScanIndex; --c)
{
int pos = scan[c];
int coefficientContext = GetLowerLevelsContext2d(levels, pos, bwl, transformSize);
int coefficientContext = Av1SymbolContextHelper.GetLowerLevelsContext2d(levels, pos, blockWidthLog2, transformSize);
int level = this.ReadCoefficientsBase(coefficientContext, transformSizeContext, planeType);
if (level > Av1Constants.BaseLevelsCount)
{
int baseRangeContext = GetBaseRangeContext2d(levels, pos, bwl);
int baseRangeContext = Av1SymbolContextHelper.GetBaseRangeContext2d(levels, pos, blockWidthLog2);
for (int idx = 0; idx < Av1Constants.CoefficientBaseRange; idx += Av1Constants.BaseRangeSizeMinus1)
{
int k = this.ReadCoefficientsBaseRange(transformSizeContext, planeType, baseRangeContext);
@ -360,20 +359,20 @@ internal ref struct Av1SymbolDecoder
}
}
levels[GetPaddedIndex(pos, bwl)] = (byte)level;
levels.GetPaddedRow(pos, blockWidthLog2)[0] = (byte)level;
}
}
public void ReadCoefficientsReverse(Av1TransformSize transformSize, Av1TransformClass transformClass, int startScanIndex, int endScanIndex, ReadOnlySpan<short> scan, int bwl, Span<byte> levels, Av1TransformSize transformSizeContext, Av1PlaneType planeType)
public void ReadCoefficientsReverse(Av1TransformSize transformSize, Av1TransformClass transformClass, int startScanIndex, int endScanIndex, ReadOnlySpan<short> scan, int blockWidthLog2, Av1LevelBuffer levels, Av1TransformSize transformSizeContext, Av1PlaneType planeType)
{
for (int c = endScanIndex; c >= startScanIndex; --c)
{
int pos = scan[c];
int coefficientContext = GetLowerLevelsContext(levels, pos, bwl, transformSize, transformClass);
int coefficientContext = Av1SymbolContextHelper.GetLowerLevelsContext(levels, pos, blockWidthLog2, transformSize, transformClass);
int level = this.ReadCoefficientsBase(coefficientContext, transformSizeContext, planeType);
if (level > Av1Constants.BaseLevelsCount)
{
int baseRangeContext = GetBaseRangeContext(levels, pos, bwl, transformClass);
int baseRangeContext = Av1SymbolContextHelper.GetBaseRangeContext(levels, pos, blockWidthLog2, transformClass);
for (int idx = 0; idx < Av1Constants.CoefficientBaseRange; idx += Av1Constants.BaseRangeSizeMinus1)
{
int k = this.ReadCoefficientsBaseRange(transformSizeContext, planeType, baseRangeContext);
@ -385,11 +384,11 @@ internal ref struct Av1SymbolDecoder
}
}
levels[GetPaddedIndex(pos, bwl)] = (byte)level;
levels.GetPaddedRow(pos, blockWidthLog2)[0] = (byte)level;
}
}
public int ReadCoefficientsDc(Span<int> coefficientBuffer, int endOfBlock, ReadOnlySpan<short> scan, int bwl, Span<byte> levels, int dcSignContext, Av1PlaneType planeType)
public int ReadCoefficientsDc(Span<int> coefficientBuffer, int endOfBlock, ReadOnlySpan<short> scan, int blockWidthLog2, Av1LevelBuffer levels, int dcSignContext, Av1PlaneType planeType)
{
int maxScanLine = 0;
int culLevel = 0;
@ -398,7 +397,7 @@ internal ref struct Av1SymbolDecoder
for (int c = 0; c < endOfBlock; c++)
{
int sign = 0;
int level = levels[GetPaddedIndex(scan[c], bwl)];
int level = levels.GetPaddedRow(scan[c], blockWidthLog2)[0];
if (level != 0)
{
maxScanLine = Math.Max(maxScanLine, scan[c]);
@ -429,7 +428,7 @@ internal ref struct Av1SymbolDecoder
}
culLevel = Math.Min(Av1Constants.CoefficientContextMask, culLevel);
SetDcSign(ref culLevel, dcValue);
Av1SymbolContextHelper.SetDcSign(ref culLevel, dcValue);
return culLevel;
}
@ -472,159 +471,6 @@ internal ref struct Av1SymbolDecoder
return r.ReadSymbol(this.coefficientsBase[(int)transformSizeContext][(int)planeType][coefficientContext]);
}
private static int RecordEndOfBlockPosition(int endOfBlockPoint, int endOfBlockExtra)
{
int endOfBlock = EndOfBlockGroupStart[endOfBlockPoint];
if (endOfBlock > 2)
{
endOfBlock += endOfBlockExtra;
}
return endOfBlock;
}
private static int GetBaseRangeContextEndOfBlock(int pos, int bwl, Av1TransformClass transformClass)
{
int row = pos >> bwl;
int col = pos - (row << bwl);
if (pos == 0)
{
return 0;
}
if (transformClass == Av1TransformClass.Class2D && row < 2 && col < 2 ||
transformClass == Av1TransformClass.ClassHorizontal && col == 0 ||
transformClass == Av1TransformClass.ClassVertical && row == 0)
{
return 7;
}
return 14;
}
private static int GetLowerLevelContextEndOfBlock(int bwl, int height, int scanIndex)
{
if (scanIndex == 0)
{
return 0;
}
if (scanIndex <= height << bwl >> 3)
{
return 1;
}
if (scanIndex <= height << bwl >> 2)
{
return 2;
}
return 3;
}
private static int GetBaseRangeContext2d(Span<byte> levels, int c, int bwl)
{
DebugGuard.MustBeGreaterThan(c, 0, nameof(c));
int row = c >> bwl;
int col = c - (row << bwl);
int stride = (1 << bwl) + Av1Constants.TransformPadHorizontal;
int pos = row * stride + col;
int mag =
Math.Min((int)levels[pos + 1], Av1Constants.MaxBaseRange) +
Math.Min((int)levels[pos + stride], Av1Constants.MaxBaseRange) +
Math.Min((int)levels[pos + 1 + stride], Av1Constants.MaxBaseRange);
mag = Math.Min(mag + 1 >> 1, 6);
if ((row | col) < 2)
{
return mag + 7;
}
return mag + 14;
}
private static int GetLowerLevelsContext2d(Span<byte> levels, int pos, int bwl, Av1TransformSize transformSize)
{
DebugGuard.MustBeGreaterThan(pos, 0, nameof(pos));
int mag;
levels = levels[GetPaddedIndex(pos, bwl)..];
mag = Math.Min((int)levels[1], 3); // { 0, 1 }
mag += Math.Min((int)levels[(1 << bwl) + Av1Constants.TransformPadHorizontal], 3); // { 1, 0 }
mag += Math.Min((int)levels[(1 << bwl) + Av1Constants.TransformPadHorizontal + 1], 3); // { 1, 1 }
mag += Math.Min((int)levels[2], 3); // { 0, 2 }
mag += Math.Min((int)levels[(2 << bwl) + (2 << Av1Constants.TransformPadHorizontalLog2)], 3); // { 2, 0 }
int ctx = Math.Min(mag + 1 >> 1, 4);
return ctx + Av1NzMap.GetNzMapContext(transformSize, pos);
}
private static int GetBaseRangeContext(Span<byte> levels, int c, int bwl, Av1TransformClass transformClass)
{
int row = c >> bwl;
int col = c - (row << bwl);
int stride = (1 << bwl) + Av1Constants.TransformPadHorizontal;
int pos = row * stride + col;
int mag = levels[pos + 1];
mag += levels[pos + stride];
switch (transformClass)
{
case Av1TransformClass.Class2D:
mag += levels[pos + stride + 1];
mag = Math.Min(mag + 1 >> 1, 6);
if (c == 0)
{
return mag;
}
if (row < 2 && col < 2)
{
return mag + 7;
}
break;
case Av1TransformClass.ClassHorizontal:
mag += levels[pos + 2];
mag = Math.Min(mag + 1 >> 1, 6);
if (c == 0)
{
return mag;
}
if (col == 0)
{
return mag + 7;
}
break;
case Av1TransformClass.ClassVertical:
mag += levels[pos + (stride << 1)];
mag = Math.Min(mag + 1 >> 1, 6);
if (c == 0)
{
return mag;
}
if (row == 0)
{
return mag + 7;
}
break;
default:
break;
}
return mag + 14;
}
private static int GetLowerLevelsContext(ReadOnlySpan<byte> levels, int pos, int bwl, Av1TransformSize transformSize, Av1TransformClass transformClass)
{
int stats = Av1NzMap.GetNzMagnitude(levels[GetPaddedIndex(pos, bwl)..], bwl, transformClass);
return Av1NzMap.GetNzMapContextFromStats(stats, pos, bwl, transformSize, transformClass);
}
public static int GetPaddedIndex(int scanIndex, int bwl)
=> scanIndex + (scanIndex >> bwl << Av1Constants.TransformPadHorizontalLog2);
private int ReadGolomb()
{
int x = 1;
@ -651,19 +497,6 @@ internal ref struct Av1SymbolDecoder
return x - 1;
}
private static void SetDcSign(ref int culLevel, int dcValue)
{
if (dcValue < 0)
{
culLevel |= 1 << Av1Constants.CoefficientContextBitCount;
}
else if (dcValue > 0)
{
culLevel += 2 << Av1Constants.CoefficientContextBitCount;
}
}
private void UpdateCoefficientContext(
Av1BlockModeInfo modeInfo,
int[] aboveContexts,
@ -721,11 +554,11 @@ internal ref struct Av1SymbolDecoder
{
// In intra mode, uv planes don't share the same prediction mode as y
// plane, so the tx_type should not be shared
transformType = ConvertIntraModeToTransformType(modeInfo, Av1PlaneType.Uv);
transformType = Av1SymbolContextHelper.ConvertIntraModeToTransformType(modeInfo, Av1PlaneType.Uv);
}
}
Av1TransformSetType transformSetType = GetExtendedTransformSetType(transformSize, useReducedTransformSet);
Av1TransformSetType transformSetType = Av1SymbolContextHelper.GetExtendedTransformSetType(transformSize, useReducedTransformSet);
if (!transformType.IsExtendedSetUsed(transformSetType))
{
transformType = Av1TransformType.DctDct;
@ -734,35 +567,6 @@ internal ref struct Av1SymbolDecoder
return transformType;
}
private static Av1TransformSetType GetExtendedTransformSetType(Av1TransformSize transformSize, bool useReducedSet)
{
Av1TransformSize squareUpSize = transformSize.GetSquareUpSize();
if (squareUpSize >= Av1TransformSize.Size32x32)
{
return Av1TransformSetType.DctOnly;
}
if (useReducedSet)
{
return Av1TransformSetType.Dtt4Identity;
}
Av1TransformSize squareSize = transformSize.GetSquareSize();
return squareSize == Av1TransformSize.Size16x16 ? Av1TransformSetType.Dtt4Identity : Av1TransformSetType.Dtt4Identity1dDct;
}
private static Av1TransformType ConvertIntraModeToTransformType(Av1BlockModeInfo modeInfo, Av1PlaneType planeType)
{
Av1PredictionMode mode = (planeType == Av1PlaneType.Y) ? modeInfo.YMode : modeInfo.UvMode;
if (mode == Av1PredictionMode.UvChromaFromLuma)
{
mode = Av1PredictionMode.DC;
}
return mode.ToTransformType();
}
internal static Av1Distribution GetSplitOrHorizontalDistribution(Av1Distribution[] inputs, Av1BlockSize blockSize, int context)
{
Av1Distribution input = inputs[context];

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

@ -1,11 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
using System.Buffers;
using System.Drawing;
using System.Formats.Asn1;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Formats.Heif.Av1;
using SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
using SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
@ -24,46 +20,6 @@ internal class Av1SymbolEncoder : IDisposable
[7, 8, 9, 12, 10, 11, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6],
];
private static readonly byte[] EndOfBlockToPositionSmall = [
0, 1, 2, // 0-2
3, 3, // 3-4
4, 4, 4, 4, // 5-8
5, 5, 5, 5, 5, 5, 5, 5, // 9-16
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 // 17-32
];
private static readonly byte[] EndOfBlockToPositionLarge = [
6, // place holder
7, // 33-64
8,
8, // 65-128
9,
9,
9,
9, // 129-256
10,
10,
10,
10,
10,
10,
10,
10, // 257-512
11 // 513-
];
private static readonly int[] TransformCountInSet = [1, 2, 5, 7, 12, 16];
// Maps tx set types to the indices.
private static readonly int[][] ExtendedTransformSetToIndex = [
// Intra
[0, -1, 2, 1, -1, -1],
// Inter
[0, 3, -1, -1, 2, 1]
];
private readonly Av1Distribution tileIntraBlockCopy = Av1DefaultDistributions.IntraBlockCopy;
private readonly Av1Distribution[] tilePartitionTypes = Av1DefaultDistributions.PartitionTypes;
private readonly Av1Distribution[] skip = Av1DefaultDistributions.Skip;
@ -75,6 +31,7 @@ internal class Av1SymbolEncoder : IDisposable
private readonly Av1Distribution[][][] endOfBlockExtra;
private readonly Av1Distribution[][][] intraExtendedTransform = Av1DefaultDistributions.IntraExtendedTransform;
private bool isDisposed;
private readonly Configuration configuration;
private Av1SymbolWriter writer;
public Av1SymbolEncoder(Configuration configuration, int initialSize, int qIndex)
@ -85,6 +42,7 @@ internal class Av1SymbolEncoder : IDisposable
this.coefficientsBaseEndOfBlock = Av1DefaultDistributions.GetBaseEndOfBlock(qIndex);
this.dcSign = Av1DefaultDistributions.GetDcSign(qIndex);
this.endOfBlockExtra = Av1DefaultDistributions.GetEndOfBlockExtra(qIndex);
this.configuration = configuration;
this.writer = new(configuration, initialSize);
}
@ -140,12 +98,11 @@ internal class Av1SymbolEncoder : IDisposable
Av1ScanOrder scanOrder = Av1ScanOrderConstants.GetScanOrder(transformSize, transformType);
ReadOnlySpan<short> scan = scanOrder.Scan;
int bwl = transformSize.GetBlockWidthLog2();
Av1TransformSize transformSizeContext = (Av1TransformSize)((int)transformSize.GetSquareSize() + (int)transformSize.GetSquareUpSize() + 1 >> 1);
Av1TransformSize transformSizeContext = (Av1TransformSize)(((int)transformSize.GetSquareSize() + (int)transformSize.GetSquareUpSize() + 1) >> 1);
ref Av1SymbolWriter w = ref this.writer;
Span<byte> levels_buf = new byte[Av1Constants.TransformPad2d];
Span<byte> levels = SetLevels(levels_buf, width);
Av1LevelBuffer levels = new(this.configuration, new Size(width, height));
Span<sbyte> coeff_contexts = new sbyte[Av1Constants.MaxTransformSize * Av1Constants.MaxTransformSize];
Guard.MustBeLessThan((int)transformSizeContext, (int)Av1TransformSize.AllSizes, nameof(transformSizeContext));
@ -158,16 +115,16 @@ internal class Av1SymbolEncoder : IDisposable
return 0;
}
InitializeLevels(coeffBuffer, width, height, levels);
levels.Initialize(coeffBuffer);
if (componentType == Av1ComponentType.Luminance)
{
this.WriteTransformType(transformType, transformSize, useReducedTransformSet, baseQIndex, filterIntraMode, intraDirection);
}
short endOfBlockPosition = GetEndOfBlockPosition(eob, out int eob_extra);
short endOfBlockPosition = Av1SymbolContextHelper.GetEndOfBlockPosition(eob, out int eob_extra);
this.WriteEndOfBlockFlag(componentType, transformClass, transformSize, endOfBlockPosition);
int eob_offset_bits = Av1SymbolDecoder.EndOfBlockOffsetBits[endOfBlockPosition];
int eob_offset_bits = Av1SymbolContextHelper.EndOfBlockOffsetBits[endOfBlockPosition];
if (eob_offset_bits > 0)
{
int eob_shift = eob_offset_bits - 1;
@ -181,7 +138,7 @@ internal class Av1SymbolEncoder : IDisposable
}
}
GetNzMapContexts(levels, scan, eob, transformSize, transformClass, coeff_contexts);
Av1SymbolContextHelper.GetNzMapContexts(levels, scan, eob, transformSize, transformClass, coeff_contexts);
int limitedTransformSizeContext = Math.Min((int)transformSizeContext, (int)Av1TransformSize.Size32x32);
for (c = eob - 1; c >= 0; --c)
{
@ -203,7 +160,7 @@ internal class Av1SymbolEncoder : IDisposable
{
// level is above 1.
int baseRange = level - 1 - Av1Constants.BaseLevelsCount;
int baseRangeContext = GetBaseRangeContext(levels, pos, bwl, transformClass);
int baseRangeContext = Av1SymbolContextHelper.GetBaseRangeContext(levels, pos, bwl, transformClass);
for (int idx = 0; idx < Av1Constants.CoefficientBaseRange; idx += Av1Constants.BaseRangeSizeMinus1)
{
int k = Math.Min(baseRange - idx, Av1Constants.BaseRangeSizeMinus1);
@ -248,196 +205,10 @@ internal class Av1SymbolEncoder : IDisposable
cul_level = Math.Min(Av1Constants.CoefficientContextMask, cul_level);
// DC value
SetDcSign(ref cul_level, coeffBuffer[0]);
Av1SymbolContextHelper.SetDcSign(ref cul_level, coeffBuffer[0]);
return cul_level;
}
/// <summary>
/// SVT: set_dc_sign
/// </summary>
private static void SetDcSign(ref int culLevel, int dcValue)
{
if (dcValue < 0)
{
culLevel |= 1 << Av1Constants.CoefficientContextBitCount;
}
else if (dcValue > 0)
{
culLevel += 2 << Av1Constants.CoefficientContextBitCount;
}
}
/// <summary>
/// SVT: get_br_ctx
/// </summary>
private static int GetBaseRangeContext(Span<byte> levels, short c, int bwl, Av1TransformClass transformClass)
{
int row = c >> bwl;
int col = c - (row << bwl);
int stride = (1 << bwl) + Av1Constants.TransformPadHorizontal;
int pos = row * stride + col;
int mag = levels[pos + 1];
mag += levels[pos + stride];
switch (transformClass)
{
case Av1TransformClass.Class2D:
mag += levels[pos + stride + 1];
mag = Math.Min(mag + 1 >> 1, 6);
if (c == 0)
{
return mag;
}
if (row < 2 && col < 2)
{
return mag + 7;
}
break;
case Av1TransformClass.ClassHorizontal:
mag += levels[pos + 2];
mag = Math.Min(mag + 1 >> 1, 6);
if (c == 0)
{
return mag;
}
if (col == 0)
{
return mag + 7;
}
break;
case Av1TransformClass.ClassVertical:
mag += levels[pos + (stride << 1)];
mag = Math.Min(mag + 1 >> 1, 6);
if (c == 0)
{
return mag;
}
if (row == 0)
{
return mag + 7;
}
break;
default:
break;
}
return mag + 14;
}
/// <summary>
/// SVT: get_eob_pos_token
/// </summary>
private static short GetEndOfBlockPosition(ushort endOfBlock, out int extra)
{
short t;
if (endOfBlock < 33)
{
t = EndOfBlockToPositionSmall[endOfBlock];
}
else
{
int e = Math.Min(endOfBlock - 1 >> 5, 16);
t = EndOfBlockToPositionLarge[e];
}
extra = endOfBlock - Av1SymbolDecoder.EndOfBlockGroupStart[t];
return t;
}
/// <summary>
/// SVT: get_nz_map_ctx
/// </summary>
private static sbyte GetNzMapContext(
ReadOnlySpan<byte> levels,
int pos,
int bwl,
int height,
int scan_idx,
bool is_eob,
Av1TransformSize transformSize,
Av1TransformClass transformClass)
{
if (is_eob)
{
if (scan_idx == 0)
{
return 0;
}
if (scan_idx <= (height << bwl) / 8)
{
return 1;
}
if (scan_idx <= (height << bwl) / 4)
{
return 2;
}
return 3;
}
int stats = Av1NzMap.GetNzMagnitude(levels[Av1SymbolDecoder.GetPaddedIndex(pos, bwl)..], bwl, transformClass);
return (sbyte)Av1NzMap.GetNzMapContextFromStats(stats, pos, bwl, transformSize, transformClass);
}
/// <summary>
/// SVT: svt_av1_get_nz_map_contexts_c
/// </summary>
private static void GetNzMapContexts(
ReadOnlySpan<byte> levels,
ReadOnlySpan<short> scan,
ushort eob,
Av1TransformSize tx_size,
Av1TransformClass tx_class,
Span<sbyte> coeff_contexts)
{
int bwl = tx_size.GetBlockWidthLog2();
int height = tx_size.GetHeight();
for (int i = 0; i < eob; ++i)
{
int pos = scan[i];
coeff_contexts[pos] = GetNzMapContext(levels, pos, bwl, height, i, i == eob - 1, tx_size, tx_class);
}
}
/// <summary>
/// SVT: svt_av1_txb_init_levels_c
/// </summary>
private static void InitializeLevels(Span<int> coefficientBuffer, int width, int height, Span<byte> levels)
{
int stride = width + Av1Constants.TransformPadHorizontal;
ref byte ls = ref levels[0];
Unsafe.InitBlock(ref levels[-Av1Constants.TransformPadTop * stride], 0, (uint)(Av1Constants.TransformPadTop * stride));
Unsafe.InitBlock(ref levels[stride * height], 0, (uint)((Av1Constants.TransformPadBottom * stride) + Av1Constants.TransformPadEnd));
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
ls = (byte)Av1Math.Clamp(Math.Abs(coefficientBuffer[(y * width) + x]), 0, byte.MaxValue);
ls = ref Unsafe.Add(ref ls, 1);
}
Unsafe.InitBlock(ref ls, 0, Av1Constants.TransformPadHorizontal);
}
}
/// <summary>
/// SVT: set_levels from EbCommonUtils.h
/// </summary>
private static Span<byte> SetLevels(Span<byte> levelsBuffer, int width)
{
int stride = width + Av1Constants.TransformPadHorizontal;
return levelsBuffer[(Av1Constants.TransformPadTop * stride)..];
}
private void WriteSkip(bool hasEndOfBlock, int context)
{
// Has EOB, means we won't skip, negating the logic.
@ -503,28 +274,20 @@ internal class Av1SymbolEncoder : IDisposable
Av1PredictionMode intraDirection)
{
ref Av1SymbolWriter w = ref this.writer;
bool isInter = false; // mbmi->block_mi.use_intrabc || is_inter_mode(mbmi->block_mi.mode);
if (GetExtendedTransformTypeCount(transformSize, isInter, useReducedTransformSet) > 1 && baseQIndex > 0)
// bool isInter = mbmi->block_mi.use_intrabc || is_inter_mode(mbmi->block_mi.mode);
if (Av1SymbolContextHelper.GetExtendedTransformTypeCount(transformSize, useReducedTransformSet) > 1 && baseQIndex > 0)
{
Av1TransformSize square_tx_size = transformSize.GetSquareSize();
Guard.MustBeLessThanOrEqualTo((int)square_tx_size, Av1Constants.ExtendedTransformCount, nameof(square_tx_size));
Av1TransformSetType tx_set_type = GetExtendedTransformSetType(transformSize, isInter, useReducedTransformSet);
int eset = GetExtendedTransformSet(transformSize, isInter, useReducedTransformSet);
Av1TransformSetType tx_set_type = Av1SymbolContextHelper.GetExtendedTransformSetType(transformSize, useReducedTransformSet);
int eset = Av1SymbolContextHelper.GetExtendedTransformSet(transformSize, useReducedTransformSet);
// eset == 0 should correspond to a set with only DCT_DCT and there
// is no need to send the tx_type
Guard.MustBeGreaterThan(eset, 0, nameof(eset));
// assert(av1_ext_tx_used[tx_set_type][transformType]);
if (isInter)
{
/*
w.WriteSymbol(
av1_ext_tx_ind[tx_set_type][transformType],
this.inter_ext_tx_cdf[eset][square_tx_size]);*/
}
else
{
Av1PredictionMode intra_dir;
if (filterIntraMode != Av1FilterIntraMode.AllFilterIntraModes)
@ -544,52 +307,4 @@ internal class Av1SymbolEncoder : IDisposable
}
}
}
/// <summary>
/// SVT: get_ext_tx_set
/// </summary>
private static int GetExtendedTransformSet(Av1TransformSize transformSize, bool isInter, bool useReducedTransformSet)
{
int set_type = (int)GetExtendedTransformSetType(transformSize, isInter, useReducedTransformSet);
return ExtendedTransformSetToIndex[isInter ? 1 : 0][set_type];
}
/// <summary>
/// SVT: get_ext_tx_set_type
/// </summary>
private static Av1TransformSetType GetExtendedTransformSetType(Av1TransformSize transformSize, bool isInter, bool useReducedTransformSet)
{
Av1TransformSize transformSizeSquareUp = transformSize.GetSquareUpSize();
if (transformSizeSquareUp > Av1TransformSize.Size32x32)
{
return Av1TransformSetType.DctOnly;
}
if (transformSizeSquareUp == Av1TransformSize.Size32x32)
{
return isInter ? Av1TransformSetType.DctIdentity : Av1TransformSetType.DctOnly;
}
if (useReducedTransformSet)
{
return isInter ? Av1TransformSetType.DctIdentity : Av1TransformSetType.Dtt4Identity;
}
Av1TransformSize transformSizeSquare = transformSize.GetSquareSize();
if (isInter)
{
return transformSizeSquare == Av1TransformSize.Size16x16 ? Av1TransformSetType.Dtt9Identity1dDct : Av1TransformSetType.All16;
}
else
{
return transformSizeSquare == Av1TransformSize.Size16x16 ? Av1TransformSetType.Dtt4Identity : Av1TransformSetType.Dtt4Identity1dDct;
}
}
private static int GetExtendedTransformTypeCount(Av1TransformSize transformSize, bool isInter, bool useReducedTransformSet)
{
int set_type = (int)GetExtendedTransformSetType(transformSize, isInter, useReducedTransformSet);
return TransformCountInSet[set_type];
}
}

73
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1LevelBuffer.cs

@ -0,0 +1,73 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Buffers;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
internal sealed class Av1LevelBuffer : IDisposable
{
private IMemoryOwner<byte>? memory;
public Av1LevelBuffer(Configuration configuration)
: this(configuration, new Size(Av1Constants.MaxTransformSize, Av1Constants.MaxTransformSize))
{
}
public Av1LevelBuffer(Configuration configuration, Size size)
{
this.Size = size;
int totalHeight = Av1Constants.TransformPadTop + size.Height + Av1Constants.TransformPadBottom;
this.Stride = Av1Constants.TransformPadHorizontal + size.Width;
this.memory = configuration.MemoryAllocator.Allocate<byte>(this.Stride * totalHeight, AllocationOptions.Clean);
}
public Size Size { get; }
public int Stride { get; }
public void Initialize(Span<int> coefficientBuffer)
{
ObjectDisposedException.ThrowIf(this.memory == null, this);
ArgumentOutOfRangeException.ThrowIfLessThan(coefficientBuffer.Length, this.Size.Width * this.Size.Height, nameof(coefficientBuffer));
for (int y = 0; y < this.Size.Height; y++)
{
ref byte destRef = ref this.GetRow(y)[0];
ref int sourceRef = ref coefficientBuffer[y * this.Size.Width];
for (int x = 0; x < this.Size.Width; x++)
{
destRef = (byte)Av1Math.Clamp(sourceRef, 0, byte.MaxValue);
destRef = ref Unsafe.Add(ref destRef, 1);
sourceRef = ref Unsafe.Add(ref sourceRef, 1);
}
}
}
public Span<byte> GetRow(int y)
{
ObjectDisposedException.ThrowIf(this.memory == null, this);
ArgumentOutOfRangeException.ThrowIfLessThan(y, -Av1Constants.TransformPadTop);
int row = y + Av1Constants.TransformPadTop;
return this.memory.Memory.Span.Slice(row * this.Stride, this.Size.Width);
}
public void Dispose()
{
this.memory?.Dispose();
this.memory = null;
}
internal void Clear()
{
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

@ -77,7 +77,7 @@ internal class Av1TileReader : IAv1TileReader
/// </summary>
public void ReadTile(Span<byte> tileData, int tileNum)
{
Av1SymbolDecoder reader = new(tileData, this.FrameHeader.QuantizationParameters.BaseQIndex);
Av1SymbolDecoder reader = new(this.configuration, tileData, this.FrameHeader.QuantizationParameters.BaseQIndex);
int tileColumnIndex = tileNum % this.FrameHeader.TilesInfo.TileColumnCount;
int tileRowIndex = tileNum / this.FrameHeader.TilesInfo.TileColumnCount;

2
tests/ImageSharp.Tests/Formats/Heif/Av1/Av1CoefficientsEntropyTests.cs

@ -46,7 +46,7 @@ public class Av1CoefficientsEntropyTests
using IMemoryOwner<byte> encoded = encoder.Exit();
Av1SymbolDecoder decoder = new(encoded.GetSpan(), 0);
Av1SymbolDecoder decoder = new(Configuration.Default, encoded.GetSpan(), 0);
Av1SymbolReader reader = new(encoded.GetSpan());
decoder.ReadCoefficients(modeInfo, new Point(0, 0), aboveContexts, leftContexts, 0, 0, 0, 1, 1, transformBlockContext, transformSize, false, true, transformInfo, 0, 0, actuals);

8
tests/ImageSharp.Tests/Formats/Heif/Av1/Av1EntropyTests.cs

@ -208,7 +208,7 @@ public class Av1EntropyTests
using IMemoryOwner<byte> encoded = encoder.Exit();
Av1SymbolDecoder decoder = new(encoded.GetSpan(), 0);
Av1SymbolDecoder decoder = new(Configuration.Default, encoded.GetSpan(), 0);
Av1SymbolReader reader = new(encoded.GetSpan());
for (int i = 0; i < values.Length; i++)
{
@ -245,7 +245,7 @@ public class Av1EntropyTests
using IMemoryOwner<byte> encoded = encoder.Exit();
Av1SymbolDecoder decoder = new(encoded.GetSpan(), 0);
Av1SymbolDecoder decoder = new(Configuration.Default, encoded.GetSpan(), 0);
Av1SymbolReader reader = new(encoded.GetSpan());
for (int i = 0; i < values.Length; i++)
{
@ -282,7 +282,7 @@ public class Av1EntropyTests
using IMemoryOwner<byte> encoded = encoder.Exit();
Av1SymbolDecoder decoder = new(encoded.GetSpan(), 0);
Av1SymbolDecoder decoder = new(Configuration.Default, encoded.GetSpan(), 0);
Av1SymbolReader reader = new(encoded.GetSpan());
for (int i = 0; i < values.Length; i++)
{
@ -310,7 +310,7 @@ public class Av1EntropyTests
using IMemoryOwner<byte> encoded = encoder.Exit();
Av1SymbolDecoder decoder = new(encoded.GetSpan(), 0);
Av1SymbolDecoder decoder = new(Configuration.Default, encoded.GetSpan(), 0);
for (int i = 0; i < values.Length; i++)
{
actuals[i] = decoder.ReadUseIntraBlockCopy();

Loading…
Cancel
Save