Browse Source

Initial coefficient encoding

pull/2633/head
Ynse Hoornenborg 2 years ago
parent
commit
13be5eb7cb
  1. 5
      src/ImageSharp/Formats/Heif/Av1/Av1Constants.cs
  2. 25
      src/ImageSharp/Formats/Heif/Av1/Av1Math.cs
  3. 4
      src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PredictionDecoder.cs
  4. 24
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1BlockGeometry.cs
  5. 30
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1BlockModeInfoEncoder.cs
  6. 8
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1BlockStruct.cs
  7. 22
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1Common.cs
  8. 14
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ComponentType.cs
  9. 190
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1DefaultDistributions.cs
  10. 16
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderBlockStruct.cs
  11. 14
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EntropyCodingContext.cs
  12. 2
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FilterIntraMode.cs
  13. 15
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FilterIntraModeExtensions.cs
  14. 22
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1MacroBlockD.cs
  15. 16
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1MacroBlockModeInfo.cs
  16. 11
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1NeighborArrayUnit.cs
  17. 2
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1NzMap.cs
  18. 11
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PaletteLumaModeInfo.cs
  19. 22
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PictureControlSet.cs
  20. 20
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PictureParentControlSet.cs
  21. 14
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SequenceControlSet.cs
  22. 11
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1Superblock.cs
  23. 44
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SymbolDecoder.cs
  24. 535
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SymbolEncoder.cs
  25. 6
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileReader.cs
  26. 12
      tests/ImageSharp.Tests/Formats/Heif/Av1/SymbolTests.cs

5
src/ImageSharp/Formats/Heif/Av1/Av1Constants.cs

@ -160,6 +160,8 @@ internal static class Av1Constants
public const int TransformPadTop = 2;
public const int TransformPadBottom = 4;
public const int BaseRangeSizeMinus1 = 3;
public const int MaxBaseRange = 15;
@ -182,4 +184,7 @@ internal static class Av1Constants
public const int MaxTransformStageNumber = 12;
public const int PartitionProbabilitySet = 4;
// Number of transform sizes that use extended transforms.
public const int ExtendedTransformCount = 4;
}

25
src/ImageSharp/Formats/Heif/Av1/Av1Math.cs

