mirror of https://github.com/SixLabors/ImageSharp
9 changed files with 670 additions and 55 deletions
@ -0,0 +1,123 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Hevc; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Contains the length-delimited NAL units and IDR slice segments carried by one HEVC still-image item.
|
||||
|
/// </summary>
|
||||
|
internal sealed class HevcImageItemBitstream |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="HevcImageItemBitstream"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="data">The complete bounded payload of one <c>hvc1</c> image item.</param>
|
||||
|
/// <param name="configuration">The codec configuration associated with the same image item.</param>
|
||||
|
/// <exception cref="InvalidImageContentException">
|
||||
|
/// NAL-unit framing is malformed, the payload contains sequence or layered coding, or the item does not contain
|
||||
|
/// exactly one independently decodable IDR picture.
|
||||
|
/// </exception>
|
||||
|
public HevcImageItemBitstream(ReadOnlySpan<byte> data, HevcCodecConfiguration configuration) |
||||
|
{ |
||||
|
List<HevcNalUnit> nalUnits = []; |
||||
|
List<HevcSliceSegmentHeader> sliceSegments = []; |
||||
|
int offset = 0; |
||||
|
while (offset < data.Length) |
||||
|
{ |
||||
|
if (data.Length - offset < configuration.NalUnitLengthSize) |
||||
|
{ |
||||
|
throw new InvalidImageContentException("The HEVC image item has a truncated NAL-unit length."); |
||||
|
} |
||||
|
|
||||
|
int nalUnitLength = ReadNalUnitLength(data[offset..], configuration.NalUnitLengthSize); |
||||
|
offset += configuration.NalUnitLengthSize; |
||||
|
if (nalUnitLength < 2 || nalUnitLength > data.Length - offset) |
||||
|
{ |
||||
|
throw new InvalidImageContentException("The HEVC image item has an invalid NAL-unit length."); |
||||
|
} |
||||
|
|
||||
|
HevcNalUnit nalUnit = new(data.Slice(offset, nalUnitLength)); |
||||
|
offset += nalUnitLength; |
||||
|
if (nalUnit.Header.LayerId != 0 || nalUnit.Header.TemporalId != 0) |
||||
|
{ |
||||
|
throw new InvalidImageContentException("The HEVC image item contains layered or temporal-substream NAL units."); |
||||
|
} |
||||
|
|
||||
|
nalUnits.Add(nalUnit); |
||||
|
if (nalUnit.Header.IsVideoCodingLayer) |
||||
|
{ |
||||
|
if (!nalUnit.Header.IsInstantaneousDecoderRefresh) |
||||
|
{ |
||||
|
throw new InvalidImageContentException("The HEVC image item contains a coded picture that is not independently decodable."); |
||||
|
} |
||||
|
|
||||
|
HevcSliceSegmentHeader sliceSegment = new(nalUnit, configuration.PictureParameterSets); |
||||
|
if (sliceSegments.Count == 0 && !sliceSegment.FirstSliceSegmentInPicture) |
||||
|
{ |
||||
|
throw new InvalidImageContentException("The first HEVC image-item slice is not marked as the first picture segment."); |
||||
|
} |
||||
|
|
||||
|
if (sliceSegments.Count != 0 && sliceSegment.FirstSliceSegmentInPicture) |
||||
|
{ |
||||
|
throw new InvalidImageContentException("The HEVC image item contains more than one coded picture."); |
||||
|
} |
||||
|
|
||||
|
sliceSegments.Add(sliceSegment); |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
if (nalUnit.Header.NalUnitType is 32 or 33 or 34) |
||||
|
{ |
||||
|
// hvc1 image items obtain all parameter sets from the associated hvcC property. Accepting in-band
|
||||
|
// replacements would silently apply the more permissive hev1 sample contract to this still image.
|
||||
|
throw new InvalidImageContentException("The HEVC hvc1 image item contains an in-band parameter set."); |
||||
|
} |
||||
|
|
||||
|
if (nalUnit.Header.NalUnitType is 36 or 37) |
||||
|
{ |
||||
|
throw new InvalidImageContentException("The HEVC image item contains an end-of-sequence NAL unit."); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (sliceSegments.Count == 0) |
||||
|
{ |
||||
|
throw new InvalidImageContentException("The HEVC image item contains no independently decodable picture."); |
||||
|
} |
||||
|
|
||||
|
this.NalUnits = nalUnits; |
||||
|
this.SliceSegments = sliceSegments; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets every decoded NAL unit in item order, including permitted delimiter, filler, and supplemental units.
|
||||
|
/// </summary>
|
||||
|
public IReadOnlyList<HevcNalUnit> NalUnits { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the ordered slice segments that reconstruct the item's single IDR picture.
|
||||
|
/// </summary>
|
||||
|
public IReadOnlyList<HevcSliceSegmentHeader> SliceSegments { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads an unsigned one-through-four-byte NAL-unit length without assuming four-byte item framing.
|
||||
|
/// </summary>
|
||||
|
/// <param name="data">The item bytes beginning at the length field.</param>
|
||||
|
/// <param name="lengthSize">The codec-configuration-selected length-field width.</param>
|
||||
|
/// <returns>The bounded signed integer NAL-unit length.</returns>
|
||||
|
/// <exception cref="InvalidImageContentException">The unsigned length exceeds the supported item-buffer range.</exception>
|
||||
|
private static int ReadNalUnitLength(ReadOnlySpan<byte> data, int lengthSize) |
||||
|
{ |
||||
|
uint value = 0; |
||||
|
for (int byteIndex = 0; byteIndex < lengthSize; byteIndex++) |
||||
|
{ |
||||
|
value = (value << 8) | data[byteIndex]; |
||||
|
} |
||||
|
|
||||
|
if (value > int.MaxValue) |
||||
|
{ |
||||
|
throw new InvalidImageContentException("The HEVC image-item NAL-unit length is too large."); |
||||
|
} |
||||
|
|
||||
|
return (int)value; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,389 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Hevc; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Contains the decoded header and entropy-coded payload of one HEVC still-picture slice segment.
|
||||
|
/// </summary>
|
||||
|
internal sealed class HevcSliceSegmentHeader |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="HevcSliceSegmentHeader"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="nalUnit">The item-local instantaneous-decoder-refresh NAL unit.</param>
|
||||
|
/// <param name="pictureParameterSets">The picture parameter sets available to the coded image item.</param>
|
||||
|
/// <exception cref="InvalidImageContentException">
|
||||
|
/// The NAL unit is not a base-layer IDR slice, references unavailable parameters, or contains malformed
|
||||
|
/// still-picture slice-header syntax.
|
||||
|
/// </exception>
|
||||
|
public HevcSliceSegmentHeader( |
||||
|
HevcNalUnit nalUnit, |
||||
|
IReadOnlyList<HevcPictureParameterSet> pictureParameterSets) |
||||
|
{ |
||||
|
if (!nalUnit.Header.IsInstantaneousDecoderRefresh |
||||
|
|| nalUnit.Header.LayerId != 0 |
||||
|
|| nalUnit.Header.TemporalId != 0) |
||||
|
{ |
||||
|
throw new InvalidImageContentException("The HEVC image item contains a non-IDR or layered coded slice."); |
||||
|
} |
||||
|
|
||||
|
this.NalUnit = nalUnit; |
||||
|
HevcBitReader reader = new(nalUnit.Rbsp.Span); |
||||
|
this.FirstSliceSegmentInPicture = reader.ReadFlag(); |
||||
|
|
||||
|
// An IDR item has no earlier picture whose output can affect the returned still image. Consume the required
|
||||
|
// random-access flag without retaining sequence-output state in the image decoder.
|
||||
|
reader.ReadFlag(); |
||||
|
|
||||
|
uint pictureParameterSetId = reader.ReadUnsignedExpGolomb(); |
||||
|
if (pictureParameterSetId > 63) |
||||
|
{ |
||||
|
throw new InvalidImageContentException("The HEVC slice segment has an invalid picture-parameter-set identifier."); |
||||
|
} |
||||
|
|
||||
|
HevcPictureParameterSet? pictureParameterSet = null; |
||||
|
foreach (HevcPictureParameterSet candidate in pictureParameterSets) |
||||
|
{ |
||||
|
if (candidate.Id == pictureParameterSetId) |
||||
|
{ |
||||
|
pictureParameterSet = candidate; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (pictureParameterSet is null) |
||||
|
{ |
||||
|
throw new InvalidImageContentException("The HEVC slice segment references an unavailable picture parameter set."); |
||||
|
} |
||||
|
|
||||
|
this.PictureParameterSet = pictureParameterSet; |
||||
|
HevcSequenceParameterSet sequenceParameterSet = pictureParameterSet.SequenceParameterSet; |
||||
|
if (pictureParameterSet.DependentSliceSegmentsEnabled && !this.FirstSliceSegmentInPicture) |
||||
|
{ |
||||
|
this.DependentSliceSegment = reader.ReadFlag(); |
||||
|
} |
||||
|
|
||||
|
int codingTreeBlockColumns = HevcParameterSetSyntax.GetCodingTreeBlockCount( |
||||
|
sequenceParameterSet.Width, |
||||
|
sequenceParameterSet.CodingTreeBlockLog2); |
||||
|
|
||||
|
int codingTreeBlockRows = HevcParameterSetSyntax.GetCodingTreeBlockCount( |
||||
|
sequenceParameterSet.Height, |
||||
|
sequenceParameterSet.CodingTreeBlockLog2); |
||||
|
|
||||
|
int codingTreeBlockCount = codingTreeBlockColumns * codingTreeBlockRows; |
||||
|
if (!this.FirstSliceSegmentInPicture) |
||||
|
{ |
||||
|
int addressBitCount = HevcParameterSetSyntax.GetCeilingLog2(codingTreeBlockCount); |
||||
|
uint address = reader.ReadBits(addressBitCount); |
||||
|
if (address >= codingTreeBlockCount) |
||||
|
{ |
||||
|
throw new InvalidImageContentException("The HEVC slice segment address is outside the coded picture."); |
||||
|
} |
||||
|
|
||||
|
this.SliceSegmentAddress = (int)address; |
||||
|
} |
||||
|
|
||||
|
if (!this.DependentSliceSegment) |
||||
|
{ |
||||
|
this.ReadIndependentHeader(ref reader); |
||||
|
} |
||||
|
|
||||
|
this.ReadEntryPoints(ref reader, codingTreeBlockCount); |
||||
|
if (pictureParameterSet.SliceSegmentHeaderExtensionPresent) |
||||
|
{ |
||||
|
uint extensionLength = reader.ReadUnsignedExpGolomb(); |
||||
|
if (extensionLength > int.MaxValue || extensionLength > reader.BitsRemaining / 8) |
||||
|
{ |
||||
|
throw new InvalidImageContentException("The HEVC slice-segment header extension is truncated."); |
||||
|
} |
||||
|
|
||||
|
for (int byteIndex = 0; byteIndex < extensionLength; byteIndex++) |
||||
|
{ |
||||
|
reader.ReadBits(8); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
reader.ReadByteAlignment(); |
||||
|
this.HeaderLength = reader.BitPosition / 8; |
||||
|
this.SliceData = nalUnit.Rbsp[this.HeaderLength..]; |
||||
|
if (this.SliceData.IsEmpty) |
||||
|
{ |
||||
|
throw new InvalidImageContentException("The HEVC slice segment contains no entropy-coded data."); |
||||
|
} |
||||
|
|
||||
|
int encodedHeaderLength = GetEncodedPayloadOffset( |
||||
|
this.HeaderLength, |
||||
|
nalUnit.EmulationPreventionBytePositions); |
||||
|
|
||||
|
int availableEncodedData = nalUnit.EncodedPayloadLength - encodedHeaderLength; |
||||
|
int cumulativeEntryPointOffset = 0; |
||||
|
foreach (int entryPointOffset in this.EntryPointOffsets) |
||||
|
{ |
||||
|
if (cumulativeEntryPointOffset > availableEncodedData - entryPointOffset) |
||||
|
{ |
||||
|
throw new InvalidImageContentException("The HEVC slice entry point extends beyond its NAL unit."); |
||||
|
} |
||||
|
|
||||
|
cumulativeEntryPointOffset += entryPointOffset; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>Gets the complete decoded NAL unit containing this slice segment.</summary>
|
||||
|
public HevcNalUnit NalUnit { get; } |
||||
|
|
||||
|
/// <summary>Gets a value indicating whether this is the first slice segment of the coded picture.</summary>
|
||||
|
public bool FirstSliceSegmentInPicture { get; } |
||||
|
|
||||
|
/// <summary>Gets a value indicating whether this segment inherits syntax from an earlier independent slice.</summary>
|
||||
|
public bool DependentSliceSegment { get; } |
||||
|
|
||||
|
/// <summary>Gets the picture parameters selected by this slice segment.</summary>
|
||||
|
public HevcPictureParameterSet PictureParameterSet { get; } |
||||
|
|
||||
|
/// <summary>Gets the raster-scan address of the first coding-tree block in this slice segment.</summary>
|
||||
|
public int SliceSegmentAddress { get; } |
||||
|
|
||||
|
/// <summary>Gets the independent slice prediction type, or <see langword="null"/> for a dependent segment.</summary>
|
||||
|
public HevcSliceType? SliceType { get; private set; } |
||||
|
|
||||
|
/// <summary>Gets the selected color-plane identifier for separate-plane 4:4:4 coding.</summary>
|
||||
|
public byte ColorPlaneId { get; private set; } |
||||
|
|
||||
|
/// <summary>Gets a value indicating whether luma sample-adaptive offset filtering is enabled.</summary>
|
||||
|
public bool? SampleAdaptiveOffsetLumaEnabled { get; private set; } |
||||
|
|
||||
|
/// <summary>Gets a value indicating whether chroma sample-adaptive offset filtering is enabled.</summary>
|
||||
|
public bool? SampleAdaptiveOffsetChromaEnabled { get; private set; } |
||||
|
|
||||
|
/// <summary>Gets the effective luma quantization parameter, or <see langword="null"/> for a dependent segment.</summary>
|
||||
|
public int? QuantizationParameter { get; private set; } |
||||
|
|
||||
|
/// <summary>Gets the slice-level Cb quantization-parameter offset.</summary>
|
||||
|
public int ChromaCbQuantizationParameterOffset { get; private set; } |
||||
|
|
||||
|
/// <summary>Gets the slice-level Cr quantization-parameter offset.</summary>
|
||||
|
public int ChromaCrQuantizationParameterOffset { get; private set; } |
||||
|
|
||||
|
/// <summary>Gets a value indicating whether coding units can select the PPS chroma-offset list.</summary>
|
||||
|
public bool? ChromaQuantizationParameterOffsetListEnabled { get; private set; } |
||||
|
|
||||
|
/// <summary>Gets a value indicating whether deblocking is disabled for this independent slice.</summary>
|
||||
|
public bool? DeblockingFilterDisabled { get; private set; } |
||||
|
|
||||
|
/// <summary>Gets half the effective deblocking beta-threshold offset.</summary>
|
||||
|
public int DeblockingFilterBetaOffsetDiv2 { get; private set; } |
||||
|
|
||||
|
/// <summary>Gets half the effective deblocking clipping-threshold offset.</summary>
|
||||
|
public int DeblockingFilterTcOffsetDiv2 { get; private set; } |
||||
|
|
||||
|
/// <summary>Gets a value indicating whether in-loop filtering crosses slice boundaries.</summary>
|
||||
|
public bool? LoopFilterAcrossSlicesEnabled { get; private set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the encoded-byte lengths that separate tile or wavefront entropy substreams after the first substream.
|
||||
|
/// </summary>
|
||||
|
public IReadOnlyList<int> EntryPointOffsets { get; private set; } = Array.Empty<int>(); |
||||
|
|
||||
|
/// <summary>Gets the slice-header length in decoded raw-byte-sequence payload bytes.</summary>
|
||||
|
public int HeaderLength { get; } |
||||
|
|
||||
|
/// <summary>Gets the entropy-coded slice data following byte alignment.</summary>
|
||||
|
public ReadOnlyMemory<byte> SliceData { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads fields carried only by an independent slice-segment header.
|
||||
|
/// </summary>
|
||||
|
/// <param name="reader">The slice-segment raw byte sequence payload reader.</param>
|
||||
|
/// <exception cref="InvalidImageContentException">
|
||||
|
/// The slice is not intra-coded or its quantization and filter fields are outside the governing parameter bounds.
|
||||
|
/// </exception>
|
||||
|
private void ReadIndependentHeader(ref HevcBitReader reader) |
||||
|
{ |
||||
|
HevcPictureParameterSet pictureParameterSet = this.PictureParameterSet; |
||||
|
HevcSequenceParameterSet sequenceParameterSet = pictureParameterSet.SequenceParameterSet; |
||||
|
for (int extraBit = 0; extraBit < pictureParameterSet.ExtraSliceHeaderBitCount; extraBit++) |
||||
|
{ |
||||
|
reader.ReadFlag(); |
||||
|
} |
||||
|
|
||||
|
uint sliceType = reader.ReadUnsignedExpGolomb(); |
||||
|
if (sliceType != (uint)HevcSliceType.Intra) |
||||
|
{ |
||||
|
throw new InvalidImageContentException("An independently coded HEVC image item must contain intra IDR slices."); |
||||
|
} |
||||
|
|
||||
|
this.SliceType = HevcSliceType.Intra; |
||||
|
if (pictureParameterSet.OutputFlagPresent && !reader.ReadFlag()) |
||||
|
{ |
||||
|
throw new InvalidImageContentException("The HEVC image-item slice is marked as unavailable for output."); |
||||
|
} |
||||
|
|
||||
|
if (sequenceParameterSet.SeparateColorPlaneFlag) |
||||
|
{ |
||||
|
this.ColorPlaneId = (byte)reader.ReadBits(2); |
||||
|
if (this.ColorPlaneId > 2) |
||||
|
{ |
||||
|
throw new InvalidImageContentException("The HEVC slice segment has an invalid separate color-plane identifier."); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
bool hasCombinedChromaPlanes = sequenceParameterSet.ChromaFormat != 0 |
||||
|
&& !sequenceParameterSet.SeparateColorPlaneFlag; |
||||
|
|
||||
|
if (sequenceParameterSet.SampleAdaptiveOffsetEnabled) |
||||
|
{ |
||||
|
this.SampleAdaptiveOffsetLumaEnabled = reader.ReadFlag(); |
||||
|
this.SampleAdaptiveOffsetChromaEnabled = hasCombinedChromaPlanes && reader.ReadFlag(); |
||||
|
} |
||||
|
|
||||
|
int sliceQuantizationParameterDelta = reader.ReadSignedExpGolomb(); |
||||
|
long quantizationParameter = 26L |
||||
|
+ pictureParameterSet.InitialQuantizationParameterMinus26 |
||||
|
+ sliceQuantizationParameterDelta; |
||||
|
|
||||
|
int minimumQuantizationParameter = -6 * (sequenceParameterSet.BitDepthLuma - 8); |
||||
|
if (quantizationParameter < minimumQuantizationParameter || quantizationParameter > 51) |
||||
|
{ |
||||
|
throw new InvalidImageContentException("The HEVC slice segment has an invalid luma quantization parameter."); |
||||
|
} |
||||
|
|
||||
|
this.QuantizationParameter = (int)quantizationParameter; |
||||
|
if (pictureParameterSet.SliceChromaQuantizationParameterOffsetsPresent && hasCombinedChromaPlanes) |
||||
|
{ |
||||
|
this.ChromaCbQuantizationParameterOffset = HevcParameterSetSyntax.ReadQuantizationParameterOffset(ref reader); |
||||
|
this.ChromaCrQuantizationParameterOffset = HevcParameterSetSyntax.ReadQuantizationParameterOffset(ref reader); |
||||
|
if (pictureParameterSet.ChromaCbQuantizationParameterOffset + this.ChromaCbQuantizationParameterOffset is < -12 or > 12 |
||||
|
|| pictureParameterSet.ChromaCrQuantizationParameterOffset + this.ChromaCrQuantizationParameterOffset is < -12 or > 12) |
||||
|
{ |
||||
|
throw new InvalidImageContentException("The HEVC slice and picture chroma quantization offsets have an invalid sum."); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (pictureParameterSet.ChromaQuantizationParameterOffsetsCb.Count != 0) |
||||
|
{ |
||||
|
this.ChromaQuantizationParameterOffsetListEnabled = reader.ReadFlag(); |
||||
|
} |
||||
|
|
||||
|
this.ReadDeblockingFilterFields(ref reader); |
||||
|
bool sampleAdaptiveOffsetEnabled = this.SampleAdaptiveOffsetLumaEnabled == true |
||||
|
|| this.SampleAdaptiveOffsetChromaEnabled == true; |
||||
|
|
||||
|
if (pictureParameterSet.LoopFilterAcrossSlicesEnabled |
||||
|
&& (sampleAdaptiveOffsetEnabled || this.DeblockingFilterDisabled == false)) |
||||
|
{ |
||||
|
this.LoopFilterAcrossSlicesEnabled = reader.ReadFlag(); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
this.LoopFilterAcrossSlicesEnabled = pictureParameterSet.LoopFilterAcrossSlicesEnabled; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Resolves the independent slice's effective deblocking mode and threshold offsets.
|
||||
|
/// </summary>
|
||||
|
/// <param name="reader">The slice-segment raw byte sequence payload reader.</param>
|
||||
|
private void ReadDeblockingFilterFields(ref HevcBitReader reader) |
||||
|
{ |
||||
|
HevcPictureParameterSet pictureParameterSet = this.PictureParameterSet; |
||||
|
bool overrideFilter = false; |
||||
|
if (pictureParameterSet.DeblockingFilterControlPresent |
||||
|
&& pictureParameterSet.DeblockingFilterOverrideEnabled) |
||||
|
{ |
||||
|
overrideFilter = reader.ReadFlag(); |
||||
|
} |
||||
|
|
||||
|
if (overrideFilter) |
||||
|
{ |
||||
|
this.DeblockingFilterDisabled = reader.ReadFlag(); |
||||
|
if (this.DeblockingFilterDisabled == false) |
||||
|
{ |
||||
|
this.DeblockingFilterBetaOffsetDiv2 = HevcParameterSetSyntax.ReadDeblockingFilterOffset(ref reader); |
||||
|
this.DeblockingFilterTcOffsetDiv2 = HevcParameterSetSyntax.ReadDeblockingFilterOffset(ref reader); |
||||
|
} |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
this.DeblockingFilterDisabled = pictureParameterSet.DeblockingFilterControlPresent |
||||
|
&& pictureParameterSet.DeblockingFilterDisabled; |
||||
|
|
||||
|
this.DeblockingFilterBetaOffsetDiv2 = pictureParameterSet.DeblockingFilterBetaOffsetDiv2; |
||||
|
this.DeblockingFilterTcOffsetDiv2 = pictureParameterSet.DeblockingFilterTcOffsetDiv2; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads tile or wavefront substream entry-point byte lengths.
|
||||
|
/// </summary>
|
||||
|
/// <param name="reader">The slice-segment raw byte sequence payload reader.</param>
|
||||
|
/// <param name="codingTreeBlockCount">The number of coding-tree blocks in the coded picture.</param>
|
||||
|
/// <exception cref="InvalidImageContentException">
|
||||
|
/// The entry-point count, field width, or byte length exceeds the bounded picture or integer range.
|
||||
|
/// </exception>
|
||||
|
private void ReadEntryPoints(ref HevcBitReader reader, int codingTreeBlockCount) |
||||
|
{ |
||||
|
HevcPictureParameterSet pictureParameterSet = this.PictureParameterSet; |
||||
|
if (!pictureParameterSet.TilesEnabled && !pictureParameterSet.EntropyCodingSynchronizationEnabled) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
uint entryPointCount = reader.ReadUnsignedExpGolomb(); |
||||
|
if (entryPointCount >= codingTreeBlockCount) |
||||
|
{ |
||||
|
throw new InvalidImageContentException("The HEVC slice segment declares too many entropy entry points."); |
||||
|
} |
||||
|
|
||||
|
if (entryPointCount == 0) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
uint offsetLengthMinusOne = reader.ReadUnsignedExpGolomb(); |
||||
|
if (offsetLengthMinusOne > 31) |
||||
|
{ |
||||
|
throw new InvalidImageContentException("The HEVC slice entry-point offset width is invalid."); |
||||
|
} |
||||
|
|
||||
|
int offsetBitCount = (int)offsetLengthMinusOne + 1; |
||||
|
int[] entryPointOffsets = new int[entryPointCount]; |
||||
|
for (int entryPoint = 0; entryPoint < entryPointOffsets.Length; entryPoint++) |
||||
|
{ |
||||
|
uint entryPointOffsetMinusOne = reader.ReadBits(offsetBitCount); |
||||
|
if (entryPointOffsetMinusOne >= int.MaxValue) |
||||
|
{ |
||||
|
throw new InvalidImageContentException("The HEVC slice entry-point byte length is too large."); |
||||
|
} |
||||
|
|
||||
|
entryPointOffsets[entryPoint] = (int)entryPointOffsetMinusOne + 1; |
||||
|
} |
||||
|
|
||||
|
this.EntryPointOffsets = entryPointOffsets; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts an RBSP byte boundary to its corresponding encoded-payload boundary.
|
||||
|
/// </summary>
|
||||
|
/// <param name="rbspOffset">The decoded raw-byte-sequence payload offset.</param>
|
||||
|
/// <param name="emulationPreventionBytePositions">The removed encoded-payload byte positions.</param>
|
||||
|
/// <returns>The encoded byte-sequence payload offset at the same syntax boundary.</returns>
|
||||
|
private static int GetEncodedPayloadOffset( |
||||
|
int rbspOffset, |
||||
|
IReadOnlyList<int> emulationPreventionBytePositions) |
||||
|
{ |
||||
|
int encodedOffset = rbspOffset; |
||||
|
foreach (int preventionBytePosition in emulationPreventionBytePositions) |
||||
|
{ |
||||
|
if (preventionBytePosition >= encodedOffset) |
||||
|
{ |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
encodedOffset++; |
||||
|
} |
||||
|
|
||||
|
return encodedOffset; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Hevc; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Identifies the prediction structure signaled for an HEVC slice segment.
|
||||
|
/// </summary>
|
||||
|
internal enum HevcSliceType |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The slice can use intra and bidirectional inter prediction.
|
||||
|
/// </summary>
|
||||
|
Bidirectional = 0, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The slice can use intra and forward inter prediction.
|
||||
|
/// </summary>
|
||||
|
Predictive = 1, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The slice uses only intra-picture prediction.
|
||||
|
/// </summary>
|
||||
|
Intra = 2 |
||||
|
} |
||||
Loading…
Reference in new issue