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 TransformPadTop = 2;
public const int TransformPadBottom = 4;
public const int BaseRangeSizeMinus1 = 3; public const int BaseRangeSizeMinus1 = 3;
public const int MaxBaseRange = 15; public const int MaxBaseRange = 15;
@ -182,4 +184,7 @@ internal static class Av1Constants
public const int MaxTransformStageNumber = 12; public const int MaxTransformStageNumber = 12;
public const int PartitionProbabilitySet = 4; 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. // Licensed under the Six Labors Split License.
using System; using System;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
namespace SixLabors.ImageSharp.Formats.Heif.Av1; namespace SixLabors.ImageSharp.Formats.Heif.Av1;
@ -50,6 +52,29 @@ internal static class Av1Math
return result; 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) public static uint FloorLog2(uint value)
{ {
uint s = 0; 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) 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)]; int angleDelta = modeInfo.AngleDelta[Math.Min(1, (int)plane)];
@ -584,7 +584,7 @@ internal class Av1PredictionDecoder
bool needAbove = (need & Av1NeighborNeed.Above) == Av1NeighborNeed.Above; bool needAbove = (need & Av1NeighborNeed.Above) == Av1NeighborNeed.Above;
bool needAboveLeft = (need & Av1NeighborNeed.AboveLeft) == Av1NeighborNeed.AboveLeft; bool needAboveLeft = (need & Av1NeighborNeed.AboveLeft) == Av1NeighborNeed.AboveLeft;
int angle = 0; int angle = 0;
bool useFilterIntra = filterIntraMode != Av1FilterIntraMode.FilterIntraModes; bool useFilterIntra = filterIntraMode != Av1FilterIntraMode.AllFilterIntraModes;
if (isDirectionalMode) 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) 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) public static Av1Distribution[][][] GetEndOfBlockFlag(int baseQIndex)
{ {
int qContext = GetQContext(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, Horizontal,
Directional157, Directional157,
Paeth, 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 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; 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[] 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[] 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]; public 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[] EndOfBlockGroupStart = [0, 1, 2, 3, 5, 9, 17, 33, 65, 129, 257, 513];
private readonly Av1Distribution tileIntraBlockCopy = Av1DefaultDistributions.IntraBlockCopy; private readonly Av1Distribution tileIntraBlockCopy = Av1DefaultDistributions.IntraBlockCopy;
private readonly Av1Distribution[] tilePartitionTypes = Av1DefaultDistributions.PartitionTypes; private readonly Av1Distribution[] tilePartitionTypes = Av1DefaultDistributions.PartitionTypes;
@ -233,7 +233,7 @@ internal ref struct Av1SymbolDecoder
return RecordEndOfBlockPosition(endOfBlockPoint, endOfBlockExtra); 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 i = endOfBlock - 1;
int pos = scan[i]; 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) 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) 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 maxScanLine = 0;
int culLevel = 0; int culLevel = 0;
@ -439,7 +439,7 @@ internal ref struct Av1SymbolDecoder
return 3; 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)); DebugGuard.MustBeGreaterThan(c, 0, nameof(c));
int row = c >> bwl; int row = c >> bwl;
@ -447,9 +447,9 @@ internal ref struct Av1SymbolDecoder
int stride = (1 << bwl) + Av1Constants.TransformPadHorizontal; int stride = (1 << bwl) + Av1Constants.TransformPadHorizontal;
int pos = (row * stride) + col; int pos = (row * stride) + col;
int mag = int mag =
Math.Min(levels[pos + 1], Av1Constants.MaxBaseRange) + Math.Min((int)levels[pos + 1], Av1Constants.MaxBaseRange) +
Math.Min(levels[pos + stride], Av1Constants.MaxBaseRange) + Math.Min((int)levels[pos + stride], Av1Constants.MaxBaseRange) +
Math.Min(levels[pos + 1 + stride], Av1Constants.MaxBaseRange); Math.Min((int)levels[pos + 1 + stride], Av1Constants.MaxBaseRange);
mag = Math.Min((mag + 1) >> 1, 6); mag = Math.Min((mag + 1) >> 1, 6);
if ((row | col) < 2) if ((row | col) < 2)
{ {
@ -459,22 +459,22 @@ internal ref struct Av1SymbolDecoder
return mag + 14; 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)); DebugGuard.MustBeGreaterThan(pos, 0, nameof(pos));
int mag; int mag;
levels = levels[GetPaddedIndex(pos, bwl)..]; levels = levels[GetPaddedIndex(pos, bwl)..];
mag = Math.Min(levels[1], 3); // { 0, 1 } mag = Math.Min((int)levels[1], 3); // { 0, 1 }
mag += Math.Min(levels[(1 << bwl) + Av1Constants.TransformPadHorizontal], 3); // { 1, 0 } mag += Math.Min((int)levels[(1 << bwl) + Av1Constants.TransformPadHorizontal], 3); // { 1, 0 }
mag += Math.Min(levels[(1 << bwl) + Av1Constants.TransformPadHorizontal + 1], 3); // { 1, 1 } mag += Math.Min((int)levels[(1 << bwl) + Av1Constants.TransformPadHorizontal + 1], 3); // { 1, 1 }
mag += Math.Min(levels[2], 3); // { 0, 2 } mag += Math.Min((int)levels[2], 3); // { 0, 2 }
mag += Math.Min(levels[(2 << bwl) + (2 << Av1Constants.TransformPadHorizontalLog2)], 3); // { 2, 0 } mag += Math.Min((int)levels[(2 << bwl) + (2 << Av1Constants.TransformPadHorizontalLog2)], 3); // { 2, 0 }
int ctx = Math.Min((mag + 1) >> 1, 4); int ctx = Math.Min((mag + 1) >> 1, 4);
return ctx + Av1NzMap.GetNzMapContext(transformSize, pos); 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 row = c >> bwl;
int col = c - (row << bwl); int col = c - (row << bwl);
@ -533,13 +533,13 @@ internal ref struct Av1SymbolDecoder
return mag + 14; 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); int stats = Av1NzMap.GetNzMagnitude(levels[GetPaddedIndex(pos, bwl)..], bwl, transformClass);
return Av1NzMap.GetNzMapContextFromStats(stats, pos, bwl, transformSize, 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); => scanIndex + ((scanIndex >> bwl) << Av1Constants.TransformPadHorizontalLog2);
private int ReadGolomb() private int ReadGolomb()

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