@ -2,6 +2,8 @@
// Licensed under the Six Labors Split License.
using System;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
namespace SixLabors.ImageSharp.Formats.Heif.Av1;
@ -50,6 +52,29 @@ internal static class Av1Math
return result;
}
/// <summary>
/// Long Log 2
/// This is a quick adaptation of a Number
/// Leading Zeros(NLZ) algorithm to get the log2f of a 32-bit number
/// </summary>
internal static uint Log2_32(uint x)
{
uint log = 0;
int i;
for (i = 4; i >= 0; --i)
{
uint shift = 1u << i;
uint n = x >> (int)shift;
if (n != 0)
{
x = n;
log += shift;
}
}
return log;
}
public static uint FloorLog2(uint value)
{
uint s = 0;

4
src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PredictionDecoder.cs

@ -224,7 +224,7 @@ internal class Av1PredictionDecoder
}
Av1FilterIntraMode filterIntraMode = (plane == Av1Plane.Y && modeInfo.FilterIntraModeInfo.UseFilterIntra)
? modeInfo.FilterIntraModeInfo.Mode : Av1FilterIntraMode.FilterIntraModes;
? modeInfo.FilterIntraModeInfo.Mode : Av1FilterIntraMode.AllFilterIntraModes;
int angleDelta = modeInfo.AngleDelta[Math.Min(1, (int)plane)];
@ -584,7 +584,7 @@ internal class Av1PredictionDecoder
bool needAbove = (need & Av1NeighborNeed.Above) == Av1NeighborNeed.Above;
bool needAboveLeft = (need & Av1NeighborNeed.AboveLeft) == Av1NeighborNeed.AboveLeft;
int angle = 0;
bool useFilterIntra = filterIntraMode != Av1FilterIntraMode.FilterIntraModes;
bool useFilterIntra = filterIntraMode != Av1FilterIntraMode.AllFilterIntraModes;
if (isDirectionalMode)
{

24
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1BlockGeometry.cs

@ -0,0 +1,24 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
internal partial class Av1TileWriter
{
internal class Av1BlockGeometry
{
public Av1BlockSize BlockSize { get; internal set; }
public Point Origin { get; internal set; }
public bool HasUv { get; internal set; }
public int BlockWidth { get; internal set; }
public int BlockHeight { get; internal set; }
public required Av1TransformSize[] TransformSize { get; internal set; }
}
}

30
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1BlockModeInfoEncoder.cs

@ -0,0 +1,30 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
internal partial class Av1TileWriter
{
internal class Av1BlockModeInfoEncoder
{
public Av1BlockSize BlockSize { get; }
public Av1PredictionMode PredictionMode { get; }
public Av1PartitionType PartitionType { get; }
public Av1PredictionMode UvPredictionMode { get; }
public bool Skip { get; } = true;
public bool SkipMode { get; } = true;
public bool UseIntraBlockCopy { get; } = true;
public int SegmentId { get; }
public int TransformDepth { get; internal set; }
}
}

8
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1BlockStruct.cs

@ -0,0 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
internal class Av1BlockStruct
{
}

22
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1Common.cs

@ -0,0 +1,22 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
internal partial class Av1TileWriter
{
internal class Av1Common
{
public int ModeInfoRowCount { get; internal set; }
public int ModeInfoColumnCount { get; internal set; }
public int ModeInfoStride { get; internal set; }
public required ObuFrameSize FrameSize { get; internal set; }
public required ObuTileGroupHeader TilesInfo { get; internal set; }
}
}

14
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ComponentType.cs

@ -0,0 +1,14 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
internal enum Av1ComponentType
{
Luminance = 0, // luma
Chroma = 1, // chroma (Cb+Cr)
ChromaCb = 2, // chroma Cb
ChromaCr = 3, // chroma Cr
All = 4, // Y+Cb+Cr
None = 15
}

190
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1DefaultDistributions.cs

@ -2006,6 +2006,196 @@ internal static class Av1DefaultDistributions
new(14738, 21678, 25779, 27901, 29024, 30302, 30980, 31843, 32144, 32413, 32520, 32594, 32622, 32656, 32660)
];
public static Av1Distribution[][][] IntraExtendedTransform =>
[
[
[
new(0),
new(0),
new(0),
new(0),
new(0),
new(0),
new(0),
new(0),
new(0),
new(0),
new(0),
new(0),
new(0)
],
[
new(0),
new(0),
new(0),
new(0),
new(0),
new(0),
new(0),
new(0),
new(0),
new(0),
new(0),
new(0),
new(0)
],
[
new(0),
new(0),
new(0),
new(0),
new(0),
new(0),
new(0),
new(0),
new(0),
new(0),
new(0),
new(0),
new(0)
],
[
new(0),
new(0),
new(0),
new(0),
new(0),
new(0),
new(0),
new(0),
new(0),
new(0),
new(0),
new(0),
new(0)
],
],
[
[
new(1535, 8035, 9461, 12751, 23467, 27825),
new(564, 3335, 9709, 10870, 18143, 28094),
new(672, 3247, 3676, 11982, 19415, 23127),
new(5279, 13885, 15487, 18044, 23527, 30252),
new(4423, 6074, 7985, 10416, 25693, 29298),
new(1486, 4241, 9460, 10662, 16456, 27694),
new(439, 2838, 3522, 6737, 18058, 23754),
new(1190, 4233, 4855, 11670, 20281, 24377),
new(1045, 4312, 8647, 10159, 18644, 29335),
new(202, 3734, 4747, 7298, 17127, 24016),
new(447, 4312, 6819, 8884, 16010, 23858),
new(277, 4369, 5255, 8905, 16465, 22271),
new(3409, 5436, 10599, 15599, 19687, 24040)
],
[
new(1870, 13742, 14530, 16498, 23770, 27698),
new(326, 8796, 14632, 15079, 19272, 27486),
new(484, 7576, 7712, 14443, 19159, 22591),
new(1126, 15340, 15895, 17023, 20896, 30279),
new(655, 4854, 5249, 5913, 22099, 27138),
new(1299, 6458, 8885, 9290, 14851, 25497),
new(311, 5295, 5552, 6885, 16107, 22672),
new(883, 8059, 8270, 11258, 17289, 21549),
new(741, 7580, 9318, 10345, 16688, 29046),
new(110, 7406, 7915, 9195, 16041, 23329),
new(363, 7974, 9357, 10673, 15629, 24474),
new(153, 7647, 8112, 9936, 15307, 19996),
new(3511, 6332, 11165, 15335, 19323, 23594)
],
[
new(4681, 9362, 14043, 18725, 23406, 28087),
new(4681, 9362, 14043, 18725, 23406, 28087),
new(4681, 9362, 14043, 18725, 23406, 28087),
new(4681, 9362, 14043, 18725, 23406, 28087),
new(4681, 9362, 14043, 18725, 23406, 28087),
new(4681, 9362, 14043, 18725, 23406, 28087),
new(4681, 9362, 14043, 18725, 23406, 28087),
new(4681, 9362, 14043, 18725, 23406, 28087),
new(4681, 9362, 14043, 18725, 23406, 28087),
new(4681, 9362, 14043, 18725, 23406, 28087),
new(4681, 9362, 14043, 18725, 23406, 28087),
new(4681, 9362, 14043, 18725, 23406, 28087),
new(4681, 9362, 14043, 18725, 23406, 28087)
],
[
new(4681, 9362, 14043, 18725, 23406, 28087),
new(4681, 9362, 14043, 18725, 23406, 28087),
new(4681, 9362, 14043, 18725, 23406, 28087),
new(4681, 9362, 14043, 18725, 23406, 28087),
new(4681, 9362, 14043, 18725, 23406, 28087),
new(4681, 9362, 14043, 18725, 23406, 28087),
new(4681, 9362, 14043, 18725, 23406, 28087),
new(4681, 9362, 14043, 18725, 23406, 28087),
new(4681, 9362, 14043, 18725, 23406, 28087),
new(4681, 9362, 14043, 18725, 23406, 28087),
new(4681, 9362, 14043, 18725, 23406, 28087),
new(4681, 9362, 14043, 18725, 23406, 28087),
new(4681, 9362, 14043, 18725, 23406, 28087),
],
],
[
[
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214)
],
[
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214)
],
[
new(1127, 12814, 22772, 27483),
new(145, 6761, 11980, 26667),
new(362, 5887, 11678, 16725),
new(385, 15213, 18587, 30693),
new(25, 2914, 23134, 27903),
new(60, 4470, 11749, 23991),
new(37, 3332, 14511, 21448),
new(157, 6320, 13036, 17439),
new(119, 6719, 12906, 29396),
new(47, 5537, 12576, 21499),
new(269, 6076, 11258, 23115),
new(83, 5615, 12001, 17228),
new(1968, 5556, 12023, 18547)
],
[
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214),
new(6554, 13107, 19661, 26214)
]
],
];
public static Av1Distribution[][][] GetEndOfBlockFlag(int baseQIndex)
{
int qContext = GetQContext(baseQIndex);

16
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderBlockStruct.cs

@ -0,0 +1,16 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
internal partial class Av1TileWriter
{
internal class Av1EncoderBlockStruct
{
public required Av1MacroBlockD av1xd { get; internal set; }
public required int[] PaletteSize { get; internal set; }
public int QIndex { get; internal set; }
}
}

14
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EntropyCodingContext.cs

@ -0,0 +1,14 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
internal partial class Av1TileWriter
{
internal class Av1EntropyCodingContext
{
public required Av1MacroBlockModeInfo MacroBlockModeInfo { get; internal set; }
public Point SuperblockOrigin { get; internal set; }
}
}

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

@ -10,5 +10,5 @@ internal enum Av1FilterIntraMode
Horizontal,
Directional157,
Paeth,
FilterIntraModes,
AllFilterIntraModes,
}

