mirror of https://github.com/SixLabors/ImageSharp
8 changed files with 1304 additions and 96 deletions
@ -0,0 +1,123 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Hevc; |
|||
|
|||
/// <summary>
|
|||
/// Provides shared bounded syntax operations used by HEVC parameter-set readers.
|
|||
/// </summary>
|
|||
internal static class HevcParameterSetSyntax |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the horizontal conformance-window unit for an HEVC chroma layout.
|
|||
/// </summary>
|
|||
/// <param name="chromaFormat">The chroma-format identifier.</param>
|
|||
/// <param name="separateColorPlane">Whether 4:4:4 components are coded as separate color planes.</param>
|
|||
/// <returns>The horizontal unit in luma samples.</returns>
|
|||
public static int GetCropUnitWidth(byte chromaFormat, bool separateColorPlane) |
|||
=> !separateColorPlane && chromaFormat is 1 or 2 ? 2 : 1; |
|||
|
|||
/// <summary>
|
|||
/// Gets the vertical conformance-window unit for an HEVC chroma layout.
|
|||
/// </summary>
|
|||
/// <param name="chromaFormat">The chroma-format identifier.</param>
|
|||
/// <param name="separateColorPlane">Whether 4:4:4 components are coded as separate color planes.</param>
|
|||
/// <returns>The vertical unit in luma samples.</returns>
|
|||
public static int GetCropUnitHeight(byte chromaFormat, bool separateColorPlane) |
|||
=> !separateColorPlane && chromaFormat == 1 ? 2 : 1; |
|||
|
|||
/// <summary>
|
|||
/// Consumes hypothetical-reference-decoder syntax without adding playback state to the still-image model.
|
|||
/// </summary>
|
|||
/// <param name="reader">The parameter-set raw byte sequence payload reader.</param>
|
|||
/// <param name="commonInformationPresent">Whether common HRD flags are coded for this parameter set.</param>
|
|||
/// <param name="maxSubLayersMinusOne">The highest declared temporal sublayer index.</param>
|
|||
/// <param name="nalHrdParametersPresent">The effective NAL HRD presence flag.</param>
|
|||
/// <param name="vclHrdParametersPresent">The effective VCL HRD presence flag.</param>
|
|||
/// <param name="subPictureHrdParametersPresent">The effective sub-picture HRD presence flag.</param>
|
|||
/// <exception cref="InvalidImageContentException">The HRD syntax is truncated or exceeds its registered bounds.</exception>
|
|||
public static void SkipHrdParameters( |
|||
ref HevcBitReader reader, |
|||
bool commonInformationPresent, |
|||
int maxSubLayersMinusOne, |
|||
ref bool nalHrdParametersPresent, |
|||
ref bool vclHrdParametersPresent, |
|||
ref bool subPictureHrdParametersPresent) |
|||
{ |
|||
if (commonInformationPresent) |
|||
{ |
|||
nalHrdParametersPresent = reader.ReadFlag(); |
|||
vclHrdParametersPresent = reader.ReadFlag(); |
|||
subPictureHrdParametersPresent = false; |
|||
if (nalHrdParametersPresent || vclHrdParametersPresent) |
|||
{ |
|||
subPictureHrdParametersPresent = reader.ReadFlag(); |
|||
if (subPictureHrdParametersPresent) |
|||
{ |
|||
reader.ReadBits(8); |
|||
reader.ReadBits(5); |
|||
reader.ReadFlag(); |
|||
reader.ReadBits(5); |
|||
} |
|||
|
|||
reader.ReadBits(4); |
|||
reader.ReadBits(4); |
|||
if (subPictureHrdParametersPresent) |
|||
{ |
|||
reader.ReadBits(4); |
|||
} |
|||
|
|||
reader.ReadBits(5); |
|||
reader.ReadBits(5); |
|||
reader.ReadBits(5); |
|||
} |
|||
} |
|||
|
|||
for (int subLayer = 0; subLayer <= maxSubLayersMinusOne; subLayer++) |
|||
{ |
|||
bool fixedPictureRateGeneral = reader.ReadFlag(); |
|||
bool fixedPictureRateWithinCvs = fixedPictureRateGeneral || reader.ReadFlag(); |
|||
bool lowDelayHrd = false; |
|||
if (fixedPictureRateWithinCvs) |
|||
{ |
|||
reader.ReadUnsignedExpGolomb(); |
|||
} |
|||
else |
|||
{ |
|||
lowDelayHrd = reader.ReadFlag(); |
|||
} |
|||
|
|||
uint cpbCountMinusOne = 0; |
|||
if (!lowDelayHrd) |
|||
{ |
|||
cpbCountMinusOne = reader.ReadUnsignedExpGolomb(); |
|||
if (cpbCountMinusOne > 31) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC HRD syntax declares too many coded-picture buffers."); |
|||
} |
|||
} |
|||
|
|||
for (int hrdKind = 0; hrdKind < 2; hrdKind++) |
|||
{ |
|||
bool parametersPresent = hrdKind == 0 ? nalHrdParametersPresent : vclHrdParametersPresent; |
|||
if (!parametersPresent) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
for (uint cpbIndex = 0; cpbIndex <= cpbCountMinusOne; cpbIndex++) |
|||
{ |
|||
reader.ReadUnsignedExpGolomb(); |
|||
reader.ReadUnsignedExpGolomb(); |
|||
if (subPictureHrdParametersPresent) |
|||
{ |
|||
reader.ReadUnsignedExpGolomb(); |
|||
reader.ReadUnsignedExpGolomb(); |
|||
} |
|||
|
|||
reader.ReadFlag(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,250 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Hevc; |
|||
|
|||
/// <summary>
|
|||
/// Contains the HEVC quantization scaling matrices and their large-transform DC coefficients.
|
|||
/// </summary>
|
|||
internal sealed class HevcScalingList |
|||
{ |
|||
/// <summary>
|
|||
/// The number of matrix identifiers defined for each transform-size category.
|
|||
/// </summary>
|
|||
private const int MatrixCount = 6; |
|||
|
|||
/// <summary>
|
|||
/// The flat default matrix used by four-by-four transforms.
|
|||
/// </summary>
|
|||
private static readonly byte[] Default4x4 = |
|||
[ |
|||
16, 16, 16, 16, |
|||
16, 16, 16, 16, |
|||
16, 16, 16, 16, |
|||
16, 16, 16, 16, |
|||
]; |
|||
|
|||
/// <summary>
|
|||
/// The default intra-predicted matrix used by transforms of eight-by-eight and larger.
|
|||
/// </summary>
|
|||
private static readonly byte[] DefaultIntra8x8 = |
|||
[ |
|||
16, 16, 16, 16, 17, 18, 21, 24, |
|||
16, 16, 16, 16, 17, 19, 22, 25, |
|||
16, 16, 17, 18, 20, 22, 25, 29, |
|||
16, 16, 18, 21, 24, 27, 31, 36, |
|||
17, 17, 20, 24, 30, 35, 41, 47, |
|||
18, 19, 22, 27, 35, 44, 54, 65, |
|||
21, 22, 25, 31, 41, 54, 70, 88, |
|||
24, 25, 29, 36, 47, 65, 88, 115, |
|||
]; |
|||
|
|||
/// <summary>
|
|||
/// The default inter-predicted matrix used by transforms of eight-by-eight and larger.
|
|||
/// </summary>
|
|||
private static readonly byte[] DefaultInter8x8 = |
|||
[ |
|||
16, 16, 16, 16, 17, 18, 20, 24, |
|||
16, 16, 16, 17, 18, 20, 24, 25, |
|||
16, 16, 17, 18, 20, 24, 25, 28, |
|||
16, 17, 18, 20, 24, 25, 28, 33, |
|||
17, 18, 20, 24, 25, 28, 33, 41, |
|||
18, 20, 24, 25, 28, 33, 41, 54, |
|||
20, 24, 25, 28, 33, 41, 54, 71, |
|||
24, 25, 28, 33, 41, 54, 71, 91, |
|||
]; |
|||
|
|||
/// <summary>
|
|||
/// The diagonal coefficient order for four-by-four matrices.
|
|||
/// </summary>
|
|||
private static readonly byte[] DiagonalScan4x4 = CreateDiagonalScan(4); |
|||
|
|||
/// <summary>
|
|||
/// The diagonal coefficient order for matrices of eight-by-eight and larger.
|
|||
/// </summary>
|
|||
private static readonly byte[] DiagonalScan8x8 = CreateDiagonalScan(8); |
|||
|
|||
/// <summary>
|
|||
/// The decoded matrices, indexed by transform-size category and matrix identifier.
|
|||
/// </summary>
|
|||
private readonly byte[][] matrices = new byte[4 * MatrixCount][]; |
|||
|
|||
/// <summary>
|
|||
/// The DC coefficients for sixteen-by-sixteen and thirty-two-by-thirty-two matrices.
|
|||
/// </summary>
|
|||
private readonly byte[] dcCoefficients = new byte[4 * MatrixCount]; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="HevcScalingList"/> class with the normative default matrices.
|
|||
/// </summary>
|
|||
public HevcScalingList() |
|||
{ |
|||
for (int sizeId = 0; sizeId < 4; sizeId++) |
|||
{ |
|||
int coefficientCount = sizeId == 0 ? 16 : 64; |
|||
for (int matrixId = 0; matrixId < MatrixCount; matrixId++) |
|||
{ |
|||
byte[] matrix = new byte[coefficientCount]; |
|||
ReadOnlySpan<byte> source = sizeId == 0 |
|||
? Default4x4 |
|||
: matrixId < 3 ? DefaultIntra8x8 : DefaultInter8x8; |
|||
|
|||
source.CopyTo(matrix); |
|||
this.matrices[GetIndex(sizeId, matrixId)] = matrix; |
|||
this.dcCoefficients[GetIndex(sizeId, matrixId)] = 16; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads a complete scaling-list-data structure.
|
|||
/// </summary>
|
|||
/// <param name="reader">The parameter-set raw byte sequence payload reader.</param>
|
|||
/// <returns>The decoded scaling matrices.</returns>
|
|||
/// <exception cref="InvalidImageContentException">A prediction reference is outside its permitted matrix set.</exception>
|
|||
public static HevcScalingList Parse(ref HevcBitReader reader) |
|||
{ |
|||
HevcScalingList scalingList = new(); |
|||
for (int sizeId = 0; sizeId < 4; sizeId++) |
|||
{ |
|||
int matrixStep = sizeId == 3 ? 3 : 1; |
|||
for (int matrixId = 0; matrixId < MatrixCount; matrixId += matrixStep) |
|||
{ |
|||
int destinationIndex = GetIndex(sizeId, matrixId); |
|||
bool predictionMode = reader.ReadFlag(); |
|||
if (!predictionMode) |
|||
{ |
|||
uint matrixIdDelta = reader.ReadUnsignedExpGolomb(); |
|||
if (sizeId == 3) |
|||
{ |
|||
if (matrixIdDelta > matrixId / 3) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC scaling list references an unavailable matrix."); |
|||
} |
|||
|
|||
matrixIdDelta *= 3; |
|||
} |
|||
|
|||
if (matrixIdDelta > matrixId) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC scaling list references an unavailable matrix."); |
|||
} |
|||
|
|||
int referenceMatrixId = matrixId - (int)matrixIdDelta; |
|||
if (referenceMatrixId != matrixId) |
|||
{ |
|||
int referenceIndex = GetIndex(sizeId, referenceMatrixId); |
|||
|
|||
// Span copying uses ImageSharp's runtime-optimized memory path and preserves one scalar
|
|||
// behavior model for these small, infrequently parsed coefficient tables.
|
|||
scalingList.matrices[referenceIndex].CopyTo(scalingList.matrices[destinationIndex], 0); |
|||
scalingList.dcCoefficients[destinationIndex] = scalingList.dcCoefficients[referenceIndex]; |
|||
} |
|||
|
|||
continue; |
|||
} |
|||
|
|||
int nextCoefficient = 8; |
|||
if (sizeId > 1) |
|||
{ |
|||
nextCoefficient = (int)(((long)reader.ReadSignedExpGolomb() + 8) & 255); |
|||
scalingList.dcCoefficients[destinationIndex] = (byte)(nextCoefficient & 255); |
|||
} |
|||
|
|||
byte[] scan = sizeId == 0 ? DiagonalScan4x4 : DiagonalScan8x8; |
|||
byte[] matrix = scalingList.matrices[destinationIndex]; |
|||
for (int coefficient = 0; coefficient < matrix.Length; coefficient++) |
|||
{ |
|||
nextCoefficient = (int)(((long)nextCoefficient + reader.ReadSignedExpGolomb()) & 255); |
|||
matrix[scan[coefficient]] = (byte)nextCoefficient; |
|||
} |
|||
} |
|||
|
|||
if (sizeId == 3) |
|||
{ |
|||
// HEVC signals only luma matrices at 32x32. Chroma uses the corresponding 16x16 matrices.
|
|||
for (int matrixId = 0; matrixId < MatrixCount; matrixId++) |
|||
{ |
|||
if (matrixId is 0 or 3) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
int destinationIndex = GetIndex(sizeId, matrixId); |
|||
int referenceIndex = GetIndex(sizeId - 1, matrixId); |
|||
scalingList.matrices[referenceIndex].CopyTo(scalingList.matrices[destinationIndex], 0); |
|||
scalingList.dcCoefficients[destinationIndex] = scalingList.dcCoefficients[referenceIndex]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return scalingList; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a decoded scaling matrix.
|
|||
/// </summary>
|
|||
/// <param name="sizeId">The transform-size category from zero for 4x4 through three for 32x32.</param>
|
|||
/// <param name="matrixId">The prediction and color-component matrix identifier.</param>
|
|||
/// <returns>The 16 or 64 decoded scaling coefficients in raster order.</returns>
|
|||
public ReadOnlySpan<byte> GetMatrix(int sizeId, int matrixId) |
|||
{ |
|||
DebugGuard.MustBeBetweenOrEqualTo(sizeId, 0, 3, nameof(sizeId)); |
|||
DebugGuard.MustBeBetweenOrEqualTo(matrixId, 0, MatrixCount - 1, nameof(matrixId)); |
|||
return this.matrices[GetIndex(sizeId, matrixId)]; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the DC scaling coefficient for a large transform matrix.
|
|||
/// </summary>
|
|||
/// <param name="sizeId">The transform-size category.</param>
|
|||
/// <param name="matrixId">The prediction and color-component matrix identifier.</param>
|
|||
/// <returns>The decoded DC coefficient.</returns>
|
|||
public byte GetDcCoefficient(int sizeId, int matrixId) |
|||
{ |
|||
DebugGuard.MustBeBetweenOrEqualTo(sizeId, 0, 3, nameof(sizeId)); |
|||
DebugGuard.MustBeBetweenOrEqualTo(matrixId, 0, MatrixCount - 1, nameof(matrixId)); |
|||
return this.dcCoefficients[GetIndex(sizeId, matrixId)]; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the flattened storage index for a size and matrix identifier.
|
|||
/// </summary>
|
|||
/// <param name="sizeId">The transform-size category.</param>
|
|||
/// <param name="matrixId">The matrix identifier.</param>
|
|||
/// <returns>The flattened storage index.</returns>
|
|||
private static int GetIndex(int sizeId, int matrixId) => (sizeId * MatrixCount) + matrixId; |
|||
|
|||
/// <summary>
|
|||
/// Creates the HEVC up-right diagonal scan for a square coefficient block.
|
|||
/// </summary>
|
|||
/// <param name="size">The coefficient block width and height.</param>
|
|||
/// <returns>The raster indices in coded order.</returns>
|
|||
private static byte[] CreateDiagonalScan(int size) |
|||
{ |
|||
byte[] scan = new byte[size * size]; |
|||
int row = 0; |
|||
int column = 0; |
|||
for (int position = 0; position < scan.Length; position++) |
|||
{ |
|||
scan[position] = (byte)((row * size) + column); |
|||
if (column == size - 1 || row == 0) |
|||
{ |
|||
row += column + 1; |
|||
column = 0; |
|||
if (row >= size) |
|||
{ |
|||
column += row - (size - 1); |
|||
row = size - 1; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
column++; |
|||
row--; |
|||
} |
|||
} |
|||
|
|||
return scan; |
|||
} |
|||
} |
|||
@ -0,0 +1,464 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Hevc; |
|||
|
|||
/// <summary>
|
|||
/// Contains the HEVC sequence fields required to reconstruct one independently coded still image.
|
|||
/// </summary>
|
|||
internal sealed class HevcSequenceParameterSet |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="HevcSequenceParameterSet"/> class.
|
|||
/// </summary>
|
|||
/// <param name="nalUnit">The decoded sequence-parameter-set NAL unit.</param>
|
|||
/// <exception cref="InvalidImageContentException">The sequence parameter set is malformed or outside the still-image profile.</exception>
|
|||
public HevcSequenceParameterSet(HevcNalUnit nalUnit) |
|||
{ |
|||
const byte sequenceParameterSetNalUnitType = 33; |
|||
if (nalUnit.Header.NalUnitType != sequenceParameterSetNalUnitType |
|||
|| nalUnit.Header.LayerId != 0 |
|||
|| nalUnit.Header.TemporalId != 0) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC sequence parameter set has an invalid NAL-unit header."); |
|||
} |
|||
|
|||
HevcBitReader reader = new(nalUnit.Rbsp.Span); |
|||
this.VideoParameterSetId = (byte)reader.ReadBits(4); |
|||
int maxSubLayersMinusOne = (int)reader.ReadBits(3); |
|||
if (maxSubLayersMinusOne > 6) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC sequence parameter set declares too many temporal sublayers."); |
|||
} |
|||
|
|||
this.MaxSubLayers = maxSubLayersMinusOne + 1; |
|||
this.TemporalIdNestingFlag = reader.ReadFlag(); |
|||
if (maxSubLayersMinusOne == 0 && !this.TemporalIdNestingFlag) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC sequence parameter set has invalid temporal nesting."); |
|||
} |
|||
|
|||
this.ProfileTierLevel = new HevcProfileTierLevel(ref reader, maxSubLayersMinusOne); |
|||
uint sequenceParameterSetId = reader.ReadUnsignedExpGolomb(); |
|||
if (sequenceParameterSetId > 15) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC sequence parameter set identifier is invalid."); |
|||
} |
|||
|
|||
this.Id = (byte)sequenceParameterSetId; |
|||
uint chromaFormat = reader.ReadUnsignedExpGolomb(); |
|||
if (chromaFormat > 3) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC sequence parameter set has an invalid chroma format."); |
|||
} |
|||
|
|||
this.ChromaFormat = (byte)chromaFormat; |
|||
this.SeparateColorPlaneFlag = this.ChromaFormat == 3 && reader.ReadFlag(); |
|||
|
|||
uint width = reader.ReadUnsignedExpGolomb(); |
|||
uint height = reader.ReadUnsignedExpGolomb(); |
|||
if (width is 0 or > int.MaxValue || height is 0 or > int.MaxValue) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC sequence parameter set has invalid coded dimensions."); |
|||
} |
|||
|
|||
this.Width = (int)width; |
|||
this.Height = (int)height; |
|||
if (reader.ReadFlag()) |
|||
{ |
|||
int cropUnitWidth = HevcParameterSetSyntax.GetCropUnitWidth(this.ChromaFormat, this.SeparateColorPlaneFlag); |
|||
int cropUnitHeight = HevcParameterSetSyntax.GetCropUnitHeight(this.ChromaFormat, this.SeparateColorPlaneFlag); |
|||
this.ConformanceWindowLeftOffset = ReadScaledOffset(ref reader, cropUnitWidth); |
|||
this.ConformanceWindowRightOffset = ReadScaledOffset(ref reader, cropUnitWidth); |
|||
this.ConformanceWindowTopOffset = ReadScaledOffset(ref reader, cropUnitHeight); |
|||
this.ConformanceWindowBottomOffset = ReadScaledOffset(ref reader, cropUnitHeight); |
|||
} |
|||
|
|||
if ((long)this.ConformanceWindowLeftOffset + this.ConformanceWindowRightOffset >= this.Width |
|||
|| (long)this.ConformanceWindowTopOffset + this.ConformanceWindowBottomOffset >= this.Height) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC sequence parameter set has an invalid conformance window."); |
|||
} |
|||
|
|||
this.DisplayWidth = this.Width - this.ConformanceWindowLeftOffset - this.ConformanceWindowRightOffset; |
|||
this.DisplayHeight = this.Height - this.ConformanceWindowTopOffset - this.ConformanceWindowBottomOffset; |
|||
|
|||
this.BitDepthLuma = ReadBitDepth(ref reader); |
|||
this.BitDepthChroma = ReadBitDepth(ref reader); |
|||
uint log2MaxPictureOrderCountLsbMinusFour = reader.ReadUnsignedExpGolomb(); |
|||
if (log2MaxPictureOrderCountLsbMinusFour > 12) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC picture-order-count width is invalid."); |
|||
} |
|||
|
|||
this.PictureOrderCountLsbBits = (int)log2MaxPictureOrderCountLsbMinusFour + 4; |
|||
|
|||
bool subLayerOrderingInfoPresent = reader.ReadFlag(); |
|||
int firstOrderingSubLayer = subLayerOrderingInfoPresent ? 0 : maxSubLayersMinusOne; |
|||
for (int subLayer = firstOrderingSubLayer; subLayer <= maxSubLayersMinusOne; subLayer++) |
|||
{ |
|||
uint maxDecodedPictureBufferingMinusOne = reader.ReadUnsignedExpGolomb(); |
|||
uint maxNumReorderPictures = reader.ReadUnsignedExpGolomb(); |
|||
reader.ReadUnsignedExpGolomb(); |
|||
if (maxNumReorderPictures > maxDecodedPictureBufferingMinusOne) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC sequence parameter set has invalid sublayer ordering limits."); |
|||
} |
|||
} |
|||
|
|||
uint minCodingBlockLog2MinusThree = reader.ReadUnsignedExpGolomb(); |
|||
if (minCodingBlockLog2MinusThree > 3) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC minimum coding-block size is invalid."); |
|||
} |
|||
|
|||
this.MinCodingBlockLog2 = (int)minCodingBlockLog2MinusThree + 3; |
|||
uint codingBlockSizeDifference = reader.ReadUnsignedExpGolomb(); |
|||
if (codingBlockSizeDifference > 6 - this.MinCodingBlockLog2) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC coding-tree-block size is invalid."); |
|||
} |
|||
|
|||
this.CodingTreeBlockLog2 = this.MinCodingBlockLog2 + (int)codingBlockSizeDifference; |
|||
|
|||
uint minTransformBlockLog2MinusTwo = reader.ReadUnsignedExpGolomb(); |
|||
if (minTransformBlockLog2MinusTwo > this.MinCodingBlockLog2 - 3) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC minimum transform-block size is invalid."); |
|||
} |
|||
|
|||
this.MinTransformBlockLog2 = (int)minTransformBlockLog2MinusTwo + 2; |
|||
uint transformBlockSizeDifference = reader.ReadUnsignedExpGolomb(); |
|||
int maximumTransformBlockLog2 = Math.Min(5, this.CodingTreeBlockLog2); |
|||
if (transformBlockSizeDifference > maximumTransformBlockLog2 - this.MinTransformBlockLog2) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC maximum transform-block size is invalid."); |
|||
} |
|||
|
|||
this.MaxTransformBlockLog2 = this.MinTransformBlockLog2 + (int)transformBlockSizeDifference; |
|||
uint maxTransformHierarchyDepthInter = reader.ReadUnsignedExpGolomb(); |
|||
uint maxTransformHierarchyDepthIntra = reader.ReadUnsignedExpGolomb(); |
|||
uint maxHierarchyDepth = (uint)(this.CodingTreeBlockLog2 - this.MinTransformBlockLog2); |
|||
if (maxTransformHierarchyDepthInter > maxHierarchyDepth || maxTransformHierarchyDepthIntra > maxHierarchyDepth) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC transform hierarchy depth is invalid."); |
|||
} |
|||
|
|||
this.MaxTransformHierarchyDepthInter = (int)maxTransformHierarchyDepthInter + 1; |
|||
this.MaxTransformHierarchyDepthIntra = (int)maxTransformHierarchyDepthIntra + 1; |
|||
|
|||
this.ScalingListEnabled = reader.ReadFlag(); |
|||
this.ScalingList = new HevcScalingList(); |
|||
if (this.ScalingListEnabled && reader.ReadFlag()) |
|||
{ |
|||
this.ScalingList = HevcScalingList.Parse(ref reader); |
|||
} |
|||
|
|||
this.AsymmetricMotionPartitionsEnabled = reader.ReadFlag(); |
|||
this.SampleAdaptiveOffsetEnabled = reader.ReadFlag(); |
|||
this.PcmEnabled = reader.ReadFlag(); |
|||
if (this.PcmEnabled) |
|||
{ |
|||
this.PcmBitDepthLuma = (int)reader.ReadBits(4) + 1; |
|||
this.PcmBitDepthChroma = (int)reader.ReadBits(4) + 1; |
|||
if (this.PcmBitDepthLuma > this.BitDepthLuma || this.PcmBitDepthChroma > this.BitDepthChroma) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC PCM bit depth exceeds the coded sample precision."); |
|||
} |
|||
|
|||
uint minPcmCodingBlockLog2MinusThree = reader.ReadUnsignedExpGolomb(); |
|||
this.MinPcmCodingBlockLog2 = (int)minPcmCodingBlockLog2MinusThree + 3; |
|||
int maximumPcmCodingBlockLog2 = Math.Min(this.CodingTreeBlockLog2, 5); |
|||
if (this.MinPcmCodingBlockLog2 < Math.Min(this.MinCodingBlockLog2, 5) |
|||
|| this.MinPcmCodingBlockLog2 > maximumPcmCodingBlockLog2) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC minimum PCM coding-block size is invalid."); |
|||
} |
|||
|
|||
uint pcmCodingBlockSizeDifference = reader.ReadUnsignedExpGolomb(); |
|||
if (pcmCodingBlockSizeDifference > maximumPcmCodingBlockLog2 - this.MinPcmCodingBlockLog2) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC maximum PCM coding-block size is invalid."); |
|||
} |
|||
|
|||
this.MaxPcmCodingBlockLog2 = this.MinPcmCodingBlockLog2 + (int)pcmCodingBlockSizeDifference; |
|||
this.PcmLoopFilterDisabled = reader.ReadFlag(); |
|||
} |
|||
|
|||
uint shortTermReferencePictureSetCount = reader.ReadUnsignedExpGolomb(); |
|||
if (shortTermReferencePictureSetCount > 64) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC sequence parameter set declares too many short-term reference-picture sets."); |
|||
} |
|||
|
|||
List<HevcShortTermReferencePictureSet> shortTermReferencePictureSets = new((int)shortTermReferencePictureSetCount); |
|||
for (int referenceSet = 0; referenceSet < shortTermReferencePictureSetCount; referenceSet++) |
|||
{ |
|||
shortTermReferencePictureSets.Add( |
|||
HevcShortTermReferencePictureSet.Parse(ref reader, shortTermReferencePictureSets, referenceSet)); |
|||
} |
|||
|
|||
this.ShortTermReferencePictureSets = shortTermReferencePictureSets; |
|||
|
|||
if (reader.ReadFlag()) |
|||
{ |
|||
uint longTermReferencePictureCount = reader.ReadUnsignedExpGolomb(); |
|||
if (longTermReferencePictureCount > 32) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC sequence parameter set declares too many long-term reference pictures."); |
|||
} |
|||
|
|||
uint[] pictureOrderCounts = new uint[longTermReferencePictureCount]; |
|||
bool[] usedByCurrentPicture = new bool[longTermReferencePictureCount]; |
|||
for (int reference = 0; reference < pictureOrderCounts.Length; reference++) |
|||
{ |
|||
pictureOrderCounts[reference] = reader.ReadBits(this.PictureOrderCountLsbBits); |
|||
usedByCurrentPicture[reference] = reader.ReadFlag(); |
|||
} |
|||
|
|||
this.LongTermReferencePictureOrderCounts = pictureOrderCounts; |
|||
this.LongTermReferencePicturesUsedByCurrent = usedByCurrentPicture; |
|||
} |
|||
else |
|||
{ |
|||
this.LongTermReferencePictureOrderCounts = Array.Empty<uint>(); |
|||
this.LongTermReferencePicturesUsedByCurrent = Array.Empty<bool>(); |
|||
} |
|||
|
|||
this.TemporalMotionVectorPredictionEnabled = reader.ReadFlag(); |
|||
this.StrongIntraSmoothingEnabled = reader.ReadFlag(); |
|||
if (reader.ReadFlag()) |
|||
{ |
|||
this.VideoUsabilityInformation = new HevcVideoUsabilityInformation( |
|||
ref reader, |
|||
this.ChromaFormat, |
|||
this.SeparateColorPlaneFlag, |
|||
maxSubLayersMinusOne); |
|||
} |
|||
|
|||
if (reader.ReadFlag()) |
|||
{ |
|||
Span<bool> extensionFlags = stackalloc bool[8]; |
|||
for (int extensionFlag = 0; extensionFlag < extensionFlags.Length; extensionFlag++) |
|||
{ |
|||
extensionFlags[extensionFlag] = reader.ReadFlag(); |
|||
} |
|||
|
|||
if (extensionFlags[1]) |
|||
{ |
|||
throw new InvalidImageContentException("Layered HEVC sequence extensions are not supported for still-image items."); |
|||
} |
|||
|
|||
if (extensionFlags[0]) |
|||
{ |
|||
this.TransformSkipRotationEnabled = reader.ReadFlag(); |
|||
this.TransformSkipContextEnabled = reader.ReadFlag(); |
|||
this.ImplicitResidualDpcmEnabled = reader.ReadFlag(); |
|||
this.ExplicitResidualDpcmEnabled = reader.ReadFlag(); |
|||
this.ExtendedPrecisionProcessingEnabled = reader.ReadFlag(); |
|||
this.IntraSmoothingDisabled = reader.ReadFlag(); |
|||
this.HighPrecisionOffsetsEnabled = reader.ReadFlag(); |
|||
this.PersistentRiceAdaptationEnabled = reader.ReadFlag(); |
|||
this.CabacBypassAlignmentEnabled = reader.ReadFlag(); |
|||
} |
|||
|
|||
bool unknownExtensionPresent = false; |
|||
for (int extensionFlag = 2; extensionFlag < extensionFlags.Length; extensionFlag++) |
|||
{ |
|||
unknownExtensionPresent |= extensionFlags[extensionFlag]; |
|||
} |
|||
|
|||
if (unknownExtensionPresent) |
|||
{ |
|||
while (reader.HasMoreRbspData()) |
|||
{ |
|||
reader.ReadFlag(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
reader.ReadRbspTrailingBits(); |
|||
} |
|||
|
|||
/// <summary>Gets the referenced video-parameter-set identifier.</summary>
|
|||
public byte VideoParameterSetId { get; } |
|||
|
|||
/// <summary>Gets the sequence-parameter-set identifier.</summary>
|
|||
public byte Id { get; } |
|||
|
|||
/// <summary>Gets the declared number of temporal sublayers.</summary>
|
|||
public int MaxSubLayers { get; } |
|||
|
|||
/// <summary>Gets a value indicating whether temporal identifiers are nested.</summary>
|
|||
public bool TemporalIdNestingFlag { get; } |
|||
|
|||
/// <summary>Gets the general profile, tier, constraint, and level description.</summary>
|
|||
public HevcProfileTierLevel ProfileTierLevel { get; } |
|||
|
|||
/// <summary>Gets the coded chroma format, from monochrome through YUV 4:4:4.</summary>
|
|||
public byte ChromaFormat { get; } |
|||
|
|||
/// <summary>Gets a value indicating whether 4:4:4 components are coded as separate color planes.</summary>
|
|||
public bool SeparateColorPlaneFlag { get; } |
|||
|
|||
/// <summary>Gets the coded luma width before conformance cropping.</summary>
|
|||
public int Width { get; } |
|||
|
|||
/// <summary>Gets the coded luma height before conformance cropping.</summary>
|
|||
public int Height { get; } |
|||
|
|||
/// <summary>Gets the displayed width after conformance cropping.</summary>
|
|||
public int DisplayWidth { get; } |
|||
|
|||
/// <summary>Gets the displayed height after conformance cropping.</summary>
|
|||
public int DisplayHeight { get; } |
|||
|
|||
/// <summary>Gets the conformance-window left offset in luma samples.</summary>
|
|||
public int ConformanceWindowLeftOffset { get; } |
|||
|
|||
/// <summary>Gets the conformance-window right offset in luma samples.</summary>
|
|||
public int ConformanceWindowRightOffset { get; } |
|||
|
|||
/// <summary>Gets the conformance-window top offset in luma samples.</summary>
|
|||
public int ConformanceWindowTopOffset { get; } |
|||
|
|||
/// <summary>Gets the conformance-window bottom offset in luma samples.</summary>
|
|||
public int ConformanceWindowBottomOffset { get; } |
|||
|
|||
/// <summary>Gets the luma sample precision in bits.</summary>
|
|||
public int BitDepthLuma { get; } |
|||
|
|||
/// <summary>Gets the chroma sample precision in bits.</summary>
|
|||
public int BitDepthChroma { get; } |
|||
|
|||
/// <summary>Gets the coded picture-order-count least-significant-bit width.</summary>
|
|||
public int PictureOrderCountLsbBits { get; } |
|||
|
|||
/// <summary>Gets the base-two logarithm of the minimum luma coding-block size.</summary>
|
|||
public int MinCodingBlockLog2 { get; } |
|||
|
|||
/// <summary>Gets the base-two logarithm of the coding-tree-block size.</summary>
|
|||
public int CodingTreeBlockLog2 { get; } |
|||
|
|||
/// <summary>Gets the base-two logarithm of the minimum luma transform-block size.</summary>
|
|||
public int MinTransformBlockLog2 { get; } |
|||
|
|||
/// <summary>Gets the base-two logarithm of the maximum luma transform-block size.</summary>
|
|||
public int MaxTransformBlockLog2 { get; } |
|||
|
|||
/// <summary>Gets the maximum inter-predicted transform hierarchy depth.</summary>
|
|||
public int MaxTransformHierarchyDepthInter { get; } |
|||
|
|||
/// <summary>Gets the maximum intra-predicted transform hierarchy depth.</summary>
|
|||
public int MaxTransformHierarchyDepthIntra { get; } |
|||
|
|||
/// <summary>Gets a value indicating whether scaling lists affect inverse quantization.</summary>
|
|||
public bool ScalingListEnabled { get; } |
|||
|
|||
/// <summary>Gets the effective quantization scaling matrices.</summary>
|
|||
public HevcScalingList ScalingList { get; } |
|||
|
|||
/// <summary>Gets a value indicating whether asymmetric motion partitions are enabled.</summary>
|
|||
public bool AsymmetricMotionPartitionsEnabled { get; } |
|||
|
|||
/// <summary>Gets a value indicating whether sample-adaptive offset filtering is enabled.</summary>
|
|||
public bool SampleAdaptiveOffsetEnabled { get; } |
|||
|
|||
/// <summary>Gets a value indicating whether pulse-code-modulated coding blocks are enabled.</summary>
|
|||
public bool PcmEnabled { get; } |
|||
|
|||
/// <summary>Gets the PCM luma sample precision in bits.</summary>
|
|||
public int PcmBitDepthLuma { get; } |
|||
|
|||
/// <summary>Gets the PCM chroma sample precision in bits.</summary>
|
|||
public int PcmBitDepthChroma { get; } |
|||
|
|||
/// <summary>Gets the base-two logarithm of the minimum PCM coding-block size.</summary>
|
|||
public int MinPcmCodingBlockLog2 { get; } |
|||
|
|||
/// <summary>Gets the base-two logarithm of the maximum PCM coding-block size.</summary>
|
|||
public int MaxPcmCodingBlockLog2 { get; } |
|||
|
|||
/// <summary>Gets a value indicating whether in-loop filtering is disabled for PCM blocks.</summary>
|
|||
public bool PcmLoopFilterDisabled { get; } |
|||
|
|||
/// <summary>Gets the SPS short-term reference-picture sets.</summary>
|
|||
public IReadOnlyList<HevcShortTermReferencePictureSet> ShortTermReferencePictureSets { get; } |
|||
|
|||
/// <summary>Gets the long-term reference picture-order-count values.</summary>
|
|||
public IReadOnlyList<uint> LongTermReferencePictureOrderCounts { get; } |
|||
|
|||
/// <summary>Gets the long-term reference-picture current-usage flags.</summary>
|
|||
public IReadOnlyList<bool> LongTermReferencePicturesUsedByCurrent { get; } |
|||
|
|||
/// <summary>Gets a value indicating whether temporal motion-vector prediction is enabled.</summary>
|
|||
public bool TemporalMotionVectorPredictionEnabled { get; } |
|||
|
|||
/// <summary>Gets a value indicating whether strong intra smoothing is enabled.</summary>
|
|||
public bool StrongIntraSmoothingEnabled { get; } |
|||
|
|||
/// <summary>Gets the optional still-image VUI presentation description.</summary>
|
|||
public HevcVideoUsabilityInformation? VideoUsabilityInformation { get; } |
|||
|
|||
/// <summary>Gets a value indicating whether transform-skip coefficient rotation is enabled.</summary>
|
|||
public bool TransformSkipRotationEnabled { get; } |
|||
|
|||
/// <summary>Gets a value indicating whether transform-skip-specific entropy contexts are enabled.</summary>
|
|||
public bool TransformSkipContextEnabled { get; } |
|||
|
|||
/// <summary>Gets a value indicating whether implicit residual DPCM is enabled.</summary>
|
|||
public bool ImplicitResidualDpcmEnabled { get; } |
|||
|
|||
/// <summary>Gets a value indicating whether explicit residual DPCM is enabled.</summary>
|
|||
public bool ExplicitResidualDpcmEnabled { get; } |
|||
|
|||
/// <summary>Gets a value indicating whether extended-precision processing is enabled.</summary>
|
|||
public bool ExtendedPrecisionProcessingEnabled { get; } |
|||
|
|||
/// <summary>Gets a value indicating whether intra smoothing is disabled.</summary>
|
|||
public bool IntraSmoothingDisabled { get; } |
|||
|
|||
/// <summary>Gets a value indicating whether high-precision prediction offsets are enabled.</summary>
|
|||
public bool HighPrecisionOffsetsEnabled { get; } |
|||
|
|||
/// <summary>Gets a value indicating whether persistent Rice adaptation is enabled.</summary>
|
|||
public bool PersistentRiceAdaptationEnabled { get; } |
|||
|
|||
/// <summary>Gets a value indicating whether CABAC bypass alignment is enabled.</summary>
|
|||
public bool CabacBypassAlignmentEnabled { get; } |
|||
|
|||
/// <summary>
|
|||
/// Reads a conformance-window offset and converts it to luma-sample units.
|
|||
/// </summary>
|
|||
/// <param name="reader">The sequence-parameter-set raw byte sequence payload reader.</param>
|
|||
/// <param name="unit">The chroma-dependent luma-sample unit.</param>
|
|||
/// <returns>The scaled offset.</returns>
|
|||
/// <exception cref="InvalidImageContentException">The scaled offset exceeds the supported image dimension range.</exception>
|
|||
private static int ReadScaledOffset(ref HevcBitReader reader, int unit) |
|||
{ |
|||
uint offset = reader.ReadUnsignedExpGolomb(); |
|||
if (offset > int.MaxValue / unit) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC conformance-window offset is too large."); |
|||
} |
|||
|
|||
return (int)offset * unit; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads and validates a coded HEVC sample precision.
|
|||
/// </summary>
|
|||
/// <param name="reader">The sequence-parameter-set raw byte sequence payload reader.</param>
|
|||
/// <returns>The sample precision in bits.</returns>
|
|||
/// <exception cref="InvalidImageContentException">The declared precision exceeds 16 bits.</exception>
|
|||
private static int ReadBitDepth(ref HevcBitReader reader) |
|||
{ |
|||
uint bitDepthMinusEight = reader.ReadUnsignedExpGolomb(); |
|||
if (bitDepthMinusEight > 8) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC sample bit depth is invalid."); |
|||
} |
|||
|
|||
return (int)bitDepthMinusEight + 8; |
|||
} |
|||
} |
|||
@ -0,0 +1,178 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Hevc; |
|||
|
|||
/// <summary>
|
|||
/// Contains the bounded picture-order differences declared by one HEVC short-term reference-picture set.
|
|||
/// </summary>
|
|||
internal sealed class HevcShortTermReferencePictureSet |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="HevcShortTermReferencePictureSet"/> class.
|
|||
/// </summary>
|
|||
/// <param name="deltaPictureOrders">The signed picture-order differences in HEVC reference order.</param>
|
|||
/// <param name="usedByCurrentPicture">The corresponding current-picture usage flags.</param>
|
|||
private HevcShortTermReferencePictureSet(int[] deltaPictureOrders, bool[] usedByCurrentPicture) |
|||
{ |
|||
this.DeltaPictureOrders = deltaPictureOrders; |
|||
this.UsedByCurrentPicture = usedByCurrentPicture; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the signed picture-order differences in HEVC reference order.
|
|||
/// </summary>
|
|||
public IReadOnlyList<int> DeltaPictureOrders { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the flags indicating which reference pictures are used by the current picture.
|
|||
/// </summary>
|
|||
public IReadOnlyList<bool> UsedByCurrentPicture { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the number of pictures declared by the reference-picture set.
|
|||
/// </summary>
|
|||
public int Count => this.DeltaPictureOrders.Count; |
|||
|
|||
/// <summary>
|
|||
/// Reads one SPS short-term reference-picture set.
|
|||
/// </summary>
|
|||
/// <param name="reader">The sequence-parameter-set raw byte sequence payload reader.</param>
|
|||
/// <param name="previousSets">The previously decoded sets available for inter-set prediction.</param>
|
|||
/// <param name="index">The zero-based index of the set being decoded.</param>
|
|||
/// <returns>The decoded reference-picture set.</returns>
|
|||
/// <exception cref="InvalidImageContentException">The set exceeds the HEVC decoded-picture-buffer bound.</exception>
|
|||
public static HevcShortTermReferencePictureSet Parse( |
|||
ref HevcBitReader reader, |
|||
IReadOnlyList<HevcShortTermReferencePictureSet> previousSets, |
|||
int index) |
|||
{ |
|||
Span<int> deltaPictureOrders = stackalloc int[16]; |
|||
Span<bool> usedByCurrentPicture = stackalloc bool[16]; |
|||
int pictureCount = 0; |
|||
bool interSetPrediction = index > 0 && reader.ReadFlag(); |
|||
if (interSetPrediction) |
|||
{ |
|||
HevcShortTermReferencePictureSet referenceSet = previousSets[index - 1]; |
|||
bool deltaPictureOrderSign = reader.ReadFlag(); |
|||
uint absoluteDeltaPictureOrderMinusOne = reader.ReadUnsignedExpGolomb(); |
|||
if (absoluteDeltaPictureOrderMinusOne >= int.MaxValue) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC short-term reference-picture delta is too large."); |
|||
} |
|||
|
|||
int deltaReferencePictureSet = (deltaPictureOrderSign ? -1 : 1) |
|||
* ((int)absoluteDeltaPictureOrderMinusOne + 1); |
|||
|
|||
for (int referenceIndex = 0; referenceIndex <= referenceSet.Count; referenceIndex++) |
|||
{ |
|||
bool used = reader.ReadFlag(); |
|||
bool useDelta = used || reader.ReadFlag(); |
|||
if (!useDelta) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
if (pictureCount == deltaPictureOrders.Length) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC short-term reference-picture set is too large."); |
|||
} |
|||
|
|||
int referenceDelta = referenceIndex < referenceSet.Count |
|||
? referenceSet.DeltaPictureOrders[referenceIndex] |
|||
: 0; |
|||
|
|||
long deltaPictureOrder = (long)deltaReferencePictureSet + referenceDelta; |
|||
if (deltaPictureOrder is < int.MinValue or > int.MaxValue) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC short-term reference-picture delta is too large."); |
|||
} |
|||
|
|||
deltaPictureOrders[pictureCount] = (int)deltaPictureOrder; |
|||
usedByCurrentPicture[pictureCount] = used; |
|||
pictureCount++; |
|||
} |
|||
|
|||
// HEVC orders negative differences nearest-first, followed by positive differences nearest-first.
|
|||
for (int outer = 1; outer < pictureCount; outer++) |
|||
{ |
|||
int delta = deltaPictureOrders[outer]; |
|||
bool used = usedByCurrentPicture[outer]; |
|||
int inner = outer - 1; |
|||
while (inner >= 0 && delta < deltaPictureOrders[inner]) |
|||
{ |
|||
deltaPictureOrders[inner + 1] = deltaPictureOrders[inner]; |
|||
usedByCurrentPicture[inner + 1] = usedByCurrentPicture[inner]; |
|||
inner--; |
|||
} |
|||
|
|||
deltaPictureOrders[inner + 1] = delta; |
|||
usedByCurrentPicture[inner + 1] = used; |
|||
} |
|||
|
|||
int negativeCount = 0; |
|||
while (negativeCount < pictureCount && deltaPictureOrders[negativeCount] < 0) |
|||
{ |
|||
negativeCount++; |
|||
} |
|||
|
|||
deltaPictureOrders[..negativeCount].Reverse(); |
|||
usedByCurrentPicture[..negativeCount].Reverse(); |
|||
} |
|||
else |
|||
{ |
|||
uint negativePictureCount = reader.ReadUnsignedExpGolomb(); |
|||
uint positivePictureCount = reader.ReadUnsignedExpGolomb(); |
|||
if (negativePictureCount > 16 || positivePictureCount > 16 - negativePictureCount) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC short-term reference-picture set is too large."); |
|||
} |
|||
|
|||
int previousDelta = 0; |
|||
for (uint negativeIndex = 0; negativeIndex < negativePictureCount; negativeIndex++) |
|||
{ |
|||
uint deltaMinusOne = reader.ReadUnsignedExpGolomb(); |
|||
if (deltaMinusOne >= int.MaxValue) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC short-term reference-picture delta is too large."); |
|||
} |
|||
|
|||
long deltaPictureOrder = (long)previousDelta - deltaMinusOne - 1; |
|||
if (deltaPictureOrder < int.MinValue) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC short-term reference-picture delta is too large."); |
|||
} |
|||
|
|||
previousDelta = (int)deltaPictureOrder; |
|||
deltaPictureOrders[pictureCount] = previousDelta; |
|||
usedByCurrentPicture[pictureCount] = reader.ReadFlag(); |
|||
pictureCount++; |
|||
} |
|||
|
|||
previousDelta = 0; |
|||
for (uint positiveIndex = 0; positiveIndex < positivePictureCount; positiveIndex++) |
|||
{ |
|||
uint deltaMinusOne = reader.ReadUnsignedExpGolomb(); |
|||
if (deltaMinusOne >= int.MaxValue) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC short-term reference-picture delta is too large."); |
|||
} |
|||
|
|||
long deltaPictureOrder = (long)previousDelta + deltaMinusOne + 1; |
|||
if (deltaPictureOrder > int.MaxValue) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC short-term reference-picture delta is too large."); |
|||
} |
|||
|
|||
previousDelta = (int)deltaPictureOrder; |
|||
deltaPictureOrders[pictureCount] = previousDelta; |
|||
usedByCurrentPicture[pictureCount] = reader.ReadFlag(); |
|||
pictureCount++; |
|||
} |
|||
} |
|||
|
|||
return new HevcShortTermReferencePictureSet( |
|||
deltaPictureOrders[..pictureCount].ToArray(), |
|||
usedByCurrentPicture[..pictureCount].ToArray()); |
|||
} |
|||
} |
|||
@ -0,0 +1,247 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Hevc; |
|||
|
|||
/// <summary>
|
|||
/// Contains the still-image presentation fields declared by HEVC video-usability information.
|
|||
/// </summary>
|
|||
internal sealed class HevcVideoUsabilityInformation |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="HevcVideoUsabilityInformation"/> class.
|
|||
/// </summary>
|
|||
/// <param name="reader">The sequence-parameter-set raw byte sequence payload reader.</param>
|
|||
/// <param name="chromaFormat">The sequence chroma-format identifier.</param>
|
|||
/// <param name="separateColorPlane">Whether 4:4:4 components are coded as separate planes.</param>
|
|||
/// <param name="maxSubLayersMinusOne">The highest declared temporal sublayer index.</param>
|
|||
/// <exception cref="InvalidImageContentException">The VUI syntax is invalid for a still-image item.</exception>
|
|||
public HevcVideoUsabilityInformation( |
|||
ref HevcBitReader reader, |
|||
byte chromaFormat, |
|||
bool separateColorPlane, |
|||
int maxSubLayersMinusOne) |
|||
{ |
|||
this.AspectRatioInfoPresent = reader.ReadFlag(); |
|||
if (this.AspectRatioInfoPresent) |
|||
{ |
|||
this.AspectRatioIdc = (byte)reader.ReadBits(8); |
|||
if (this.AspectRatioIdc == byte.MaxValue) |
|||
{ |
|||
this.SarWidth = (ushort)reader.ReadBits(16); |
|||
this.SarHeight = (ushort)reader.ReadBits(16); |
|||
if (this.SarWidth == 0 || this.SarHeight == 0) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC VUI declares an invalid extended sample aspect ratio."); |
|||
} |
|||
} |
|||
else if (this.AspectRatioIdc > 16) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC VUI declares a reserved sample aspect ratio."); |
|||
} |
|||
} |
|||
|
|||
if (reader.ReadFlag()) |
|||
{ |
|||
reader.ReadFlag(); |
|||
} |
|||
|
|||
this.VideoSignalTypePresent = reader.ReadFlag(); |
|||
if (this.VideoSignalTypePresent) |
|||
{ |
|||
reader.ReadBits(3); |
|||
this.FullRange = reader.ReadFlag(); |
|||
this.ColorDescriptionPresent = reader.ReadFlag(); |
|||
if (this.ColorDescriptionPresent) |
|||
{ |
|||
this.ColorPrimaries = (byte)reader.ReadBits(8); |
|||
this.TransferCharacteristics = (byte)reader.ReadBits(8); |
|||
this.MatrixCoefficients = (byte)reader.ReadBits(8); |
|||
} |
|||
} |
|||
|
|||
this.ChromaLocationInfoPresent = reader.ReadFlag(); |
|||
if (this.ChromaLocationInfoPresent) |
|||
{ |
|||
uint topFieldLocation = reader.ReadUnsignedExpGolomb(); |
|||
uint bottomFieldLocation = reader.ReadUnsignedExpGolomb(); |
|||
if (topFieldLocation > 5 || bottomFieldLocation > 5) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC VUI declares an invalid chroma sample location."); |
|||
} |
|||
|
|||
this.ChromaSampleLocationTopField = (byte)topFieldLocation; |
|||
this.ChromaSampleLocationBottomField = (byte)bottomFieldLocation; |
|||
} |
|||
|
|||
reader.ReadFlag(); |
|||
if (reader.ReadFlag()) |
|||
{ |
|||
// A field sequence requires paired-field presentation state and is not a single HEIF image item.
|
|||
throw new InvalidImageContentException("Interlaced HEVC field sequences are not supported as still-image items."); |
|||
} |
|||
|
|||
reader.ReadFlag(); |
|||
|
|||
this.DefaultDisplayWindowPresent = reader.ReadFlag(); |
|||
if (this.DefaultDisplayWindowPresent) |
|||
{ |
|||
int cropUnitWidth = HevcParameterSetSyntax.GetCropUnitWidth(chromaFormat, separateColorPlane); |
|||
int cropUnitHeight = HevcParameterSetSyntax.GetCropUnitHeight(chromaFormat, separateColorPlane); |
|||
this.DefaultDisplayWindowLeftOffset = ReadScaledOffset(ref reader, cropUnitWidth); |
|||
this.DefaultDisplayWindowRightOffset = ReadScaledOffset(ref reader, cropUnitWidth); |
|||
this.DefaultDisplayWindowTopOffset = ReadScaledOffset(ref reader, cropUnitHeight); |
|||
this.DefaultDisplayWindowBottomOffset = ReadScaledOffset(ref reader, cropUnitHeight); |
|||
} |
|||
|
|||
if (reader.ReadFlag()) |
|||
{ |
|||
// VUI timing and HRD fields affect scheduling, not the reconstructed still-image samples.
|
|||
reader.ReadBits(32); |
|||
reader.ReadBits(32); |
|||
if (reader.ReadFlag()) |
|||
{ |
|||
reader.ReadUnsignedExpGolomb(); |
|||
} |
|||
|
|||
if (reader.ReadFlag()) |
|||
{ |
|||
bool nalHrdParametersPresent = false; |
|||
bool vclHrdParametersPresent = false; |
|||
bool subPictureHrdParametersPresent = false; |
|||
HevcParameterSetSyntax.SkipHrdParameters( |
|||
ref reader, |
|||
true, |
|||
maxSubLayersMinusOne, |
|||
ref nalHrdParametersPresent, |
|||
ref vclHrdParametersPresent, |
|||
ref subPictureHrdParametersPresent); |
|||
} |
|||
} |
|||
|
|||
if (reader.ReadFlag()) |
|||
{ |
|||
reader.ReadFlag(); |
|||
reader.ReadFlag(); |
|||
reader.ReadFlag(); |
|||
uint minimumSpatialSegmentation = reader.ReadUnsignedExpGolomb(); |
|||
if (minimumSpatialSegmentation >= 4096) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC VUI spatial-segmentation value is invalid."); |
|||
} |
|||
|
|||
reader.ReadUnsignedExpGolomb(); |
|||
reader.ReadUnsignedExpGolomb(); |
|||
reader.ReadUnsignedExpGolomb(); |
|||
reader.ReadUnsignedExpGolomb(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether sample-aspect-ratio information is present.
|
|||
/// </summary>
|
|||
public bool AspectRatioInfoPresent { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the registered sample-aspect-ratio identifier.
|
|||
/// </summary>
|
|||
public byte AspectRatioIdc { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the explicit horizontal sample spacing when <see cref="AspectRatioIdc"/> is 255.
|
|||
/// </summary>
|
|||
public ushort SarWidth { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the explicit vertical sample spacing when <see cref="AspectRatioIdc"/> is 255.
|
|||
/// </summary>
|
|||
public ushort SarHeight { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether video-signal-type information is present.
|
|||
/// </summary>
|
|||
public bool VideoSignalTypePresent { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether component samples use the full numeric range.
|
|||
/// </summary>
|
|||
public bool FullRange { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether color-description fields are present.
|
|||
/// </summary>
|
|||
public bool ColorDescriptionPresent { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the coded color-primary identifier.
|
|||
/// </summary>
|
|||
public byte ColorPrimaries { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the coded transfer-characteristic identifier.
|
|||
/// </summary>
|
|||
public byte TransferCharacteristics { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the coded matrix-coefficient identifier.
|
|||
/// </summary>
|
|||
public byte MatrixCoefficients { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether chroma sample-location information is present.
|
|||
/// </summary>
|
|||
public bool ChromaLocationInfoPresent { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the top-field chroma sample-location identifier.
|
|||
/// </summary>
|
|||
public byte ChromaSampleLocationTopField { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the bottom-field chroma sample-location identifier.
|
|||
/// </summary>
|
|||
public byte ChromaSampleLocationBottomField { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether a default display window is present.
|
|||
/// </summary>
|
|||
public bool DefaultDisplayWindowPresent { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the default display-window left offset in luma samples.
|
|||
/// </summary>
|
|||
public int DefaultDisplayWindowLeftOffset { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the default display-window right offset in luma samples.
|
|||
/// </summary>
|
|||
public int DefaultDisplayWindowRightOffset { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the default display-window top offset in luma samples.
|
|||
/// </summary>
|
|||
public int DefaultDisplayWindowTopOffset { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the default display-window bottom offset in luma samples.
|
|||
/// </summary>
|
|||
public int DefaultDisplayWindowBottomOffset { get; } |
|||
|
|||
/// <summary>
|
|||
/// Reads a conformance-window offset and converts it to luma-sample units.
|
|||
/// </summary>
|
|||
/// <param name="reader">The sequence-parameter-set raw byte sequence payload reader.</param>
|
|||
/// <param name="unit">The chroma-dependent luma-sample unit.</param>
|
|||
/// <returns>The scaled offset.</returns>
|
|||
/// <exception cref="InvalidImageContentException">The scaled offset exceeds the supported image dimension range.</exception>
|
|||
private static int ReadScaledOffset(ref HevcBitReader reader, int unit) |
|||
{ |
|||
uint offset = reader.ReadUnsignedExpGolomb(); |
|||
if (offset > int.MaxValue / unit) |
|||
{ |
|||
throw new InvalidImageContentException("The HEVC VUI display-window offset is too large."); |
|||
} |
|||
|
|||
return (int)offset * unit; |
|||
} |
|||
} |
|||
Loading…
Reference in new issue