Browse Source

Initialize HEVC still image CABAC contexts

pull/2633/head
James Jackson-South 1 week ago
parent
commit
020181d22e
  1. 3
      HEIF_IMPLEMENTATION_PLAN.md
  2. 225
      src/ImageSharp/Formats/Heif/Hevc/HevcCabacContexts.cs

3
HEIF_IMPLEMENTATION_PLAN.md

@ -118,6 +118,7 @@ This snapshot pins or classifies the available references and failures; it does
| `HevcPictureParameterSet` and `HevcCodecConfiguration.PictureParameterSets` | HEVC sections 7.3.2.3 picture parameter sets, 7.3.4 scaling-list data, and Range Extensions PPS syntax | HM `source/Lib/TLibDecoder/TDecCAVLC.cpp` function `parsePPS`, `source/Lib/TLibDecoder/TDecConformance.cpp` PPS range checks, and `source/Lib/TLibCommon/TComPicSym.cpp` tile derivation at `9c1f298659ab0cee9dc13d23d0304221575410b9`; Android `libhevc` `decoder/ihevcd_parse_headers.c` PPS and tile paths at `c83a76b084498d55f252f48b2e3786804cdf24b7` | Link each PPS to a configuration-local SPS and retain the CABAC, quantization, transform-skip, scaling-list, tile/wavefront, deblocking, SAO, slice-header, and Range Extensions controls required to reconstruct the independently coded picture. Derive the complete CTB tile grid and validate every SPS-dependent depth, size, precision, and offset bound during parsing. Reject multilayer extensions and add no access-unit, reference-picture lifetime, playback, track, timing, or sample-table model. |
| `HevcImageItemBitstream`, `HevcSliceSegmentHeader`, and `HevcSliceType` | HEVC sections 7.3.1.2 NAL-unit syntax, 7.3.6 slice-segment headers, and 7.4.7 slice-segment-header semantics; HEVC image-item length delimiting | HM `source/Lib/TLibDecoder/TDecCAVLC.cpp` function `parseSliceHeader`, `source/Lib/TLibDecoder/NALread.cpp`, and slice byte-alignment and entry-point handling at `9c1f298659ab0cee9dc13d23d0304221575410b9`; Android `libhevc` `decoder/ihevcd_nal.c`, `ihevcd_parse_slice_header.c`, and `ihevcd_parse_headers.c` at `c83a76b084498d55f252f48b2e3786804cdf24b7` | Split only the selected `hvc1` image item's one-through-four-byte length-delimited NAL units, reject in-band parameter-set replacement and end-of-sequence state, and require exactly one base-layer IDR picture. Retain the intra slice, separate-plane, SAO, quantization, deblocking, cross-slice filtering, tile/wavefront entry-point, extension, byte-alignment, and entropy-payload fields required by reconstruction. Keep emulation-prevention positions only to translate slice substream byte boundaries; add no Annex B scanner, access-unit collection, non-IDR reference lifetime, playback, timing, track, or sample model. |
| `HevcCabacContext` and `HevcCabacDecoder` | HEVC sections 9.3.3.2 through 9.3.3.4 context initialization, arithmetic decoding, bypass decoding, and termination | HM `source/Lib/TLibCommon/ContextModel.cpp`, `source/Lib/TLibCommon/TComCABACTables.cpp`, and `source/Lib/TLibDecoder/TDecBinCoderCABAC.cpp` at `9c1f298659ab0cee9dc13d23d0304221575410b9`; Android `libhevc` `common/ihevc_cabac_tables.c`, `common/ihevc_cabac_tables.h`, `decoder/ihevcd_cabac.c`, and `decoder/ihevcd_cabac.h` at `c83a76b084498d55f252f48b2e3786804cdf24b7` | Implement the bit-exact scalar probability-state transitions, range normalization, bounded byte refills, bypass modes, termination, and alignment validation for one slice tile or wavefront substream. CABAC decisions are serial and adaptive, so this primitive is the scalar oracle rather than a SIMD target; later prediction, transform, filtering, and color-conversion stages must reuse ImageSharp's compatible SIMD kernels. The decoder owns no container boxes, access units, reference pictures, tracks, timing, playback, or general-purpose bitstream surface. |
| `HevcCabacContexts` | HEVC section 9.3.2.2 initialization of context variables for an intra-coded slice and Range Extensions context additions | HM `source/Lib/TLibCommon/ContextTables.h` and `source/Lib/TLibDecoder/TDecSbac.cpp` function `resetEntropy` at `9c1f298659ab0cee9dc13d23d0304221575410b9`; Android `libhevc` `common/ihevc_cabac_tables.c` and `decoder/ihevcd_cabac.c` at `c83a76b084498d55f252f48b2e3786804cdf24b7` | Initialize only the context groups reachable from the independently coded intra-picture syntax, including transform skip, chroma quantization adjustment, and cross-component prediction. Keep one contiguous mutable context set per entropy substream so tile and wavefront synchronization can copy the normative adaptive state without retaining reference-picture or playback state. Omit skip, merge, motion, reference-index, and other inter-picture-only groups. |
| `HeifContentColorVolume`, `HeifItem.ContentColorVolume`, and `HeifDecoderCore` content color-volume parsing and presentation | HEIF content color-volume item property; AVIF 1.2 content color-volume requirements; ITU-T H.274 (V4) content colour volume syntax and semantics | libavif `src/read.c` function `avifSkipContentColourVolume` at `092276ce89098ead06db80975173191e5fee1826`; official ITU-T H.274 (V4), January 2026 | Decode only the bounded per-image `cclv` property: require zero cancellation, persistence, and reserved bits; preserve optional signed G/B/R primary coordinates and normalized minimum, maximum, and average luminance values; and validate their registered ranges and ordering. Expose the effective grid-or-tile still-image value through `HeifMetadata`. Do not add SEI persistence, retained video state, tracks, samples, timing, or a generic ISO BMFF color-volume box model. |
| `HeifAmbientViewingEnvironment`, `HeifReferenceViewingEnvironment`, `HeifNominalDiffuseWhite`, and their per-item presentation metadata | ISOBMFF ambient viewing environment; ITU-T H.274 (V4) section 8.13; HEIF Amendment 1 sections 6.5.44 and 6.5.45; AVIF 1.2 image-item box requirements | libavif `src/read.c` functions `avifSkipAmbientViewingEnvironment`, `avifSkipReferenceViewingEnvironment`, and `avifSkipNominalDiffuseWhite` at `092276ce89098ead06db80975173191e5fee1826`; official ITU-T H.274 (V4), January 2026 | Decode the fixed-size `amve`, version-zero `reve`, and version-zero `ndwt` properties only when associated with the presented still-image item. Preserve physical illuminance and luminance units, distinct surround/periphery chromaticities, and the coded zero that requests the standard nominal diffuse-white default. Validate registered coordinate ranges and duplicate associations. Retain no video-SEI persistence, visual sample entry, display pipeline, track, timing, or generic viewing-environment box model. |
| `GridHeifItemDecoder` and `HeifDecoderCore` grid/thumbnail selection | ISO/IEC 23008-12 section 6.6.2.3 image-grid syntax and MIAF grid-cell constraints | libavif `src/read.c` functions `avifParseImageGridBox`, `avifDecoderDataAllocateImagePlanes`, and `avifDecoderDataCopyTileToImage` at `092276ce89098ead06db80975173191e5fee1826` | Parse version-zero 16-bit and 32-bit grid descriptors, preserve row-major `dimg` order, require the declared tile count and one coding format, validate canvas coverage and edge overlap, and crop only the rightmost column and bottom row while copying through ImageSharp pixel buffers. A primary grid whose tile codec is unavailable may use only a decodable thumbnail that explicitly references that grid. |
@ -158,7 +159,7 @@ This assessment is based on the current source after the upstream ImageSharp mer
### HEVC decoder and encoder
- `Heif4CharCode` recognizes `hvc1` image items, `HevcCodecConfiguration` validates and associates the bounded `hvcC` property, and Identify reports its HEVC precision and monochrome shape, but `HeifCompressionFactory` has no HEVC item decoder.
- The HEVC path now validates NAL headers and RBSP escaping, parses and links `hvcC` VPS/SPS/PPS structures through Range Extensions, and parses the selected image item's length-delimited base-layer IDR slice headers and entropy payload boundaries. Its scalar CABAC primitive implements the exact adaptive state transitions, arithmetic range normalization, bypass modes, bounded refills, termination, and stop-bit validation from the pinned HM reference; context-set initialization and coding-tree syntax are not yet wired. The path is deliberately not registered as an item decoder until the complete CABAC syntax, coding-tree reconstruction, still-image intra prediction, inverse quantization/transform, deblocking, sample-adaptive offset, color conversion, and high-bit-depth pixel output are implemented.
- The HEVC path now validates NAL headers and RBSP escaping, parses and links `hvcC` VPS/SPS/PPS structures through Range Extensions, and parses the selected image item's length-delimited base-layer IDR slice headers and entropy payload boundaries. Its scalar CABAC primitive implements the exact adaptive state transitions, arithmetic range normalization, bypass modes, bounded refills, termination, and stop-bit validation from the pinned HM reference. The one-allocation context owner initializes every intra-picture and Range Extensions context that still-image reconstruction can select while omitting inter-picture-only groups; coding-tree syntax is not yet wired. The path is deliberately not registered as an item decoder until the complete CABAC syntax, coding-tree reconstruction, still-image intra prediction, inverse quantization/transform, deblocking, sample-adaptive offset, color conversion, and high-bit-depth pixel output are implemented.
- There is no HEVC encoder. The current HEIC-branded encoder writes a legacy JPEG payload and therefore cannot provide HEIC output.
- Existing HEVC tests prove container identification only; they do not decode or compare HEIC pixels.