15
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FilterIntraModeExtensions.cs

@ -0,0 +1,15 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
internal static class Av1FilterIntraModeExtensions
{
private static readonly Av1PredictionMode[] IntraDirection =
[Av1PredictionMode.DC, Av1PredictionMode.Vertical, Av1PredictionMode.Horizontal, Av1PredictionMode.Directional157Degrees, Av1PredictionMode.DC];
public static Av1PredictionMode ToIntraDirection(this Av1FilterIntraMode mode)
=> IntraDirection[(int)mode];
}

22
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1MacroBlockD.cs

@ -0,0 +1,22 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
internal partial class Av1TileWriter
{
internal class Av1MacroBlockD
{
public required Av1BlockModeInfo ModeInfo { get; internal set; }
public required Av1TileInfo Tile { get; internal set; }
public bool IsUpAvailable { get; }
public bool IsLeftAvailable { get; }
public Av1MacroBlockModeInfo? AboveMacroBlock { get; internal set; }
public Av1MacroBlockModeInfo? LeftMacroBlock { get; internal set; }
}
}

16
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1MacroBlockModeInfo.cs

@ -0,0 +1,16 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
internal partial class Av1TileWriter
{
internal class Av1MacroBlockModeInfo
{
public required Av1BlockModeInfoEncoder Block { get; internal set; }
public required Av1PaletteLumaModeInfo Palette { get; internal set; }
public int CdefStrength { get; internal set; }
}
}

