diff --git a/HEIF_IMPLEMENTATION_PLAN.md b/HEIF_IMPLEMENTATION_PLAN.md
index 506b0f454..f62035a6a 100644
--- a/HEIF_IMPLEMENTATION_PLAN.md
+++ b/HEIF_IMPLEMENTATION_PLAN.md
@@ -109,6 +109,7 @@ This snapshot pins or classifies the available references and failures; it does
| `HeifDecoderCore` color-property parsing/association, `HeifItem` color profiles, and `Av1Decoder` effective color validation | ISO/IEC 14496-12 section 12.1.5 color information; HEIF section 6.5.5.1 color-information properties; AV1-ISOBMFF section 2.3.4 configuration semantics | libavif `src/read.c` functions `avifParseColourInformationBox`, `avifReadColorNclxProperty`, and `avifReadColorProperties`, plus `src/write.c` function `avifEncoderWriteColorProperties`, at `092276ce89098ead06db80975173191e5fee1826` | Associate at most one ICC and one `nclx` property with each presented color image item, validate ICC payloads and CICP reserved bits, expose them through ImageSharp's existing profile types, and inherit a grid's CICP description only for tiles that do not declare one. For AV1 items, require every specified sequence-header CICP field and the range flag to match `nclx`; use the item property only to supply sequence-header fields coded as unspecified before color conversion, then expose the effective CICP values even when no item property is present. Retain only the two image color profiles; do not add a reusable color-box, sample-entry, track, or media model. |
| `HeifPixelAspectRatio`, `HeifItem.PixelAspectRatio`, and `HeifDecoderCore.ApplyItemPixelAspectRatioMetadata` | ISO/IEC 14496-12 section 12.1.4.3 pixel aspect ratio | libavif `src/read.c` function `avifParsePixelAspectRatioBox`, `src/write.c` function `avifEncoderWritePaspProperty`, and the presented-image property selection in `src/read.c` at `092276ce89098ead06db80975173191e5fee1826` | Preserve the two unsigned 32-bit relative spacings on the associated image item, reject zero or duplicate ratios, and map the displayed pixel width-to-height ratio into ImageSharp's existing unitless resolution metadata. Exchange the metadata axes after a quarter-turn presentation rotation and fall back from a derived grid to its first decodable tile only when the grid does not declare `pasp`. This remains one still-image presentation property and introduces no generic transform, sample-entry, or display model. |
| `Av1CodecConfiguration`, `HeifItem.Av1CodecConfiguration`, `Av1HeifItemDecoder`, and AV1 grid configuration checks | AV1-ISOBMFF sections 2.3.3 and 2.3.4 codec-configuration record syntax and semantics; AVIF sections 2.1, 2.2.1, and 2.2.3 AV1 image-item, item-configuration, and HDR metadata constraints; AV1 sections 5.8.3, 5.8.4, 6.7.3, and 6.7.4 HDR metadata syntax and semantics; ISOBMFF mastering-display and content-light image properties; ITU-T H.274 section 8.9 mastering-display field semantics; MIAF section 7.3.11.4.1 grid input constraints | libavif `src/read.c` functions `avifParseCodecConfiguration`, `avifDecoderItemValidateProperties`, `avifReadCodecConfigProperty`, `avifParseContentLightLevelInformation`, and `avifSkipMasteringDisplayColourVolume` at `092276ce89098ead06db80975173191e5fee1826`; libaom `av1/decoder/obu.c` functions `read_metadata`, `read_metadata_hdr_cll`, and `read_metadata_hdr_mdcv` at `03087864cf4bea6abb0d28f95cf7843511413d8f` | Associate exactly one `av1C` property with each decoded `av01` image item, validate the fixed record and its bit depth/chroma fields against the item's AV1 sequence header and optional `pixi` channel depths, require matching configurations across grid tiles, and report the encoded image precision and monochrome shape through `HeifMetadata`. Validate low-overhead OBU framing, require exactly one sequence header in the image item, allow at most one first-position sequence header in `configOBUs`, and compare a repeated header's extension and payload exactly while ignoring only its legal size-field representation. Decode `clli` and `mdcv` as still-image item properties, validate matching HDR CLL and HDR MDCV metadata OBUs from the combined configuration/item sequence, and account for the different primary order and fixed-point precision of the ISOBMFF and AV1 MDCV representations. Expose the effective HDR values without adding sample groups, tracks, or media metadata. Related still-image HDR properties remain required. Consume but do not retain presentation-delay syntax, and introduce no sample entry, sample description, track, timing, or generic decoder-configuration model. |
+| `HevcCodecConfiguration`, `HevcNalUnitArray`, and `HeifItem.HevcCodecConfiguration` | ISO/IEC 14496-15 `HEVCDecoderConfigurationRecord`, HEVC NAL-unit header syntax, and HEVC image-item configuration requirements | Android `libhevc` v1.6.0 decoder NAL and parameter-header paths at `c83a76b084498d55f252f48b2e3786804cdf24b7`; independently produced HEIC fixtures for `hvcC` record layout | Associate exactly one `hvcC` property with each `hvc1` image item; validate the fixed profile, level, chroma, bit-depth, reserved, length-size, array, and NAL-header fields within the property boundary; and retain only the image description plus bounded configuration NAL units required to decode that item. Consume but do not retain average-frame-rate or temporal-layer presentation fields. Do not add visual sample entries, sample descriptions, tracks, timing, access-unit timelines, or a generic decoder-configuration model. Apache-2.0 notice preservation and separate HEVC patent/release clearance remain final integration gates. |
| `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. |
diff --git a/THIRD-PARTY-NOTICES.TXT b/THIRD-PARTY-NOTICES.TXT
index 55dc93702..3c43774f2 100644
--- a/THIRD-PARTY-NOTICES.TXT
+++ b/THIRD-PARTY-NOTICES.TXT
@@ -172,3 +172,215 @@ Alliance for Open Media Patent License 1.0
2.12. Specification. "Specification" means the specification designated by
the Alliance for Open Media as a Final Deliverable for which this
License was issued.
+
+
+License notice for the Ittiam libhevc Codec Library
+-----
+
+Parts of the HEVC implementation are adapted from the Android fork of the
+Ittiam libhevc Codec Library.
+https://android.googlesource.com/platform/external/libhevc/
+
+Copyright (C) 2012 Ittiam Systems Pvt Ltd, Bangalore
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/src/ImageSharp/Formats/Heif/Heif4CharCode.cs b/src/ImageSharp/Formats/Heif/Heif4CharCode.cs
index fa354caad..94bfe685e 100644
--- a/src/ImageSharp/Formats/Heif/Heif4CharCode.cs
+++ b/src/ImageSharp/Formats/Heif/Heif4CharCode.cs
@@ -104,7 +104,7 @@ public enum Heif4CharCode : uint
Ndwt = 0x6E647774U,
///
- /// HVC configuration.
+ /// HEVC codec-configuration item property.
///
HvcC = 0x68766343U,
@@ -219,7 +219,7 @@ public enum Heif4CharCode : uint
Avis = 0x61766973U,
///
- /// High Efficiency Coding tile.
+ /// HEVC-coded image item.
///
Hvc1 = 0x68766331U,
diff --git a/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs b/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs
index 7c4e412af..ccc2e55e4 100644
--- a/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs
+++ b/src/ImageSharp/Formats/Heif/HeifDecoderCore.cs
@@ -7,6 +7,7 @@ using System.Text;
using SixLabors.ImageSharp.ColorProfiles;
using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Formats.Heif.Av1;
+using SixLabors.ImageSharp.Formats.Heif.Hevc;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
@@ -269,6 +270,19 @@ internal sealed class HeifDecoderCore : ImageDecoderCore
meta.BitDepth = codecConfiguration.BitDepth;
meta.IsMonochrome = codecConfiguration.IsMonochrome;
}
+ else if (metadataItem.Type == Heif4CharCode.Hvc1)
+ {
+ HevcCodecConfiguration codecConfiguration = metadataItem.HevcCodecConfiguration
+ ?? throw new InvalidImageContentException($"HEVC image item {metadataItem.Id} has no codec configuration property.");
+
+ if (metadataItem.ChannelBitDepths is not null)
+ {
+ codecConfiguration.ValidateChannelBitDepths(metadataItem.ChannelBitDepths);
+ }
+
+ meta.BitDepth = codecConfiguration.BitDepth;
+ meta.IsMonochrome = codecConfiguration.IsMonochrome;
+ }
else if (metadataItem.Type == Heif4CharCode.Jpeg)
{
compressionMethod = HeifCompressionMethod.LegacyJpeg;
@@ -1267,6 +1281,13 @@ internal sealed class HeifDecoderCore : ImageDecoderCore
Heif4CharCode.Av1C,
new Av1CodecConfiguration(boxBuffer)));
+ break;
+ case Heif4CharCode.HvcC:
+ properties.Add(
+ new KeyValuePair(
+ Heif4CharCode.HvcC,
+ new HevcCodecConfiguration(boxBuffer)));
+
break;
case Heif4CharCode.Clap:
EnsureBufferRemaining(boxBuffer, 0, 32, "clean aperture");
@@ -1302,7 +1323,6 @@ internal sealed class HeifDecoderCore : ImageDecoderCore
break;
case Heif4CharCode.Altt:
case Heif4CharCode.Iscl:
- case Heif4CharCode.HvcC:
case Heif4CharCode.Rloc:
case Heif4CharCode.Udes:
// These registered image properties are not arbitrary unknown boxes. Preserve their indices so
@@ -1442,6 +1462,19 @@ internal sealed class HeifDecoderCore : ImageDecoderCore
item.Av1CodecConfiguration = (Av1CodecConfiguration)prop.Value;
break;
+ case Heif4CharCode.HvcC:
+ if (item.Type != Heif4CharCode.Hvc1)
+ {
+ throw new InvalidImageContentException($"Item {itemId} associates an HEVC codec configuration with non-HEVC item type '{PrettyPrint(item.Type)}'.");
+ }
+
+ if (item.HevcCodecConfiguration is not null)
+ {
+ throw new InvalidImageContentException($"Item {itemId} associates more than one HEVC codec configuration property.");
+ }
+
+ item.HevcCodecConfiguration = (HevcCodecConfiguration)prop.Value;
+ break;
case Heif4CharCode.AuxC:
if (item.AuxiliaryType is not null)
{
diff --git a/src/ImageSharp/Formats/Heif/HeifItem.cs b/src/ImageSharp/Formats/Heif/HeifItem.cs
index 7dc8d5e7e..e26467cda 100644
--- a/src/ImageSharp/Formats/Heif/HeifItem.cs
+++ b/src/ImageSharp/Formats/Heif/HeifItem.cs
@@ -2,6 +2,7 @@
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1;
+using SixLabors.ImageSharp.Formats.Heif.Hevc;
using SixLabors.ImageSharp.Metadata.Profiles.Cicp;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
@@ -108,6 +109,12 @@ internal class HeifItem(Heif4CharCode type, uint id)
///
public Av1CodecConfiguration? Av1CodecConfiguration { get; set; }
+ ///
+ /// Gets or sets the HEVC codec configuration associated with this coded image item, or
+ /// when the item has no HEVC codec-configuration property.
+ ///
+ public HevcCodecConfiguration? HevcCodecConfiguration { get; set; }
+
///
/// Gets or sets the relative pixel spacing associated with this image item, or when the
/// item has no pixel-aspect-ratio property.
diff --git a/src/ImageSharp/Formats/Heif/Hevc/HevcCodecConfiguration.cs b/src/ImageSharp/Formats/Heif/Hevc/HevcCodecConfiguration.cs
new file mode 100644
index 000000000..c3375549c
--- /dev/null
+++ b/src/ImageSharp/Formats/Heif/Hevc/HevcCodecConfiguration.cs
@@ -0,0 +1,275 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System.Buffers.Binary;
+
+namespace SixLabors.ImageSharp.Formats.Heif.Hevc;
+
+///
+/// Contains the image-description fields and parameter-set arrays stored in an HEVC codec-configuration item
+/// property.
+///
+internal sealed class HevcCodecConfiguration
+{
+ ///
+ /// The NAL-unit arrays carried by the codec-configuration property.
+ ///
+ private readonly HevcNalUnitArray[] nalUnitArrays;
+
+ ///
+ /// Initializes a new instance of the class from an HEVC
+ /// codec-configuration item-property payload.
+ ///
+ /// The complete bounded configuration payload.
+ public HevcCodecConfiguration(ReadOnlySpan data)
+ {
+ const int fixedRecordLength = 23;
+ if (data.Length < fixedRecordLength)
+ {
+ throw new InvalidImageContentException("The HEVC codec configuration is truncated.");
+ }
+
+ int offset = 0;
+ if (data[offset++] != 1)
+ {
+ throw new InvalidImageContentException("The HEVC codec configuration has an unsupported version.");
+ }
+
+ byte profile = data[offset++];
+ this.GeneralProfileSpace = (byte)(profile >> 6);
+ this.GeneralTierFlag = (profile & 0x20) != 0;
+ this.GeneralProfileIdc = (byte)(profile & 0x1F);
+ this.GeneralProfileCompatibilityFlags = BinaryPrimitives.ReadUInt32BigEndian(data[offset..]);
+ offset += 4;
+ this.GeneralConstraintIndicatorFlags = ((ulong)BinaryPrimitives.ReadUInt32BigEndian(data[offset..]) << 16)
+ | BinaryPrimitives.ReadUInt16BigEndian(data[(offset + 4)..]);
+
+ offset += 6;
+ this.GeneralLevelIdc = data[offset++];
+
+ ushort spatialSegmentation = BinaryPrimitives.ReadUInt16BigEndian(data[offset..]);
+ offset += 2;
+ byte parallelism = data[offset++];
+ byte chromaFormat = data[offset++];
+ byte lumaBitDepth = data[offset++];
+ byte chromaBitDepth = data[offset++];
+ if ((spatialSegmentation & 0xF000) != 0xF000
+ || (parallelism & 0xFC) != 0xFC
+ || (chromaFormat & 0xFC) != 0xFC
+ || (lumaBitDepth & 0xF8) != 0xF8
+ || (chromaBitDepth & 0xF8) != 0xF8)
+ {
+ throw new InvalidImageContentException("The HEVC codec configuration has invalid reserved bits.");
+ }
+
+ this.ChromaFormat = (byte)(chromaFormat & 3);
+ this.BitDepthLuma = 8 + (lumaBitDepth & 7);
+ this.BitDepthChroma = 8 + (chromaBitDepth & 7);
+
+ // Average frame rate and temporal-layer signaling describe timed samples. Consume those fixed-record fields
+ // to reach the image item's NAL length width without retaining playback state in the still-image model.
+ offset += 2;
+ byte temporalAndLengthFields = data[offset++];
+ this.NalUnitLengthSize = (temporalAndLengthFields & 3) + 1;
+
+ int arrayCount = data[offset++];
+ this.nalUnitArrays = new HevcNalUnitArray[arrayCount];
+ Span seenNalUnitTypes = stackalloc bool[64];
+ for (int arrayIndex = 0; arrayIndex < arrayCount; arrayIndex++)
+ {
+ if (data.Length - offset < 3)
+ {
+ throw new InvalidImageContentException("The HEVC codec configuration contains a truncated NAL-unit array header.");
+ }
+
+ byte arrayHeader = data[offset++];
+ if ((arrayHeader & 0x40) != 0)
+ {
+ throw new InvalidImageContentException("The HEVC codec configuration NAL-unit array has a nonzero reserved bit.");
+ }
+
+ bool isComplete = (arrayHeader & 0x80) != 0;
+ byte nalUnitType = (byte)(arrayHeader & 0x3F);
+ if (seenNalUnitTypes[nalUnitType])
+ {
+ throw new InvalidImageContentException($"The HEVC codec configuration contains more than one array for NAL-unit type {nalUnitType}.");
+ }
+
+ seenNalUnitTypes[nalUnitType] = true;
+ int nalUnitCount = BinaryPrimitives.ReadUInt16BigEndian(data[offset..]);
+ offset += 2;
+ ReadOnlyMemory[] nalUnits = new ReadOnlyMemory[nalUnitCount];
+ for (int nalUnitIndex = 0; nalUnitIndex < nalUnitCount; nalUnitIndex++)
+ {
+ if (data.Length - offset < 2)
+ {
+ throw new InvalidImageContentException("The HEVC codec configuration contains a truncated NAL-unit length.");
+ }
+
+ int nalUnitLength = BinaryPrimitives.ReadUInt16BigEndian(data[offset..]);
+ offset += 2;
+ if (nalUnitLength < 2 || nalUnitLength > data.Length - offset)
+ {
+ throw new InvalidImageContentException("The HEVC codec configuration contains an invalid NAL-unit length.");
+ }
+
+ ReadOnlySpan nalUnit = data.Slice(offset, nalUnitLength);
+ byte actualNalUnitType = (byte)((nalUnit[0] >> 1) & 0x3F);
+
+ // The two-byte HEVC NAL header repeats the array's type. temporal_id_plus1 cannot be zero because
+ // zero is reserved to make header damage detectable before any parameter syntax is consumed.
+ if ((nalUnit[0] & 0x80) != 0 || (nalUnit[1] & 7) == 0 || actualNalUnitType != nalUnitType)
+ {
+ throw new InvalidImageContentException("The HEVC codec configuration contains an invalid NAL-unit header.");
+ }
+
+ nalUnits[nalUnitIndex] = nalUnit.ToArray();
+ offset += nalUnitLength;
+ }
+
+ this.nalUnitArrays[arrayIndex] = new HevcNalUnitArray(nalUnitType, isComplete, nalUnits);
+ }
+
+ if (offset != data.Length)
+ {
+ throw new InvalidImageContentException("The HEVC codec configuration contains unexpected trailing data.");
+ }
+ }
+
+ ///
+ /// Gets the profile namespace declared by the coded image.
+ ///
+ public byte GeneralProfileSpace { get; }
+
+ ///
+ /// Gets a value indicating whether the coded image uses the high tier.
+ ///
+ public bool GeneralTierFlag { get; }
+
+ ///
+ /// Gets the profile identifier declared by the coded image.
+ ///
+ public byte GeneralProfileIdc { get; }
+
+ ///
+ /// Gets the profile-compatibility flags declared by the coded image.
+ ///
+ public uint GeneralProfileCompatibilityFlags { get; }
+
+ ///
+ /// Gets the 48-bit profile-constraint flags declared by the coded image.
+ ///
+ public ulong GeneralConstraintIndicatorFlags { get; }
+
+ ///
+ /// Gets the level identifier declared by the coded image.
+ ///
+ public byte GeneralLevelIdc { get; }
+
+ ///
+ /// Gets the coded chroma format, where zero denotes monochrome and one through three denote 4:2:0, 4:2:2,
+ /// and 4:4:4 respectively.
+ ///
+ public byte ChromaFormat { get; }
+
+ ///
+ /// Gets the coded luma sample precision in bits.
+ ///
+ public int BitDepthLuma { get; }
+
+ ///
+ /// Gets the coded chroma sample precision in bits.
+ ///
+ public int BitDepthChroma { get; }
+
+ ///
+ /// Gets the maximum coded color-component precision in bits.
+ ///
+ public int BitDepth => this.IsMonochrome ? this.BitDepthLuma : Math.Max(this.BitDepthLuma, this.BitDepthChroma);
+
+ ///
+ /// Gets a value indicating whether the coded image contains only a luma plane.
+ ///
+ public bool IsMonochrome => this.ChromaFormat == 0;
+
+ ///
+ /// Gets the number of bytes used by each length-delimited NAL unit in the associated image item.
+ ///
+ public int NalUnitLengthSize { get; }
+
+ ///
+ /// Gets the bounded NAL-unit arrays carried by the codec-configuration property.
+ ///
+ public IReadOnlyList NalUnitArrays => this.nalUnitArrays;
+
+ ///
+ /// Validates the associated pixel-information property against the coded luma and chroma sample precisions.
+ ///
+ /// The per-channel precisions associated with the HEVC image item.
+ public void ValidateChannelBitDepths(ReadOnlySpan channelBitDepths)
+ {
+ int expectedChannelCount = this.IsMonochrome ? 1 : 3;
+ if (channelBitDepths.Length != expectedChannelCount || channelBitDepths[0] != this.BitDepthLuma)
+ {
+ throw new InvalidImageContentException("The HEVC item pixel information does not match its codec configuration.");
+ }
+
+ for (int channel = 1; channel < channelBitDepths.Length; channel++)
+ {
+ if (channelBitDepths[channel] != this.BitDepthChroma)
+ {
+ throw new InvalidImageContentException("The HEVC item pixel information does not match its codec configuration.");
+ }
+ }
+ }
+
+ ///
+ /// Determines whether another configuration describes the same coded-image sample layout.
+ ///
+ /// The configuration to compare.
+ /// when the profile, level, chroma format, and sample precisions match.
+ public bool HasMatchingImageConfiguration(HevcCodecConfiguration other)
+ => this.GeneralProfileSpace == other.GeneralProfileSpace
+ && this.GeneralTierFlag == other.GeneralTierFlag
+ && this.GeneralProfileIdc == other.GeneralProfileIdc
+ && this.GeneralProfileCompatibilityFlags == other.GeneralProfileCompatibilityFlags
+ && this.GeneralConstraintIndicatorFlags == other.GeneralConstraintIndicatorFlags
+ && this.GeneralLevelIdc == other.GeneralLevelIdc
+ && this.ChromaFormat == other.ChromaFormat
+ && this.BitDepthLuma == other.BitDepthLuma
+ && this.BitDepthChroma == other.BitDepthChroma;
+}
+
+///
+/// Contains every configuration NAL unit declared for one HEVC NAL-unit type.
+///
+internal sealed class HevcNalUnitArray
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The six-bit HEVC NAL-unit type.
+ /// A value indicating whether the array contains every NAL unit of this type.
+ /// The complete bounded NAL units, including their two-byte headers.
+ public HevcNalUnitArray(byte nalUnitType, bool isComplete, ReadOnlyMemory[] nalUnits)
+ {
+ this.NalUnitType = nalUnitType;
+ this.IsComplete = isComplete;
+ this.NalUnits = nalUnits;
+ }
+
+ ///
+ /// Gets the six-bit HEVC NAL-unit type shared by every entry in the array.
+ ///
+ public byte NalUnitType { get; }
+
+ ///
+ /// Gets a value indicating whether the array contains every NAL unit of this type for the coded image.
+ ///
+ public bool IsComplete { get; }
+
+ ///
+ /// Gets the complete NAL units, including their two-byte HEVC headers.
+ ///
+ public IReadOnlyList> NalUnits { get; }
+}