@ -1,20 +1,90 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System;
using System.Buffers; 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; namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
internal class Av1SymbolEncoder : IDisposable 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 tileIntraBlockCopy = Av1DefaultDistributions.IntraBlockCopy;
private readonly Av1Distribution[] tilePartitionTypes = Av1DefaultDistributions.PartitionTypes; 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 bool isDisposed;
private Av1SymbolWriter writer; private Av1SymbolWriter writer;
public Av1SymbolEncoder(Configuration configuration, int initialSize) public Av1SymbolEncoder(Configuration configuration, int initialSize, int qIndex)
=> this.writer = new(configuration, initialSize); {
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) public void WriteUseIntraBlockCopy(bool value)
{ {
@ -44,6 +114,332 @@ internal class Av1SymbolEncoder : IDisposable
w.WriteSymbol(value, distribution); 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() public IMemoryOwner<byte> Exit()
{ {
ref Av1SymbolWriter w = ref this.writer; ref Av1SymbolWriter w = ref this.writer;
@ -58,4 +454,137 @@ internal class Av1SymbolEncoder : IDisposable
this.isDisposed = true; 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); Av1PlaneType planeType = (Av1PlaneType)Math.Min(plane, 1);
int culLevel = 0; int culLevel = 0;
int[] levelsBuffer = new int[Av1Constants.TransformPad2d]; byte[] levelsBuffer = new byte[Av1Constants.TransformPad2d];
Span<int> levels = levelsBuffer.AsSpan()[(Av1Constants.TransformPadTop * (width + Av1Constants.TransformPadHorizontal))..]; Span<byte> levels = levelsBuffer.AsSpan()[(Av1Constants.TransformPadTop * (width + Av1Constants.TransformPadHorizontal))..];
bool allZero = reader.ReadTransformBlockSkip(transformSizeContext, transformBlockContext.SkipContext); bool allZero = reader.ReadTransformBlockSkip(transformSizeContext, transformBlockContext.SkipContext);
int bwl = transformSize.GetBlockWidthLog2(); int bwl = transformSize.GetBlockWidthLog2();
@ -590,7 +590,7 @@ internal class Av1TileReader : IAv1TileReader
endOfBlock = reader.ReadEndOfBlockPosition(transformSize, transformClass, transformSizeContext, planeType); endOfBlock = reader.ReadEndOfBlockPosition(transformSize, transformClass, transformSizeContext, planeType);
if (endOfBlock > 1) 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); 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. // Licensed under the Six Labors Split License.
using System.Buffers; using System.Buffers;
using System.Reflection;
using SixLabors.ImageSharp.Formats.Heif.Av1; using SixLabors.ImageSharp.Formats.Heif.Av1;
using SixLabors.ImageSharp.Formats.Heif.Av1.Tiling; using SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1; namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1;
[Trait("Format", "Avif")] [Trait("Format", "Avif")]
public class SymbolTests public class SymbolTests
{ {
private const int baseQIndex = 23;
[Fact] [Fact]
public void ReadRandomLiteral() public void ReadRandomLiteral()
{ {
@ -194,7 +194,7 @@ public class SymbolTests
// Assign // Assign
int ctx = 7; int ctx = 7;
Configuration configuration = Configuration.Default; Configuration configuration = Configuration.Default;
Av1SymbolEncoder encoder = new(configuration, 100 / 8); Av1SymbolEncoder encoder = new(configuration, 100 / 8, baseQIndex);
Av1PartitionType[] values = [ Av1PartitionType[] values = [
Av1PartitionType.Split, Av1PartitionType.Split, Av1PartitionType.Split, Av1PartitionType.None, Av1PartitionType.Split, Av1PartitionType.Split, Av1PartitionType.Split, Av1PartitionType.None,
Av1PartitionType.Split, Av1PartitionType.Split, Av1PartitionType.None, Av1PartitionType.None]; Av1PartitionType.Split, Av1PartitionType.Split, Av1PartitionType.None, Av1PartitionType.None];
@ -231,7 +231,7 @@ public class SymbolTests
{ {
// Assign // Assign
Configuration configuration = Configuration.Default; Configuration configuration = Configuration.Default;
Av1SymbolEncoder encoder = new(configuration, 100 / 8); Av1SymbolEncoder encoder = new(configuration, 100 / 8, baseQIndex);
Av1PartitionType[] values = [ Av1PartitionType[] values = [
Av1PartitionType.Split, Av1PartitionType.Split, Av1PartitionType.Split, Av1PartitionType.Horizontal, Av1PartitionType.Split, Av1PartitionType.Split, Av1PartitionType.Split, Av1PartitionType.Horizontal,
Av1PartitionType.Split, Av1PartitionType.Split, Av1PartitionType.Horizontal, Av1PartitionType.Horizontal]; Av1PartitionType.Split, Av1PartitionType.Split, Av1PartitionType.Horizontal, Av1PartitionType.Horizontal];
@ -268,7 +268,7 @@ public class SymbolTests
{ {
// Assign // Assign
Configuration configuration = Configuration.Default; Configuration configuration = Configuration.Default;
Av1SymbolEncoder encoder = new(configuration, 100 / 8); Av1SymbolEncoder encoder = new(configuration, 100 / 8, baseQIndex);
Av1PartitionType[] values = [ Av1PartitionType[] values = [
Av1PartitionType.Split, Av1PartitionType.Split, Av1PartitionType.Split, Av1PartitionType.Vertical, Av1PartitionType.Split, Av1PartitionType.Split, Av1PartitionType.Split, Av1PartitionType.Vertical,
Av1PartitionType.Split, Av1PartitionType.Split, Av1PartitionType.Vertical, Av1PartitionType.Vertical]; Av1PartitionType.Split, Av1PartitionType.Split, Av1PartitionType.Vertical, Av1PartitionType.Vertical];
@ -299,7 +299,7 @@ public class SymbolTests
// Assign // Assign
bool[] values = [true, true, false, true, false, false, false]; bool[] values = [true, true, false, true, false, false, false];
Configuration configuration = Configuration.Default; Configuration configuration = Configuration.Default;
Av1SymbolEncoder encoder = new(configuration, 100 / 8); Av1SymbolEncoder encoder = new(configuration, 100 / 8, baseQIndex);
bool[] actuals = new bool[values.Length]; bool[] actuals = new bool[values.Length];
// Act // Act

Loading…
Cancel
Save