11
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1NeighborArrayUnit.cs

@ -0,0 +1,11 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
internal partial class Av1TileWriter
{
internal class Av1NeighborArrayUnit
{
}
}

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

@ -282,7 +282,7 @@ internal static class Av1NzMap
NzMapContextOffset64x32, // TX_64x16
];
public static int GetNzMagnitude(Span<int> levels, int bwl, Av1TransformClass transformClass)
public static int GetNzMagnitude(ReadOnlySpan<byte> levels, int bwl, Av1TransformClass transformClass)
{
int mag;

11
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PaletteLumaModeInfo.cs

@ -0,0 +1,11 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
internal partial class Av1TileWriter
{
internal class Av1PaletteLumaModeInfo
{
}
}

22
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PictureControlSet.cs

@ -0,0 +1,22 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
internal partial class Av1TileWriter
{
internal class Av1PictureControlSet
{
public required Av1NeighborArrayUnit[] luma_dc_sign_level_coeff_na { get; internal set; }
public required Av1NeighborArrayUnit[] cr_dc_sign_level_coeff_na { get; internal set; }
public required Av1NeighborArrayUnit[] cb_dc_sign_level_coeff_na { get; internal set; }
public required Av1NeighborArrayUnit[] txfm_context_array { get; internal set; }
public required Av1SequenceControlSet Sequence { get; internal set; }
public required Av1PictureParentControlSet Parent { get; internal set; }
}
}

20
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PictureParentControlSet.cs

@ -0,0 +1,20 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
internal partial class Av1TileWriter
{
internal class Av1PictureParentControlSet
{
public required Av1Common Common { get; internal set; }
public required ObuFrameHeader FrameHeader { get; internal set; }
public required int[] PreviousQIndex { get; internal set; }
public int PaletteLevel { get; internal set; }
}
}

14
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SequenceControlSet.cs

@ -0,0 +1,14 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
internal partial class Av1TileWriter
{
internal class Av1SequenceControlSet
{
public required ObuSequenceHeader SequenceHeader { get; internal set; }
}
}

11
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1Superblock.cs

@ -0,0 +1,11 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
internal partial class Av1TileWriter
{
internal class Av1Superblock
{
}
}

44
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SymbolDecoder.cs

@ -10,8 +10,8 @@ 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];
private static readonly int[] EndOfBlockOffsetBits = [0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
private static readonly int[] EndOfBlockGroupStart = [0, 1, 2, 3, 5, 9, 17, 33, 65, 129, 257, 513];
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;
@ -233,7 +233,7 @@ internal ref struct Av1SymbolDecoder
return RecordEndOfBlockPosition(endOfBlockPoint, endOfBlockExtra);
}
public void ReadCoefficientsEndOfBlock(Av1TransformClass transformClass, int endOfBlock, int height, ReadOnlySpan<short> scan, int bwl, Span<int> levels, Av1TransformSize transformSizeContext, Av1PlaneType planeType)
public void ReadCoefficientsEndOfBlock(Av1TransformClass transformClass, int endOfBlock, int height, ReadOnlySpan<short> scan, int bwl, Span<byte> levels, Av1TransformSize transformSizeContext, Av1PlaneType planeType)
{
int i = endOfBlock - 1;
int pos = scan[i];
@ -253,10 +253,10 @@ internal ref struct Av1SymbolDecoder
}
}
levels[GetPaddedIndex(pos, bwl)] = level;
levels[GetPaddedIndex(pos, bwl)] = (byte)level;
}
public void ReadCoefficientsReverse2d(Av1TransformSize transformSize, int startScanIndex, int endScanIndex, ReadOnlySpan<short> scan, int bwl, Span<int> levels, Av1TransformSize transformSizeContext, Av1PlaneType planeType)
public void ReadCoefficientsReverse2d(Av1TransformSize transformSize, int startScanIndex, int endScanIndex, ReadOnlySpan<short> scan, int bwl, Span<byte> levels, Av1TransformSize transformSizeContext, Av1PlaneType planeType)
{
for (int c = endScanIndex; c >= startScanIndex; --c)
{
@ -277,11 +277,11 @@ internal ref struct Av1SymbolDecoder
}
}
levels[GetPaddedIndex(pos, bwl)] = level;
levels[GetPaddedIndex(pos, bwl)] = (byte)level;
}
}
public void ReadCoefficientsReverse(Av1TransformSize transformSize, Av1TransformClass transformClass, int startScanIndex, int endScanIndex, ReadOnlySpan<short> scan, int bwl, Span<int> levels, Av1TransformSize transformSizeContext, Av1PlaneType planeType)
public void ReadCoefficientsReverse(Av1TransformSize transformSize, Av1TransformClass transformClass, int startScanIndex, int endScanIndex, ReadOnlySpan<short> scan, int bwl, Span<byte> levels, Av1TransformSize transformSizeContext, Av1PlaneType planeType)
{
for (int c = endScanIndex; c >= startScanIndex; --c)
{
@ -302,11 +302,11 @@ internal ref struct Av1SymbolDecoder
}
}
levels[GetPaddedIndex(pos, bwl)] = level;
levels[GetPaddedIndex(pos, bwl)] = (byte)level;
}
}
public int ReadCoefficientsDc(Span<int> coefficientBuffer, int endOfBlock, ReadOnlySpan<short> scan, int bwl, Span<int> levels, int dcSignContext, Av1PlaneType planeType)
public int ReadCoefficientsDc(Span<int> coefficientBuffer, int endOfBlock, ReadOnlySpan<short> scan, int bwl, Span<byte> levels, int dcSignContext, Av1PlaneType planeType)
{
int maxScanLine = 0;
int culLevel = 0;
@ -439,7 +439,7 @@ internal ref struct Av1SymbolDecoder
return 3;
}
private static int GetBaseRangeContext2d(Span<int> levels, int c, int bwl)
private static int GetBaseRangeContext2d(Span<byte> levels, int c, int bwl)
{
DebugGuard.MustBeGreaterThan(c, 0, nameof(c));
int row = c >> bwl;
@ -447,9 +447,9 @@ internal ref struct Av1SymbolDecoder
int stride = (1 << bwl) + Av1Constants.TransformPadHorizontal;
int pos = (row * stride) + col;
int mag =
Math.Min(levels[pos + 1], Av1Constants.MaxBaseRange) +
Math.Min(levels[pos + stride], Av1Constants.MaxBaseRange) +
Math.Min(levels[pos + 1 + stride], Av1Constants.MaxBaseRange);
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)
{
@ -459,22 +459,22 @@ internal ref struct Av1SymbolDecoder
return mag + 14;
}
private static int GetLowerLevelsContext2d(Span<int> levels, int pos, int bwl, Av1TransformSize transformSize)
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(levels[1], 3); // { 0, 1 }
mag += Math.Min(levels[(1 << bwl) + Av1Constants.TransformPadHorizontal], 3); // { 1, 0 }
mag += Math.Min(levels[(1 << bwl) + Av1Constants.TransformPadHorizontal + 1], 3); // { 1, 1 }
mag += Math.Min(levels[2], 3); // { 0, 2 }
mag += Math.Min(levels[(2 << bwl) + (2 << Av1Constants.TransformPadHorizontalLog2)], 3); // { 2, 0 }
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<int> levels, int c, int bwl, Av1TransformClass transformClass)
private static int GetBaseRangeContext(Span<byte> levels, int c, int bwl, Av1TransformClass transformClass)
{
int row = c >> bwl;
int col = c - (row << bwl);
@ -533,13 +533,13 @@ internal ref struct Av1SymbolDecoder
return mag + 14;
}
private static int GetLowerLevelsContext(Span<int> levels, int pos, int bwl, Av1TransformSize transformSize, Av1TransformClass transformClass)
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);
}
private static int GetPaddedIndex(int scanIndex, int bwl)
public static int GetPaddedIndex(int scanIndex, int bwl)
=> scanIndex + ((scanIndex >> bwl) << Av1Constants.TransformPadHorizontalLog2);
private int ReadGolomb()

