// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Buffers;
using System.Numerics;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Heif.Av1;
using SixLabors.ImageSharp.Formats.Heif.Av1.Entropy;
using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
using SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline;
using SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
using SixLabors.ImageSharp.Formats.Heif.Av1.Prediction.ChromaFromLuma;
using SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1;
///
/// Verifies live intra superblock mode decisions, traversal, and reconstruction.
///
[Trait("Format", "Avif")]
public class Av1IntraSuperblockEncoderTests
{
///
/// Gets the normative eight-sample weights used to build independent smooth-mode fixtures.
///
private static ReadOnlySpan Smooth8Weights => [255, 197, 146, 105, 73, 50, 37, 32];
[Fact]
public void EncodesClipped128SuperblockInWriterPreorderWithoutAllocation()
{
const int Width = 16;
const int Height = 16;
ObuColorConfig colorConfig = new()
{
IsMonochrome = false,
SubSamplingX = true,
SubSamplingY = true,
BitDepth = Av1BitDepth.EightBit
};
using Av1EncoderFrameBuffer source = new(
Configuration.Default,
Width,
Height,
8,
Av1ColorFormat.Yuv420,
1,
1);
using Av1EncoderFrameBuffer reconstruction = new(
Configuration.Default,
Width,
Height,
8,
Av1ColorFormat.Yuv420,
1,
1);
FillPlane(source.Frame.CodedView.GetPlane(Av1Plane.Y), (byte)128);
FillPlane(source.Frame.CodedView.GetPlane(Av1Plane.U), (byte)128);
FillPlane(source.Frame.CodedView.GetPlane(Av1Plane.V), (byte)128);
ClearPlane(reconstruction.Luma);
ClearPlane(Assert.IsType>(reconstruction.ChromaBlue));
ClearPlane(Assert.IsType>(reconstruction.ChromaRed));
using Av1EncoderModeInfoBuffer modeInfo = new(Configuration.Default, Width, Height, disallow4x4AllFrames: true);
Av1PictureControlSet picture = CreatePicture(modeInfo, colorConfig, use128x128Superblock: true, qIndex: 73);
picture.Sequence.SequenceHeader.EnableFilterIntra = true;
using Av1EncoderCoefficientBuffer coefficients = new(
Configuration.Default,
picture.Sequence.SequenceHeader,
Width,
Height);
using Av1EncoderSuperblockWorkspace superblockWorkspace = new(Configuration.Default);
using Av1EncoderBlockWorkspace blockWorkspace = new(Configuration.Default);
Av1Superblock superblock = new()
{
Workspace = superblockWorkspace,
TileInfo = new Av1TileInfo(0, 0, picture.Parent.FrameHeader),
Index = 0
};
Av1IntraSuperblockEncoder.Encode(
source.Frame,
reconstruction.Frame,
picture,
superblock,
coefficients,
blockWorkspace);
long before = GC.GetAllocatedBytesForCurrentThread();
for (int iteration = 0; iteration < 8; iteration++)
{
Av1IntraSuperblockEncoder.Encode(
source.Frame,
reconstruction.Frame,
picture,
superblock,
coefficients,
blockWorkspace);
}
Assert.Equal(0, GC.GetAllocatedBytesForCurrentThread() - before);
Av1PartitionType[] expectedPartitions =
[
Av1PartitionType.Split,
Av1PartitionType.Split,
Av1PartitionType.Split,
Av1PartitionType.Split,
Av1PartitionType.None,
Av1PartitionType.None,
Av1PartitionType.None,
Av1PartitionType.None
];
for (int index = 0; index < expectedPartitions.Length; index++)
{
Assert.Equal(expectedPartitions[index], (Av1PartitionType)superblock.CodingUnitPartitionTypes[index]);
}
for (int index = 0; index < 4; index++)
{
Assert.True(superblock.FinalBlocks[index].HasChroma);
Assert.Equal(73, superblock.FinalBlocks[index].QuantizationIndex);
Assert.Equal(Av1FilterIntraMode.AllFilterIntraModes, superblock.FinalBlocks[index].FilterIntraMode);
}
Point[] modeInfoPositions = [new(0, 0), new(2, 0), new(0, 2), new(2, 2)];
foreach (Point position in modeInfoPositions)
{
ref Av1MacroBlockModeInfo block = ref picture.GetMacroBlockModeInfo(position);
Assert.Equal(Av1BlockSize.Block8x8, block.Block.BlockSize);
Assert.Equal(Av1TransformSize.Size8x8, block.Block.TransformSize);
Assert.Equal(Av1PredictionMode.DC, block.Block.Mode);
Assert.Equal(Av1ChromaPredictionMode.DC, block.Block.UvMode);
Assert.True(block.Block.Skip);
}
Span lumaStates = coefficients.GetTransformBlockSpan(0, Av1Plane.Y);
Span blueStates = coefficients.GetTransformBlockSpan(0, Av1Plane.U);
Span redStates = coefficients.GetTransformBlockSpan(0, Av1Plane.V);
int[] lumaStateIndices = [0, 4, 8, 12];
for (int index = 0; index < 4; index++)
{
Assert.Equal((ushort)0, lumaStates[lumaStateIndices[index]].EndOfBlock);
Assert.Equal(Av1TransformType.DctDct, lumaStates[lumaStateIndices[index]].TransformType);
Assert.Equal((ushort)0, blueStates[index].EndOfBlock);
Assert.Equal((ushort)0, redStates[index].EndOfBlock);
}
AssertContainsNonzero(reconstruction.Frame.CodedView.GetPlane(Av1Plane.Y));
AssertContainsNonzero(reconstruction.Frame.CodedView.GetPlane(Av1Plane.U));
AssertContainsNonzero(reconstruction.Frame.CodedView.GetPlane(Av1Plane.V));
// Edge contexts cover the complete 128x128 superblock because partition updates retain the coded geometry
// even when most of the superblock lies beyond this deliberately clipped frame.
const int ContextUnitCount = 128 >> Av1Constants.ModeInfoSizeLog2;
using Av1NeighborArrayUnit partitions = new(
Configuration.Default,
ContextUnitCount,
ContextUnitCount)
{
GranularityNormalLog2 = Av1Constants.ModeInfoSizeLog2
};
using Av1NeighborArrayUnit lumaContexts = new(
Configuration.Default,
ContextUnitCount,
ContextUnitCount)
{
GranularityNormalLog2 = Av1Constants.ModeInfoSizeLog2
};
using Av1NeighborArrayUnit blueContexts = new(
Configuration.Default,
ContextUnitCount,
ContextUnitCount)
{
GranularityNormalLog2 = Av1Constants.ModeInfoSizeLog2
};
using Av1NeighborArrayUnit redContexts = new(
Configuration.Default,
ContextUnitCount,
ContextUnitCount)
{
GranularityNormalLog2 = Av1Constants.ModeInfoSizeLog2
};
using Av1NeighborArrayUnit transformContexts = new(
Configuration.Default,
ContextUnitCount,
ContextUnitCount)
{
GranularityNormalLog2 = Av1Constants.ModeInfoSizeLog2
};
picture.PartitionContexts = [partitions];
picture.LuminanceDcSignLevelCoefficientNeighbors = [lumaContexts];
picture.CbDcSignLevelCoefficientNeighbors = [blueContexts];
picture.CrDcSignLevelCoefficientNeighbors = [redContexts];
picture.TransformFunctionContexts = [transformContexts];
Av1TileWriter.Av1EntropyCodingContext entropyContext = new()
{
MacroBlock = new Av1MacroBlockD { Tile = superblock.TileInfo },
MacroBlockModeInfo = picture.GetMacroBlockModeInfo(default),
SuperblockOrigin = default
};
using Av1SymbolEncoder writer = new(Configuration.Default, 512, 73);
Av1TileWriter.WriteSuperblock(
picture,
entropyContext,
writer,
superblock,
coefficients,
tileIndex: 0);
using IMemoryOwner encoded = writer.Exit();
// The writer must consume exactly the transform areas populated above, proving both traversals stay synchronized.
Assert.Equal(256, entropyContext.CodedAreaSuperblock);
Assert.Equal(64, entropyContext.CodedAreaSuperblockUv);
Assert.NotEqual(0, encoded.GetSpan().Length);
using Av1EncoderFrameBuffer tileReconstruction = new(
Configuration.Default,
Width,
Height,
8,
Av1ColorFormat.Yuv420,
1,
1);
ClearPlane(tileReconstruction.Luma);
ClearPlane(Assert.IsType>(tileReconstruction.ChromaBlue));
ClearPlane(Assert.IsType>(tileReconstruction.ChromaRed));
using Av1EncoderPictureBuffer tilePicture = new(
Configuration.Default,
picture.Sequence.SequenceHeader,
picture.Parent.FrameHeader,
Width,
Height);
using Av1EncoderCoefficientBuffer tileCoefficients = new(
Configuration.Default,
picture.Sequence.SequenceHeader,
Width,
Height);
using Av1EncoderSuperblockWorkspace tileSuperblockWorkspace = new(Configuration.Default);
using Av1EncoderBlockWorkspace tileBlockWorkspace = new(Configuration.Default);
using Av1IntraTileWriter tileWriter = new(
Configuration.Default,
source.Frame,
tileReconstruction.Frame,
tilePicture.Picture,
tileCoefficients,
tileSuperblockWorkspace,
tileBlockWorkspace,
initialSize: 512,
effort: 5);
// The production tile traversal must be byte-identical to the explicit analyze-then-write composition above.
Assert.True(encoded.GetSpan().SequenceEqual(tileWriter.GetTileData(0)));
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void MarksAllZeroTransformBlockAsSkipped(bool isMonochrome)
{
const int Width = 8;
const int Height = 8;
Av1ColorFormat colorFormat = isMonochrome ? Av1ColorFormat.Yuv400 : Av1ColorFormat.Yuv420;
ObuColorConfig colorConfig = new()
{
IsMonochrome = isMonochrome,
SubSamplingX = true,
SubSamplingY = true,
BitDepth = Av1BitDepth.EightBit
};
using Av1EncoderFrameBuffer source = new(
Configuration.Default,
Width,
Height,
8,
colorFormat,
1,
1);
using Av1EncoderFrameBuffer reconstruction = new(
Configuration.Default,
Width,
Height,
8,
colorFormat,
1,
1);
FillPlane(source.Frame.CodedView.GetPlane(Av1Plane.Y), (byte)128);
ClearPlane(reconstruction.Luma);
if (!isMonochrome)
{
FillPlane(source.Frame.CodedView.GetPlane(Av1Plane.U), (byte)128);
FillPlane(source.Frame.CodedView.GetPlane(Av1Plane.V), (byte)128);
ClearPlane(Assert.IsType>(reconstruction.ChromaBlue));
ClearPlane(Assert.IsType>(reconstruction.ChromaRed));
}
using Av1EncoderModeInfoBuffer modeInfo = new(Configuration.Default, Width, Height, disallow4x4AllFrames: true);
Av1PictureControlSet pictureTemplate = CreatePicture(modeInfo, colorConfig, use128x128Superblock: false, qIndex: 37);
using Av1EncoderPictureBuffer pictureBuffer = new(
Configuration.Default,
pictureTemplate.Sequence.SequenceHeader,
pictureTemplate.Parent.FrameHeader,
Width,
Height);
Av1PictureControlSet picture = pictureBuffer.Picture;
using Av1EncoderCoefficientBuffer coefficients = new(
Configuration.Default,
pictureTemplate.Sequence.SequenceHeader,
Width,
Height);
using Av1EncoderSuperblockWorkspace superblockWorkspace = new(Configuration.Default);
using Av1EncoderBlockWorkspace blockWorkspace = new(Configuration.Default);
Av1Superblock superblock = new()
{
Workspace = superblockWorkspace,
TileInfo = new Av1TileInfo(0, 0, picture.Parent.FrameHeader),
Index = 0
};
Av1IntraSuperblockEncoder.Encode(
source.Frame,
reconstruction.Frame,
picture,
superblock,
coefficients,
blockWorkspace);
ref Av1MacroBlockModeInfo block = ref picture.GetMacroBlockModeInfo(default);
Assert.True(block.Block.Skip);
Assert.Equal((ushort)0, coefficients.GetTransformBlockSpan(0, Av1Plane.Y)[0].EndOfBlock);
if (!isMonochrome)
{
Assert.Equal((ushort)0, coefficients.GetTransformBlockSpan(0, Av1Plane.U)[0].EndOfBlock);
Assert.Equal((ushort)0, coefficients.GetTransformBlockSpan(0, Av1Plane.V)[0].EndOfBlock);
}
Av1TileWriter.Av1EntropyCodingContext entropyContext = new()
{
MacroBlock = new Av1MacroBlockD { Tile = superblock.TileInfo },
MacroBlockModeInfo = picture.GetMacroBlockModeInfo(default),
SuperblockOrigin = default
};
using Av1SymbolEncoder writer = new(Configuration.Default, 256, 37);
Av1TileWriter.WriteSuperblock(
picture,
entropyContext,
writer,
superblock,
coefficients,
tileIndex: 0);
using IMemoryOwner precomputedTile = writer.Exit();
using Av1EncoderFrameBuffer liveReconstruction = new(
Configuration.Default,
Width,
Height,
8,
colorFormat,
1,
1);
ClearPlane(liveReconstruction.Luma);
if (!isMonochrome)
{
ClearPlane(Assert.IsType>(liveReconstruction.ChromaBlue));
ClearPlane(Assert.IsType>(liveReconstruction.ChromaRed));
}
using Av1EncoderPictureBuffer livePicture = new(
Configuration.Default,
pictureTemplate.Sequence.SequenceHeader,
pictureTemplate.Parent.FrameHeader,
Width,
Height);
using Av1EncoderCoefficientBuffer liveCoefficients = new(
Configuration.Default,
pictureTemplate.Sequence.SequenceHeader,
Width,
Height);
using Av1EncoderSuperblockWorkspace liveSuperblockWorkspace = new(Configuration.Default);
using Av1EncoderBlockWorkspace liveBlockWorkspace = new(Configuration.Default);
using Av1IntraTileWriter liveTileWriter = new(
Configuration.Default,
source.Frame,
liveReconstruction.Frame,
livePicture.Picture,
liveCoefficients,
liveSuperblockWorkspace,
liveBlockWorkspace,
initialSize: 256,
effort: 5);
Assert.True(precomputedTile.GetSpan().SequenceEqual(liveTileWriter.GetTileData(0)));
}
[Fact]
public void PreservesTwelveBitMonochromeReconstructionPrecision()
{
const int Width = 8;
const int Height = 8;
ObuColorConfig colorConfig = new()
{
IsMonochrome = true,
SubSamplingX = true,
SubSamplingY = true,
BitDepth = Av1BitDepth.TwelveBit
};
using Av1EncoderFrameBuffer source = new(
Configuration.Default,
Width,
Height,
12,
Av1ColorFormat.Yuv400,
0,
0);
using Av1EncoderFrameBuffer reconstruction = new(
Configuration.Default,
Width,
Height,
12,
Av1ColorFormat.Yuv400,
0,
0);
Buffer2DRegion sourcePlane = source.Frame.CodedView.GetPlane(Av1Plane.Y);
for (int y = 0; y < sourcePlane.Height; y++)
{
Span row = sourcePlane.DangerousGetRowSpan(y);
for (int x = 0; x < row.Length; x++)
{
row[x] = (ushort)(3000 + (((x * 71) + (y * 113)) % 1000));
}
}
ClearPlane(reconstruction.Luma);
using Av1EncoderModeInfoBuffer modeInfo = new(Configuration.Default, Width, Height, disallow4x4AllFrames: true);
Av1PictureControlSet picture = CreatePicture(modeInfo, colorConfig, use128x128Superblock: false, qIndex: 37);
using Av1EncoderCoefficientBuffer coefficients = new(
Configuration.Default,
picture.Sequence.SequenceHeader,
Width,
Height);
using Av1EncoderSuperblockWorkspace superblockWorkspace = new(Configuration.Default);
using Av1EncoderBlockWorkspace blockWorkspace = new(Configuration.Default);
Av1Superblock superblock = new()
{
Workspace = superblockWorkspace,
TileInfo = new Av1TileInfo(0, 0, picture.Parent.FrameHeader),
Index = 0
};
Av1IntraSuperblockEncoder.Encode(
source.Frame,
reconstruction.Frame,
picture,
superblock,
coefficients,
blockWorkspace);
Buffer2DRegion reconstructionPlane = reconstruction.Frame.CodedView.GetPlane(Av1Plane.Y);
ushort maximum = 0;
for (int y = 0; y < reconstructionPlane.Height; y++)
{
foreach (ushort sample in reconstructionPlane.DangerousGetRowSpan(y))
{
maximum = Math.Max(maximum, sample);
Assert.InRange(sample, (ushort)0, (ushort)4095);
}
}
Assert.InRange(maximum, (ushort)(byte.MaxValue + 1), (ushort)4095);
Assert.False(superblock.FinalBlocks[0].HasChroma);
Assert.Equal(37, superblock.FinalBlocks[0].QuantizationIndex);
Assert.NotEqual((ushort)0, coefficients.GetTransformBlockSpan(0, Av1Plane.Y)[0].EndOfBlock);
Assert.Equal(0, coefficients.GetPlaneSpan(0, Av1Plane.U).Length);
Assert.Equal(0, coefficients.GetPlaneSpan(0, Av1Plane.V).Length);
using Av1EncoderFrameBuffer tileReconstruction = new(
Configuration.Default,
Width,
Height,
12,
Av1ColorFormat.Yuv400,
0,
0);
ClearPlane(tileReconstruction.Luma);
using Av1EncoderPictureBuffer tilePicture = new(
Configuration.Default,
picture.Sequence.SequenceHeader,
picture.Parent.FrameHeader,
Width,
Height);
using Av1EncoderCoefficientBuffer tileCoefficients = new(
Configuration.Default,
picture.Sequence.SequenceHeader,
Width,
Height);
using Av1EncoderSuperblockWorkspace tileSuperblockWorkspace = new(Configuration.Default);
using Av1EncoderBlockWorkspace tileBlockWorkspace = new(Configuration.Default);
using Av1IntraTileWriter tileWriter = new(
Configuration.Default,
source.Frame,
tileReconstruction.Frame,
tilePicture.Picture,
tileCoefficients,
tileSuperblockWorkspace,
tileBlockWorkspace,
initialSize: 256,
effort: 5);
Assert.NotEqual(0, tileWriter.GetTileData(0).Length);
ushort reconstructedSample = tileReconstruction.Frame.CodedView
.GetPlane(Av1Plane.Y)
.DangerousGetRowSpan(0)[0];
Assert.InRange(reconstructedSample, (ushort)(byte.MaxValue + 1), (ushort)4095);
}
[Fact]
public void BlockDecisionObservesLiveCdfInWriterOrder()
{
const int Width = 16;
const int Height = 8;
const int QIndex = 37;
ObuColorConfig colorConfig = new()
{
IsMonochrome = true,
SubSamplingX = true,
SubSamplingY = true,
BitDepth = Av1BitDepth.EightBit,
};
using Av1EncoderModeInfoBuffer modeInfo = new(Configuration.Default, Width, Height, disallow4x4AllFrames: true);
Av1PictureControlSet pictureTemplate = CreatePicture(
modeInfo,
colorConfig,
use128x128Superblock: false,
QIndex);
using Av1EncoderPictureBuffer picture = new(
Configuration.Default,
pictureTemplate.Sequence.SequenceHeader,
pictureTemplate.Parent.FrameHeader,
Width,
Height);
using Av1EncoderCoefficientBuffer coefficients = new(
Configuration.Default,
pictureTemplate.Sequence.SequenceHeader,
Width,
Height);
using Av1EncoderSuperblockWorkspace superblockWorkspace = new(Configuration.Default);
Av1Superblock superblock = new()
{
Workspace = superblockWorkspace,
TileInfo = new Av1TileInfo(0, 0, picture.Picture.Parent.FrameHeader),
Index = 0,
};
Av1IntraSuperblockEncoder.Prepare(picture.Picture, superblock, Point.Empty);
Av1TileWriter.Av1EntropyCodingContext entropyContext = new()
{
MacroBlock = new Av1MacroBlockD { Tile = superblock.TileInfo },
MacroBlockModeInfo = picture.Picture.GetMacroBlockModeInfo(default),
SuperblockOrigin = default,
};
int[] costs = new int[2];
BlockCostRecorder blockEncoder = new(costs, QIndex);
using Av1SymbolEncoder writer = new(Configuration.Default, 256, QIndex);
Av1TileWriter.WriteSuperblock(
picture.Picture,
entropyContext,
writer,
superblock,
coefficients,
tileIndex: 0,
ref blockEncoder);
using IMemoryOwner encoded = writer.Exit();
Assert.Equal(2, blockEncoder.Count);
Assert.True(costs[1] < costs[0]);
Assert.NotEqual(0, encoded.GetSpan().Length);
}
[Fact]
public void ProductionWriterConsumesPaletteMapAndPublishesPaletteEdges()
{
const int Width = 8;
const int Height = 8;
const int QIndex = 23;
ObuColorConfig colorConfig = new()
{
IsMonochrome = true,
SubSamplingX = true,
SubSamplingY = true,
BitDepth = Av1BitDepth.EightBit
};
ObuTileGroupHeader tiles = new()
{
TileColumnCount = 1,
TileRowCount = 1
};
tiles.TileColumnStartModeInfo[1] = Width >> Av1Constants.ModeInfoSizeLog2;
tiles.TileRowStartModeInfo[1] = Height >> Av1Constants.ModeInfoSizeLog2;
ObuSequenceHeader sequenceHeader = new()
{
ColorConfig = colorConfig
};
ObuFrameHeader frameHeader = new()
{
AllowScreenContentTools = true,
ModeInfoColumnCount = Width >> Av1Constants.ModeInfoSizeLog2,
ModeInfoRowCount = Height >> Av1Constants.ModeInfoSizeLog2,
FrameSize = new ObuFrameSize
{
FrameWidth = Width,
FrameHeight = Height
},
TilesInfo = tiles
};
frameHeader.QuantizationParameters.BaseQIndex = QIndex;
frameHeader.QuantizationParameters.QIndex.Fill(QIndex);
byte[][] payloads = new byte[2][];
for (int mapVariant = 0; mapVariant < payloads.Length; mapVariant++)
{
using Av1EncoderPictureBuffer pictureBuffer = new(
Configuration.Default,
sequenceHeader,
frameHeader,
Width,
Height);
using Av1EncoderCoefficientBuffer coefficients = new(
Configuration.Default,
sequenceHeader,
Width,
Height);
using Av1EncoderSuperblockWorkspace workspace = new(Configuration.Default);
Av1Superblock superblock = new()
{
Workspace = workspace,
TileInfo = new Av1TileInfo(0, 0, frameHeader),
Index = 0
};
Av1PictureControlSet picture = pictureBuffer.Picture;
Av1IntraSuperblockEncoder.Prepare(picture, superblock, Point.Empty);
Av1TileWriter.Av1EntropyCodingContext entropyContext = new()
{
MacroBlock = new Av1MacroBlockD { Tile = superblock.TileInfo },
MacroBlockModeInfo = picture.GetMacroBlockModeInfo(default),
SuperblockOrigin = default
};
PaletteBlockEncoder blockEncoder = new(workspace, QIndex, mapVariant);
using Av1SymbolEncoder writer = new(Configuration.Default, 128, QIndex);
Av1TileWriter.WriteSuperblock(
picture,
entropyContext,
writer,
superblock,
coefficients,
tileIndex: 0,
ref blockEncoder);
using IMemoryOwner encoded = writer.Exit();
payloads[mapVariant] = encoded.GetSpan().ToArray();
Assert.Equal(1, blockEncoder.Count);
Av1NeighborArrayUnit paletteContext = Assert.Single(picture.PaletteContexts);
for (int index = 0; index < 2; index++)
{
Assert.Equal(3, paletteContext.Top[index].PaletteSizes[0]);
Assert.Equal(3, paletteContext.Left[index].PaletteSizes[0]);
Assert.Equal([16, 128, 240], paletteContext.Top[index].GetColors(Av1Plane.Y).ToArray());
Assert.Equal([16, 128, 240], paletteContext.Left[index].GetColors(Av1Plane.Y).ToArray());
}
Assert.Equal(0, paletteContext.Top[2].PaletteSizes[0]);
Assert.Equal(0, paletteContext.Left[2].PaletteSizes[0]);
}
// Changing only the selected color indices must change the range-coded tile payload.
Assert.False(payloads[0].SequenceEqual(payloads[1]));
}
[Fact]
public void ProductionTileSelectsExactLumaPaletteAtFullAndClippedSizes()
{
AssertProductionTileSelectsExactLumaPalette(
Av1BitDepth.EightBit,
8,
8,
8,
(byte)32,
(byte)224,
32,
224,
static (source, reconstruction, picture, coefficients, superblockWorkspace, blockWorkspace) =>
new Av1IntraTileWriter(
Configuration.Default,
source,
reconstruction,
picture,
coefficients,
superblockWorkspace,
blockWorkspace,
initialSize: 256,
effort: 5));
AssertProductionTileSelectsExactLumaPalette(
Av1BitDepth.TwelveBit,
12,
8,
8,
(ushort)512,
(ushort)3584,
512,
3584,
static (source, reconstruction, picture, coefficients, superblockWorkspace, blockWorkspace) =>
new Av1IntraTileWriter(
Configuration.Default,
source,
reconstruction,
picture,
coefficients,
superblockWorkspace,
blockWorkspace,
initialSize: 256,
effort: 5));
AssertProductionTileSelectsExactLumaPalette(
Av1BitDepth.EightBit,
8,
5,
3,
(byte)48,
(byte)208,
48,
208,
static (source, reconstruction, picture, coefficients, superblockWorkspace, blockWorkspace) =>
new Av1IntraTileWriter(
Configuration.Default,
source,
reconstruction,
picture,
coefficients,
superblockWorkspace,
blockWorkspace,
initialSize: 256,
effort: 5));
}
[Fact]
public void ProductionTileSelectsExactPairedChromaPalette()
{
AssertProductionTileSelectsExactPairedChromaPalette(useLumaPalette: false);
AssertProductionTileSelectsExactPairedChromaPalette(useLumaPalette: true);
}
private static void AssertProductionTileSelectsExactPairedChromaPalette(bool useLumaPalette)
{
const int Width = 8;
const int Height = 8;
const int QIndex = 37;
ObuColorConfig colorConfig = new()
{
IsMonochrome = false,
SubSamplingX = false,
SubSamplingY = false,
BitDepth = Av1BitDepth.EightBit
};
using Av1EncoderFrameBuffer source = new(
Configuration.Default,
Width,
Height,
8,
Av1ColorFormat.Yuv444,
0,
0);
using Av1EncoderFrameBuffer reconstruction = new(
Configuration.Default,
Width,
Height,
8,
Av1ColorFormat.Yuv444,
0,
0);
Buffer2DRegion lumaSource = source.Frame.CodedView.GetPlane(Av1Plane.Y);
Buffer2DRegion blueSource = source.Frame.CodedView.GetPlane(Av1Plane.U);
Buffer2DRegion redSource = source.Frame.CodedView.GetPlane(Av1Plane.V);
for (int row = 0; row < Height; row++)
{
Span lumaRow = lumaSource.DangerousGetRowSpan(row);
if (useLumaPalette)
{
for (int column = 0; column < Width; column++)
{
lumaRow[column] = column < Width / 2 ? (byte)64 : (byte)192;
}
}
else
{
lumaRow.Fill(128);
}
blueSource.DangerousGetRowSpan(row).Fill(row < Height / 2 ? (byte)32 : (byte)224);
redSource.DangerousGetRowSpan(row).Fill(row < Height / 2 ? (byte)200 : (byte)40);
}
ClearPlane(reconstruction.Luma);
ClearPlane(Assert.IsType>(reconstruction.ChromaBlue));
ClearPlane(Assert.IsType>(reconstruction.ChromaRed));
using Av1EncoderModeInfoBuffer modeInfo = new(
Configuration.Default,
Width,
Height,
disallow4x4AllFrames: true);
Av1PictureControlSet pictureTemplate = CreatePicture(
modeInfo,
colorConfig,
use128x128Superblock: false,
QIndex);
pictureTemplate.Parent.FrameHeader.AllowScreenContentTools = true;
pictureTemplate.Parent.FrameHeader.FrameSize.FrameWidth = Width;
pictureTemplate.Parent.FrameHeader.FrameSize.FrameHeight = Height;
using Av1EncoderPictureBuffer picture = new(
Configuration.Default,
pictureTemplate.Sequence.SequenceHeader,
pictureTemplate.Parent.FrameHeader,
Width,
Height);
using Av1EncoderCoefficientBuffer coefficients = new(
Configuration.Default,
pictureTemplate.Sequence.SequenceHeader,
Width,
Height);
using Av1EncoderSuperblockWorkspace superblockWorkspace = new(Configuration.Default);
using Av1EncoderBlockWorkspace blockWorkspace = new(Configuration.Default);
using Av1IntraTileWriter tileWriter = new(
Configuration.Default,
source.Frame,
reconstruction.Frame,
picture.Picture,
coefficients,
superblockWorkspace,
blockWorkspace,
initialSize: 256,
effort: 5);
ref Av1MacroBlockModeInfo mode = ref picture.Picture.GetMacroBlockModeInfo(default);
Assert.Equal(Av1ChromaPredictionMode.DC, mode.Block.UvMode);
Assert.Equal(useLumaPalette ? 2 : 0, superblockWorkspace.PaletteInfo.PaletteSizes[0]);
Assert.Equal(2, superblockWorkspace.PaletteInfo.PaletteSizes[1]);
Assert.Equal([32, 224], superblockWorkspace.PaletteInfo.GetColors(Av1Plane.U).ToArray());
Assert.Equal([200, 40], superblockWorkspace.PaletteInfo.GetColors(Av1Plane.V).ToArray());
Assert.Equal((ushort)0, coefficients.GetTransformBlockSpan(0, Av1Plane.U)[0].EndOfBlock);
Assert.Equal((ushort)0, coefficients.GetTransformBlockSpan(0, Av1Plane.V)[0].EndOfBlock);
Buffer2DRegion colorIndexMap = superblockWorkspace
.GetPaletteMaps()
.GetMap(Av1PlaneType.Uv, Width, Height);
Buffer2DRegion blueReconstruction = reconstruction.Frame.CodedView.GetPlane(Av1Plane.U);
Buffer2DRegion redReconstruction = reconstruction.Frame.CodedView.GetPlane(Av1Plane.V);
for (int row = 0; row < Height; row++)
{
byte expectedIndex = (byte)(row < Height / 2 ? 0 : 1);
foreach (byte index in colorIndexMap.DangerousGetRowSpan(row))
{
Assert.Equal(expectedIndex, index);
}
Assert.True(blueSource.DangerousGetRowSpan(row).SequenceEqual(blueReconstruction.DangerousGetRowSpan(row)));
Assert.True(redSource.DangerousGetRowSpan(row).SequenceEqual(redReconstruction.DangerousGetRowSpan(row)));
}
Assert.NotEqual(0, tileWriter.GetTileData(0).Length);
}
[Theory]
[InlineData((int)Av1PredictionMode.Vertical, 0)]
[InlineData((int)Av1PredictionMode.Horizontal, 0)]
[InlineData((int)Av1PredictionMode.Smooth, 0)]
[InlineData((int)Av1PredictionMode.Paeth, 0)]
[InlineData((int)Av1PredictionMode.SmoothVertical, 0)]
[InlineData((int)Av1PredictionMode.SmoothHorizontal, 0)]
[InlineData((int)Av1PredictionMode.Directional135Degrees, 0)]
[InlineData((int)Av1PredictionMode.Directional203Degrees, 0)]
[InlineData((int)Av1PredictionMode.Directional157Degrees, 0)]
[InlineData((int)Av1PredictionMode.Directional67Degrees, 0)]
[InlineData((int)Av1PredictionMode.Directional113Degrees, 0)]
[InlineData((int)Av1PredictionMode.Directional45Degrees, 0)]
[InlineData((int)Av1PredictionMode.Directional45Degrees, -3)]
[InlineData((int)Av1PredictionMode.Directional45Degrees, 3)]
[InlineData((int)Av1PredictionMode.Directional135Degrees, -3)]
[InlineData((int)Av1PredictionMode.Directional135Degrees, 3)]
[InlineData((int)Av1PredictionMode.Directional203Degrees, -3)]
[InlineData((int)Av1PredictionMode.Directional203Degrees, 3)]
public void ProductionTileSelectsModeFromCurrentReconstruction(int expectedModeValue, int expectedAngleDelta)
{
const int Width = 16;
const int Height = 16;
const byte TopReference = 48;
const byte LeftReference = 208;
const int QIndex = 1;
Av1PredictionMode expectedMode = (Av1PredictionMode)expectedModeValue;
bool isDiagonal = expectedMode is >= Av1PredictionMode.Directional45Degrees and <= Av1PredictionMode.Directional67Degrees;
int cornerReference = expectedMode == Av1PredictionMode.Horizontal
? LeftReference
: expectedMode == Av1PredictionMode.Vertical ? TopReference : 128;
Span directionalTarget = stackalloc byte[64];
if (isDiagonal)
{
Span aboveStorage = stackalloc byte[17];
Span above = aboveStorage[1..];
Span leftStorage = stackalloc byte[17];
Span left = leftStorage[1..];
aboveStorage[0] = 128;
leftStorage[0] = 128;
for (int i = 0; i < 8; i++)
{
above[i] = (byte)(32 + (i * 24));
left[i] = (byte)(224 - (i * 24));
}
above[8..].Fill(above[7]);
left[8..].Fill(left[7]);
// Directional arithmetic has separate byte-exact reference coverage. This fixture uses its scalar
// path only to isolate production mode traversal, reference gathering, and rate-distortion selection.
Av1DirectionalIntraPredictor.PredictScalar(
directionalTarget,
8,
Av1TransformSize.Size8x8,
above,
left,
false,
false,
expectedMode.ToAngle() + (expectedAngleDelta * Av1Constants.AngleStep));
}
ObuColorConfig colorConfig = new()
{
IsMonochrome = true,
SubSamplingX = true,
SubSamplingY = true,
BitDepth = Av1BitDepth.EightBit
};
using Av1EncoderFrameBuffer source = new(
Configuration.Default,
Width,
Height,
8,
Av1ColorFormat.Yuv400,
0,
0);
using Av1EncoderFrameBuffer reconstruction = new(
Configuration.Default,
Width,
Height,
8,
Av1ColorFormat.Yuv400,
0,
0);
Buffer2DRegion sourcePlane = source.Frame.CodedView.GetPlane(Av1Plane.Y);
// The first three 8x8 blocks establish the corner, top, and left reconstruction consumed by
// the bottom-right target. This makes the assertion exercise production traversal and live state.
for (int y = 0; y < Height; y++)
{
Span row = sourcePlane.DangerousGetRowSpan(y);
for (int x = 0; x < Width; x++)
{
int rowIndex = y - 8;
int columnIndex = x - 8;
int value;
if (y < 8)
{
value = x < 8
? cornerReference
: isDiagonal
? 32 + (columnIndex * 24)
: expectedMode == Av1PredictionMode.Paeth ? 40 + (columnIndex * 20) : TopReference;
}
else if (x < 8)
{
value = isDiagonal
? 224 - (rowIndex * 24)
: expectedMode == Av1PredictionMode.Paeth ? 200 - (rowIndex * 20) : LeftReference;
}
else if (isDiagonal)
{
value = directionalTarget[(rowIndex * 8) + columnIndex];
}
else if (expectedMode == Av1PredictionMode.Paeth)
{
// Build the target from the nearest of left, top, and corner without calling the production predictor.
int top = 40 + (columnIndex * 20);
int left = 200 - (rowIndex * 20);
int predictor = top + left - 128;
int leftDistance = Math.Abs(predictor - left);
int topDistance = Math.Abs(predictor - top);
int cornerDistance = Math.Abs(predictor - 128);
value = leftDistance <= topDistance && leftDistance <= cornerDistance
? left
: topDistance <= cornerDistance ? top : 128;
}
else
{
// Apply the normative interpolation directly so a production predictor cannot generate its own fixture.
int rowWeight = Smooth8Weights[rowIndex];
int columnWeight = Smooth8Weights[columnIndex];
value = expectedMode switch
{
Av1PredictionMode.Horizontal => LeftReference,
Av1PredictionMode.Vertical => TopReference,
Av1PredictionMode.SmoothVertical => ((rowWeight * TopReference) + ((256 - rowWeight) * LeftReference) + 128) >> 8,
Av1PredictionMode.SmoothHorizontal => ((columnWeight * LeftReference) + ((256 - columnWeight) * TopReference) + 128) >> 8,
_ => ((rowWeight * TopReference) + ((256 - rowWeight) * LeftReference) +
(columnWeight * LeftReference) + ((256 - columnWeight) * TopReference) + 256) >> 9
};
}
row[x] = (byte)value;
}
}
ClearPlane(reconstruction.Luma);
using Av1EncoderModeInfoBuffer modeInfo = new(Configuration.Default, Width, Height, disallow4x4AllFrames: true);
Av1PictureControlSet pictureTemplate = CreatePicture(modeInfo, colorConfig, use128x128Superblock: false, QIndex);
using Av1EncoderPictureBuffer picture = new(
Configuration.Default,
pictureTemplate.Sequence.SequenceHeader,
pictureTemplate.Parent.FrameHeader,
Width,
Height);
using Av1EncoderCoefficientBuffer coefficients = new(
Configuration.Default,
pictureTemplate.Sequence.SequenceHeader,
Width,
Height);
using Av1EncoderSuperblockWorkspace superblockWorkspace = new(Configuration.Default);
using Av1EncoderBlockWorkspace blockWorkspace = new(Configuration.Default);
using Av1IntraTileWriter tileWriter = new(
Configuration.Default,
source.Frame,
reconstruction.Frame,
picture.Picture,
coefficients,
superblockWorkspace,
blockWorkspace,
initialSize: 512,
effort: 5);
ref Av1MacroBlockModeInfo targetBlock = ref picture.Picture.GetMacroBlockModeInfo(new Point(2, 2));
Assert.Equal(expectedMode, targetBlock.Block.Mode);
Assert.Equal(
expectedAngleDelta,
superblockWorkspace.FinalBlocks[3].PredictionUnit.AngleDelta[(int)Av1PlaneType.Y]);
Av1EncoderTransformBlockState targetState =
coefficients.GetTransformBlockSpan(0, Av1Plane.Y)[12];
// Every transform has the same skip cost for this exact-prediction target, so reference enum order
// requires DCT-DCT to win even when the mode-derived first pass used another transform.
Assert.Equal((ushort)0, targetState.EndOfBlock);
Assert.Equal(Av1TransformType.DctDct, targetState.TransformType);
Assert.NotEqual(0, tileWriter.GetTileData(0).Length);
}
[Theory]
[InlineData((int)Av1ChromaPredictionMode.Vertical, 0, (int)Av1TransformType.AdstDct, (int)Av1ColorFormat.Yuv444)]
[InlineData((int)Av1ChromaPredictionMode.Horizontal, 0, (int)Av1TransformType.DctAdst, (int)Av1ColorFormat.Yuv420)]
[InlineData((int)Av1ChromaPredictionMode.Paeth, 0, (int)Av1TransformType.AdstAdst, (int)Av1ColorFormat.Yuv422)]
[InlineData((int)Av1ChromaPredictionMode.Directional45Degrees, -3, (int)Av1TransformType.DctDct, (int)Av1ColorFormat.Yuv420)]
[InlineData((int)Av1ChromaPredictionMode.Directional135Degrees, 3, (int)Av1TransformType.AdstAdst, (int)Av1ColorFormat.Yuv422)]
[InlineData((int)Av1ChromaPredictionMode.Directional203Degrees, -3, (int)Av1TransformType.DctAdst, (int)Av1ColorFormat.Yuv444)]
public void ProductionTileSelectsChromaModeFromCurrentReconstruction(
int expectedModeValue,
int expectedAngleDelta,
int expectedTransformTypeValue,
int colorFormatValue)
{
const int Width = 16;
const int Height = 16;
const int QIndex = 1;
Av1ChromaPredictionMode expectedMode = (Av1ChromaPredictionMode)expectedModeValue;
Av1TransformType expectedTransformType = (Av1TransformType)expectedTransformTypeValue;
Av1ColorFormat colorFormat = (Av1ColorFormat)colorFormatValue;
bool subsamplingX = colorFormat is Av1ColorFormat.Yuv420 or Av1ColorFormat.Yuv422;
bool subsamplingY = colorFormat == Av1ColorFormat.Yuv420;
int chromaSubsamplingX = subsamplingX ? 1 : 0;
int chromaSubsamplingY = subsamplingY ? 1 : 0;
Av1TransformSize transformSize = Av1BlockSize.Block8x8.GetMaxUvTransformSize(
subsamplingX,
subsamplingY);
ObuColorConfig colorConfig = new()
{
IsMonochrome = false,
SubSamplingX = subsamplingX,
SubSamplingY = subsamplingY,
BitDepth = Av1BitDepth.EightBit
};
using Av1EncoderFrameBuffer source = new(
Configuration.Default,
Width,
Height,
8,
colorFormat,
chromaSubsamplingX,
chromaSubsamplingY);
using Av1EncoderFrameBuffer reconstruction = new(
Configuration.Default,
Width,
Height,
8,
colorFormat,
chromaSubsamplingX,
chromaSubsamplingY);
FillPlane(source.Frame.CodedView.GetPlane(Av1Plane.Y), (byte)128);
FillChromaModeSelectionPlane(
source.Frame.CodedView.GetPlane(Av1Plane.U),
transformSize,
expectedMode,
expectedAngleDelta);
FillChromaModeSelectionPlane(
source.Frame.CodedView.GetPlane(Av1Plane.V),
transformSize,
expectedMode,
expectedAngleDelta);
ClearPlane(reconstruction.Luma);
ClearPlane(Assert.IsType>(reconstruction.ChromaBlue));
ClearPlane(Assert.IsType>(reconstruction.ChromaRed));
using Av1EncoderModeInfoBuffer modeInfo = new(Configuration.Default, Width, Height, disallow4x4AllFrames: true);
Av1PictureControlSet pictureTemplate = CreatePicture(modeInfo, colorConfig, use128x128Superblock: false, QIndex);
using Av1EncoderPictureBuffer picture = new(
Configuration.Default,
pictureTemplate.Sequence.SequenceHeader,
pictureTemplate.Parent.FrameHeader,
Width,
Height);
using Av1EncoderCoefficientBuffer coefficients = new(
Configuration.Default,
pictureTemplate.Sequence.SequenceHeader,
Width,
Height);
using Av1EncoderSuperblockWorkspace superblockWorkspace = new(Configuration.Default);
using Av1EncoderBlockWorkspace blockWorkspace = new(Configuration.Default);
using Av1IntraTileWriter tileWriter = new(
Configuration.Default,
source.Frame,
reconstruction.Frame,
picture.Picture,
coefficients,
superblockWorkspace,
blockWorkspace,
initialSize: 512,
effort: 5);
ref Av1MacroBlockModeInfo targetBlock = ref picture.Picture.GetMacroBlockModeInfo(new Point(2, 2));
Assert.Equal(expectedMode, targetBlock.Block.UvMode);
Assert.Equal(
expectedAngleDelta,
superblockWorkspace.FinalBlocks[3].PredictionUnit.AngleDelta[(int)Av1PlaneType.Uv]);
int targetTransformIndex = (3 * transformSize.GetSize2d()) /
Av1EncoderCoefficientBuffer.TransformBlockUnitCoefficientCount;
Av1EncoderTransformBlockState blueState =
coefficients.GetTransformBlockSpan(0, Av1Plane.U)[targetTransformIndex];
Av1EncoderTransformBlockState redState =
coefficients.GetTransformBlockSpan(0, Av1Plane.V)[targetTransformIndex];
Assert.NotEqual((ushort)0, blueState.EndOfBlock);
Assert.NotEqual((ushort)0, redState.EndOfBlock);
Assert.Equal(expectedTransformType, blueState.TransformType);
Assert.Equal(expectedTransformType, redState.TransformType);
Assert.NotEqual(0, tileWriter.GetTileData(0).Length);
}
[Theory]
[InlineData((int)Av1ColorFormat.Yuv420)]
[InlineData((int)Av1ColorFormat.Yuv422)]
[InlineData((int)Av1ColorFormat.Yuv444)]
public void ProductionTileSelectsChromaFromReconstructedLuma(int colorFormatValue)
=> VerifyProductionTileSelectsChromaFromReconstructedLuma(
colorFormatValue,
8,
static (source, reconstruction, picture, coefficients, superblockWorkspace, blockWorkspace) =>
new(
Configuration.Default,
source,
reconstruction,
picture,
coefficients,
superblockWorkspace,
blockWorkspace,
initialSize: 512,
effort: 5));
[Theory]
[InlineData((int)Av1ColorFormat.Yuv420, 10)]
[InlineData((int)Av1ColorFormat.Yuv420, 12)]
[InlineData((int)Av1ColorFormat.Yuv422, 10)]
[InlineData((int)Av1ColorFormat.Yuv422, 12)]
[InlineData((int)Av1ColorFormat.Yuv444, 10)]
[InlineData((int)Av1ColorFormat.Yuv444, 12)]
public void ProductionTileSelectsChromaFromReconstructedLumaHighBitDepth(
int colorFormatValue,
int bitDepth)
=> VerifyProductionTileSelectsChromaFromReconstructedLuma(
colorFormatValue,
bitDepth,
static (source, reconstruction, picture, coefficients, superblockWorkspace, blockWorkspace) =>
new(
Configuration.Default,
source,
reconstruction,
picture,
coefficients,
superblockWorkspace,
blockWorkspace,
initialSize: 512,
effort: 5));
private static void VerifyProductionTileSelectsChromaFromReconstructedLuma(
int colorFormatValue,
int bitDepth,
TileWriterFactory createWriter)
where TSample : unmanaged, IBinaryInteger
{
const int Width = 16;
const int Height = 16;
const int QIndex = 1;
const int AlphaU = 16;
const int AlphaV = -16;
Av1ColorFormat colorFormat = (Av1ColorFormat)colorFormatValue;
bool subsamplingX = colorFormat is Av1ColorFormat.Yuv420 or Av1ColorFormat.Yuv422;
bool subsamplingY = colorFormat == Av1ColorFormat.Yuv420;
int chromaSubsamplingX = subsamplingX ? 1 : 0;
int chromaSubsamplingY = subsamplingY ? 1 : 0;
int sampleScale = 1 << (bitDepth - 8);
int midpoint = 1 << (bitDepth - 1);
int maxSample = (1 << bitDepth) - 1;
Av1TransformSize transformSize = Av1BlockSize.Block8x8.GetMaxUvTransformSize(
subsamplingX,
subsamplingY);
ObuColorConfig colorConfig = new()
{
IsMonochrome = false,
SubSamplingX = subsamplingX,
SubSamplingY = subsamplingY,
BitDepth = (Av1BitDepth)((bitDepth - 8) / 2)
};
using Av1EncoderFrameBuffer pilotSource = new(
Configuration.Default,
Width,
Height,
bitDepth,
colorFormat,
chromaSubsamplingX,
chromaSubsamplingY);
using Av1EncoderFrameBuffer pilotReconstruction = new(
Configuration.Default,
Width,
Height,
bitDepth,
colorFormat,
chromaSubsamplingX,
chromaSubsamplingY);
Buffer2DRegion pilotLuma = pilotSource.Frame.CodedView.GetPlane(Av1Plane.Y);
for (int y = 0; y < pilotLuma.Height; y++)
{
Span row = pilotLuma.DangerousGetRowSpan(y);
for (int x = 0; x < row.Length; x++)
{
row[x] = TSample.CreateChecked(
(96 + (((x * 29) + (y * 47) + (((x ^ y) & 1) * 53)) & 63)) * sampleScale);
}
}
FillPlane(pilotSource.Frame.CodedView.GetPlane(Av1Plane.U), TSample.CreateChecked(midpoint));
FillPlane(pilotSource.Frame.CodedView.GetPlane(Av1Plane.V), TSample.CreateChecked(midpoint));
ClearPlane(pilotReconstruction.Luma);
ClearPlane(Assert.IsType>(pilotReconstruction.ChromaBlue));
ClearPlane(Assert.IsType>(pilotReconstruction.ChromaRed));
using Av1EncoderModeInfoBuffer pilotModeInfo = new(Configuration.Default, Width, Height, disallow4x4AllFrames: true);
Av1PictureControlSet pilotTemplate = CreatePicture(pilotModeInfo, colorConfig, use128x128Superblock: false, QIndex);
using Av1EncoderPictureBuffer pilotPicture = new(
Configuration.Default,
pilotTemplate.Sequence.SequenceHeader,
pilotTemplate.Parent.FrameHeader,
Width,
Height);
using Av1EncoderCoefficientBuffer pilotCoefficients = new(
Configuration.Default,
pilotTemplate.Sequence.SequenceHeader,
Width,
Height);
using Av1EncoderSuperblockWorkspace pilotSuperblockWorkspace = new(Configuration.Default);
using Av1EncoderBlockWorkspace pilotBlockWorkspace = new(Configuration.Default);
using Av1IntraTileWriter pilotWriter = createWriter(
pilotSource.Frame,
pilotReconstruction.Frame,
pilotPicture.Picture,
pilotCoefficients,
pilotSuperblockWorkspace,
pilotBlockWorkspace);
Buffer2DRegion reconstructedLuma = pilotReconstruction.Frame.CodedView.GetPlane(Av1Plane.Y);
int chromaWidth = transformSize.GetWidth();
int chromaHeight = transformSize.GetHeight();
int sampleCount = transformSize.GetSize2d();
int lumaScaleShift = 3 - chromaSubsamplingX - chromaSubsamplingY;
Span lumaQ3 = stackalloc short[64];
int sumQ3 = sampleCount >> 1;
for (int row = 0; row < chromaHeight; row++)
{
for (int column = 0; column < chromaWidth; column++)
{
int lumaSum = 0;
int lumaX = 8 + (column << chromaSubsamplingX);
int lumaY = 8 + (row << chromaSubsamplingY);
for (int offsetY = 0; offsetY <= chromaSubsamplingY; offsetY++)
{
ReadOnlySpan lumaRow = reconstructedLuma.DangerousGetRowSpan(lumaY + offsetY);
for (int offsetX = 0; offsetX <= chromaSubsamplingX; offsetX++)
{
lumaSum += int.CreateChecked(lumaRow[lumaX + offsetX]);
}
}
short sampleQ3 = (short)(lumaSum << lumaScaleShift);
lumaQ3[(row * chromaWidth) + column] = sampleQ3;
sumQ3 += sampleQ3;
}
}
int averageQ3 = sumQ3 >> (transformSize.GetBlockWidthLog2() + transformSize.GetBlockHeightLog2());
using Av1EncoderFrameBuffer source = new(
Configuration.Default,
Width,
Height,
bitDepth,
colorFormat,
chromaSubsamplingX,
chromaSubsamplingY);
using Av1EncoderFrameBuffer reconstruction = new(
Configuration.Default,
Width,
Height,
bitDepth,
colorFormat,
chromaSubsamplingX,
chromaSubsamplingY);
for (int y = 0; y < pilotLuma.Height; y++)
{
pilotLuma.DangerousGetRowSpan(y).CopyTo(source.Frame.CodedView.GetPlane(Av1Plane.Y).DangerousGetRowSpan(y));
}
Buffer2DRegion blue = source.Frame.CodedView.GetPlane(Av1Plane.U);
Buffer2DRegion red = source.Frame.CodedView.GetPlane(Av1Plane.V);
FillPlane(blue, TSample.CreateChecked(midpoint));
FillPlane(red, TSample.CreateChecked(midpoint));
for (int row = 0; row < chromaHeight; row++)
{
Span blueRow = blue.DangerousGetRowSpan(chromaHeight + row);
Span redRow = red.DangerousGetRowSpan(chromaHeight + row);
for (int column = 0; column < chromaWidth; column++)
{
int acQ3 = lumaQ3[(row * chromaWidth) + column] - averageQ3;
int blueProduct = AlphaU * acQ3;
int redProduct = AlphaV * acQ3;
int blueAdjustment = (blueProduct + 32 + (blueProduct >> 31)) >> 6;
int redAdjustment = (redProduct + 32 + (redProduct >> 31)) >> 6;
blueRow[chromaWidth + column] = TSample.CreateChecked(Math.Clamp(midpoint + blueAdjustment, 0, maxSample));
redRow[chromaWidth + column] = TSample.CreateChecked(Math.Clamp(midpoint + redAdjustment, 0, maxSample));
}
}
ClearPlane(reconstruction.Luma);
ClearPlane(Assert.IsType>(reconstruction.ChromaBlue));
ClearPlane(Assert.IsType>(reconstruction.ChromaRed));
using Av1EncoderModeInfoBuffer modeInfo = new(Configuration.Default, Width, Height, disallow4x4AllFrames: true);
Av1PictureControlSet pictureTemplate = CreatePicture(modeInfo, colorConfig, use128x128Superblock: false, QIndex);
using Av1EncoderPictureBuffer picture = new(
Configuration.Default,
pictureTemplate.Sequence.SequenceHeader,
pictureTemplate.Parent.FrameHeader,
Width,
Height);
using Av1EncoderCoefficientBuffer coefficients = new(
Configuration.Default,
pictureTemplate.Sequence.SequenceHeader,
Width,
Height);
using Av1EncoderSuperblockWorkspace superblockWorkspace = new(Configuration.Default);
using Av1EncoderBlockWorkspace blockWorkspace = new(Configuration.Default);
using Av1IntraTileWriter tileWriter = createWriter(
source.Frame,
reconstruction.Frame,
picture.Picture,
coefficients,
superblockWorkspace,
blockWorkspace);
Buffer2DRegion actualLuma = reconstruction.Frame.CodedView.GetPlane(Av1Plane.Y);
for (int y = 0; y < reconstructedLuma.Height; y++)
{
Assert.Equal(reconstructedLuma.DangerousGetRowSpan(y), actualLuma.DangerousGetRowSpan(y));
}
ref Av1MacroBlockModeInfo targetBlock = ref picture.Picture.GetMacroBlockModeInfo(new Point(2, 2));
Assert.Equal(Av1ChromaPredictionMode.ChromaFromLuma, targetBlock.Block.UvMode);
Assert.Equal(
Av1ChromaFromLumaMath.JointSign(
Av1ChromaFromLumaMath.SignPositive,
Av1ChromaFromLumaMath.SignNegative),
superblockWorkspace.FinalBlocks[3].PredictionUnit.ChromaFromLumaSigns);
Assert.Equal(
Av1ChromaFromLumaMath.PackIndices(
Av1ChromaFromLumaMath.AlphaToMagnitudeIndex(AlphaU),
Av1ChromaFromLumaMath.AlphaToMagnitudeIndex(AlphaV)),
superblockWorkspace.FinalBlocks[3].PredictionUnit.ChromaFromLumaIndex);
int targetTransformIndex = (3 * sampleCount) /
Av1EncoderCoefficientBuffer.TransformBlockUnitCoefficientCount;
Av1EncoderTransformBlockState blueState =
coefficients.GetTransformBlockSpan(0, Av1Plane.U)[targetTransformIndex];
Av1EncoderTransformBlockState redState =
coefficients.GetTransformBlockSpan(0, Av1Plane.V)[targetTransformIndex];
Assert.Equal((ushort)0, blueState.EndOfBlock);
Assert.Equal((ushort)0, redState.EndOfBlock);
Assert.Equal(Av1TransformType.DctDct, blueState.TransformType);
Assert.Equal(Av1TransformType.DctDct, redState.TransformType);
Assert.NotEqual(0, pilotWriter.GetTileData(0).Length);
Assert.NotEqual(0, tileWriter.GetTileData(0).Length);
}
[Theory]
[InlineData((int)Av1FilterIntraMode.DC)]
[InlineData((int)Av1FilterIntraMode.Vertical)]
[InlineData((int)Av1FilterIntraMode.Horizontal)]
[InlineData((int)Av1FilterIntraMode.Directional157)]
[InlineData((int)Av1FilterIntraMode.Paeth)]
public void ProductionTileSelectsFilterIntraMode(int filterIntraModeValue)
=> VerifyProductionTileSelectsFilterIntraMode(
filterIntraModeValue,
8,
static (source, reconstruction, picture, coefficients, superblockWorkspace, blockWorkspace) =>
new(
Configuration.Default,
source,
reconstruction,
picture,
coefficients,
superblockWorkspace,
blockWorkspace,
initialSize: 512,
effort: 5),
static (mode, destination, above, left, _, scratch) =>
Av1FilterIntraPredictorBase.GetPredictor(mode)
.Predict(destination, 8, above, left, 8, 8, scratch));
[Theory]
[InlineData((int)Av1FilterIntraMode.DC, 10)]
[InlineData((int)Av1FilterIntraMode.DC, 12)]
[InlineData((int)Av1FilterIntraMode.Vertical, 10)]
[InlineData((int)Av1FilterIntraMode.Vertical, 12)]
[InlineData((int)Av1FilterIntraMode.Horizontal, 10)]
[InlineData((int)Av1FilterIntraMode.Horizontal, 12)]
[InlineData((int)Av1FilterIntraMode.Directional157, 10)]
[InlineData((int)Av1FilterIntraMode.Directional157, 12)]
[InlineData((int)Av1FilterIntraMode.Paeth, 10)]
[InlineData((int)Av1FilterIntraMode.Paeth, 12)]
public void ProductionTileSelectsFilterIntraModeHighBitDepth(
int filterIntraModeValue,
int bitDepth)
=> VerifyProductionTileSelectsFilterIntraMode(
filterIntraModeValue,
bitDepth,
static (source, reconstruction, picture, coefficients, superblockWorkspace, blockWorkspace) =>
new(
Configuration.Default,
source,
reconstruction,
picture,
coefficients,
superblockWorkspace,
blockWorkspace,
initialSize: 512,
effort: 5),
static (mode, destination, above, left, sampleBitDepth, scratch) =>
Av1FilterIntraPredictorBase.GetPredictor(mode)
.Predict(
MemoryMarshal.Cast(destination),
8,
MemoryMarshal.Cast(above),
MemoryMarshal.Cast(left),
8,
8,
sampleBitDepth,
MemoryMarshal.Cast(scratch)));
private static void VerifyProductionTileSelectsFilterIntraMode(
int filterIntraModeValue,
int bitDepth,
TileWriterFactory createWriter,
FilterPrediction predictFilter)
where TSample : unmanaged, IBinaryInteger
{
const int Width = 16;
const int Height = 16;
const int QIndex = 1;
const int TargetX = 8;
const int TargetY = 8;
const Av1TransformSize TransformSize = Av1TransformSize.Size8x8;
Av1FilterIntraMode filterIntraMode = (Av1FilterIntraMode)filterIntraModeValue;
int sampleScale = 1 << (bitDepth - 8);
ObuColorConfig colorConfig = new()
{
IsMonochrome = true,
SubSamplingX = true,
SubSamplingY = true,
BitDepth = (Av1BitDepth)((bitDepth - 8) / 2)
};
using Av1EncoderFrameBuffer pilotSource = new(
Configuration.Default,
Width,
Height,
bitDepth,
Av1ColorFormat.Yuv400,
1,
1);
using Av1EncoderFrameBuffer pilotReconstruction = new(
Configuration.Default,
Width,
Height,
bitDepth,
Av1ColorFormat.Yuv400,
1,
1);
Buffer2DRegion pilotLuma = pilotSource.Frame.CodedView.GetPlane(Av1Plane.Y);
for (int y = 0; y < pilotLuma.Height; y++)
{
Span row = pilotLuma.DangerousGetRowSpan(y);
for (int x = 0; x < row.Length; x++)
{
row[x] = TSample.CreateChecked(
(64 + (((x * 71) + (y * 109) + (((x ^ y) & 3) * 37)) & 127)) * sampleScale);
}
}
ClearPlane(pilotReconstruction.Luma);
using Av1EncoderModeInfoBuffer pilotModeInfo = new(Configuration.Default, Width, Height, disallow4x4AllFrames: true);
Av1PictureControlSet pilotTemplate = CreatePicture(pilotModeInfo, colorConfig, use128x128Superblock: false, QIndex);
pilotTemplate.Sequence.SequenceHeader.EnableFilterIntra = true;
using Av1EncoderPictureBuffer pilotPicture = new(
Configuration.Default,
pilotTemplate.Sequence.SequenceHeader,
pilotTemplate.Parent.FrameHeader,
Width,
Height);
using Av1EncoderCoefficientBuffer pilotCoefficients = new(
Configuration.Default,
pilotTemplate.Sequence.SequenceHeader,
Width,
Height);
using Av1EncoderSuperblockWorkspace pilotSuperblockWorkspace = new(Configuration.Default);
using Av1EncoderBlockWorkspace pilotBlockWorkspace = new(Configuration.Default);
using Av1IntraTileWriter pilotWriter = createWriter(
pilotSource.Frame,
pilotReconstruction.Frame,
pilotPicture.Picture,
pilotCoefficients,
pilotSuperblockWorkspace,
pilotBlockWorkspace);
Buffer2DRegion reconstructedLuma = pilotReconstruction.Frame.CodedView.GetPlane(Av1Plane.Y);
Span aboveStorage = stackalloc TSample[9];
Span above = aboveStorage[1..];
Span left = stackalloc TSample[8];
ReadOnlySpan reconstructedAbove = reconstructedLuma.DangerousGetRowSpan(TargetY - 1);
aboveStorage[0] = reconstructedAbove[TargetX - 1];
reconstructedAbove.Slice(TargetX, 8).CopyTo(above);
for (int row = 0; row < 8; row++)
{
left[row] = reconstructedLuma.DangerousGetRowSpan(TargetY + row)[TargetX - 1];
}
Span target = stackalloc TSample[TransformSize.GetSize2d()];
Span filterScratch = stackalloc TSample[Av1FilterIntraPredictorBase.ScratchLength];
predictFilter(filterIntraMode, target, above, left, bitDepth, filterScratch);
using Av1EncoderFrameBuffer source = new(
Configuration.Default,
Width,
Height,
bitDepth,
Av1ColorFormat.Yuv400,
1,
1);
using Av1EncoderFrameBuffer reconstruction = new(
Configuration.Default,
Width,
Height,
bitDepth,
Av1ColorFormat.Yuv400,
1,
1);
Buffer2DRegion sourceLuma = source.Frame.CodedView.GetPlane(Av1Plane.Y);
for (int y = 0; y < pilotLuma.Height; y++)
{
pilotLuma.DangerousGetRowSpan(y).CopyTo(sourceLuma.DangerousGetRowSpan(y));
}
for (int row = 0; row < 8; row++)
{
target.Slice(row * 8, 8).CopyTo(sourceLuma.DangerousGetRowSpan(TargetY + row).Slice(TargetX, 8));
}
ClearPlane(reconstruction.Luma);
using Av1EncoderModeInfoBuffer modeInfo = new(Configuration.Default, Width, Height, disallow4x4AllFrames: true);
Av1PictureControlSet pictureTemplate = CreatePicture(modeInfo, colorConfig, use128x128Superblock: false, QIndex);
pictureTemplate.Sequence.SequenceHeader.EnableFilterIntra = true;
using Av1EncoderPictureBuffer picture = new(
Configuration.Default,
pictureTemplate.Sequence.SequenceHeader,
pictureTemplate.Parent.FrameHeader,
Width,
Height);
using Av1EncoderCoefficientBuffer coefficients = new(
Configuration.Default,
pictureTemplate.Sequence.SequenceHeader,
Width,
Height);
using Av1EncoderSuperblockWorkspace superblockWorkspace = new(Configuration.Default);
using Av1EncoderBlockWorkspace blockWorkspace = new(Configuration.Default);
using Av1IntraTileWriter tileWriter = createWriter(
source.Frame,
reconstruction.Frame,
picture.Picture,
coefficients,
superblockWorkspace,
blockWorkspace);
Buffer2DRegion actualLuma = reconstruction.Frame.CodedView.GetPlane(Av1Plane.Y);
Assert.Equal(above, actualLuma.DangerousGetRowSpan(TargetY - 1).Slice(TargetX, 8));
for (int row = 0; row < 8; row++)
{
Assert.Equal(left[row], actualLuma.DangerousGetRowSpan(TargetY + row)[TargetX - 1]);
Assert.Equal(
target.Slice(row * 8, 8),
actualLuma.DangerousGetRowSpan(TargetY + row).Slice(TargetX, 8));
}
ref Av1MacroBlockModeInfo targetBlock = ref picture.Picture.GetMacroBlockModeInfo(new Point(2, 2));
Assert.Equal(Av1PredictionMode.DC, targetBlock.Block.Mode);
Assert.Equal(filterIntraMode, superblockWorkspace.FinalBlocks[3].FilterIntraMode);
int targetTransformIndex = (3 * TransformSize.GetSize2d()) /
Av1EncoderCoefficientBuffer.TransformBlockUnitCoefficientCount;
Av1EncoderTransformBlockState targetState =
coefficients.GetTransformBlockSpan(0, Av1Plane.Y)[targetTransformIndex];
Assert.Equal((ushort)0, targetState.EndOfBlock);
Assert.Equal(Av1TransformType.DctDct, targetState.TransformType);
Assert.NotEqual(0, pilotWriter.GetTileData(0).Length);
Assert.NotEqual(0, tileWriter.GetTileData(0).Length);
}
[Fact]
public void ProductionDirectionalModesConsumeAvailableExtendedEdges()
{
const int Width = 72;
const int Height = 16;
const int QIndex = 1;
ObuColorConfig colorConfig = new()
{
IsMonochrome = true,
SubSamplingX = true,
SubSamplingY = true,
BitDepth = Av1BitDepth.EightBit
};
using Av1EncoderFrameBuffer source = new(
Configuration.Default,
Width,
Height,
8,
Av1ColorFormat.Yuv400,
0,
0);
using Av1EncoderFrameBuffer reconstruction = new(
Configuration.Default,
Width,
Height,
8,
Av1ColorFormat.Yuv400,
0,
0);
Buffer2DRegion sourcePlane = source.Frame.CodedView.GetPlane(Av1Plane.Y);
FillPlane(sourcePlane, (byte)128);
Span aboveStorage = stackalloc byte[17];
Span above = aboveStorage[1..];
Span leftStorage = stackalloc byte[17];
Span left = leftStorage[1..];
aboveStorage[0] = 128;
leftStorage[0] = 128;
for (int i = 0; i < 16; i++)
{
above[i] = (byte)(32 + (i * 12));
left[i] = (byte)(224 - (i * 12));
}
Span topRightTarget = stackalloc byte[64];
Span bottomLeftTarget = stackalloc byte[64];
Span predictionScratch = stackalloc byte[64];
Av1DirectionalIntraPredictor.Predict(
topRightTarget,
8,
Av1TransformSize.Size8x8,
above,
left,
false,
false,
45,
predictionScratch);
Av1DirectionalIntraPredictor.Predict(
bottomLeftTarget,
8,
Av1TransformSize.Size8x8,
above,
left,
false,
false,
203,
predictionScratch);
// The lower-left target consumes top-right samples from the already reconstructed row above.
// The upper-right superblock target consumes bottom-left samples from the completed superblock to its left.
for (int y = 0; y < Height; y++)
{
Span row = sourcePlane.DangerousGetRowSpan(y);
if (y < 8)
{
above.CopyTo(row[..16]);
bottomLeftTarget.Slice(y * 8, 8).CopyTo(row.Slice(64, 8));
}
else
{
topRightTarget.Slice((y - 8) * 8, 8).CopyTo(row[..8]);
}
row.Slice(56, 8).Fill(left[y]);
}
ClearPlane(reconstruction.Luma);
using Av1EncoderModeInfoBuffer modeInfo = new(Configuration.Default, Width, Height, disallow4x4AllFrames: true);
Av1PictureControlSet pictureTemplate = CreatePicture(modeInfo, colorConfig, use128x128Superblock: false, QIndex);
using Av1EncoderPictureBuffer picture = new(
Configuration.Default,
pictureTemplate.Sequence.SequenceHeader,
pictureTemplate.Parent.FrameHeader,
Width,
Height);
using Av1EncoderCoefficientBuffer coefficients = new(
Configuration.Default,
pictureTemplate.Sequence.SequenceHeader,
Width,
Height);
using Av1EncoderSuperblockWorkspace superblockWorkspace = new(Configuration.Default);
using Av1EncoderBlockWorkspace blockWorkspace = new(Configuration.Default);
using Av1IntraTileWriter tileWriter = new(
Configuration.Default,
source.Frame,
reconstruction.Frame,
picture.Picture,
coefficients,
superblockWorkspace,
blockWorkspace,
initialSize: 2048,
effort: 5);
ref Av1MacroBlockModeInfo topRightBlock = ref picture.Picture.GetMacroBlockModeInfo(new Point(0, 2));
ref Av1MacroBlockModeInfo bottomLeftBlock = ref picture.Picture.GetMacroBlockModeInfo(new Point(16, 0));
Assert.Equal(Av1PredictionMode.Directional45Degrees, topRightBlock.Block.Mode);
Assert.Equal(Av1PredictionMode.Directional203Degrees, bottomLeftBlock.Block.Mode);
Assert.NotEqual(0, tileWriter.GetTileData(0).Length);
}
[Fact]
public void ProductionTileSelectsIntraBlockCopyByFullRateDistortion()
{
VerifyProductionTileSelectsIntraBlockCopy(
Av1BitDepth.EightBit,
8,
static value => (byte)value,
static (source, reconstruction, picture, coefficients, superblockWorkspace, blockWorkspace) =>
new Av1IntraTileWriter(
Configuration.Default,
source,
reconstruction,
picture,
coefficients,
superblockWorkspace,
blockWorkspace,
initialSize: 4096,
effort: 5));
VerifyProductionTileSelectsIntraBlockCopy(
Av1BitDepth.TwelveBit,
12,
static value => (ushort)(value << 4),
static (source, reconstruction, picture, coefficients, superblockWorkspace, blockWorkspace) =>
new Av1IntraTileWriter(
Configuration.Default,
source,
reconstruction,
picture,
coefficients,
superblockWorkspace,
blockWorkspace,
initialSize: 4096,
effort: 5));
}
private static void VerifyProductionTileSelectsIntraBlockCopy(
Av1BitDepth bitDepth,
int bitDepthValue,
SampleFactory createSample,
TileWriterFactory createTileWriter)
where TSample : unmanaged
{
const int Width = 328;
const int Height = 8;
const int QIndex = 1;
const int ReferenceColumn = 0;
const int TargetColumn = 320;
ObuColorConfig colorConfig = new()
{
IsMonochrome = true,
SubSamplingX = true,
SubSamplingY = true,
BitDepth = bitDepth
};
using Av1EncoderFrameBuffer source = new(
Configuration.Default,
Width,
Height,
bitDepthValue,
Av1ColorFormat.Yuv400,
0,
0);
using Av1EncoderFrameBuffer reconstruction = new(
Configuration.Default,
Width,
Height,
bitDepthValue,
Av1ColorFormat.Yuv400,
0,
0);
Buffer2DRegion sourcePlane = source.Frame.CodedView.GetPlane(Av1Plane.Y);
for (int row = 0; row < Height; row++)
{
Span sourceRow = sourcePlane.DangerousGetRowSpan(row);
for (int column = 0; column < Width; column++)
{
sourceRow[column] = createSample(17 + (((column * 29) + (row * 43)) % 211));
}
for (int column = 0; column < 8; column++)
{
// The repeated high-contrast block has one legal hash match five completed 64-pixel regions earlier.
TSample sample = createSample(((column * 73) + (row * 109) + (((column + row) & 1) * 127)) & 255);
sourceRow[ReferenceColumn + column] = sample;
sourceRow[TargetColumn + column] = sample;
}
}
ClearPlane(reconstruction.Luma);
using Av1EncoderModeInfoBuffer modeInfo = new(
Configuration.Default,
Width,
Height,
disallow4x4AllFrames: true);
Av1PictureControlSet pictureTemplate = CreatePicture(
modeInfo,
colorConfig,
use128x128Superblock: false,
QIndex);
pictureTemplate.Parent.FrameHeader.AllowScreenContentTools = true;
pictureTemplate.Parent.FrameHeader.AllowIntraBlockCopy = true;
pictureTemplate.Parent.FrameHeader.FrameSize.FrameWidth = Width;
pictureTemplate.Parent.FrameHeader.FrameSize.FrameHeight = Height;
using Av1EncoderPictureBuffer picture = new(
Configuration.Default,
pictureTemplate.Sequence.SequenceHeader,
pictureTemplate.Parent.FrameHeader,
Width,
Height);
using Av1EncoderCoefficientBuffer coefficients = new(
Configuration.Default,
pictureTemplate.Sequence.SequenceHeader,
Width,
Height);
using Av1EncoderSuperblockWorkspace superblockWorkspace = new(Configuration.Default);
using Av1EncoderBlockWorkspace blockWorkspace = new(Configuration.Default);
using Av1IntraTileWriter tileWriter = createTileWriter(
source.Frame,
reconstruction.Frame,
picture.Picture,
coefficients,
superblockWorkspace,
blockWorkspace);
Point targetModeInfoPosition = new(TargetColumn >> Av1Constants.ModeInfoSizeLog2, 0);
ref Av1MacroBlockModeInfo targetMode = ref picture.Picture.GetMacroBlockModeInfo(targetModeInfoPosition);
Assert.True(targetMode.Block.UseIntraBlockCopy);
Assert.Equal(Av1PredictionMode.DC, targetMode.Block.Mode);
Assert.Equal(Av1ChromaPredictionMode.DC, targetMode.Block.UvMode);
var displacementVector = picture.Picture.GetDisplacementVector(targetModeInfoPosition);
Assert.Equal(0, displacementVector.Row);
Assert.Equal((ReferenceColumn - TargetColumn) * 8, displacementVector.Column);
Assert.Equal(Av1FilterIntraMode.AllFilterIntraModes, superblockWorkspace.FinalBlocks[0].FilterIntraMode);
Assert.Equal(0, superblockWorkspace.PaletteInfo.PaletteSizes[0]);
Assert.NotEqual(0, tileWriter.GetTileData(0).Length);
}
[Fact]
public void ProductionTileRetainsHalfSampleChromaIntraBlockCopy()
{
const int Width = 328;
const int Height = 8;
const int QIndex = 1;
const int ReferenceColumn = 1;
const int TargetColumn = 320;
ObuColorConfig colorConfig = new()
{
IsMonochrome = false,
SubSamplingX = true,
SubSamplingY = true,
BitDepth = Av1BitDepth.EightBit
};
using Av1EncoderFrameBuffer source = new(
Configuration.Default,
Width,
Height,
8,
Av1ColorFormat.Yuv420,
1,
1);
using Av1EncoderFrameBuffer reconstruction = new(
Configuration.Default,
Width,
Height,
8,
Av1ColorFormat.Yuv420,
1,
1);
Buffer2DRegion lumaSource = source.Frame.CodedView.GetPlane(Av1Plane.Y);
for (int row = 0; row < Height; row++)
{
Span lumaRow = lumaSource.DangerousGetRowSpan(row);
for (int column = 0; column < Width; column++)
{
lumaRow[column] = (byte)(23 + (((column * 31) + (row * 47)) % 197));
}
for (int column = 0; column < 8; column++)
{
byte sample = (byte)(((column * 79) + (row * 113) + (((column + row) & 1) * 127)) & 255);
lumaRow[ReferenceColumn + column] = sample;
lumaRow[TargetColumn + column] = sample;
}
}
int chromaTargetColumn = TargetColumn >> 1;
Buffer2DRegion blueSource = source.Frame.CodedView.GetPlane(Av1Plane.U);
Buffer2DRegion redSource = source.Frame.CodedView.GetPlane(Av1Plane.V);
for (int row = 0; row < Height >> 1; row++)
{
Span blueRow = blueSource.DangerousGetRowSpan(row);
Span redRow = redSource.DangerousGetRowSpan(row);
for (int column = 0; column < Width >> 1; column++)
{
blueRow[column] = (byte)(32 + (((column * 17) + (row * 29)) % 160));
redRow[column] = (byte)(40 + (((column * 23) + (row * 37)) % 152));
}
for (int column = 0; column < 4; column++)
{
// An odd luma displacement maps 4:2:0 chroma between adjacent reference samples.
blueRow[chromaTargetColumn + column] = (byte)((blueRow[column] + blueRow[column + 1] + 1) >> 1);
redRow[chromaTargetColumn + column] = (byte)((redRow[column] + redRow[column + 1] + 1) >> 1);
}
}
ClearPlane(reconstruction.Luma);
ClearPlane(Assert.IsType>(reconstruction.ChromaBlue));
ClearPlane(Assert.IsType>(reconstruction.ChromaRed));
using Av1EncoderModeInfoBuffer modeInfo = new(
Configuration.Default,
Width,
Height,
disallow4x4AllFrames: true);
Av1PictureControlSet pictureTemplate = CreatePicture(
modeInfo,
colorConfig,
use128x128Superblock: false,
QIndex);
pictureTemplate.Parent.FrameHeader.AllowScreenContentTools = true;
pictureTemplate.Parent.FrameHeader.AllowIntraBlockCopy = true;
pictureTemplate.Parent.FrameHeader.FrameSize.FrameWidth = Width;
pictureTemplate.Parent.FrameHeader.FrameSize.FrameHeight = Height;
using Av1EncoderPictureBuffer picture = new(
Configuration.Default,
pictureTemplate.Sequence.SequenceHeader,
pictureTemplate.Parent.FrameHeader,
Width,
Height);
using Av1EncoderCoefficientBuffer coefficients = new(
Configuration.Default,
pictureTemplate.Sequence.SequenceHeader,
Width,
Height);
using Av1EncoderSuperblockWorkspace superblockWorkspace = new(Configuration.Default);
using Av1EncoderBlockWorkspace blockWorkspace = new(Configuration.Default);
using Av1IntraTileWriter tileWriter = new(
Configuration.Default,
source.Frame,
reconstruction.Frame,
picture.Picture,
coefficients,
superblockWorkspace,
blockWorkspace,
initialSize: 4096,
effort: 5);
Point targetModeInfoPosition = new(TargetColumn >> Av1Constants.ModeInfoSizeLog2, 0);
ref Av1MacroBlockModeInfo targetMode = ref picture.Picture.GetMacroBlockModeInfo(targetModeInfoPosition);
Assert.True(targetMode.Block.UseIntraBlockCopy);
var displacementVector = picture.Picture.GetDisplacementVector(targetModeInfoPosition);
Assert.Equal(0, displacementVector.Row);
Assert.Equal((ReferenceColumn - TargetColumn) * 8, displacementVector.Column);
Assert.Equal(8, displacementVector.Column & 15);
Av1TransformSetType interTransformSet = Av1SymbolContextHelper.GetExtendedTransformSetType(
Av1TransformSize.Size4x4,
isInter: true,
useReducedSet: false);
Assert.True(coefficients.GetTransformBlockSpan(5, Av1Plane.U)[0].TransformType.IsExtendedSetUsed(interTransformSet));
Assert.True(coefficients.GetTransformBlockSpan(5, Av1Plane.V)[0].TransformType.IsExtendedSetUsed(interTransformSet));
Assert.NotEqual(
(byte)0,
reconstruction.Frame.CodedView.GetPlane(Av1Plane.U).DangerousGetRowSpan(0)[chromaTargetColumn]);
Assert.NotEqual(
(byte)0,
reconstruction.Frame.CodedView.GetPlane(Av1Plane.V).DangerousGetRowSpan(0)[chromaTargetColumn]);
Assert.NotEqual(0, tileWriter.GetTileData(0).Length);
}
[Fact]
public void TileWriterMapsClippedRasterTraversalToEverySuperblockCoefficientSegment()
{
const int Width = 72;
const int Height = 72;
const int QIndex = 53;
ObuColorConfig colorConfig = new()
{
IsMonochrome = true,
SubSamplingX = true,
SubSamplingY = true,
BitDepth = Av1BitDepth.EightBit
};
ObuTileGroupHeader tiles = new()
{
TileColumnCount = 1,
TileRowCount = 1
};
int modeInfoColumnCount = Width >> Av1Constants.ModeInfoSizeLog2;
int modeInfoRowCount = Height >> Av1Constants.ModeInfoSizeLog2;
tiles.TileColumnStartModeInfo[1] = modeInfoColumnCount;
tiles.TileRowStartModeInfo[1] = modeInfoRowCount;
ObuSequenceHeader sequenceHeader = new()
{
Use128x128Superblock = false,
ColorConfig = colorConfig
};
ObuFrameHeader frameHeader = new()
{
ModeInfoColumnCount = modeInfoColumnCount,
ModeInfoRowCount = modeInfoRowCount,
TilesInfo = tiles
};
frameHeader.QuantizationParameters.BaseQIndex = QIndex;
frameHeader.QuantizationParameters.QIndex.Fill(QIndex);
using Av1EncoderFrameBuffer source = new(
Configuration.Default,
Width,
Height,
8,
Av1ColorFormat.Yuv400,
0,
0);
using Av1EncoderFrameBuffer reconstruction = new(
Configuration.Default,
Width,
Height,
8,
Av1ColorFormat.Yuv400,
0,
0);
FillPlane(source.Frame.CodedView.GetPlane(Av1Plane.Y), 251, 29);
ClearPlane(reconstruction.Luma);
using Av1EncoderPictureBuffer picture = new(
Configuration.Default,
sequenceHeader,
frameHeader,
Width,
Height);
using Av1EncoderCoefficientBuffer coefficients = new(
Configuration.Default,
sequenceHeader,
Width,
Height);
using Av1EncoderSuperblockWorkspace superblockWorkspace = new(Configuration.Default);
using Av1EncoderBlockWorkspace blockWorkspace = new(Configuration.Default);
using Av1IntraTileWriter tileWriter = new(
Configuration.Default,
source.Frame,
reconstruction.Frame,
picture.Picture,
coefficients,
superblockWorkspace,
blockWorkspace,
initialSize: 4096,
effort: 5);
Assert.Equal(4, coefficients.SuperblockCount);
bool usesNonDctTransform = false;
for (int superblockIndex = 0; superblockIndex < coefficients.SuperblockCount; superblockIndex++)
{
Span transformBlocks =
coefficients.GetTransformBlockSpan(superblockIndex, Av1Plane.Y);
Assert.NotEqual((ushort)0, transformBlocks[0].EndOfBlock);
foreach (Av1EncoderTransformBlockState transformBlock in transformBlocks)
{
usesNonDctTransform |=
transformBlock.EndOfBlock > 0 && transformBlock.TransformType != Av1TransformType.DctDct;
}
}
Assert.True(usesNonDctTransform);
Assert.NotEqual(
(byte)0,
reconstruction.Frame.CodedView.GetPlane(Av1Plane.Y).DangerousGetRowSpan(Height - 1)[Width - 1]);
ref Av1MacroBlockModeInfo bottomRight = ref picture.Picture.GetMacroBlockModeInfo(new Point(16, 16));
Assert.Equal(Av1BlockSize.Block8x8, bottomRight.Block.BlockSize);
Assert.NotEqual(0, tileWriter.GetTileData(0).Length);
}
private static Av1PictureControlSet CreatePicture(
Av1EncoderModeInfoBuffer modeInfo,
ObuColorConfig colorConfig,
bool use128x128Superblock,
int qIndex)
{
ObuTileGroupHeader tiles = new()
{
TileColumnCount = 1,
TileRowCount = 1
};
tiles.TileColumnStartModeInfo[1] = modeInfo.ModeInfoColumnCount;
tiles.TileRowStartModeInfo[1] = modeInfo.ModeInfoRowCount;
ObuSequenceHeader sequenceHeader = new()
{
Use128x128Superblock = use128x128Superblock,
ColorConfig = colorConfig
};
ObuFrameHeader frameHeader = new()
{
ModeInfoColumnCount = modeInfo.ModeInfoColumnCount,
ModeInfoRowCount = modeInfo.ModeInfoRowCount,
TilesInfo = tiles
};
frameHeader.QuantizationParameters.BaseQIndex = qIndex;
frameHeader.QuantizationParameters.QIndex.Fill(qIndex);
return new Av1PictureControlSet
{
PartitionContexts = [],
LuminanceDcSignLevelCoefficientNeighbors = [],
CrDcSignLevelCoefficientNeighbors = [],
CbDcSignLevelCoefficientNeighbors = [],
TransformFunctionContexts = [],
Sequence = new Av1SequenceControlSet { SequenceHeader = sequenceHeader },
Parent = new Av1PictureParentControlSet
{
Common = new Av1EncoderCommon
{
ModeInfoColumnCount = modeInfo.ModeInfoColumnCount,
ModeInfoRowCount = modeInfo.ModeInfoRowCount,
ModeInfoStride = modeInfo.ModeInfoStride,
TilesInfo = tiles,
FrameSize = new ObuFrameSize()
},
FrameHeader = frameHeader,
PreviousQIndex = [qIndex]
},
SegmentationNeighborMap = new byte[modeInfo.ModeInfoColumnCount * modeInfo.ModeInfoRowCount],
ModeInfoGrid = modeInfo.Grid,
ModeInfoAllocation = modeInfo.Allocation,
ModeInfoStride = modeInfo.ModeInfoStride,
Disallow4x4AllFrames = modeInfo.Disallow4x4AllFrames,
CdefPreset = [[-1, -1, -1, -1]]
};
}
private static void AssertProductionTileSelectsExactLumaPalette(
Av1BitDepth bitDepth,
int bitDepthValue,
int width,
int height,
TSample lowerColor,
TSample upperColor,
ushort expectedLowerColor,
ushort expectedUpperColor,
TileWriterFactory createTileWriter)
where TSample : unmanaged
{
const int QIndex = 37;
ObuColorConfig colorConfig = new()
{
IsMonochrome = true,
SubSamplingX = true,
SubSamplingY = true,
BitDepth = bitDepth
};
using Av1EncoderFrameBuffer source = new(
Configuration.Default,
width,
height,
bitDepthValue,
Av1ColorFormat.Yuv400,
0,
0);
using Av1EncoderFrameBuffer reconstruction = new(
Configuration.Default,
width,
height,
bitDepthValue,
Av1ColorFormat.Yuv400,
0,
0);
Buffer2DRegion sourcePlane = source.Frame.CodedView.GetPlane(Av1Plane.Y);
for (int row = 0; row < sourcePlane.Height; row++)
{
int visibleRow = Math.Min(row, height - 1);
sourcePlane.DangerousGetRowSpan(row).Fill(visibleRow < height / 2 ? lowerColor : upperColor);
}
ClearPlane(reconstruction.Luma);
using Av1EncoderModeInfoBuffer modeInfo = new(
Configuration.Default,
width,
height,
disallow4x4AllFrames: true);
Av1PictureControlSet pictureTemplate = CreatePicture(
modeInfo,
colorConfig,
use128x128Superblock: false,
QIndex);
pictureTemplate.Parent.FrameHeader.AllowScreenContentTools = true;
pictureTemplate.Parent.FrameHeader.FrameSize.FrameWidth = width;
pictureTemplate.Parent.FrameHeader.FrameSize.FrameHeight = height;
using Av1EncoderPictureBuffer picture = new(
Configuration.Default,
pictureTemplate.Sequence.SequenceHeader,
pictureTemplate.Parent.FrameHeader,
width,
height);
using Av1EncoderCoefficientBuffer coefficients = new(
Configuration.Default,
pictureTemplate.Sequence.SequenceHeader,
width,
height);
using Av1EncoderSuperblockWorkspace superblockWorkspace = new(Configuration.Default);
using Av1EncoderBlockWorkspace blockWorkspace = new(Configuration.Default);
using Av1IntraTileWriter tileWriter = createTileWriter(
source.Frame,
reconstruction.Frame,
picture.Picture,
coefficients,
superblockWorkspace,
blockWorkspace);
ref Av1MacroBlockModeInfo mode = ref picture.Picture.GetMacroBlockModeInfo(default);
Assert.Equal(Av1PredictionMode.DC, mode.Block.Mode);
Assert.Equal(Av1FilterIntraMode.AllFilterIntraModes, superblockWorkspace.FinalBlocks[0].FilterIntraMode);
Assert.Equal((ushort)0, coefficients.GetTransformBlockSpan(0, Av1Plane.Y)[0].EndOfBlock);
Assert.Equal(2, superblockWorkspace.PaletteInfo.PaletteSizes[0]);
Assert.Equal(
[expectedLowerColor, expectedUpperColor],
superblockWorkspace.PaletteInfo.GetColors(Av1Plane.Y).ToArray());
Buffer2DRegion colorIndexMap = superblockWorkspace
.GetPaletteMaps()
.GetMap(Av1PlaneType.Y, 8, 8);
Buffer2DRegion reconstructionPlane = reconstruction.Frame.CodedView.GetPlane(Av1Plane.Y);
for (int row = 0; row < reconstructionPlane.Height; row++)
{
int visibleRow = Math.Min(row, height - 1);
byte expectedIndex = (byte)(visibleRow < height / 2 ? 0 : 1);
foreach (byte index in colorIndexMap.DangerousGetRowSpan(row))
{
Assert.Equal(expectedIndex, index);
}
Assert.True(sourcePlane.DangerousGetRowSpan(row).SequenceEqual(reconstructionPlane.DangerousGetRowSpan(row)));
}
Assert.NotEqual(0, tileWriter.GetTileData(0).Length);
}
private static void FillChromaModeSelectionPlane(
Buffer2DRegion plane,
Av1TransformSize transformSize,
Av1ChromaPredictionMode expectedMode,
int expectedAngleDelta)
{
int width = transformSize.GetWidth();
int height = transformSize.GetHeight();
Span aboveStorage = stackalloc byte[17];
Span above = aboveStorage.Slice(1, width * 2);
Span leftStorage = stackalloc byte[17];
Span left = leftStorage.Slice(1, height * 2);
aboveStorage[0] = 128;
leftStorage[0] = 128;
for (int column = 0; column < width; column++)
{
above[column] = (byte)(32 + ((192 * column) / (width - 1)));
}
for (int row = 0; row < height; row++)
{
left[row] = (byte)(224 - ((192 * row) / (height - 1)));
}
above[width..].Fill(above[width - 1]);
left[height..].Fill(left[height - 1]);
Span target = stackalloc byte[64];
int sampleCount = transformSize.GetSize2d();
if (expectedMode.IsDirectional())
{
// Directional arithmetic has separate byte-exact reference coverage. This fixture uses its scalar
// path only to isolate chroma traversal, joint U/V rate-distortion selection, and packed mode state.
Av1DirectionalIntraPredictor.PredictScalar(
target[..sampleCount],
width,
transformSize,
above,
left,
false,
false,
expectedMode.ToLumaMode().ToAngle() + (expectedAngleDelta * Av1Constants.AngleStep));
}
else
{
// Build the supported non-directional targets directly so production prediction cannot self-validate.
for (int row = 0; row < height; row++)
{
for (int column = 0; column < width; column++)
{
int top = above[column];
int leftSample = left[row];
int predictor = top + leftSample - 128;
int leftDistance = Math.Abs(predictor - leftSample);
int topDistance = Math.Abs(predictor - top);
int cornerDistance = Math.Abs(predictor - 128);
target[(row * width) + column] = expectedMode switch
{
Av1ChromaPredictionMode.Vertical => (byte)top,
Av1ChromaPredictionMode.Horizontal => (byte)leftSample,
_ => (byte)(leftDistance <= topDistance && leftDistance <= cornerDistance
? leftSample
: topDistance <= cornerDistance ? top : 128)
};
}
}
}
// The first three transform-sized quadrants establish the references consumed by the bottom-right
// target. Its checkerboard offset keeps coefficients nonzero so the implicit transform affects the stream.
for (int row = 0; row < plane.Height; row++)
{
Span destination = plane.DangerousGetRowSpan(row);
for (int column = 0; column < plane.Width; column++)
{
destination[column] = row < height
? column < width ? (byte)128 : above[column - width]
: column < width
? left[row - height]
: (byte)Math.Clamp(
target[((row - height) * width) + column - width] +
((((row - height) + column - width) & 1) == 0 ? 5 : -5),
0,
255);
}
}
}
private static void FillPlane(Buffer2DRegion plane, int modulus, int seed)
{
for (int y = 0; y < plane.Height; y++)
{
Span row = plane.DangerousGetRowSpan(y);
for (int x = 0; x < row.Length; x++)
{
row[x] = (byte)(1 + ((seed + (x * 43) + (y * 79)) % modulus));
}
}
}
private static void FillPlane(Buffer2DRegion plane, TSample value)
where TSample : unmanaged
{
for (int y = 0; y < plane.Height; y++)
{
plane.DangerousGetRowSpan(y).Fill(value);
}
}
private delegate Av1IntraTileWriter TileWriterFactory(
Av1EncoderFrame source,
Av1EncoderFrame reconstruction,
Av1PictureControlSet picture,
Av1EncoderCoefficientBuffer coefficients,
Av1EncoderSuperblockWorkspace superblockWorkspace,
Av1EncoderBlockWorkspace blockWorkspace)
where TSample : unmanaged;
private delegate TSample SampleFactory(int value)
where TSample : unmanaged;
private delegate void FilterPrediction(
Av1FilterIntraMode mode,
Span destination,
ReadOnlySpan above,
ReadOnlySpan left,
int bitDepth,
Span scratch)
where TSample : unmanaged;
private static void ClearPlane(Buffer2D plane)
where TSample : unmanaged
{
for (int y = 0; y < plane.Height; y++)
{
plane.DangerousGetRowSpan(y).Clear();
}
}
private static void AssertContainsNonzero(Buffer2DRegion plane)
where TSample : unmanaged, IEquatable
{
bool containsNonzero = false;
for (int y = 0; y < plane.Height; y++)
{
foreach (TSample sample in plane.DangerousGetRowSpan(y))
{
containsNonzero |= !sample.Equals(default);
}
}
Assert.True(containsNonzero);
}
///
/// Records the live luma-mode cost while supplying an all-skipped final block.
///
private struct BlockCostRecorder : Av1TileWriter.IBlockEncodingHandler
{
private readonly int[] costs;
private readonly int qIndex;
///
/// Initializes a new instance of the struct.
///
/// The destination for costs observed in writer order.
/// The block quantizer index.
public BlockCostRecorder(int[] costs, int qIndex)
{
this.costs = costs;
this.qIndex = qIndex;
this.Count = 0;
}
///
/// Gets the number of final blocks visited by the writer.
///
public int Count { get; private set; }
///
public void EncodeBlock(
Av1SymbolEncoder writer,
Av1MacroBlockD macroBlock,
Point blockOrigin,
ushort tileIndex,
ref Av1MacroBlockModeInfo modeInfo,
ref Av1EncoderBlockStruct block,
ref Av1EncoderPaletteInfo paletteInfo)
{
this.costs[this.Count++] = Av1TileWriter.GetLumaModeCost(
writer,
macroBlock,
Av1BlockSize.Block8x8,
Av1PredictionMode.DC,
0);
modeInfo.Block = new Av1EncoderBlockModeInfo
{
BlockSize = Av1BlockSize.Block8x8,
PartitionType = Av1PartitionType.None,
SegmentId = 0,
Skip = true,
TransformSize = Av1TransformSize.Size8x8,
Mode = Av1PredictionMode.DC,
UvMode = Av1ChromaPredictionMode.DC,
};
block.HasChroma = false;
block.QuantizationIndex = this.qIndex;
block.SegmentId = 0;
}
}
///
/// Supplies one skipped monochrome palette block to the production tile writer.
///
private struct PaletteBlockEncoder : Av1TileWriter.IBlockEncodingHandler
{
private readonly Av1EncoderSuperblockWorkspace workspace;
private readonly int qIndex;
private readonly int mapVariant;
///
/// Initializes a new instance of the struct.
///
/// The workspace that owns the palette index map.
/// The block quantizer index.
/// The map pattern selected by the test.
public PaletteBlockEncoder(
Av1EncoderSuperblockWorkspace workspace,
int qIndex,
int mapVariant)
{
this.workspace = workspace;
this.qIndex = qIndex;
this.mapVariant = mapVariant;
this.Count = 0;
}
///
/// Gets the number of final blocks visited by the writer.
///
public int Count { get; private set; }
///
public void EncodeBlock(
Av1SymbolEncoder writer,
Av1MacroBlockD macroBlock,
Point blockOrigin,
ushort tileIndex,
ref Av1MacroBlockModeInfo modeInfo,
ref Av1EncoderBlockStruct block,
ref Av1EncoderPaletteInfo paletteInfo)
{
this.Count++;
modeInfo.Block = new Av1EncoderBlockModeInfo
{
BlockSize = Av1BlockSize.Block8x8,
PartitionType = Av1PartitionType.None,
SegmentId = 0,
Skip = true,
TransformSize = Av1TransformSize.Size8x8,
Mode = Av1PredictionMode.DC,
UvMode = Av1ChromaPredictionMode.DC
};
block.HasChroma = false;
block.QuantizationIndex = this.qIndex;
block.SegmentId = 0;
paletteInfo.PaletteSizes[0] = 3;
paletteInfo.SetColors(Av1Plane.Y, [16, 128, 240]);
Buffer2DRegion map = this.workspace
.GetPaletteMaps()
.GetMap(Av1PlaneType.Y, 8, 8);
for (int row = 0; row < map.Height; row++)
{
Span mapRow = map.DangerousGetRowSpan(row);
for (int column = 0; column < map.Width; column++)
{
mapRow[column] = this.mapVariant == 0
? (byte)0
: (byte)((row + column) % 3);
}
}
}
}
}