mirror of https://github.com/SixLabors/ImageSharp
33 changed files with 1858 additions and 0 deletions
@ -0,0 +1,150 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1; |
|||
|
|||
internal ref struct Av1BitStreamReader(Span<byte> data) |
|||
{ |
|||
private const int WordSize = sizeof(byte); |
|||
private const int DoubleWordSize = 2 * WordSize; |
|||
private readonly Span<byte> data = data; |
|||
private int wordPosition = 0; |
|||
private int bitOffset = 0; |
|||
|
|||
public readonly int BitPosition => (this.wordPosition * WordSize) + this.bitOffset; |
|||
|
|||
public readonly int Length => this.data.Length; |
|||
|
|||
public void Reset() |
|||
{ |
|||
this.wordPosition = 0; |
|||
this.bitOffset = 0; |
|||
} |
|||
|
|||
public void Skip(int bitCount) |
|||
{ |
|||
this.bitOffset += bitCount; |
|||
while (this.bitOffset >= WordSize) |
|||
{ |
|||
this.bitOffset -= WordSize; |
|||
this.wordPosition++; |
|||
} |
|||
} |
|||
|
|||
public uint ReadLiteral(int bitCount) |
|||
{ |
|||
uint bits = (uint)(this.data[this.wordPosition] << this.bitOffset) >> (WordSize - bitCount); |
|||
this.bitOffset += bitCount; |
|||
while (this.bitOffset > WordSize) |
|||
{ |
|||
uint nextWord = this.data[this.wordPosition + 1]; |
|||
bits |= nextWord << (DoubleWordSize - bitCount); |
|||
} |
|||
|
|||
if (this.bitOffset >= WordSize) |
|||
{ |
|||
this.bitOffset -= WordSize; |
|||
} |
|||
|
|||
return bits; |
|||
} |
|||
|
|||
internal bool ReadBoolean() |
|||
{ |
|||
bool bit = (this.data[this.wordPosition] & (1 << (WordSize - this.bitOffset))) > 0; |
|||
this.Skip(1); |
|||
return bit; |
|||
} |
|||
|
|||
public ulong ReadLittleEndianBytes128(out int length) |
|||
{ |
|||
// See section 4.10.5 of the AV1-Specification
|
|||
DebugGuard.IsTrue((this.bitOffset & (WordSize - 1)) == 0, "Reading of Little Endian 128 value only allowed on byte alignment"); |
|||
|
|||
ulong value = 0; |
|||
length = 0; |
|||
for (int i = 0; i < 56; i += 7) |
|||
{ |
|||
uint leb128Byte = this.ReadLiteral(8); |
|||
value |= (leb128Byte & 0x7FUL) << i; |
|||
length++; |
|||
if ((leb128Byte & 0x80U) != 0x80U) |
|||
{ |
|||
break; |
|||
} |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
|
|||
public uint ReadUnsignedVariableLength() |
|||
{ |
|||
// See section 4.10.3 of the AV1-Specification
|
|||
int leadingZerosCount = 0; |
|||
while (leadingZerosCount < 32 && this.ReadLiteral(1) == 0U) |
|||
{ |
|||
leadingZerosCount++; |
|||
} |
|||
|
|||
if (leadingZerosCount == 32) |
|||
{ |
|||
return uint.MaxValue; |
|||
} |
|||
|
|||
uint basis = (1U << leadingZerosCount) - 1U; |
|||
uint value = this.ReadLiteral(leadingZerosCount); |
|||
return basis + value; |
|||
} |
|||
|
|||
public uint ReadNonSymmetric(uint n) |
|||
{ |
|||
// See section 4.10.7 of the AV1-Specification
|
|||
if (n <= 1) |
|||
{ |
|||
return 0; |
|||
} |
|||
|
|||
int w = (int)(Av1Math.MostSignificantBit(n) + 1); |
|||
uint m = (uint)((1 << w) - n); |
|||
uint v = this.ReadLiteral(w - 1); |
|||
if (v < m) |
|||
{ |
|||
return v; |
|||
} |
|||
|
|||
return (v << 1) - m + this.ReadLiteral(1); |
|||
} |
|||
|
|||
public int ReadSignedFromUnsigned(int n) |
|||
{ |
|||
// See section 4.10.6 of the AV1-Specification
|
|||
int signedValue; |
|||
uint value = this.ReadLiteral(n); |
|||
uint signMask = 1U << (n - 1); |
|||
if ((value & signMask) == signMask) |
|||
{ |
|||
// Prevent overflow by casting to long;
|
|||
signedValue = (int)((long)value - (signMask << 1)); |
|||
} |
|||
else |
|||
{ |
|||
signedValue = (int)value; |
|||
} |
|||
|
|||
return signedValue; |
|||
} |
|||
|
|||
public uint ReadLittleEndian(int n) |
|||
{ |
|||
// See section 4.10.4 of the AV1-Specification
|
|||
DebugGuard.IsTrue((this.bitOffset & (WordSize - 1)) == 0, "Reading of Little Endian value only allowed on byte alignment"); |
|||
|
|||
uint t = 0; |
|||
for (int i = 0; i < 8 * n; i += 8) |
|||
{ |
|||
t += this.ReadLiteral(8) << i; |
|||
} |
|||
|
|||
return t; |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1; |
|||
|
|||
internal enum Av1BlockSize |
|||
{ |
|||
Block4x4, |
|||
Block4x8, |
|||
Block8x4, |
|||
Block8x8, |
|||
Block8x16, |
|||
Block16x8, |
|||
Block16x16, |
|||
Block16x32, |
|||
Block32x16, |
|||
Block32x32, |
|||
Block32x64, |
|||
Block64x32, |
|||
Block64x64, |
|||
Block64x128, |
|||
Block128x64, |
|||
Block128x128, |
|||
Block4x16, |
|||
Block16x4, |
|||
Block8x32, |
|||
Block32x8, |
|||
Block16x64, |
|||
Block64x16, |
|||
BlockSizeSAll, |
|||
BlockSizeS = Block4x16, |
|||
BlockInvalid = 255, |
|||
BlockLargest = BlockSizeS - 1, |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1; |
|||
|
|||
internal enum Av1ColorFormat |
|||
{ |
|||
Yuv400, |
|||
Yuv420, |
|||
Yuv422, |
|||
Yuv444, |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1; |
|||
|
|||
internal class Av1MainParseContext |
|||
{ |
|||
} |
|||
@ -0,0 +1,108 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1; |
|||
|
|||
internal static class Av1Math |
|||
{ |
|||
public static uint MostSignificantBit(uint value) => value >> 31; |
|||
|
|||
public static uint Log2(uint n) |
|||
{ |
|||
uint result = 0U; |
|||
while ((n >>= 1) > 0) |
|||
{ |
|||
result++; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public static int Log2(int n) |
|||
{ |
|||
int result = 0; |
|||
while ((n >>= 1) > 0) |
|||
{ |
|||
result++; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public static uint FloorLog2(uint value) |
|||
{ |
|||
uint s = 0; |
|||
while (value != 0U) |
|||
{ |
|||
value >>= 1; |
|||
s++; |
|||
} |
|||
|
|||
return s - 1; |
|||
} |
|||
|
|||
public static uint CeilLog2(uint value) |
|||
{ |
|||
if (value < 2) |
|||
{ |
|||
return 0; |
|||
} |
|||
|
|||
uint i = 1; |
|||
uint p = 2; |
|||
while (p < value) |
|||
{ |
|||
i++; |
|||
p <<= 1; |
|||
} |
|||
|
|||
return i; |
|||
} |
|||
|
|||
public static uint Clip1(uint value, int bitDepth) => |
|||
Clip3(0, (1U << bitDepth) - 1, value); |
|||
|
|||
public static uint Clip3(uint x, uint y, uint z) |
|||
{ |
|||
if (z < x) |
|||
{ |
|||
return x; |
|||
} |
|||
|
|||
if (z > y) |
|||
{ |
|||
return y; |
|||
} |
|||
|
|||
return z; |
|||
} |
|||
|
|||
public static uint Round2(uint value, int n) |
|||
{ |
|||
if (n == 0) |
|||
{ |
|||
return value; |
|||
} |
|||
|
|||
return (uint)((value + (1 << (n - 1))) >> n); |
|||
} |
|||
|
|||
public static int Round2(int value, int n) |
|||
{ |
|||
if (value < 0) |
|||
{ |
|||
value = -value; |
|||
} |
|||
|
|||
return (int)Round2((uint)value, n); |
|||
} |
|||
|
|||
internal static int AlignPowerOf2(int value, int n) |
|||
{ |
|||
int mask = (1 << n) - 1; |
|||
return (value + mask) & ~mask; |
|||
} |
|||
|
|||
internal static int Clamp(int value, int low, int high) |
|||
=> value < low ? low : (value > high ? high : value); |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1; |
|||
|
|||
internal enum Av1Plane : int |
|||
{ |
|||
Y = 0, |
|||
U = 1, |
|||
V = 2, |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; |
|||
|
|||
internal enum ObuChromoSamplePosition : byte |
|||
{ |
|||
/// <summary>
|
|||
/// Unknown.
|
|||
/// </summary>
|
|||
Unknown = 0, |
|||
|
|||
/// <summary>
|
|||
/// Horizontally co-located with luma(0, 0) sample, between two vertical samples.
|
|||
/// </summary>
|
|||
Vertical = 1, |
|||
|
|||
/// <summary>
|
|||
/// Co-located with luma(0, 0) sample
|
|||
/// </summary>
|
|||
Colocated = 2, |
|||
} |
|||
@ -0,0 +1,52 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; |
|||
|
|||
internal class ObuColorConfig |
|||
{ |
|||
internal bool IsColorDescriptionPresent { get; set; } |
|||
|
|||
internal int ChannelCount { get; set; } |
|||
|
|||
internal bool Monochrome { get; set; } |
|||
|
|||
internal ObuColorPrimaries ColorPrimaries { get; set; } |
|||
|
|||
internal ObuTransferCharacteristics TransferCharacteristics { get; set; } |
|||
|
|||
internal ObuMatrixCoefficients MatrixCoefficients { get; set; } |
|||
|
|||
internal bool ColorRange { get; set; } |
|||
|
|||
internal bool SubSamplingX { get; set; } |
|||
|
|||
internal bool SubSamplingY { get; set; } |
|||
|
|||
internal bool HasSeparateUvDelta { get; set; } |
|||
|
|||
internal ObuChromoSamplePosition ChromaSamplePosition { get; set; } |
|||
|
|||
internal int BitDepth { get; set; } |
|||
|
|||
internal bool HasSeparateUvDeltaQ { get; set; } |
|||
|
|||
public Av1ColorFormat GetColorFormat() |
|||
{ |
|||
Av1ColorFormat format = Av1ColorFormat.Yuv400; |
|||
if (this.SubSamplingX && this.SubSamplingY) |
|||
{ |
|||
format = Av1ColorFormat.Yuv420; |
|||
} |
|||
else if (this.SubSamplingX & !this.SubSamplingY) |
|||
{ |
|||
format = Av1ColorFormat.Yuv422; |
|||
} |
|||
else if (!this.SubSamplingX && !this.SubSamplingY) |
|||
{ |
|||
format = Av1ColorFormat.Yuv444; |
|||
} |
|||
|
|||
return format; |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; |
|||
|
|||
internal enum ObuColorPrimaries |
|||
{ |
|||
None = 0, |
|||
Bt709 = 1, |
|||
Unspecified = 2, |
|||
Bt470M = 4, |
|||
Bt470BG = 5, |
|||
Bt601 = 6, |
|||
Smpte240 = 7, |
|||
GenericFilm = 8, |
|||
Bt2020 = 9, |
|||
Xyz = 10, |
|||
Smpte431 = 11, |
|||
Smpte432 = 12, |
|||
Ebu3213 = 22, |
|||
} |
|||
@ -0,0 +1,82 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; |
|||
|
|||
internal static class ObuConstants |
|||
{ |
|||
public const ObuSequenceProfile MaxSequenceProfile = ObuSequenceProfile.Professional; |
|||
|
|||
public const int LevelBits = -1; |
|||
|
|||
/// <summary>
|
|||
/// Number of fractional bits for computing position in upscaling.
|
|||
/// </summary>
|
|||
public const int SuperResolutionScaleBits = 14; |
|||
|
|||
public const int ScaleNumerator = -1; |
|||
|
|||
/// <summary>
|
|||
/// Number of reference frames that can be used for inter prediction.
|
|||
/// </summary>
|
|||
public const int ReferencesPerFrame = 7; |
|||
|
|||
/// <summary>
|
|||
/// Maximum area of a tile in units of luma samples.
|
|||
/// </summary>
|
|||
public const int MaxTileArea = 4096 * 2304; |
|||
|
|||
/// <summary>
|
|||
/// Maximum width of a tile in units of luma samples.
|
|||
/// </summary>
|
|||
public const int MaxTileWidth = 4096; |
|||
|
|||
/// <summary>
|
|||
/// Maximum number of tile columns.
|
|||
/// </summary>
|
|||
public const int MaxTileColumnCount = 64; |
|||
|
|||
/// <summary>
|
|||
/// Maximum number of tile rows.
|
|||
/// </summary>
|
|||
public const int MaxTileRowCount = 64; |
|||
|
|||
/// <summary>
|
|||
/// Number of frames that can be stored for future reference.
|
|||
/// </summary>
|
|||
public const int ReferenceFrameCount = 8; |
|||
|
|||
/// <summary>
|
|||
/// Value of 'PrimaryReferenceFrame' indicating that there is no primary reference frame.
|
|||
/// </summary>
|
|||
public const uint PrimaryReferenceFrameNone = 7; |
|||
|
|||
public const int PimaryReferenceBits = -1; |
|||
|
|||
/// <summary>
|
|||
/// Number of segments allowed in segmentation map.
|
|||
/// </summary>
|
|||
public const int MaxSegmentCount = 8; |
|||
|
|||
/// <summary>
|
|||
/// Smallest denominator for upscaling ratio.
|
|||
/// </summary>
|
|||
public const int SuperResolutionScaleDenominatorMinimum = 9; |
|||
|
|||
/// <summary>
|
|||
/// Base 2 logarithm of maximum size of a superblock in luma samples.
|
|||
/// </summary>
|
|||
public const int MaxSuperBlockSizeLog2 = 7; |
|||
|
|||
/// <summary>
|
|||
/// Base 2 logarithm of smallest size of a mode info block.
|
|||
/// </summary>
|
|||
public const int ModeInfoSizeLog2 = 2; |
|||
|
|||
public const int MaxQ = 255; |
|||
|
|||
/// <summary>
|
|||
/// Number of segmentation features.
|
|||
/// </summary>
|
|||
public const int SegmentationLevelMax = 8; |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; |
|||
|
|||
internal class ObuConstraintDirectionalEnhancementFilterParameters |
|||
{ |
|||
public bool BitCount { get; internal set; } |
|||
|
|||
public int[] YStrength { get; internal set; } |
|||
|
|||
public int[] UVStrength { get; internal set; } |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; |
|||
|
|||
internal class ObuFilmGrainParameters |
|||
{ |
|||
} |
|||
@ -0,0 +1,81 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; |
|||
|
|||
internal class ObuFrameHeader |
|||
{ |
|||
public bool ForceIntegerMotionVector { get; set; } |
|||
|
|||
public bool AllowIntraBlockCopy { get; set; } |
|||
|
|||
public bool UseReferenceFrameMotionVectors { get; set; } |
|||
|
|||
public bool AllowHighPrecisionMotionVector { get; set; } |
|||
|
|||
public ObuTileInfo TilesInfo { get; internal set; } = new ObuTileInfo(); |
|||
|
|||
public bool CodedLossless { get; internal set; } |
|||
|
|||
public bool[] LosslessArray { get; internal set; } = new bool[ObuConstants.MaxSegmentCount]; |
|||
|
|||
public ObuQuantizationParameters QuantizationParameters { get; set; } = new ObuQuantizationParameters(); |
|||
|
|||
public ObuSegmentationParameters SegmentationParameters { get; set; } = new ObuSegmentationParameters(); |
|||
|
|||
public bool AllLossless { get; internal set; } |
|||
|
|||
public bool AllowWarpedMotion { get; internal set; } |
|||
|
|||
public ObuReferenceMode ReferenceMode { get; internal set; } |
|||
|
|||
public ObuFilmGrainParameters FilmGrainParameters { get; internal set; } = new ObuFilmGrainParameters(); |
|||
|
|||
public bool ReducedTxSet { get; internal set; } |
|||
|
|||
public ObuLoopFilterParameters LoopFilterParameters { get; internal set; } = new ObuLoopFilterParameters(); |
|||
|
|||
public ObuLoopRestorationParameters[] LoopRestorationParameters { get; internal set; } = new ObuLoopRestorationParameters[3]; |
|||
|
|||
public ObuConstraintDirectionalEnhancementFilterParameters ConstraintDirectionalEnhancementFilterParameters { get; internal set; } = new ObuConstraintDirectionalEnhancementFilterParameters(); |
|||
|
|||
public int ModeInfoStride { get; internal set; } |
|||
|
|||
public bool DisableFrameEndUpdateCdf { get; internal set; } |
|||
|
|||
internal ObuFrameSize FrameSize { get; set; } = new ObuFrameSize(); |
|||
|
|||
internal int ModeInfoColumnCount { get; set; } |
|||
|
|||
internal int ModeInfoRowCount { get; set; } |
|||
|
|||
internal bool ShowExistingFrame { get; set; } |
|||
|
|||
internal ObuFrameType FrameType { get; set; } |
|||
|
|||
internal bool[] ReferenceValid { get; set; } = new bool[ObuConstants.ReferenceFrameCount]; |
|||
|
|||
internal bool[] ReferenceOrderHint { get; set; } = new bool[ObuConstants.ReferenceFrameCount]; |
|||
|
|||
internal bool ShowFrame { get; set; } |
|||
|
|||
internal bool ShowableFrame { get; set; } |
|||
|
|||
internal bool ErrorResilientMode { get; set; } |
|||
|
|||
internal bool AllowScreenContentTools { get; set; } |
|||
|
|||
internal bool DisableCdfUpdate { get; set; } |
|||
|
|||
internal bool ForeceIntegerMotionVector { get; set; } |
|||
|
|||
internal uint CurrentFrameId { get; set; } |
|||
|
|||
internal uint[] ReferenceFrameIndex { get; set; } = new uint[ObuConstants.ReferenceFrameCount]; |
|||
|
|||
internal uint OrderHint { get; set; } |
|||
|
|||
internal uint PrimaryReferenceFrame { get; set; } = ObuConstants.PrimaryReferenceFrameNone; |
|||
|
|||
internal uint RefreshFrameFlags { get; set; } |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; |
|||
|
|||
internal class ObuFrameSize |
|||
{ |
|||
internal int FrameWidth { get; set; } |
|||
|
|||
internal int FrameHeight { get; set; } |
|||
|
|||
internal int SuperResolutionDenominator { get; set; } |
|||
|
|||
internal int SuperResolutionUpscaledWidth { get; set; } |
|||
|
|||
internal int RenderWidth { get; set; } |
|||
|
|||
internal int RenderHeight { get; set; } |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; |
|||
|
|||
internal enum ObuFrameType |
|||
{ |
|||
KeyFrame = 0, |
|||
InterFrame = 1, |
|||
IntraOnlyFrame = 2, |
|||
SwitchFrame = 3, |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; |
|||
|
|||
internal class ObuHeader |
|||
{ |
|||
public int Size { get; set; } |
|||
|
|||
public ObuType Type { get; set; } |
|||
|
|||
public bool HasSize { get; set; } |
|||
|
|||
public bool HasExtension { get; set; } |
|||
|
|||
public int TemporalId { get; set; } |
|||
|
|||
public int SpatialId { get; set; } |
|||
|
|||
public int PayloadSize { get; set; } |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; |
|||
|
|||
internal class ObuLoopFilterParameters |
|||
{ |
|||
public bool[] FilterLevel { get; internal set; } |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; |
|||
|
|||
internal class ObuLoopRestorationParameters |
|||
{ |
|||
public ObuRestorationType FrameRestorationType { get; internal set; } |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; |
|||
|
|||
internal enum ObuMatrixCoefficients |
|||
{ |
|||
Identity = 0, |
|||
Bt407 = 1, |
|||
Unspecified = 2, |
|||
Fcc = 4, |
|||
Bt470BG = 5, |
|||
Bt601 = 6, |
|||
Smpte240 = 7, |
|||
SmpteYCgCo = 8, |
|||
Bt2020NonConstantLuminance = 9, |
|||
Bt2020ConstantLuminance = 10, |
|||
Smpte2085 = 11, |
|||
ChromaticityDerivedNonConstantLuminance = 12, |
|||
ChromaticityDerivedConstandLuminance = 13, |
|||
Bt2100ICtCp = 14, |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; |
|||
|
|||
internal enum ObuMetadataType |
|||
{ |
|||
ItutT35, |
|||
HdrCll, |
|||
HdrMdcv, |
|||
Scalability, |
|||
Timecode |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; |
|||
|
|||
internal class ObuOperatingPoint |
|||
{ |
|||
internal int OperatorIndex { get; set; } |
|||
|
|||
internal int SequenceLevelIndex { get; set; } |
|||
|
|||
internal int SequenceTier { get; set; } |
|||
|
|||
internal bool IsDecoderModelPresent { get; set; } |
|||
|
|||
internal bool IsInitialDisplayDelayPresent { get; set; } |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; |
|||
|
|||
internal class ObuOrderHintInfo |
|||
{ |
|||
internal bool EnableJointCompound { get; set; } |
|||
|
|||
internal bool EnableReferenceFrameMotionVectors { get; set; } |
|||
|
|||
internal int OrderHintBits { get; set; } |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; |
|||
|
|||
internal class ObuQuantizationParameters |
|||
{ |
|||
public int BaseQIndex { get; set; } |
|||
|
|||
public int[] QIndex { get; set; } = new int[ObuConstants.MaxSegmentCount]; |
|||
|
|||
public bool IsUsingQMatrix { get; internal set; } |
|||
|
|||
public int[] DeltaQDc { get; internal set; } = new int[3]; |
|||
|
|||
public int[] DeltaQAc { get; internal set; } = new int[3]; |
|||
|
|||
public int[] QMatrix { get; internal set; } = new int[3]; |
|||
} |
|||
@ -0,0 +1,888 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; |
|||
|
|||
internal class ObuReader |
|||
{ |
|||
private static readonly int[] WienerTapsMid = new[] { 3, -7, 15 }; |
|||
private static readonly int[] SgrprojXqdMid = new[] { -32, 31 }; |
|||
|
|||
private static ObuHeader ReadObuHeader(Av1BitStreamReader reader) |
|||
{ |
|||
ObuHeader header = new(); |
|||
if (!reader.ReadBoolean()) |
|||
{ |
|||
throw new ImageFormatException("Forbidden bit in header should be unset."); |
|||
} |
|||
|
|||
header.Type = (ObuType)reader.ReadLiteral(4); |
|||
header.HasExtension = reader.ReadBoolean(); |
|||
header.HasSize = reader.ReadBoolean(); |
|||
if (!reader.ReadBoolean()) |
|||
{ |
|||
throw new ImageFormatException("Reserved bit in header should be unset."); |
|||
} |
|||
|
|||
if (header.HasExtension) |
|||
{ |
|||
header.Size++; |
|||
header.TemporalId = (int)reader.ReadLiteral(3); |
|||
header.SpatialId = (int)reader.ReadLiteral(3); |
|||
if (reader.ReadLiteral(3) != 0u) |
|||
{ |
|||
throw new ImageFormatException("Reserved bits in header extension should be unset."); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
header.SpatialId = 0; |
|||
header.TemporalId = 0; |
|||
} |
|||
|
|||
return header; |
|||
} |
|||
|
|||
private static void ReadObuSize(Av1BitStreamReader reader, out int obuSize) |
|||
{ |
|||
ulong rawSize = reader.ReadLittleEndianBytes128(out _); |
|||
if (rawSize > uint.MaxValue) |
|||
{ |
|||
throw new ImageFormatException("OBU block too large."); |
|||
} |
|||
|
|||
obuSize = (int)rawSize; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Read OBU header and size.
|
|||
/// </summary>
|
|||
private static ObuHeader ReadObuHeaderSize(Av1BitStreamReader reader) |
|||
{ |
|||
ObuHeader header = ReadObuHeader(reader); |
|||
if (header.HasSize) |
|||
{ |
|||
ReadObuSize(reader, out int payloadSize); |
|||
header.PayloadSize = payloadSize; |
|||
} |
|||
|
|||
return header; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Check that the trailing bits start with a 1 and end with 0s.
|
|||
/// </summary>
|
|||
/// <remarks>Consumes a byte, if already byte aligned before the check.</remarks>
|
|||
private static void ReadTrailingBits(Av1BitStreamReader reader) |
|||
{ |
|||
int bitsBeforeAlignment = 8 - (reader.BitPosition & 0x7); |
|||
uint trailing = reader.ReadLiteral(bitsBeforeAlignment); |
|||
if (trailing != (1 << (bitsBeforeAlignment - 1))) |
|||
{ |
|||
throw new ImageFormatException("Trailing bits not properly formatted."); |
|||
} |
|||
} |
|||
|
|||
private static void AlignToByteBoundary(Av1BitStreamReader reader) |
|||
{ |
|||
while ((reader.BitPosition & 0x7) > 0) |
|||
{ |
|||
if (reader.ReadBoolean()) |
|||
{ |
|||
throw new ImageFormatException("Incorrect byte alignment padding bits."); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private static void ComputeImageSize(ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo) |
|||
{ |
|||
frameInfo.ModeInfoColumnCount = 2 * ((frameInfo.FrameSize.FrameWidth + 7) >> 3); |
|||
frameInfo.ModeInfoRowCount = 2 * ((frameInfo.FrameSize.FrameHeight + 7) >> 3); |
|||
frameInfo.ModeInfoStride = Av1Math.AlignPowerOf2(sequenceHeader.MaxFrameWidth, ObuConstants.MaxSuperBlockSizeLog2) >> ObuConstants.ModeInfoSizeLog2; |
|||
} |
|||
|
|||
private static bool IsValidObuType(ObuType type) => type switch |
|||
{ |
|||
ObuType.SequenceHeader or ObuType.TemporalDelimiter or ObuType.FrameHeader or |
|||
ObuType.TileGroup or ObuType.Metadata or ObuType.Frame or ObuType.RedundantFrameHeader or |
|||
ObuType.TileList or ObuType.Padding => true, |
|||
_ => false, |
|||
}; |
|||
|
|||
private static ObuSequenceHeader ReadSequenceHeader(Av1BitStreamReader reader) |
|||
{ |
|||
ObuSequenceHeader sequenceHeader = new(); |
|||
sequenceHeader.SequenceProfile = (ObuSequenceProfile)reader.ReadLiteral(3); |
|||
if (sequenceHeader.SequenceProfile > ObuConstants.MaxSequenceProfile) |
|||
{ |
|||
throw new ImageFormatException("Unknown sequence profile."); |
|||
} |
|||
|
|||
sequenceHeader.IsStillPicture = reader.ReadBoolean(); |
|||
sequenceHeader.IsReducedStillPictureHeader = reader.ReadBoolean(); |
|||
if (!sequenceHeader.IsStillPicture || !sequenceHeader.IsReducedStillPictureHeader) |
|||
{ |
|||
throw new ImageFormatException("Not a picture header, is this a movie file ??"); |
|||
} |
|||
|
|||
sequenceHeader.TimingInfo = null; |
|||
sequenceHeader.DecoderModelInfoPresentFlag = false; |
|||
sequenceHeader.InitialDisplayDelayPresentFlag = false; |
|||
sequenceHeader.OperatingPoint[0].OperatorIndex = 0; |
|||
sequenceHeader.OperatingPoint[0].SequenceLevelIndex = (int)reader.ReadLiteral(ObuConstants.LevelBits); |
|||
if (!IsValidSequenceLevel(sequenceHeader.OperatingPoint[0].SequenceLevelIndex)) |
|||
{ |
|||
throw new ImageFormatException("Unknown sequnce level."); |
|||
} |
|||
|
|||
sequenceHeader.OperatingPoint[0].SequenceTier = 0; |
|||
sequenceHeader.OperatingPoint[0].IsDecoderModelPresent = false; |
|||
sequenceHeader.OperatingPoint[0].IsInitialDisplayDelayPresent = false; |
|||
|
|||
// Video related flags removed
|
|||
|
|||
// SVT-TODO: int operatingPoint = this.ChooseOperatingPoint();
|
|||
// sequenceHeader.OperatingPointIndex = (int)operatingPointIndices[operatingPoint];
|
|||
sequenceHeader.FrameWidthBits = (int)reader.ReadLiteral(4) + 1; |
|||
sequenceHeader.FrameHeightBits = (int)reader.ReadLiteral(4) + 1; |
|||
sequenceHeader.MaxFrameWidth = (int)reader.ReadLiteral(sequenceHeader.FrameWidthBits) + 1; |
|||
sequenceHeader.MaxFrameHeight = (int)reader.ReadLiteral(sequenceHeader.FrameHeightBits) + 1; |
|||
sequenceHeader.IsFrameIdNumbersPresent = false; |
|||
|
|||
// Video related flags removed
|
|||
sequenceHeader.Use128x128SuperBlock = reader.ReadBoolean(); |
|||
sequenceHeader.SuperBlockSize = sequenceHeader.Use128x128SuperBlock ? Av1BlockSize.Block128x128 : Av1BlockSize.Block64x64; |
|||
sequenceHeader.ModeInfoSize = sequenceHeader.Use128x128SuperBlock ? 32 : 16; |
|||
sequenceHeader.SuperBlockSizeLog2 = sequenceHeader.Use128x128SuperBlock ? 7 : 6; |
|||
sequenceHeader.FilterIntraLevel = (int)reader.ReadLiteral(1); |
|||
sequenceHeader.EnableIntraEdgeFilter = reader.ReadBoolean(); |
|||
sequenceHeader.EnableInterIntraCompound = false; |
|||
sequenceHeader.EnableMaskedCompound = false; |
|||
sequenceHeader.EnableWarpedMotion = false; |
|||
sequenceHeader.EnableDualFilter = false; |
|||
sequenceHeader.OrderHintInfo.EnableJointCompound = false; |
|||
sequenceHeader.OrderHintInfo.EnableReferenceFrameMotionVectors = false; |
|||
sequenceHeader.SequenceForceScreenContentTools = 2; |
|||
sequenceHeader.SequenceForceIntegerMotionVector = 2; |
|||
sequenceHeader.OrderHintInfo.OrderHintBits = 0; |
|||
|
|||
// Video related flags removed
|
|||
sequenceHeader.EnableSuperResolution = reader.ReadBoolean(); |
|||
sequenceHeader.CdefLevel = (int)reader.ReadLiteral(1); |
|||
sequenceHeader.EnableRestoration = reader.ReadBoolean(); |
|||
sequenceHeader.ColorConfig = ReadColorConfig(reader, sequenceHeader); |
|||
sequenceHeader.AreFilmGrainingParametersPresent = reader.ReadBoolean(); |
|||
ReadTrailingBits(reader); |
|||
return sequenceHeader; |
|||
} |
|||
|
|||
private static ObuColorConfig ReadColorConfig(Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader) |
|||
{ |
|||
ObuColorConfig colorConfig = new(); |
|||
ReadBitDepth(reader, colorConfig, sequenceHeader); |
|||
colorConfig.Monochrome = false; |
|||
if (sequenceHeader.SequenceProfile == ObuSequenceProfile.High) |
|||
{ |
|||
colorConfig.Monochrome = reader.ReadBoolean(); |
|||
} |
|||
|
|||
colorConfig.ChannelCount = colorConfig.Monochrome ? 1 : 3; |
|||
colorConfig.IsColorDescriptionPresent = reader.ReadBoolean(); |
|||
colorConfig.ColorPrimaries = ObuColorPrimaries.Unspecified; |
|||
colorConfig.TransferCharacteristics = ObuTransferCharacteristics.Unspecified; |
|||
colorConfig.MatrixCoefficients = ObuMatrixCoefficients.Unspecified; |
|||
if (colorConfig.IsColorDescriptionPresent) |
|||
{ |
|||
colorConfig.ColorPrimaries = (ObuColorPrimaries)reader.ReadLiteral(8); |
|||
colorConfig.TransferCharacteristics = (ObuTransferCharacteristics)reader.ReadLiteral(8); |
|||
colorConfig.MatrixCoefficients = (ObuMatrixCoefficients)reader.ReadLiteral(8); |
|||
} |
|||
|
|||
colorConfig.ColorRange = false; |
|||
colorConfig.SubSamplingX = false; |
|||
colorConfig.SubSamplingY = false; |
|||
colorConfig.ChromaSamplePosition = ObuChromoSamplePosition.Unknown; |
|||
colorConfig.HasSeparateUvDelta = false; |
|||
if (colorConfig.Monochrome) |
|||
{ |
|||
colorConfig.ColorRange = reader.ReadBoolean(); |
|||
colorConfig.SubSamplingX = true; |
|||
colorConfig.SubSamplingY = true; |
|||
return colorConfig; |
|||
} |
|||
else if ( |
|||
colorConfig.ColorPrimaries == ObuColorPrimaries.Bt709 && |
|||
colorConfig.TransferCharacteristics == ObuTransferCharacteristics.Srgb && |
|||
colorConfig.MatrixCoefficients == ObuMatrixCoefficients.Identity) |
|||
{ |
|||
colorConfig.ColorRange = true; |
|||
colorConfig.SubSamplingX = false; |
|||
colorConfig.SubSamplingY = false; |
|||
} |
|||
else |
|||
{ |
|||
colorConfig.ColorRange = reader.ReadBoolean(); |
|||
if (sequenceHeader.SequenceProfile != ObuSequenceProfile.Main) |
|||
{ |
|||
if (colorConfig.BitDepth == 12) |
|||
{ |
|||
colorConfig.SubSamplingX = reader.ReadBoolean(); |
|||
if (colorConfig.SubSamplingX) |
|||
{ |
|||
colorConfig.SubSamplingY = reader.ReadBoolean(); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
colorConfig.SubSamplingX = true; |
|||
colorConfig.SubSamplingY = false; |
|||
} |
|||
} |
|||
|
|||
if (colorConfig.SubSamplingX && colorConfig.SubSamplingY) |
|||
{ |
|||
colorConfig.ChromaSamplePosition = (ObuChromoSamplePosition)reader.ReadLiteral(2); |
|||
} |
|||
} |
|||
|
|||
colorConfig.HasSeparateUvDeltaQ = reader.ReadBoolean(); |
|||
return colorConfig; |
|||
} |
|||
|
|||
private static void ReadBitDepth(Av1BitStreamReader reader, ObuColorConfig colorConfig, ObuSequenceHeader sequenceHeader) |
|||
{ |
|||
bool hasHighBitDepth = reader.ReadBoolean(); |
|||
if (sequenceHeader.SequenceProfile == ObuSequenceProfile.Professional && hasHighBitDepth) |
|||
{ |
|||
colorConfig.BitDepth = reader.ReadBoolean() ? 12 : 10; |
|||
} |
|||
else if (sequenceHeader.SequenceProfile <= ObuSequenceProfile.Professional) |
|||
{ |
|||
colorConfig.BitDepth = hasHighBitDepth ? 10 : 8; |
|||
} |
|||
} |
|||
|
|||
private static void ReadSuperResolutionParameters(Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo) |
|||
{ |
|||
bool useSuperResolution = false; |
|||
if (sequenceHeader.EnableSuperResolution) |
|||
{ |
|||
useSuperResolution = reader.ReadBoolean(); |
|||
} |
|||
|
|||
if (useSuperResolution) |
|||
{ |
|||
frameInfo.FrameSize.SuperResolutionDenominator = (int)reader.ReadLiteral(ObuConstants.SuperResolutionScaleBits) + ObuConstants.SuperResolutionScaleDenominatorMinimum; |
|||
} |
|||
else |
|||
{ |
|||
frameInfo.FrameSize.SuperResolutionDenominator = ObuConstants.ScaleNumerator; |
|||
} |
|||
|
|||
frameInfo.FrameSize.SuperResolutionUpscaledWidth = frameInfo.FrameSize.FrameWidth; |
|||
frameInfo.FrameSize.FrameWidth = |
|||
(frameInfo.FrameSize.SuperResolutionUpscaledWidth * ObuConstants.ScaleNumerator) + |
|||
(frameInfo.FrameSize.SuperResolutionDenominator / 2); |
|||
|
|||
if (frameInfo.FrameSize.SuperResolutionDenominator != ObuConstants.ScaleNumerator) |
|||
{ |
|||
int manWidth = Math.Min(16, frameInfo.FrameSize.SuperResolutionUpscaledWidth); |
|||
frameInfo.FrameSize.FrameWidth = Math.Max(manWidth, frameInfo.FrameSize.FrameWidth); |
|||
} |
|||
} |
|||
|
|||
private static void ReadRenderSize(Av1BitStreamReader reader, ObuFrameHeader frameInfo) |
|||
{ |
|||
bool renderSizeAndFrameSizeDifferent = reader.ReadBoolean(); |
|||
if (renderSizeAndFrameSizeDifferent) |
|||
{ |
|||
frameInfo.FrameSize.RenderWidth = (int)reader.ReadLiteral(16) + 1; |
|||
frameInfo.FrameSize.RenderHeight = (int)reader.ReadLiteral(16) + 1; |
|||
} |
|||
else |
|||
{ |
|||
frameInfo.FrameSize.RenderWidth = frameInfo.FrameSize.SuperResolutionUpscaledWidth; |
|||
frameInfo.FrameSize.RenderHeight = frameInfo.FrameSize.FrameHeight; |
|||
} |
|||
} |
|||
|
|||
private static void ReadFrameSizeWithReferences(Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, bool frameSizeOverrideFlag) |
|||
{ |
|||
bool foundReference = false; |
|||
for (int i = 0; i < ObuConstants.ReferencesPerFrame; i++) |
|||
{ |
|||
foundReference = reader.ReadBoolean(); |
|||
if (foundReference) |
|||
{ |
|||
// Take values over from reference frame
|
|||
break; |
|||
} |
|||
} |
|||
|
|||
if (!foundReference) |
|||
{ |
|||
ReadFrameSize(reader, sequenceHeader, frameInfo, frameSizeOverrideFlag); |
|||
ReadRenderSize(reader, frameInfo); |
|||
} |
|||
else |
|||
{ |
|||
ReadSuperResolutionParameters(reader, sequenceHeader, frameInfo); |
|||
ComputeImageSize(sequenceHeader, frameInfo); |
|||
} |
|||
} |
|||
|
|||
private static void ReadFrameSize(Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, bool frameSizeOverrideFlag) |
|||
{ |
|||
if (frameSizeOverrideFlag) |
|||
{ |
|||
frameInfo.FrameSize.FrameWidth = (int)reader.ReadLiteral(sequenceHeader.FrameWidthBits) + 1; |
|||
frameInfo.FrameSize.FrameHeight = (int)reader.ReadLiteral(sequenceHeader.FrameHeightBits) + 1; |
|||
} |
|||
else |
|||
{ |
|||
frameInfo.FrameSize.FrameWidth = sequenceHeader.MaxFrameWidth; |
|||
frameInfo.FrameSize.FrameHeight = sequenceHeader.MaxFrameHeight; |
|||
} |
|||
|
|||
ReadSuperResolutionParameters(reader, sequenceHeader, frameInfo); |
|||
ComputeImageSize(sequenceHeader, frameInfo); |
|||
} |
|||
|
|||
private static ObuTileInfo ReadTileInfo(Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo) |
|||
{ |
|||
ObuTileInfo tileInfo = new(); |
|||
int superBlockColumnCount; |
|||
int superBlockRowCount; |
|||
int superBlockShift; |
|||
if (sequenceHeader.Use128x128SuperBlock) |
|||
{ |
|||
superBlockColumnCount = (frameInfo.ModeInfoColumnCount + 31) >> 5; |
|||
superBlockRowCount = (frameInfo.ModeInfoRowCount + 31) >> 5; |
|||
superBlockShift = 5; |
|||
} |
|||
else |
|||
{ |
|||
superBlockColumnCount = (frameInfo.ModeInfoColumnCount + 15) >> 4; |
|||
superBlockRowCount = (frameInfo.ModeInfoRowCount + 15) >> 4; |
|||
superBlockShift = 4; |
|||
} |
|||
|
|||
int superBlockSize = superBlockShift + 2; |
|||
int maxTileAreaOfSuperBlock = ObuConstants.MaxTileArea >> (2 * superBlockSize); |
|||
|
|||
tileInfo.MaxTileWidthSuperBlock = ObuConstants.MaxTileWidth >> superBlockSize; |
|||
tileInfo.MaxTileHeightSuperBlock = (ObuConstants.MaxTileArea / ObuConstants.MaxTileWidth) >> superBlockSize; |
|||
tileInfo.MinLog2TileColumnCount = TileLog2(tileInfo.MaxTileWidthSuperBlock, superBlockColumnCount); |
|||
tileInfo.MaxLog2TileColumnCount = TileLog2(1, Math.Min(superBlockColumnCount, ObuConstants.MaxTileColumnCount)); |
|||
tileInfo.MaxLog2TileRowCount = TileLog2(1, Math.Min(superBlockRowCount, ObuConstants.MaxTileRowCount)); |
|||
tileInfo.MinLog2TileCount = Math.Max(tileInfo.MinLog2TileColumnCount, TileLog2(maxTileAreaOfSuperBlock, superBlockColumnCount * superBlockRowCount)); |
|||
tileInfo.HasUniformTileSpacing = reader.ReadBoolean(); |
|||
if (tileInfo.HasUniformTileSpacing) |
|||
{ |
|||
tileInfo.TileColumnCountLog2 = tileInfo.MinLog2TileColumnCount; |
|||
while (tileInfo.TileColumnCountLog2 < tileInfo.MaxLog2TileColumnCount) |
|||
{ |
|||
if (reader.ReadBoolean()) |
|||
{ |
|||
tileInfo.TileColumnCountLog2++; |
|||
} |
|||
else |
|||
{ |
|||
break; |
|||
} |
|||
} |
|||
|
|||
int tileWidthSuperBlock = (superBlockColumnCount + (1 << tileInfo.TileColumnCountLog2) - 1) >> tileInfo.TileColumnCountLog2; |
|||
if (tileWidthSuperBlock > tileInfo.MaxTileWidthSuperBlock) |
|||
{ |
|||
throw new ImageFormatException("Invalid tile width specified."); |
|||
} |
|||
|
|||
int i = 0; |
|||
tileInfo.TileColumnStartModeInfo = new int[superBlockColumnCount + 1]; |
|||
for (int startSuperBlock = 0; startSuperBlock < superBlockColumnCount; startSuperBlock += tileWidthSuperBlock) |
|||
{ |
|||
tileInfo.TileColumnStartModeInfo[i] = startSuperBlock << superBlockShift; |
|||
i++; |
|||
} |
|||
|
|||
tileInfo.TileColumnStartModeInfo[i] = frameInfo.ModeInfoColumnCount; |
|||
tileInfo.TileColumnCount = i; |
|||
|
|||
tileInfo.MinLog2TileRowCount = Math.Max(tileInfo.MinLog2TileCount - tileInfo.TileColumnCountLog2, 0); |
|||
tileInfo.TileRowCountLog2 = tileInfo.MinLog2TileRowCount; |
|||
while (tileInfo.TileRowCountLog2 < tileInfo.MaxLog2TileRowCount) |
|||
{ |
|||
if (reader.ReadBoolean()) |
|||
{ |
|||
tileInfo.TileRowCountLog2++; |
|||
} |
|||
else |
|||
{ |
|||
break; |
|||
} |
|||
} |
|||
|
|||
int tileHeightSuperBlock = (superBlockRowCount + (1 << tileInfo.TileRowCountLog2) - 1) >> tileInfo.TileRowCountLog2; |
|||
if (tileHeightSuperBlock > tileInfo.MaxTileHeightSuperBlock) |
|||
{ |
|||
throw new ImageFormatException("Invalid tile height specified."); |
|||
} |
|||
|
|||
i = 0; |
|||
tileInfo.TileRowStartModeInfo = new int[superBlockRowCount + 1]; |
|||
for (int startSuperBlock = 0; startSuperBlock < superBlockRowCount; startSuperBlock += tileHeightSuperBlock) |
|||
{ |
|||
tileInfo.TileRowStartModeInfo[i] = startSuperBlock << superBlockShift; |
|||
i++; |
|||
} |
|||
|
|||
tileInfo.TileRowStartModeInfo[i] = frameInfo.ModeInfoRowCount; |
|||
tileInfo.TileRowCount = i; |
|||
} |
|||
else |
|||
{ |
|||
uint widestTileSuperBlock = 0U; |
|||
int startSuperBlock = 0; |
|||
int i = 0; |
|||
for (; startSuperBlock < superBlockColumnCount; i++) |
|||
{ |
|||
tileInfo.TileColumnStartModeInfo[i] = startSuperBlock << superBlockShift; |
|||
uint maxWidth = (uint)Math.Min(superBlockColumnCount - startSuperBlock, tileInfo.MaxTileWidthSuperBlock); |
|||
uint widthInSuperBlocks = reader.ReadNonSymmetric(maxWidth) + 1; |
|||
widestTileSuperBlock = Math.Max(widthInSuperBlocks, widestTileSuperBlock); |
|||
startSuperBlock += (int)widthInSuperBlocks; |
|||
} |
|||
|
|||
if (startSuperBlock != superBlockColumnCount) |
|||
{ |
|||
throw new ImageFormatException("Super block tiles width does not add up to total width."); |
|||
} |
|||
|
|||
tileInfo.TileColumnStartModeInfo[i] = frameInfo.ModeInfoColumnCount; |
|||
tileInfo.TileColumnCount = i; |
|||
tileInfo.TileColumnCountLog2 = TileLog2(1, tileInfo.TileColumnCount); |
|||
if (tileInfo.MinLog2TileCount > 0) |
|||
{ |
|||
maxTileAreaOfSuperBlock = (superBlockRowCount * superBlockColumnCount) >> (tileInfo.MinLog2TileCount + 1); |
|||
} |
|||
else |
|||
{ |
|||
maxTileAreaOfSuperBlock = superBlockRowCount * superBlockColumnCount; |
|||
} |
|||
|
|||
DebugGuard.MustBeGreaterThan(widestTileSuperBlock, 0U, nameof(widestTileSuperBlock)); |
|||
tileInfo.MaxTileHeightSuperBlock = Math.Max(maxTileAreaOfSuperBlock / (int)widestTileSuperBlock, 1); |
|||
|
|||
startSuperBlock = 0; |
|||
for (i = 0; startSuperBlock < superBlockRowCount; i++) |
|||
{ |
|||
tileInfo.TileRowStartModeInfo[i] = startSuperBlock << superBlockShift; |
|||
uint maxHeight = (uint)Math.Min(superBlockRowCount - startSuperBlock, tileInfo.MaxTileHeightSuperBlock); |
|||
uint heightInSuperBlocks = reader.ReadNonSymmetric(maxHeight) + 1; |
|||
startSuperBlock += (int)heightInSuperBlocks; |
|||
} |
|||
|
|||
if (startSuperBlock != superBlockRowCount) |
|||
{ |
|||
throw new ImageFormatException("Super block tiles height does not add up to total height."); |
|||
} |
|||
|
|||
tileInfo.TileRowStartModeInfo[i] = frameInfo.ModeInfoRowCount; |
|||
tileInfo.TileRowCount = i; |
|||
tileInfo.TileRowCountLog2 = TileLog2(1, tileInfo.TileRowCount); |
|||
} |
|||
|
|||
if (tileInfo.TileColumnCount > ObuConstants.MaxTileColumnCount || tileInfo.TileRowCount > ObuConstants.MaxTileRowCount) |
|||
{ |
|||
throw new ImageFormatException("Tile width or height too big."); |
|||
} |
|||
|
|||
if (tileInfo.TileColumnCountLog2 > 0 || tileInfo.TileRowCountLog2 > 0) |
|||
{ |
|||
tileInfo.ContextUpdateTileId = reader.ReadLiteral(tileInfo.TileRowCountLog2 + tileInfo.TileColumnCountLog2); |
|||
tileInfo.TileSizeBytes = (int)reader.ReadLiteral(2) + 1; |
|||
} |
|||
else |
|||
{ |
|||
tileInfo.ContextUpdateTileId = 0; |
|||
} |
|||
|
|||
if (tileInfo.ContextUpdateTileId >= (tileInfo.TileColumnCount * tileInfo.TileRowCount)) |
|||
{ |
|||
throw new ImageFormatException("Context update Tile ID too large."); |
|||
} |
|||
|
|||
return tileInfo; |
|||
} |
|||
|
|||
private static void ReadUncompressedFrameHeader(Av1BitStreamReader reader, ObuHeader header, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, int planesCount) |
|||
{ |
|||
int idLength = 0; |
|||
uint previousFrameId = 0; |
|||
bool isIntraFrame = false; |
|||
bool frameSizeOverrideFlag = false; |
|||
if (sequenceHeader.IsFrameIdNumbersPresent) |
|||
{ |
|||
idLength = sequenceHeader.FrameIdLength - 1 + sequenceHeader.DeltaFrameIdLength - 2 + 3; |
|||
DebugGuard.MustBeLessThanOrEqualTo(idLength, 16, nameof(idLength)); |
|||
} |
|||
|
|||
if (sequenceHeader.IsReducedStillPictureHeader) |
|||
{ |
|||
frameInfo.ShowExistingFrame = false; |
|||
frameInfo.FrameType = ObuFrameType.KeyFrame; |
|||
isIntraFrame = true; |
|||
frameInfo.ShowFrame = true; |
|||
frameInfo.ShowableFrame = false; |
|||
frameInfo.ErrorResilientMode = true; |
|||
} |
|||
|
|||
if (frameInfo.FrameType == ObuFrameType.KeyFrame && frameInfo.ShowFrame) |
|||
{ |
|||
frameInfo.ReferenceValid = new bool[ObuConstants.ReferenceFrameCount]; |
|||
frameInfo.ReferenceOrderHint = new bool[ObuConstants.ReferenceFrameCount]; |
|||
for (int i = 0; i < ObuConstants.ReferenceFrameCount; i++) |
|||
{ |
|||
frameInfo.ReferenceValid[i] = false; |
|||
frameInfo.ReferenceOrderHint[i] = false; |
|||
} |
|||
} |
|||
|
|||
frameInfo.DisableCdfUpdate = reader.ReadBoolean(); |
|||
frameInfo.AllowScreenContentTools = sequenceHeader.SequenceForceScreenContentTools == 1; |
|||
if (frameInfo.AllowScreenContentTools) |
|||
{ |
|||
frameInfo.AllowScreenContentTools = reader.ReadBoolean(); |
|||
} |
|||
|
|||
if (frameInfo.AllowScreenContentTools) |
|||
{ |
|||
if (sequenceHeader.SequenceForceIntegerMotionVector == 1) |
|||
{ |
|||
frameInfo.ForeceIntegerMotionVector = reader.ReadBoolean(); |
|||
} |
|||
else |
|||
{ |
|||
frameInfo.ForceIntegerMotionVector = sequenceHeader.SequenceForceIntegerMotionVector != 0; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
frameInfo.ForeceIntegerMotionVector = false; |
|||
} |
|||
|
|||
if (isIntraFrame) |
|||
{ |
|||
frameInfo.ForeceIntegerMotionVector = true; |
|||
} |
|||
|
|||
bool havePreviousFrameId = !(frameInfo.FrameType == ObuFrameType.KeyFrame && frameInfo.ShowFrame); |
|||
if (havePreviousFrameId) |
|||
{ |
|||
previousFrameId = frameInfo.CurrentFrameId; |
|||
} |
|||
|
|||
if (sequenceHeader.IsFrameIdNumbersPresent) |
|||
{ |
|||
frameInfo.CurrentFrameId = reader.ReadLiteral(idLength); |
|||
if (havePreviousFrameId) |
|||
{ |
|||
uint diffFrameId = (frameInfo.CurrentFrameId > previousFrameId) ? |
|||
frameInfo.CurrentFrameId - previousFrameId : |
|||
(uint)((1 << idLength) + (int)frameInfo.CurrentFrameId - previousFrameId); |
|||
if (frameInfo.CurrentFrameId == previousFrameId || diffFrameId >= 1 << (idLength - 1)) |
|||
{ |
|||
throw new ImageFormatException("Current frame ID cannot be same as previous Frame ID"); |
|||
} |
|||
} |
|||
|
|||
int diffLength = sequenceHeader.DeltaFrameIdLength; |
|||
for (int i = 0; i < ObuConstants.ReferenceFrameCount; i++) |
|||
{ |
|||
if (frameInfo.CurrentFrameId > (1U << diffLength)) |
|||
{ |
|||
if ((frameInfo.ReferenceFrameIndex[i] > frameInfo.CurrentFrameId) || |
|||
frameInfo.ReferenceFrameIndex[i] > (frameInfo.CurrentFrameId - (1 - diffLength))) |
|||
{ |
|||
frameInfo.ReferenceValid[i] = false; |
|||
} |
|||
} |
|||
else if (frameInfo.ReferenceFrameIndex[i] > frameInfo.CurrentFrameId && |
|||
frameInfo.ReferenceFrameIndex[i] < ((1 << idLength) + (frameInfo.CurrentFrameId - (1 << diffLength)))) |
|||
{ |
|||
frameInfo.ReferenceValid[i] = false; |
|||
} |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
frameInfo.CurrentFrameId = 0; |
|||
} |
|||
|
|||
if (frameInfo.FrameType == ObuFrameType.SwitchFrame) |
|||
{ |
|||
frameSizeOverrideFlag = true; |
|||
} |
|||
else if (sequenceHeader.IsReducedStillPictureHeader) |
|||
{ |
|||
frameSizeOverrideFlag = false; |
|||
} |
|||
else |
|||
{ |
|||
frameSizeOverrideFlag = reader.ReadBoolean(); |
|||
} |
|||
|
|||
frameInfo.OrderHint = reader.ReadLiteral(sequenceHeader.OrderHintInfo.OrderHintBits); |
|||
if (isIntraFrame || frameInfo.ErrorResilientMode) |
|||
{ |
|||
frameInfo.PrimaryReferenceFrame = ObuConstants.PrimaryReferenceFrameNone; |
|||
} |
|||
else |
|||
{ |
|||
frameInfo.PrimaryReferenceFrame = reader.ReadLiteral(ObuConstants.PimaryReferenceBits); |
|||
} |
|||
|
|||
// Skipping, as no decoder info model present
|
|||
frameInfo.AllowHighPrecisionMotionVector = false; |
|||
frameInfo.UseReferenceFrameMotionVectors = false; |
|||
frameInfo.AllowIntraBlockCopy = false; |
|||
if (frameInfo.FrameType == ObuFrameType.SwitchFrame || (frameInfo.FrameType == ObuFrameType.KeyFrame && frameInfo.ShowFrame)) |
|||
{ |
|||
frameInfo.RefreshFrameFlags = 0xffU; |
|||
} |
|||
else |
|||
{ |
|||
frameInfo.RefreshFrameFlags = reader.ReadLiteral(8); |
|||
} |
|||
|
|||
if (frameInfo.FrameType == ObuFrameType.IntraOnlyFrame) |
|||
{ |
|||
DebugGuard.IsTrue(frameInfo.RefreshFrameFlags != 0xFFU, nameof(frameInfo.RefreshFrameFlags)); |
|||
} |
|||
|
|||
if (!isIntraFrame || (frameInfo.RefreshFrameFlags != 0xFFU)) |
|||
{ |
|||
if (frameInfo.ErrorResilientMode && sequenceHeader.OrderHintInfo != null) |
|||
{ |
|||
for (int i = 0; i < ObuConstants.ReferenceFrameCount; i++) |
|||
{ |
|||
int referenceOrderHint = (int)reader.ReadLiteral(sequenceHeader.OrderHintInfo.OrderHintBits); |
|||
if (referenceOrderHint != (frameInfo.ReferenceOrderHint[i] ? 1U : 0U)) |
|||
{ |
|||
frameInfo.ReferenceValid[i] = false; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (isIntraFrame) |
|||
{ |
|||
ReadFrameSize(reader, sequenceHeader, frameInfo, frameSizeOverrideFlag); |
|||
ReadRenderSize(reader, frameInfo); |
|||
if (frameInfo.AllowScreenContentTools && frameInfo.FrameSize.RenderWidth != 0) |
|||
{ |
|||
if (frameInfo.FrameSize.FrameWidth == frameInfo.FrameSize.SuperResolutionUpscaledWidth) |
|||
{ |
|||
frameInfo.AllowIntraBlockCopy = reader.ReadBoolean(); |
|||
} |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
// Single image is always Intra.
|
|||
} |
|||
|
|||
SetupFrameBufferReferences(sequenceHeader, frameInfo); |
|||
CheckAddTemporalMotionVectorBuffer(sequenceHeader, frameInfo); |
|||
SetupFrameSignBias(sequenceHeader, frameInfo); |
|||
|
|||
if (sequenceHeader.IsReducedStillPictureHeader || frameInfo.DisableCdfUpdate) |
|||
{ |
|||
frameInfo.DisableFrameEndUpdateCdf = true; |
|||
} |
|||
|
|||
if (frameInfo.PrimaryReferenceFrame == ObuConstants.PrimaryReferenceFrameNone) |
|||
{ |
|||
SetupPastIndependence(); |
|||
} |
|||
|
|||
GenerateNextReferenceFrameMap(sequenceHeader, frameInfo); |
|||
|
|||
frameInfo.TilesInfo = ReadTileInfo(reader, sequenceHeader, frameInfo); |
|||
frameInfo.QuantizationParameters = ReadQuantizationParameters(reader, sequenceHeader.ColorConfig, planesCount); |
|||
ReadSegmentationParameters(reader, sequenceHeader, frameInfo); |
|||
ReadDeltaQParameters(reader, frameInfo); |
|||
ReadDeltaLoopFilterParameters(reader, frameInfo); |
|||
SetupSegmentationDequantization(); |
|||
|
|||
Av1MainParseContext mainParseContext = new(); |
|||
if (frameInfo.PrimaryReferenceFrame == ObuConstants.PrimaryReferenceFrameNone) |
|||
{ |
|||
ResetParseContext(mainParseContext, frameInfo.QuantizationParameters.BaseQIndex); |
|||
} |
|||
|
|||
int tilesCount = frameInfo.TilesInfo.TileColumnCount * frameInfo.TilesInfo.TileRowCount; |
|||
frameInfo.CodedLossless = true; |
|||
for (int segmentId = 0; segmentId < ObuConstants.MaxSegmentCount; segmentId++) |
|||
{ |
|||
int qIndex = GetQIndex(frameInfo.SegmentationParameters, segmentId, frameInfo.QuantizationParameters.BaseQIndex); |
|||
frameInfo.QuantizationParameters.QIndex[segmentId] = qIndex; |
|||
frameInfo.LosslessArray[segmentId] = qIndex == 0 && |
|||
frameInfo.QuantizationParameters.DeltaQDc[(int)Av1Plane.Y] == 0 && |
|||
frameInfo.QuantizationParameters.DeltaQAc[(int)Av1Plane.U] == 0 && |
|||
frameInfo.QuantizationParameters.DeltaQDc[(int)Av1Plane.U] == 0 && |
|||
frameInfo.QuantizationParameters.DeltaQAc[(int)Av1Plane.V] == 0 && |
|||
frameInfo.QuantizationParameters.DeltaQDc[(int)Av1Plane.V] == 0; |
|||
if (!frameInfo.LosslessArray[segmentId]) |
|||
{ |
|||
frameInfo.CodedLossless = false; |
|||
} |
|||
|
|||
if (frameInfo.QuantizationParameters.IsUsingQMatrix) |
|||
{ |
|||
if (frameInfo.LosslessArray[segmentId]) |
|||
{ |
|||
frameInfo.SegmentationParameters.QMLevel[0, segmentId] = 15; |
|||
frameInfo.SegmentationParameters.QMLevel[1, segmentId] = 15; |
|||
frameInfo.SegmentationParameters.QMLevel[2, segmentId] = 15; |
|||
} |
|||
else |
|||
{ |
|||
frameInfo.SegmentationParameters.QMLevel[0, segmentId] = frameInfo.QuantizationParameters.QMatrix[(int)Av1Plane.Y]; |
|||
frameInfo.SegmentationParameters.QMLevel[1, segmentId] = frameInfo.QuantizationParameters.QMatrix[(int)Av1Plane.U]; |
|||
frameInfo.SegmentationParameters.QMLevel[2, segmentId] = frameInfo.QuantizationParameters.QMatrix[(int)Av1Plane.V]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
frameInfo.AllLossless = frameInfo.CodedLossless && frameInfo.FrameSize.FrameWidth == frameInfo.FrameSize.SuperResolutionUpscaledWidth; |
|||
ReadLoopFilterParameters(reader, sequenceHeader, frameInfo, planesCount); |
|||
ReadConstraintDirectionalEnhancementFilterParameters(reader, sequenceHeader, frameInfo, planesCount); |
|||
ReadLoopRestorationParameter(reader, sequenceHeader, frameInfo, planesCount); |
|||
ReadTransformMode(reader, frameInfo); |
|||
|
|||
frameInfo.ReferenceMode = ReadFrameReferenceMode(reader, isIntraFrame); |
|||
ReadSkipModeParameters(reader, frameInfo, isIntraFrame, sequenceHeader, frameInfo.ReferenceMode); |
|||
if (isIntraFrame || frameInfo.ErrorResilientMode || !sequenceHeader.EnableWarpedMotion) |
|||
{ |
|||
frameInfo.AllowWarpedMotion = false; |
|||
} |
|||
|
|||
frameInfo.ReducedTxSet = reader.ReadBoolean(); |
|||
ReadGlobalMotionParameters(reader, sequenceHeader, frameInfo, isIntraFrame); |
|||
frameInfo.FilmGrainParameters = ReadFilmGrainParameters(reader, sequenceHeader, frameInfo); |
|||
} |
|||
|
|||
private static bool IsSegmentationFeatureActive(ObuSegmentationParameters segmentationParameters, int segmentId, ObuSegmentationLevelFeature feature) |
|||
=> segmentationParameters.SegmentationEnabled && segmentationParameters.FeatureEnabled[segmentId, (int)feature]; |
|||
|
|||
private static int GetQIndex(ObuSegmentationParameters segmentationParameters, int segmentId, int baseQIndex) |
|||
{ |
|||
if (IsSegmentationFeatureActive(segmentationParameters, segmentId, ObuSegmentationLevelFeature.AlternativeQuantizer)) |
|||
{ |
|||
int data = segmentationParameters.FeatureData[segmentId, (int)ObuSegmentationLevelFeature.AlternativeQuantizer]; |
|||
int qIndex = baseQIndex + data; |
|||
return Av1Math.Clamp(qIndex, 0, ObuConstants.MaxQ); |
|||
} |
|||
else |
|||
{ |
|||
return baseQIndex; |
|||
} |
|||
} |
|||
|
|||
private static void ReadFrameHeader(Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, ObuHeader header, bool trailingBit) |
|||
{ |
|||
int planeCount = sequenceHeader.ColorConfig.Monochrome ? 1 : 3; |
|||
int startBitPosition = reader.BitPosition; |
|||
ReadUncompressedFrameHeader(reader, header, sequenceHeader, frameInfo, planeCount); |
|||
if (trailingBit) |
|||
{ |
|||
ReadTrailingBits(reader); |
|||
} |
|||
|
|||
AlignToByteBoundary(reader); |
|||
|
|||
int endPosition = reader.BitPosition; |
|||
int headerBytes = (endPosition - startBitPosition) / 8; |
|||
header.PayloadSize -= headerBytes; |
|||
} |
|||
|
|||
private static void ReadTileGroup(Av1BitStreamReader reader, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameInfo, ObuTileInfo tileInfo, ObuHeader header, out bool isLastTileGroup) |
|||
{ |
|||
int tileCount = tileInfo.TileColumnCount * tileInfo.TileRowCount; |
|||
int startBitPosition = reader.BitPosition; |
|||
bool tileStartAndEndPresentFlag = false; |
|||
if (tileCount > 1) |
|||
{ |
|||
tileStartAndEndPresentFlag = reader.ReadBoolean(); |
|||
} |
|||
|
|||
if (header.Type == ObuType.FrameHeader) |
|||
{ |
|||
DebugGuard.IsFalse(tileStartAndEndPresentFlag, nameof(tileStartAndEndPresentFlag), "Frame header should not set 'tileStartAndEndPresentFlag'."); |
|||
} |
|||
|
|||
int tileGroupStart = 0; |
|||
int tileGroupEnd = tileCount - 1; |
|||
if (tileCount != 1 && tileStartAndEndPresentFlag) |
|||
{ |
|||
int tileBits = Av1Math.Log2(tileInfo.TileColumnCount) + Av1Math.Log2(tileInfo.TileRowCount); |
|||
tileGroupStart = (int)reader.ReadLiteral(tileBits); |
|||
tileGroupEnd = (int)reader.ReadLiteral(tileBits); |
|||
} |
|||
|
|||
isLastTileGroup = (tileGroupEnd + 1) == tileCount; |
|||
AlignToByteBoundary(reader); |
|||
int endBitPosition = reader.BitPosition; |
|||
int headerBytes = (endBitPosition - startBitPosition) / 8; |
|||
header.PayloadSize -= headerBytes; |
|||
|
|||
bool noIbc = !frameInfo.AllowIntraBlockCopy; |
|||
bool doLoopFilter = noIbc && (frameInfo.LoopFilterParameters.FilterLevel[0] || frameInfo.LoopFilterParameters.FilterLevel[1]); |
|||
bool doCdef = noIbc && (!frameInfo.CodedLossless && |
|||
(frameInfo.ConstraintDirectionalEnhancementFilterParameters.BitCount || |
|||
frameInfo.ConstraintDirectionalEnhancementFilterParameters.YStrength[0] != 0 || |
|||
frameInfo.ConstraintDirectionalEnhancementFilterParameters.UVStrength[0] != 0)); |
|||
bool doLoopRestoration = noIbc && |
|||
(frameInfo.LoopRestorationParameters[(int)Av1Plane.Y].FrameRestorationType != ObuRestorationType.RestoreNone || |
|||
frameInfo.LoopRestorationParameters[(int)Av1Plane.U].FrameRestorationType != ObuRestorationType.RestoreNone || |
|||
frameInfo.LoopRestorationParameters[(int)Av1Plane.V].FrameRestorationType != ObuRestorationType.RestoreNone); |
|||
|
|||
for (int tileNum = tileGroupStart; tileNum <= tileGroupEnd; tileNum++) |
|||
{ |
|||
int tileRow = tileNum / tileInfo.TileColumnCount; |
|||
int tileColumn = tileNum % tileInfo.TileColumnCount; |
|||
bool isLastTile = tileNum == tileGroupEnd; |
|||
int tileSize = header.PayloadSize; |
|||
if (!isLastTile) |
|||
{ |
|||
tileSize = (int)reader.ReadLittleEndian(tileInfo.TileSizeBytes) + 1; |
|||
header.PayloadSize -= tileSize + tileInfo.TileSizeBytes; |
|||
} |
|||
|
|||
// TODO: Pass more info to the decoder.
|
|||
DecodeTile(sequenceHeader, frameInfo, tileInfo, tileNum); |
|||
} |
|||
|
|||
if (tileGroupEnd != tileCount - 1) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
FinishDecodeTiles(sequenceHeader, frameInfo, doCdef, doLoopRestoration); |
|||
} |
|||
|
|||
private static bool IsValidSequenceLevel(int sequenceLevelIndex) |
|||
=> sequenceLevelIndex is < 24 or 31; |
|||
|
|||
private static int TileLog2(int blockSize, int target) |
|||
{ |
|||
int k; |
|||
for (k = 0; (blockSize << k) < target; k++) |
|||
{ |
|||
} |
|||
|
|||
return k; |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; |
|||
|
|||
internal enum ObuReferenceMode |
|||
{ |
|||
ReferenceModeSelect, |
|||
SingleReference, |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; |
|||
|
|||
internal enum ObuRestorationType |
|||
{ |
|||
RestoreNone |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; |
|||
|
|||
internal enum ObuSegmentationLevelFeature |
|||
{ |
|||
AlternativeQuantizer, |
|||
AlternativeLoopFilterYVertical, |
|||
AlternativeLoopFilterYHorizontal, |
|||
AlternativeLoopFilterU, |
|||
AlternativeLoopFilterV, |
|||
ReferenceFrame, |
|||
Skip, |
|||
GlobalMotionVector, |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; |
|||
|
|||
internal class ObuSegmentationParameters |
|||
{ |
|||
public int[,] QMLevel { get; internal set; } = new int[3, ObuConstants.MaxSegmentCount]; |
|||
|
|||
public bool[,] FeatureEnabled { get; internal set; } = new bool[ObuConstants.MaxSegmentCount, ObuConstants.SegmentationLevelMax]; |
|||
|
|||
public bool SegmentationEnabled { get; internal set; } |
|||
|
|||
public int[,] FeatureData { get; internal set; } = new int[ObuConstants.MaxSegmentCount, ObuConstants.SegmentationLevelMax]; |
|||
} |
|||
@ -0,0 +1,71 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; |
|||
|
|||
internal class ObuSequenceHeader |
|||
{ |
|||
internal bool IsStillPicture { get; set; } |
|||
|
|||
internal bool IsReducedStillPictureHeader { get; set; } |
|||
|
|||
internal ObuSequenceProfile SequenceProfile { get; set; } |
|||
|
|||
internal ObuOperatingPoint[] OperatingPoint { get; set; } = new ObuOperatingPoint[1]; |
|||
|
|||
internal bool InitialDisplayDelayPresentFlag { get; set; } |
|||
|
|||
internal bool DecoderModelInfoPresentFlag { get; set; } |
|||
|
|||
internal object? TimingInfo { get; set; } |
|||
|
|||
internal bool IsFrameIdNumbersPresent { get; set; } |
|||
|
|||
internal int FrameWidthBits { get; set; } |
|||
|
|||
internal int FrameHeightBits { get; set; } |
|||
|
|||
internal int MaxFrameWidth { get; set; } |
|||
|
|||
internal int MaxFrameHeight { get; set; } |
|||
|
|||
internal bool Use128x128SuperBlock { get; set; } |
|||
|
|||
internal Av1BlockSize SuperBlockSize { get; set; } |
|||
|
|||
internal int ModeInfoSize { get; set; } |
|||
|
|||
internal int SuperBlockSizeLog2 { get; set; } |
|||
|
|||
internal int FilterIntraLevel { get; set; } |
|||
|
|||
internal bool EnableIntraEdgeFilter { get; set; } |
|||
|
|||
internal ObuOrderHintInfo OrderHintInfo { get; set; } = new ObuOrderHintInfo(); |
|||
|
|||
internal bool EnableInterIntraCompound { get; set; } |
|||
|
|||
internal bool EnableMaskedCompound { get; set; } |
|||
|
|||
internal bool EnableWarpedMotion { get; set; } |
|||
|
|||
internal bool EnableDualFilter { get; set; } |
|||
|
|||
internal int SequenceForceIntegerMotionVector { get; set; } |
|||
|
|||
internal int SequenceForceScreenContentTools { get; set; } |
|||
|
|||
internal bool EnableSuperResolution { get; set; } |
|||
|
|||
internal int CdefLevel { get; set; } |
|||
|
|||
internal bool EnableRestoration { get; set; } |
|||
|
|||
internal ObuColorConfig ColorConfig { get; set; } = new ObuColorConfig(); |
|||
|
|||
internal bool AreFilmGrainingParametersPresent { get; set; } |
|||
|
|||
internal int FrameIdLength { get; set; } |
|||
|
|||
internal int DeltaFrameIdLength { get; set; } |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; |
|||
|
|||
internal enum ObuSequenceProfile : uint |
|||
{ |
|||
Main = 0, |
|||
High = 1, |
|||
Professional = 2, |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; |
|||
|
|||
internal class ObuTileInfo |
|||
{ |
|||
internal int MaxTileWidthSuperBlock { get; set; } |
|||
|
|||
internal int MaxTileHeightSuperBlock { get; set; } |
|||
|
|||
internal int MinLog2TileColumnCount { get; set; } |
|||
|
|||
internal int MaxLog2TileColumnCount { get; set; } |
|||
|
|||
internal int MaxLog2TileRowCount { get; set; } |
|||
|
|||
internal int MinLog2TileCount { get; set; } |
|||
|
|||
public bool HasUniformTileSpacing { get; set; } |
|||
|
|||
internal int TileColumnCountLog2 { get; set; } |
|||
|
|||
internal int TileColumnCount { get; set; } |
|||
|
|||
internal int[] TileColumnStartModeInfo { get; set; } |
|||
|
|||
internal int MinLog2TileRowCount { get; set; } |
|||
|
|||
internal int TileRowCountLog2 { get; set; } |
|||
|
|||
internal int[] TileRowStartModeInfo { get; set; } |
|||
|
|||
internal int TileRowCount { get; set; } |
|||
|
|||
internal uint ContextUpdateTileId { get; set; } |
|||
|
|||
internal int TileSizeBytes { get; set; } |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; |
|||
|
|||
internal enum ObuTransferCharacteristics |
|||
{ |
|||
Bt709 = 1, |
|||
Unspecified = 2, |
|||
Bt470M = 4, |
|||
Bt470BG = 5, |
|||
Bt601 = 6, |
|||
Smpte240 = 7, |
|||
Linear = 8, |
|||
Log100 = 9, |
|||
Log100Sqrt10 = 10, |
|||
Iec61966 = 11, |
|||
Bt1361 = 12, |
|||
Srgb = 13, |
|||
Bt202010Bit = 14, |
|||
Bt202012Bit = 15, |
|||
Smpte2084 = 16, |
|||
Smpte248 = 17, |
|||
Hlg = 18, |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; |
|||
|
|||
internal enum ObuType |
|||
{ |
|||
None = 0, |
|||
SequenceHeader = 1, |
|||
TemporalDelimiter = 2, |
|||
FrameHeader = 3, |
|||
RedundantFrameHeader = 7, |
|||
TileGroup = 4, |
|||
Metadata = 5, |
|||
Frame = 6, |
|||
TileList = 8, |
|||
Padding = 15, |
|||
} |
|||
Loading…
Reference in new issue