535
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SymbolEncoder.cs

@ -1,20 +1,90 @@
// 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.Prediction;
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
internal class Av1SymbolEncoder : IDisposable
{
private static readonly int[][] ExtendedTransformIndices = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 3, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 5, 6, 4, 0, 0, 0, 0, 0, 0, 2, 3, 0, 0, 0, 0],
[3, 4, 5, 8, 6, 7, 9, 10, 11, 0, 1, 2, 0, 0, 0, 0],
[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;
private readonly Av1Distribution[][][] endOfBlockFlag;
private readonly Av1Distribution[][][] coefficientsBaseRange;
private readonly Av1Distribution[][][] coefficientsBase;
private readonly Av1Distribution[][][] coefficientsBaseEndOfBlock;
private readonly Av1Distribution[][] dcSign;
private readonly Av1Distribution[][][] endOfBlockExtra;
private readonly Av1Distribution[][][] intraExtendedTransform = Av1DefaultDistributions.IntraExtendedTransform;
private bool isDisposed;
private Av1SymbolWriter writer;
public Av1SymbolEncoder(Configuration configuration, int initialSize)
=> this.writer = new(configuration, initialSize);
public Av1SymbolEncoder(Configuration configuration, int initialSize, int qIndex)
{
this.endOfBlockFlag = Av1DefaultDistributions.GetEndOfBlockFlag(qIndex);
this.coefficientsBaseRange = Av1DefaultDistributions.GetCoefficientsBaseRange(qIndex);
this.coefficientsBase = Av1DefaultDistributions.GetCoefficientsBase(qIndex);
this.coefficientsBaseEndOfBlock = Av1DefaultDistributions.GetBaseEndOfBlock(qIndex);
this.dcSign = Av1DefaultDistributions.GetDcSign(qIndex);
this.endOfBlockExtra = Av1DefaultDistributions.GetEndOfBlockExtra(qIndex);
this.writer = new(configuration, initialSize);
}
public void WriteUseIntraBlockCopy(bool value)
{
@ -44,6 +114,332 @@ internal class Av1SymbolEncoder : IDisposable
w.WriteSymbol(value, distribution);
}
/// <summary>
/// SVT: av1_write_coeffs_txb_1d
/// </summary>
public int WriteCoefficients(
Av1TransformSize transformSize,
Av1TransformType transformType,
uint txb_index, // TODO: Doesn't seem to be used, remove.
Av1PredictionMode intraLumaDir,
Span<int> coeff_buffer_ptr,
Av1ComponentType componentType,
short txb_skip_ctx,
short dc_sign_ctx,
ushort eob,
bool useReducedTransformSet,
int baseQIndex,
Av1FilterIntraMode filterIntraMode)
{
int c;
int width = transformSize.GetWidth();
int height = transformSize.GetHeight();
Av1TransformClass transformClass = transformType.ToClass();
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);
ref Av1SymbolWriter w = ref this.writer;
Span<byte> levels_buf = new byte[Av1Constants.TransformPad2d];
Span<byte> levels = SetLevels(levels_buf, width);
Span<sbyte> coeff_contexts = new sbyte[Av1Constants.MaxTransformSize * Av1Constants.MaxTransformSize];
Guard.MustBeLessThan((int)transformSizeContext, (int)Av1TransformSize.AllSizes, nameof(transformSizeContext));
bool hasEndOfBlock = eob != 0;
this.WriteSkip(!hasEndOfBlock, txb_skip_ctx);
if (eob == 0)
{
return 0;
}
InitializeLevels(coeff_buffer_ptr, width, height, levels);
if (componentType == Av1ComponentType.Luminance)
{
this.WriteTransformType(transformType, transformSize, useReducedTransformSet, baseQIndex, filterIntraMode, intraLumaDir);
}
short endOfBlockPosition = GetEndOfBlockPosition(eob, out int eob_extra);
this.WriteEndOfBlockFlag(componentType, transformClass, transformSize, endOfBlockPosition);
int eob_offset_bits = Av1SymbolDecoder.EndOfBlockOffsetBits[endOfBlockPosition];
if (eob_offset_bits > 0)
{
int eob_shift = eob_offset_bits - 1;
int bit = Math.Max(1, eob_extra & (1 << eob_shift));
w.WriteSymbol(bit, this.endOfBlockExtra[(int)transformSizeContext][(int)componentType][endOfBlockPosition]);
for (int i = 1; i < eob_offset_bits; i++)
{
eob_shift = eob_offset_bits - 1 - i;
bit = Math.Max(1, eob_extra & (1 << eob_shift));
w.WriteLiteral((uint)bit, 1);
}
}
GetNzMapContexts(levels, scan, eob, transformSize, transformClass, coeff_contexts);
int limitedTransformSizeContext = Math.Min((int)transformSizeContext, (int)Av1TransformSize.Size32x32);
for (c = eob - 1; c >= 0; --c)
{
short pos = scan[c];
int v = coeff_buffer_ptr[pos];
short coeff_ctx = coeff_contexts[pos];
int level = Math.Abs(v);
if (c == eob - 1)
{
w.WriteSymbol(Math.Min(level, 3) - 1, this.coefficientsBaseEndOfBlock[(int)transformSizeContext][(int)componentType][coeff_ctx]);
}
else
{
w.WriteSymbol(Math.Min(level, 3), this.coefficientsBase[(int)transformSizeContext][(int)componentType][coeff_ctx]);
}
if (level > Av1Constants.BaseLevelsCount)
{
// level is above 1.
int baseRange = level - 1 - Av1Constants.BaseLevelsCount;
int baseRangeContext = GetBaseRangeContext(levels, pos, bwl, transformClass);
for (int idx = 0; idx < Av1Constants.CoefficientBaseRange; idx += Av1Constants.BaseRangeSizeMinus1)
{
int k = Math.Min(baseRange - idx, Av1Constants.BaseRangeSizeMinus1);
w.WriteSymbol(k, this.coefficientsBaseRange[limitedTransformSizeContext][(int)componentType][baseRangeContext]);
if (k < Av1Constants.BaseRangeSizeMinus1)
{
break;
}
}
}
}
// Loop to code all signs in the transform block,
// starting with the sign of DC (if applicable)
int cul_level = 0;
for (c = 0; c < eob; ++c)
{
short pos = scan[c];
int v = coeff_buffer_ptr[pos];
int level = Math.Abs(v);
cul_level += level;
uint sign = (v < 0) ? 1u : 0u;
if (level > 0)
{
if (c == 0)
{
w.WriteSymbol((int)sign, this.dcSign[(int)componentType][dc_sign_ctx]);
}
else
{
w.WriteLiteral(sign, 1);
}
if (level > Av1Constants.CoefficientBaseRange + Av1Constants.BaseLevelsCount)
{
this.WriteGolomb(level - Av1Constants.CoefficientBaseRange - 1 - Av1Constants.BaseLevelsCount);
}
}
}
cul_level = Math.Min(Av1Constants.CoefficientContextMask, cul_level);
// DC value
SetDcSign(ref cul_level, coeff_buffer_ptr[0]);
return cul_level;
}
/// <summary>
/// SVT: set_dc_sign
/// </summary>
private static void SetDcSign(ref int cul_level, int dc_val)
{
if (dc_val < 0)
{
cul_level |= 1 << Av1Constants.CoefficientContextBitCount;
}
else if (dc_val > 0)
{
cul_level += 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 i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
ls = (byte)Av1Math.Clamp(Math.Abs(coefficientBuffer[(i * width) + j]), 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> levels_buf, int width)
=> levels_buf.Slice(Av1Constants.TransformPadTop * (width + Av1Constants.TransformPadHorizontal));
private void WriteSkip(bool hasEndOfBlock, int context)
{
// Has EOB, means we won't skip, negating the logic.
ref Av1SymbolWriter w = ref this.writer;
w.WriteSymbol(hasEndOfBlock ? 0 : 1, this.skip[context]);
}
public IMemoryOwner<byte> Exit()
{
ref Av1SymbolWriter w = ref this.writer;
@ -58,4 +454,137 @@ internal class Av1SymbolEncoder : IDisposable
this.isDisposed = true;
}
}
/// <summary>
/// SVT: write_golomb
/// </summary>
private void WriteGolomb(int level)
{
int x = level + 1;
int i = x;
int length = (int)Av1Math.Log2_32((uint)x) + 1;
Guard.MustBeGreaterThan(length, 0, nameof(length));
ref Av1SymbolWriter w = ref this.writer;
for (i = 0; i < length - 1; ++i)
{
w.WriteLiteral(0, 1);
}
for (int j = length - 1; j >= 0; --j)
{
w.WriteLiteral((uint)((x >> j) & 0x01), 1);
}
}
private void WriteEndOfBlockFlag(Av1ComponentType componentType, Av1TransformClass transformClass, Av1TransformSize transformSize, int endOfBlockPosition)
{
int endOfBlockMultiSize = transformSize.GetLog2Minus4();
int endOfBlockContext = transformClass == Av1TransformClass.Class2D ? 0 : 1;
ref Av1SymbolWriter w = ref this.writer;
w.WriteSymbol(endOfBlockPosition - 1, this.endOfBlockFlag[endOfBlockMultiSize][(int)componentType][endOfBlockContext]);
}
/// <summary>
/// SVT: av1_write_tx_type
/// </summary>
private void WriteTransformType(
Av1TransformType transformType,
Av1TransformSize transformSize,
bool useReducedTransformSet,
int baseQIndex,
Av1FilterIntraMode filterIntraMode,
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))
{
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);
// 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)
{
intra_dir = filterIntraMode.ToIntraDirection();
}
else
{
intra_dir = intraDirection;
}
Guard.MustBeLessThan((int)intra_dir, 13, nameof(intra_dir));
Guard.MustBeLessThan((int)square_tx_size, 4, nameof(square_tx_size));
w.WriteSymbol(
ExtendedTransformIndices[(int)tx_set_type][(int)transformType],
this.intraExtendedTransform[eset][(int)square_tx_size][(int)intra_dir]);
}
}
}
/// <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];
}
}

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