225
src/ImageSharp/Formats/Heif/Hevc/HevcCabacContexts.cs

@ -0,0 +1,225 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Hevc;
/// <summary>
/// Owns the adaptive CABAC probability contexts used to decode one intra-coded HEVC entropy substream.
/// </summary>
internal sealed class HevcCabacContexts
{
/// <summary>The first transquant-bypass context.</summary>
private const int TransquantBypassOffset = 0;
/// <summary>The first coding-unit split context.</summary>
private const int SplitOffset = 1;
/// <summary>The intra partition-size context.</summary>
private const int PartitionSizeOffset = 4;
/// <summary>The luma intra-prediction context.</summary>
private const int IntraPredictionOffset = 5;
/// <summary>The first chroma intra-prediction context.</summary>
private const int ChromaPredictionOffset = 6;
/// <summary>The first luma quantization-delta context.</summary>
private const int DeltaQuantizationOffset = 8;
/// <summary>The chroma quantization-adjustment flag context.</summary>
private const int ChromaQuantizationAdjustmentFlagOffset = 11;
/// <summary>The chroma quantization-adjustment index context.</summary>
private const int ChromaQuantizationAdjustmentIndexOffset = 12;
/// <summary>The first transform-tree coded-block-flag context.</summary>
private const int TransformCodedBlockFlagOffset = 13;
/// <summary>The first horizontal last-significant-coefficient context.</summary>
private const int LastSignificantXOffset = 23;
/// <summary>The first vertical last-significant-coefficient context.</summary>
private const int LastSignificantYOffset = 53;
/// <summary>The first significant-coefficient-group context.</summary>
private const int SignificantCoefficientGroupOffset = 83;
/// <summary>The first significant-coefficient context.</summary>
private const int SignificantCoefficientOffset = 87;
/// <summary>The first greater-than-one coefficient-level context.</summary>
private const int GreaterThanOneOffset = 131;
/// <summary>The first greater-than-two coefficient-level context.</summary>
private const int GreaterThanTwoOffset = 155;
/// <summary>The sample-adaptive-offset merge context.</summary>
private const int SampleAdaptiveOffsetMergeOffset = 161;
/// <summary>The sample-adaptive-offset type context.</summary>
private const int SampleAdaptiveOffsetTypeOffset = 162;
/// <summary>The first transform-tree subdivision context.</summary>
private const int TransformSubdivisionOffset = 163;
/// <summary>The first transform-skip context.</summary>
private const int TransformSkipOffset = 166;
/// <summary>The first cross-component prediction context.</summary>
private const int CrossComponentPredictionOffset = 168;
/// <summary>The number of contexts used by the independently coded intra-picture syntax.</summary>
private const int ContextCount = 178;
/// <summary>
/// The HEVC intra-slice initialization values in the same order as the owned context ranges.
/// </summary>
private static readonly byte[] IntraInitializationValues =
[
// cu_transquant_bypass_flag
154,
// split_cu_flag
139, 141, 157,
// part_mode and prev_intra_luma_pred_flag
184,
184,
// intra_chroma_pred_mode
63, 139,
// cu_qp_delta_abs, cu_chroma_qp_offset_flag, and cu_chroma_qp_offset_idx
154, 154, 154,
154,
154,
// cbf_luma followed by the chroma coded-block flags
111, 141, 154, 154, 154,
94, 138, 182, 154, 154,
// last_sig_coeff_x_prefix: luma followed by chroma
110, 110, 124, 125, 140, 153, 125, 127, 140, 109, 111, 143, 127, 111, 79,
108, 123, 63, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154,
// last_sig_coeff_y_prefix: luma followed by chroma
110, 110, 124, 125, 140, 153, 125, 127, 140, 109, 111, 143, 127, 111, 79,
108, 123, 63, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154,
// coded_sub_block_flag: luma followed by chroma
91, 171, 134, 141,
// sig_coeff_flag: luma followed by chroma
111, 111, 125, 110, 110, 94, 124, 108, 124, 107, 125, 141, 179, 153,
125, 107, 125, 141, 179, 153, 125, 107, 125, 141, 179, 153, 125, 141,
140, 139, 182, 182, 152, 136, 152, 136, 153, 136, 139, 111, 136, 139, 111, 111,
// coeff_abs_level_greater1_flag: luma followed by chroma
140, 92, 137, 138, 140, 152, 138, 139, 153, 74, 149, 92, 139, 107, 122, 152,
140, 179, 166, 182, 140, 227, 122, 197,
// coeff_abs_level_greater2_flag: luma followed by chroma
138, 153, 136, 167, 152, 152,
// sao_merge_flag and sao_type_idx
153,
200,
// split_transform_flag
153, 138, 138,
// transform_skip_flag: luma followed by chroma
139, 139,
// cross_comp_pred: five sign/magnitude contexts for Cb followed by five for Cr
154, 154, 154, 154, 154, 154, 154, 154, 154, 154
];
/// <summary>
/// The contiguous adaptive context storage owned by the entropy substream.
/// </summary>
private readonly HevcCabacContext[] contexts;
/// <summary>
/// Initializes a new instance of the <see cref="HevcCabacContexts"/> class for an intra-coded slice.
/// </summary>
/// <param name="quantizationParameter">The slice luma quantization parameter.</param>
public HevcCabacContexts(int quantizationParameter)
{
this.contexts = new HevcCabacContext[ContextCount];
for (int index = 0; index < this.contexts.Length; index++)
{
this.contexts[index] = new HevcCabacContext(quantizationParameter, IntraInitializationValues[index]);
}
}
/// <summary>Gets the coding-unit transquant-bypass context.</summary>
public Span<HevcCabacContext> TransquantBypass => this.contexts.AsSpan(TransquantBypassOffset, 1);
/// <summary>Gets the coding-unit split contexts, ordered by neighboring split depth.</summary>
public Span<HevcCabacContext> Split => this.contexts.AsSpan(SplitOffset, 3);
/// <summary>Gets the intra partition-size context.</summary>
public Span<HevcCabacContext> PartitionSize => this.contexts.AsSpan(PartitionSizeOffset, 1);
/// <summary>Gets the luma intra-prediction context.</summary>
public Span<HevcCabacContext> IntraPrediction => this.contexts.AsSpan(IntraPredictionOffset, 1);
/// <summary>Gets the chroma intra-prediction contexts.</summary>
public Span<HevcCabacContext> ChromaPrediction => this.contexts.AsSpan(ChromaPredictionOffset, 2);
/// <summary>Gets the luma quantization-delta contexts.</summary>
public Span<HevcCabacContext> DeltaQuantization => this.contexts.AsSpan(DeltaQuantizationOffset, 3);
/// <summary>Gets the chroma quantization-adjustment flag context.</summary>
public Span<HevcCabacContext> ChromaQuantizationAdjustmentFlag =>
this.contexts.AsSpan(ChromaQuantizationAdjustmentFlagOffset, 1);
/// <summary>Gets the chroma quantization-adjustment index context.</summary>
public Span<HevcCabacContext> ChromaQuantizationAdjustmentIndex =>
this.contexts.AsSpan(ChromaQuantizationAdjustmentIndexOffset, 1);
/// <summary>Gets the transform-tree coded-block-flag contexts, with luma preceding chroma.</summary>
public Span<HevcCabacContext> TransformCodedBlockFlag =>
this.contexts.AsSpan(TransformCodedBlockFlagOffset, 10);
/// <summary>Gets the horizontal last-significant-coefficient contexts, with luma preceding chroma.</summary>
public Span<HevcCabacContext> LastSignificantX => this.contexts.AsSpan(LastSignificantXOffset, 30);
/// <summary>Gets the vertical last-significant-coefficient contexts, with luma preceding chroma.</summary>
public Span<HevcCabacContext> LastSignificantY => this.contexts.AsSpan(LastSignificantYOffset, 30);
/// <summary>Gets the significant-coefficient-group contexts, with luma preceding chroma.</summary>
public Span<HevcCabacContext> SignificantCoefficientGroup =>
this.contexts.AsSpan(SignificantCoefficientGroupOffset, 4);
/// <summary>Gets the significant-coefficient contexts, with luma preceding chroma.</summary>
public Span<HevcCabacContext> SignificantCoefficient =>
this.contexts.AsSpan(SignificantCoefficientOffset, 44);
/// <summary>Gets the greater-than-one coefficient-level contexts, with luma preceding chroma.</summary>
public Span<HevcCabacContext> GreaterThanOne => this.contexts.AsSpan(GreaterThanOneOffset, 24);
/// <summary>Gets the greater-than-two coefficient-level contexts, with luma preceding chroma.</summary>
public Span<HevcCabacContext> GreaterThanTwo => this.contexts.AsSpan(GreaterThanTwoOffset, 6);
/// <summary>Gets the sample-adaptive-offset merge context.</summary>
public Span<HevcCabacContext> SampleAdaptiveOffsetMerge =>
this.contexts.AsSpan(SampleAdaptiveOffsetMergeOffset, 1);
/// <summary>Gets the sample-adaptive-offset type context.</summary>
public Span<HevcCabacContext> SampleAdaptiveOffsetType =>
this.contexts.AsSpan(SampleAdaptiveOffsetTypeOffset, 1);
/// <summary>Gets the transform-tree subdivision contexts.</summary>
public Span<HevcCabacContext> TransformSubdivision =>
this.contexts.AsSpan(TransformSubdivisionOffset, 3);
/// <summary>Gets the transform-skip contexts, with luma preceding chroma.</summary>
public Span<HevcCabacContext> TransformSkip => this.contexts.AsSpan(TransformSkipOffset, 2);
/// <summary>Gets the cross-component prediction contexts, with Cb preceding Cr.</summary>
public Span<HevcCabacContext> CrossComponentPrediction =>
this.contexts.AsSpan(CrossComponentPredictionOffset, 10);
}
Loading…
Cancel
Save