Browse Source

Decode HEIF content color volume metadata

pull/2633/head
James Jackson-South 2 months ago
parent
commit
6f8023ed65
  1. 4
      HEIF_IMPLEMENTATION_PLAN.md
  2. 5
      src/ImageSharp/Formats/Heif/Heif4CharCode.cs
  3. 1
      src/ImageSharp/Formats/Heif/Heif4CharCode.tt
  4. 117
      src/ImageSharp/Formats/Heif/HeifContentColorVolume.cs
  5. 159
      src/ImageSharp/Formats/Heif/HeifDecoderCore.cs
  6. 6
      src/ImageSharp/Formats/Heif/HeifItem.cs
  7. 7
      src/ImageSharp/Formats/Heif/HeifMetadata.cs

4
HEIF_IMPLEMENTATION_PLAN.md

@ -72,6 +72,7 @@ The initial post-merge snapshot was established on 2026-08-24:
- the official libaom reference is tag `v3.14.1`, commit `03087864cf4bea6abb0d28f95cf7843511413d8f`, matching the revision selected by the local libavif `ext/aom.cmd` dependency script;
- the local libavif container, color-conversion, and interoperability oracle is commit `092276ce89098ead06db80975173191e5fee1826`, described as `v1.4.2-66-g092276ce`;
- the official ITU-T H.274 (V4) recommendation from January 2026 is the normative semantics reference for mastering-display and content color-volume fields reused by still-image item properties; its video-SEI persistence and cancellation behavior is outside this container scope;
- the independently reviewed HEVC implementation and interoperability references remain unresolved and must be pinned before HEVC algorithm work begins;
- `dotnet build ImageSharp.sln -c Release --no-restore -m:1 -v minimal` succeeds with no errors after the upstream compatibility fixes; and
- the existing HEIF-focused test run executes 8,198 cases, with 8,184 passing and 14 failing. Thirteen failures are isolated to the WIP AV1 YUV conversion tests, and one is the existing legacy JPEG HIF reference-image mismatch. Golden artifacts have not been changed.
@ -108,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` container color override | 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, inherit a grid's CICP description only for tiles that do not declare one, and let container CICP values override the matching AV1 sequence-header fields before still-image reconstruction and YUV-to-RGB conversion. 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. |
| `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. |
| `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. |
| `HeifDecoderCore` alpha auxiliary selection/composition and `GridHeifItemDecoder` auxiliary tile ordering | ISO/IEC 23008-12 alpha auxiliary image semantics, `auxC`, `auxl`, `prem`, and per-grid-tile alpha relationships | libavif `src/read.c` functions `avifParseAuxiliaryTypeProperty`, `avifDecoderItemIsAlphaAux`, `avifMetaFindAlphaItem`, and `avifDecoderCheckAlphaProperties`, plus `src/scale.c` box-filter scaling at `092276ce89098ead06db80975173191e5fee1826` | Recognize both registered alpha URNs, decode a direct alpha image/grid or the complete row-major set of per-color-tile alpha auxiliaries, normalize through `L16`, box-resample differing auxiliary dimensions, compose through `Rgba64` and `PixelOperations<TPixel>`, and unassociate `prem` color samples with transparent-black handling. This remains an image-item relationship only; no track or generic media-reference model is introduced. |
@ -139,7 +141,7 @@ This assessment is based on the current source after the upstream ImageSharp mer
- Decode now resolves `cdsc`-associated Exif and `application/rdf+xml` XMP items for the primary still image, validates the declared Exif TIFF-header offset, and attaches the payloads through ImageSharp's existing profile types before presentation transforms. Independent AVIF, HEIC, and HIF metadata fixtures and Identify-time profile reporting remain required.
- ICC and `nclx` CICP color properties are now associated with the presented color item instead of global parser state, validated, exposed on Decode and Identify through the existing ImageSharp profiles, and used to override matching AV1 bitstream color fields before still-image color conversion. Independent ICC/CICP fixtures, decoded AV1 bitstream-CICP fallback metadata, ICC conversion coverage, and HEVC integration remain required.
- Pixel aspect ratio now preserves the complete unsigned spacing pair, affects Decode and Identify through ImageSharp's existing unitless resolution metadata, and follows quarter-turn presentation rotation. Independent grid, rotation, and maximum-spacing fixtures remain required.
- AV1 codec configuration is now retained per `av01` image item rather than in decoder-global state. Decode requires the property, validates its fixed record against the item's sequence header and any associated `pixi` channel depths, and requires matching configurations across grid tiles. Identify now reports the configuration's 8/10/12-bit precision and monochrome shape. The optional `configOBUs` sequence is bounded and validated, including its mandatory size fields, first-position/at-most-one sequence-header rule, the image item's exactly-one sequence-header rule, and exact comparison of a repeated configuration header with the item header. Content light-level and mastering-display color-volume information are decoded from the bounded `clli` and `mdcv` image properties and matching AV1 HDR metadata OBUs, with the representations' distinct fixed-point precision, grid/property precedence, and `SkipMetadata` behavior preserved. Related still-image HDR properties remain required; HEVC `hvcC` remains unimplemented.
- AV1 codec configuration is now retained per `av01` image item rather than in decoder-global state. Decode requires the property, validates its fixed record against the item's sequence header and any associated `pixi` channel depths, and requires matching configurations across grid tiles. Identify now reports the configuration's 8/10/12-bit precision and monochrome shape. The optional `configOBUs` sequence is bounded and validated, including its mandatory size fields, first-position/at-most-one sequence-header rule, the image item's exactly-one sequence-header rule, and exact comparison of a repeated configuration header with the item header. Content light-level and mastering-display color-volume information are decoded from the bounded `clli` and `mdcv` image properties and matching AV1 HDR metadata OBUs, with the representations' distinct fixed-point precision, grid/property precedence, and `SkipMetadata` behavior preserved. The still-image `cclv` property preserves optional content primaries and normalized luminance limits without importing its video-SEI state model. Ambient, reference-viewing-environment, and nominal-diffuse-white image properties remain required; HEVC `hvcC` remains unimplemented.
- Several image-item properties and relationships remain missing or parsed without fully affecting output.
- Identify and decode now use the same bounded metadata parser and both validate the complete leading file type box. The parsed state is still mutable and Identify does not yet report the complete bit depth, color, profile, or transform model.

5
src/ImageSharp/Formats/Heif/Heif4CharCode.cs

@ -83,6 +83,11 @@ public enum Heif4CharCode : uint
/// </summary>
Mdcv = 0x6D646376U,
/// <summary>
/// Content colour volume.
/// </summary>
Cclv = 0x63636C76U,
/// <summary>
/// HVC configuration.
/// </summary>

1
src/ImageSharp/Formats/Heif/Heif4CharCode.tt

@ -21,6 +21,7 @@
"colr", "Colour information",
"clli", "Content light level information",
"mdcv", "Mastering display colour volume",
"cclv", "Content colour volume",
"hvcC", "HVC configuration",
"av1C", "AV1 configuration",
"imir", "Image Mirror",

117
src/ImageSharp/Formats/Heif/HeifContentColorVolume.cs

@ -0,0 +1,117 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.ColorProfiles;
namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary>
/// Describes the optional color-primary and luminance limits of the image content represented by a HEIF image.
/// </summary>
public readonly struct HeifContentColorVolume : IEquatable<HeifContentColorVolume>
{
/// <summary>
/// Initializes a new instance of the <see cref="HeifContentColorVolume"/> struct.
/// </summary>
/// <param name="primaries">
/// The CIE 1931 chromaticity coordinates of the content color primaries, or <see langword="null"/> when they
/// are not specified.
/// </param>
/// <param name="minimumLuminance">
/// The normalized minimum content luminance, or <see langword="null"/> when it is not specified.
/// </param>
/// <param name="maximumLuminance">
/// The normalized maximum content luminance, or <see langword="null"/> when it is not specified.
/// </param>
/// <param name="averageLuminance">
/// The normalized average content luminance, or <see langword="null"/> when it is not specified.
/// </param>
public HeifContentColorVolume(
RgbPrimariesChromaticityCoordinates? primaries,
double? minimumLuminance,
double? maximumLuminance,
double? averageLuminance)
{
this.Primaries = primaries;
this.MinimumLuminance = minimumLuminance;
this.MaximumLuminance = maximumLuminance;
this.AverageLuminance = averageLuminance;
}
/// <summary>
/// Gets the CIE 1931 chromaticity coordinates of the content color primaries, or <see langword="null"/> when
/// they are not specified.
/// </summary>
public RgbPrimariesChromaticityCoordinates? Primaries { get; }
/// <summary>
/// Gets the normalized minimum content luminance, or <see langword="null"/> when it is not specified.
/// </summary>
/// <remarks>
/// The value is interpreted according to the transfer characteristics signaled for the image and does not
/// necessarily represent luminance in candelas per square metre.
/// </remarks>
public double? MinimumLuminance { get; }
/// <summary>
/// Gets the normalized maximum content luminance, or <see langword="null"/> when it is not specified.
/// </summary>
/// <remarks>
/// The value is interpreted according to the transfer characteristics signaled for the image and does not
/// necessarily represent luminance in candelas per square metre.
/// </remarks>
public double? MaximumLuminance { get; }
/// <summary>
/// Gets the normalized average content luminance, or <see langword="null"/> when it is not specified.
/// </summary>
/// <remarks>
/// The value is interpreted according to the transfer characteristics signaled for the image and does not
/// necessarily represent luminance in candelas per square metre.
/// </remarks>
public double? AverageLuminance { get; }
/// <summary>
/// Compares two content color volumes for equality.
/// </summary>
/// <param name="left">The first content color volume.</param>
/// <param name="right">The second content color volume.</param>
/// <returns><see langword="true"/> when every specified color-volume value is equal.</returns>
public static bool operator ==(HeifContentColorVolume left, HeifContentColorVolume right)
=> left.Equals(right);
/// <summary>
/// Compares two content color volumes for inequality.
/// </summary>
/// <param name="left">The first content color volume.</param>
/// <param name="right">The second content color volume.</param>
/// <returns><see langword="true"/> when any specified color-volume value differs.</returns>
public static bool operator !=(HeifContentColorVolume left, HeifContentColorVolume right)
=> !left.Equals(right);
/// <summary>
/// Determines whether the specified object is a content color volume with the same values.
/// </summary>
/// <param name="obj">The object to compare with this value.</param>
/// <returns><see langword="true"/> when <paramref name="obj"/> contains the same color-volume values.</returns>
public override bool Equals(object? obj)
=> obj is HeifContentColorVolume other && this.Equals(other);
/// <summary>
/// Determines whether the specified content color volume has the same values as this value.
/// </summary>
/// <param name="other">The content color volume to compare with this value.</param>
/// <returns><see langword="true"/> when every specified color-volume value is equal.</returns>
public bool Equals(HeifContentColorVolume other)
=> this.Primaries.Equals(other.Primaries)
&& this.MinimumLuminance.Equals(other.MinimumLuminance)
&& this.MaximumLuminance.Equals(other.MaximumLuminance)
&& this.AverageLuminance.Equals(other.AverageLuminance);
/// <summary>
/// Returns a hash code for this content color volume.
/// </summary>
/// <returns>A hash code derived from the specified color-volume values.</returns>
public override int GetHashCode()
=> HashCode.Combine(this.Primaries, this.MinimumLuminance, this.MaximumLuminance, this.AverageLuminance);
}

159
src/ImageSharp/Formats/Heif/HeifDecoderCore.cs

@ -1009,6 +1009,150 @@ internal sealed class HeifDecoderCore : ImageDecoderCore
BinaryPrimitives.ReadUInt32BigEndian(boxBuffer[16..]) * luminanceScale,
BinaryPrimitives.ReadUInt32BigEndian(boxBuffer[20..]) * luminanceScale)));
break;
case Heif4CharCode.Cclv:
EnsureBufferRemaining(boxBuffer, 0, 1, "content color volume");
byte contentColorVolumeFlags = boxBuffer[0];
if ((contentColorVolumeFlags & 0xC3) != 0)
{
throw new InvalidImageContentException("The content color-volume property has nonzero reserved flags.");
}
bool contentPrimariesPresent = (contentColorVolumeFlags & 0x20) != 0;
bool minimumLuminancePresent = (contentColorVolumeFlags & 0x10) != 0;
bool maximumLuminancePresent = (contentColorVolumeFlags & 0x08) != 0;
bool averageLuminancePresent = (contentColorVolumeFlags & 0x04) != 0;
if (!contentPrimariesPresent
&& !minimumLuminancePresent
&& !maximumLuminancePresent
&& !averageLuminancePresent)
{
throw new InvalidImageContentException("The content color-volume property does not describe any values.");
}
int expectedContentColorVolumeLength = 1
+ (contentPrimariesPresent ? 24 : 0)
+ (minimumLuminancePresent ? 4 : 0)
+ (maximumLuminancePresent ? 4 : 0)
+ (averageLuminancePresent ? 4 : 0);
if (boxBuffer.Length != expectedContentColorVolumeLength)
{
throw new InvalidImageContentException("The content color-volume property has an invalid length.");
}
int contentColorVolumeOffset = 1;
RgbPrimariesChromaticityCoordinates? contentPrimaries = null;
if (contentPrimariesPresent)
{
int greenPrimaryX = BinaryPrimitives.ReadInt32BigEndian(boxBuffer[contentColorVolumeOffset..]);
int greenPrimaryY = BinaryPrimitives.ReadInt32BigEndian(boxBuffer[(contentColorVolumeOffset + 4)..]);
int bluePrimaryX = BinaryPrimitives.ReadInt32BigEndian(boxBuffer[(contentColorVolumeOffset + 8)..]);
int bluePrimaryY = BinaryPrimitives.ReadInt32BigEndian(boxBuffer[(contentColorVolumeOffset + 12)..]);
int redPrimaryX = BinaryPrimitives.ReadInt32BigEndian(boxBuffer[(contentColorVolumeOffset + 16)..]);
int redPrimaryY = BinaryPrimitives.ReadInt32BigEndian(boxBuffer[(contentColorVolumeOffset + 20)..]);
const int maximumContentChromaticityValue = 5_000_000;
if (greenPrimaryX < -maximumContentChromaticityValue
|| greenPrimaryX > maximumContentChromaticityValue
|| greenPrimaryY < -maximumContentChromaticityValue
|| greenPrimaryY > maximumContentChromaticityValue
|| bluePrimaryX < -maximumContentChromaticityValue
|| bluePrimaryX > maximumContentChromaticityValue
|| bluePrimaryY < -maximumContentChromaticityValue
|| bluePrimaryY > maximumContentChromaticityValue
|| redPrimaryX < -maximumContentChromaticityValue
|| redPrimaryX > maximumContentChromaticityValue
|| redPrimaryY < -maximumContentChromaticityValue
|| redPrimaryY > maximumContentChromaticityValue)
{
throw new InvalidImageContentException("The content color-volume property has an out-of-range primary coordinate.");
}
const float contentChromaticityScale = 1F / 50000F;
// Content-color-volume syntax stores signed coordinates in G, B, R order. Reorder the
// optional primaries into ImageSharp's existing RGB coordinate representation.
contentPrimaries = new RgbPrimariesChromaticityCoordinates(
new CieXyChromaticityCoordinates(
redPrimaryX * contentChromaticityScale,
redPrimaryY * contentChromaticityScale),
new CieXyChromaticityCoordinates(
greenPrimaryX * contentChromaticityScale,
greenPrimaryY * contentChromaticityScale),
new CieXyChromaticityCoordinates(
bluePrimaryX * contentChromaticityScale,
bluePrimaryY * contentChromaticityScale));
contentColorVolumeOffset += 24;
}
uint? minimumLuminanceValue = null;
if (minimumLuminancePresent)
{
minimumLuminanceValue = BinaryPrimitives.ReadUInt32BigEndian(boxBuffer[contentColorVolumeOffset..]);
contentColorVolumeOffset += 4;
}
uint? maximumLuminanceValue = null;
if (maximumLuminancePresent)
{
maximumLuminanceValue = BinaryPrimitives.ReadUInt32BigEndian(boxBuffer[contentColorVolumeOffset..]);
contentColorVolumeOffset += 4;
}
uint? averageLuminanceValue = null;
if (averageLuminancePresent)
{
averageLuminanceValue = BinaryPrimitives.ReadUInt32BigEndian(boxBuffer[contentColorVolumeOffset..]);
}
if (minimumLuminanceValue is not null
&& averageLuminanceValue is not null
&& minimumLuminanceValue.Value > averageLuminanceValue.Value)
{
throw new InvalidImageContentException("The content color-volume minimum luminance exceeds its average luminance.");
}
if (averageLuminanceValue is not null
&& maximumLuminanceValue is not null
&& averageLuminanceValue.Value > maximumLuminanceValue.Value)
{
throw new InvalidImageContentException("The content color-volume average luminance exceeds its maximum luminance.");
}
if (minimumLuminanceValue is not null
&& maximumLuminanceValue is not null
&& minimumLuminanceValue.Value > maximumLuminanceValue.Value)
{
throw new InvalidImageContentException("The content color-volume minimum luminance exceeds its maximum luminance.");
}
const double contentLuminanceScale = 1D / 10000000D;
// These values are normalized according to the signaled transfer characteristics. Preserve
// that unitless meaning instead of presenting them as physical display luminance.
double? minimumContentLuminance = minimumLuminanceValue is not null
? minimumLuminanceValue.Value * contentLuminanceScale
: null;
double? maximumContentLuminance = maximumLuminanceValue is not null
? maximumLuminanceValue.Value * contentLuminanceScale
: null;
double? averageContentLuminance = averageLuminanceValue is not null
? averageLuminanceValue.Value * contentLuminanceScale
: null;
properties.Add(
new KeyValuePair<Heif4CharCode, object>(
Heif4CharCode.Cclv,
new HeifContentColorVolume(
contentPrimaries,
minimumContentLuminance,
maximumContentLuminance,
averageContentLuminance)));
break;
case Heif4CharCode.Av1C:
EnsureBufferRemaining(boxBuffer, 0, 4, "AV1 codec configuration");
@ -1237,6 +1381,14 @@ internal sealed class HeifDecoderCore : ImageDecoderCore
item.MasteringDisplayColorVolume = (HeifMasteringDisplayColorVolume)prop.Value;
break;
case Heif4CharCode.Cclv:
if (item.ContentColorVolume is not null)
{
throw new InvalidImageContentException($"Item {itemId} associates more than one content color-volume property.");
}
item.ContentColorVolume = (HeifContentColorVolume)prop.Value;
break;
case Heif4CharCode.Clap:
if (item.CleanAperture is not null)
{
@ -1586,6 +1738,7 @@ internal sealed class HeifDecoderCore : ImageDecoderCore
// parsed value here so the public decoder option continues to suppress encoded metadata.
meta.ContentLightLevel = null;
meta.MasteringDisplayColorVolume = null;
meta.ContentColorVolume = null;
}
return image;
@ -1650,6 +1803,12 @@ internal sealed class HeifDecoderCore : ImageDecoderCore
{
metadata.GetHeifMetadata().MasteringDisplayColorVolume = masteringDisplayColorVolume;
}
HeifContentColorVolume? contentColorVolume = imageItem.ContentColorVolume ?? gridTile?.ContentColorVolume;
if (contentColorVolume is not null)
{
metadata.GetHeifMetadata().ContentColorVolume = contentColorVolume;
}
}
/// <summary>

6
src/ImageSharp/Formats/Heif/HeifItem.cs

@ -78,6 +78,12 @@ internal class HeifItem(Heif4CharCode type, uint id)
/// </summary>
public HeifMasteringDisplayColorVolume? MasteringDisplayColorVolume { get; set; }
/// <summary>
/// Gets or sets the content color volume associated with this image item, or <see langword="null"/> when the
/// item has no content color-volume property.
/// </summary>
public HeifContentColorVolume? ContentColorVolume { get; set; }
/// <summary>
/// Gets or sets the AV1 codec configuration associated with this coded image item, or <see langword="null"/>
/// when the item has no AV1 codec-configuration property.

7
src/ImageSharp/Formats/Heif/HeifMetadata.cs

@ -30,6 +30,7 @@ public class HeifMetadata : IFormatMetadata<HeifMetadata>
this.HasAlpha = other.HasAlpha;
this.ContentLightLevel = other.ContentLightLevel;
this.MasteringDisplayColorVolume = other.MasteringDisplayColorVolume;
this.ContentColorVolume = other.ContentColorVolume;
}
/// <summary>
@ -64,6 +65,12 @@ public class HeifMetadata : IFormatMetadata<HeifMetadata>
/// </summary>
public HeifMasteringDisplayColorVolume? MasteringDisplayColorVolume { get; set; }
/// <summary>
/// Gets or sets the content color volume for the primary image, or <see langword="null"/> when it is not
/// available.
/// </summary>
public HeifContentColorVolume? ContentColorVolume { get; set; }
/// <inheritdoc/>
public static HeifMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) => new()
{

Loading…
Cancel
Save