@ -564,8 +564,8 @@ internal class Av1TileReader : IAv1TileReader
Av1PlaneType planeType = (Av1PlaneType)Math.Min(plane, 1);
int culLevel = 0;
int[] levelsBuffer = new int[Av1Constants.TransformPad2d];
Span<int> levels = levelsBuffer.AsSpan()[(Av1Constants.TransformPadTop * (width + Av1Constants.TransformPadHorizontal))..];
byte[] levelsBuffer = new byte[Av1Constants.TransformPad2d];
Span<byte> levels = levelsBuffer.AsSpan()[(Av1Constants.TransformPadTop * (width + Av1Constants.TransformPadHorizontal))..];
bool allZero = reader.ReadTransformBlockSkip(transformSizeContext, transformBlockContext.SkipContext);
int bwl = transformSize.GetBlockWidthLog2();
@ -590,7 +590,7 @@ internal class Av1TileReader : IAv1TileReader
endOfBlock = reader.ReadEndOfBlockPosition(transformSize, transformClass, transformSizeContext, planeType);
if (endOfBlock > 1)
{
Array.Fill(levelsBuffer, 0, 0, ((width + Av1Constants.TransformPadHorizontal) * (height + Av1Constants.TransformPadVertical)) + Av1Constants.TransformPadEnd);
Array.Fill(levelsBuffer, (byte)0, 0, ((width + Av1Constants.TransformPadHorizontal) * (height + Av1Constants.TransformPadVertical)) + Av1Constants.TransformPadEnd);
}
reader.ReadCoefficientsEndOfBlock(transformClass, endOfBlock, height, scan, bwl, levels, transformSizeContext, planeType);

