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