12
tests/ImageSharp.Tests/Formats/Heif/Av1/SymbolTests.cs

@ -2,17 +2,17 @@
// Licensed under the Six Labors Split License.
using System.Buffers;
using System.Reflection;
using SixLabors.ImageSharp.Formats.Heif.Av1;
using SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
using SixLabors.ImageSharp.Memory;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1;
[Trait("Format", "Avif")]
public class SymbolTests
{
private const int baseQIndex = 23;
[Fact]
public void ReadRandomLiteral()
{
@ -194,7 +194,7 @@ public class SymbolTests
// Assign
int ctx = 7;
Configuration configuration = Configuration.Default;
Av1SymbolEncoder encoder = new(configuration, 100 / 8);
Av1SymbolEncoder encoder = new(configuration, 100 / 8, baseQIndex);
Av1PartitionType[] values = [
Av1PartitionType.Split, Av1PartitionType.Split, Av1PartitionType.Split, Av1PartitionType.None,
Av1PartitionType.Split, Av1PartitionType.Split, Av1PartitionType.None, Av1PartitionType.None];
@ -231,7 +231,7 @@ public class SymbolTests
{
// Assign
Configuration configuration = Configuration.Default;
Av1SymbolEncoder encoder = new(configuration, 100 / 8);
Av1SymbolEncoder encoder = new(configuration, 100 / 8, baseQIndex);
Av1PartitionType[] values = [
Av1PartitionType.Split, Av1PartitionType.Split, Av1PartitionType.Split, Av1PartitionType.Horizontal,
Av1PartitionType.Split, Av1PartitionType.Split, Av1PartitionType.Horizontal, Av1PartitionType.Horizontal];
@ -268,7 +268,7 @@ public class SymbolTests
{
// Assign
Configuration configuration = Configuration.Default;
Av1SymbolEncoder encoder = new(configuration, 100 / 8);
Av1SymbolEncoder encoder = new(configuration, 100 / 8, baseQIndex);
Av1PartitionType[] values = [
Av1PartitionType.Split, Av1PartitionType.Split, Av1PartitionType.Split, Av1PartitionType.Vertical,
Av1PartitionType.Split, Av1PartitionType.Split, Av1PartitionType.Vertical, Av1PartitionType.Vertical];
@ -299,7 +299,7 @@ public class SymbolTests
// Assign
bool[] values = [true, true, false, true, false, false, false];
Configuration configuration = Configuration.Default;
Av1SymbolEncoder encoder = new(configuration, 100 / 8);
Av1SymbolEncoder encoder = new(configuration, 100 / 8, baseQIndex);
bool[] actuals = new bool[values.Length];
// Act

Loading…
Cancel
Save