Browse Source

Decode HEIF viewing environment metadata

pull/2633/head
James Jackson-South 1 week ago
parent
commit
dc727439a7
  1. 3
      HEIF_IMPLEMENTATION_PLAN.md
  2. 15
      src/ImageSharp/Formats/Heif/Heif4CharCode.cs
  3. 3
      src/ImageSharp/Formats/Heif/Heif4CharCode.tt
  4. 75
      src/ImageSharp/Formats/Heif/HeifAmbientViewingEnvironment.cs
  5. 155
      src/ImageSharp/Formats/Heif/HeifDecoderCore.cs
  6. 18
      src/ImageSharp/Formats/Heif/HeifItem.cs
  7. 21
      src/ImageSharp/Formats/Heif/HeifMetadata.cs
  8. 64
      src/ImageSharp/Formats/Heif/HeifNominalDiffuseWhite.cs
  9. 99
      src/ImageSharp/Formats/Heif/HeifReferenceViewingEnvironment.cs

3
HEIF_IMPLEMENTATION_PLAN.md

@ -110,6 +110,7 @@ This snapshot pins or classifies the available references and failures; it does
| `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. |
| `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. |
| `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. |
@ -141,7 +142,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. 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.
- 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`, `amve`, `reve`, and `ndwt` properties preserve content-volume, ambient-viewing, mastering-environment, and diffuse-white descriptions without importing their video-SEI or sample-entry state models. Independent fixtures and encoder preservation 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.

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

@ -88,6 +88,21 @@ public enum Heif4CharCode : uint
/// </summary>
Cclv = 0x63636C76U,
/// <summary>
/// Ambient viewing environment.
/// </summary>
Amve = 0x616D7665U,
/// <summary>
/// Reference viewing environment.
/// </summary>
Reve = 0x72657665U,
/// <summary>
/// Nominal diffuse white.
/// </summary>
Ndwt = 0x6E647774U,
/// <summary>
/// HVC configuration.
/// </summary>

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

@ -22,6 +22,9 @@
"clli", "Content light level information",
"mdcv", "Mastering display colour volume",
"cclv", "Content colour volume",
"amve", "Ambient viewing environment",
"reve", "Reference viewing environment",
"ndwt", "Nominal diffuse white",
"hvcC", "HVC configuration",
"av1C", "AV1 configuration",
"imir", "Image Mirror",

75
src/ImageSharp/Formats/Heif/HeifAmbientViewingEnvironment.cs

@ -0,0 +1,75 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.ColorProfiles;
namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary>
/// Describes the nominal ambient environment intended for viewing a HEIF image.
/// </summary>
public readonly struct HeifAmbientViewingEnvironment : IEquatable<HeifAmbientViewingEnvironment>
{
/// <summary>
/// Initializes a new instance of the <see cref="HeifAmbientViewingEnvironment"/> struct.
/// </summary>
/// <param name="illuminance">The environmental illuminance in lux.</param>
/// <param name="ambientLight">
/// The CIE 1931 chromaticity coordinates of the ambient light in the nominal viewing environment.
/// </param>
public HeifAmbientViewingEnvironment(double illuminance, CieXyChromaticityCoordinates ambientLight)
{
this.Illuminance = illuminance;
this.AmbientLight = ambientLight;
}
/// <summary>
/// Gets the environmental illuminance in lux.
/// </summary>
public double Illuminance { get; }
/// <summary>
/// Gets the CIE 1931 chromaticity coordinates of the ambient light in the nominal viewing environment.
/// </summary>
public CieXyChromaticityCoordinates AmbientLight { get; }
/// <summary>
/// Compares two ambient viewing environments for equality.
/// </summary>
/// <param name="left">The first ambient viewing environment.</param>
/// <param name="right">The second ambient viewing environment.</param>
/// <returns><see langword="true"/> when the illuminance and ambient-light coordinates are equal.</returns>
public static bool operator ==(HeifAmbientViewingEnvironment left, HeifAmbientViewingEnvironment right)
=> left.Equals(right);
/// <summary>
/// Compares two ambient viewing environments for inequality.
/// </summary>
/// <param name="left">The first ambient viewing environment.</param>
/// <param name="right">The second ambient viewing environment.</param>
/// <returns><see langword="true"/> when the illuminance or ambient-light coordinates differ.</returns>
public static bool operator !=(HeifAmbientViewingEnvironment left, HeifAmbientViewingEnvironment right)
=> !left.Equals(right);
/// <summary>
/// Determines whether the specified object is an ambient viewing environment 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 environment values.</returns>
public override bool Equals(object? obj)
=> obj is HeifAmbientViewingEnvironment other && this.Equals(other);
/// <summary>
/// Determines whether the specified ambient viewing environment has the same values as this value.
/// </summary>
/// <param name="other">The ambient viewing environment to compare with this value.</param>
/// <returns><see langword="true"/> when the illuminance and ambient-light coordinates are equal.</returns>
public bool Equals(HeifAmbientViewingEnvironment other)
=> this.Illuminance.Equals(other.Illuminance) && this.AmbientLight.Equals(other.AmbientLight);
/// <summary>
/// Returns a hash code for this ambient viewing environment.
/// </summary>
/// <returns>A hash code derived from the illuminance and ambient-light coordinates.</returns>
public override int GetHashCode() => HashCode.Combine(this.Illuminance, this.AmbientLight);
}

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

@ -1153,6 +1153,112 @@ internal sealed class HeifDecoderCore : ImageDecoderCore
maximumContentLuminance,
averageContentLuminance)));
break;
case Heif4CharCode.Amve:
EnsureBufferRemaining(boxBuffer, 0, 8, "ambient viewing environment");
if (boxBuffer.Length != 8)
{
throw new InvalidImageContentException("The ambient viewing-environment property has an invalid length.");
}
uint ambientIlluminanceValue = BinaryPrimitives.ReadUInt32BigEndian(boxBuffer);
ushort ambientLightX = BinaryPrimitives.ReadUInt16BigEndian(boxBuffer[4..]);
ushort ambientLightY = BinaryPrimitives.ReadUInt16BigEndian(boxBuffer[6..]);
if (ambientIlluminanceValue == 0)
{
throw new InvalidImageContentException("The ambient viewing-environment property has zero illuminance.");
}
if (ambientLightX > 50000 || ambientLightY > 50000)
{
throw new InvalidImageContentException("The ambient viewing-environment property has an out-of-range chromaticity coordinate.");
}
const double ambientIlluminanceScale = 1D / 10000D;
const float ambientChromaticityScale = 1F / 50000F;
// The item property inherits H.274's fixed-point units: 0.0001 lux for illuminance and
// 0.00002 for each normalized CIE chromaticity coordinate.
properties.Add(
new KeyValuePair<Heif4CharCode, object>(
Heif4CharCode.Amve,
new HeifAmbientViewingEnvironment(
ambientIlluminanceValue * ambientIlluminanceScale,
new CieXyChromaticityCoordinates(
ambientLightX * ambientChromaticityScale,
ambientLightY * ambientChromaticityScale))));
break;
case Heif4CharCode.Reve:
EnsureBufferRemaining(boxBuffer, 0, 20, "reference viewing environment");
if (boxBuffer.Length != 20)
{
throw new InvalidImageContentException("The reference viewing-environment property has an invalid length.");
}
if (BinaryPrimitives.ReadUInt32BigEndian(boxBuffer) != 0)
{
throw new InvalidImageContentException("The reference viewing-environment property has an unsupported version or flags.");
}
ushort surroundLightX = BinaryPrimitives.ReadUInt16BigEndian(boxBuffer[8..]);
ushort surroundLightY = BinaryPrimitives.ReadUInt16BigEndian(boxBuffer[10..]);
ushort peripheryLightX = BinaryPrimitives.ReadUInt16BigEndian(boxBuffer[16..]);
ushort peripheryLightY = BinaryPrimitives.ReadUInt16BigEndian(boxBuffer[18..]);
if (surroundLightX > 10000
|| surroundLightY > 10000
|| peripheryLightX > 10000
|| peripheryLightY > 10000)
{
throw new InvalidImageContentException("The reference viewing-environment property has an out-of-range chromaticity coordinate.");
}
const double viewingEnvironmentLuminanceScale = 1D / 10000D;
const float referenceChromaticityScale = 1F / 10000F;
// The full-box header is followed by the display surround and then the wider periphery.
// Both field groups use 0.0001 increments, but luminance is physical cd/m2 while the CIE
// coordinates are normalized. Keep the regions distinct because they affect different areas.
properties.Add(
new KeyValuePair<Heif4CharCode, object>(
Heif4CharCode.Reve,
new HeifReferenceViewingEnvironment(
BinaryPrimitives.ReadUInt32BigEndian(boxBuffer[4..]) * viewingEnvironmentLuminanceScale,
new CieXyChromaticityCoordinates(
surroundLightX * referenceChromaticityScale,
surroundLightY * referenceChromaticityScale),
BinaryPrimitives.ReadUInt32BigEndian(boxBuffer[12..]) * viewingEnvironmentLuminanceScale,
new CieXyChromaticityCoordinates(
peripheryLightX * referenceChromaticityScale,
peripheryLightY * referenceChromaticityScale))));
break;
case Heif4CharCode.Ndwt:
EnsureBufferRemaining(boxBuffer, 0, 8, "nominal diffuse white");
if (boxBuffer.Length != 8)
{
throw new InvalidImageContentException("The nominal diffuse-white property has an invalid length.");
}
if (BinaryPrimitives.ReadUInt32BigEndian(boxBuffer) != 0)
{
throw new InvalidImageContentException("The nominal diffuse-white property has an unsupported version or flags.");
}
uint diffuseWhiteLuminanceValue = BinaryPrimitives.ReadUInt32BigEndian(boxBuffer[4..]);
const double diffuseWhiteLuminanceScale = 1D / 10000D;
// A zero coded value requests the standard-defined default; it does not describe a black
// diffuse white. Preserve that distinction separately from an absent item property.
double? diffuseWhiteLuminance = diffuseWhiteLuminanceValue == 0
? null
: diffuseWhiteLuminanceValue * diffuseWhiteLuminanceScale;
properties.Add(
new KeyValuePair<Heif4CharCode, object>(
Heif4CharCode.Ndwt,
new HeifNominalDiffuseWhite(diffuseWhiteLuminance)));
break;
case Heif4CharCode.Av1C:
EnsureBufferRemaining(boxBuffer, 0, 4, "AV1 codec configuration");
@ -1389,6 +1495,30 @@ internal sealed class HeifDecoderCore : ImageDecoderCore
item.ContentColorVolume = (HeifContentColorVolume)prop.Value;
break;
case Heif4CharCode.Amve:
if (item.AmbientViewingEnvironment is not null)
{
throw new InvalidImageContentException($"Item {itemId} associates more than one ambient viewing-environment property.");
}
item.AmbientViewingEnvironment = (HeifAmbientViewingEnvironment)prop.Value;
break;
case Heif4CharCode.Reve:
if (item.ReferenceViewingEnvironment is not null)
{
throw new InvalidImageContentException($"Item {itemId} associates more than one reference viewing-environment property.");
}
item.ReferenceViewingEnvironment = (HeifReferenceViewingEnvironment)prop.Value;
break;
case Heif4CharCode.Ndwt:
if (item.NominalDiffuseWhite is not null)
{
throw new InvalidImageContentException($"Item {itemId} associates more than one nominal diffuse-white property.");
}
item.NominalDiffuseWhite = (HeifNominalDiffuseWhite)prop.Value;
break;
case Heif4CharCode.Clap:
if (item.CleanAperture is not null)
{
@ -1739,6 +1869,9 @@ internal sealed class HeifDecoderCore : ImageDecoderCore
meta.ContentLightLevel = null;
meta.MasteringDisplayColorVolume = null;
meta.ContentColorVolume = null;
meta.AmbientViewingEnvironment = null;
meta.ReferenceViewingEnvironment = null;
meta.NominalDiffuseWhite = null;
}
return image;
@ -1809,6 +1942,28 @@ internal sealed class HeifDecoderCore : ImageDecoderCore
{
metadata.GetHeifMetadata().ContentColorVolume = contentColorVolume;
}
HeifAmbientViewingEnvironment? ambientViewingEnvironment = imageItem.AmbientViewingEnvironment
?? gridTile?.AmbientViewingEnvironment;
if (ambientViewingEnvironment is not null)
{
metadata.GetHeifMetadata().AmbientViewingEnvironment = ambientViewingEnvironment;
}
HeifReferenceViewingEnvironment? referenceViewingEnvironment = imageItem.ReferenceViewingEnvironment
?? gridTile?.ReferenceViewingEnvironment;
if (referenceViewingEnvironment is not null)
{
metadata.GetHeifMetadata().ReferenceViewingEnvironment = referenceViewingEnvironment;
}
HeifNominalDiffuseWhite? nominalDiffuseWhite = imageItem.NominalDiffuseWhite ?? gridTile?.NominalDiffuseWhite;
if (nominalDiffuseWhite is not null)
{
metadata.GetHeifMetadata().NominalDiffuseWhite = nominalDiffuseWhite;
}
}
/// <summary>

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

@ -84,6 +84,24 @@ internal class HeifItem(Heif4CharCode type, uint id)
/// </summary>
public HeifContentColorVolume? ContentColorVolume { get; set; }
/// <summary>
/// Gets or sets the nominal ambient viewing environment associated with this image item, or
/// <see langword="null"/> when the item has no ambient viewing-environment property.
/// </summary>
public HeifAmbientViewingEnvironment? AmbientViewingEnvironment { get; set; }
/// <summary>
/// Gets or sets the reference mastering environment associated with this image item, or
/// <see langword="null"/> when the item has no reference viewing-environment property.
/// </summary>
public HeifReferenceViewingEnvironment? ReferenceViewingEnvironment { get; set; }
/// <summary>
/// Gets or sets the nominal diffuse-white description associated with this image item, or
/// <see langword="null"/> when the item has no nominal diffuse-white property.
/// </summary>
public HeifNominalDiffuseWhite? NominalDiffuseWhite { 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.

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

@ -31,6 +31,9 @@ public class HeifMetadata : IFormatMetadata<HeifMetadata>
this.ContentLightLevel = other.ContentLightLevel;
this.MasteringDisplayColorVolume = other.MasteringDisplayColorVolume;
this.ContentColorVolume = other.ContentColorVolume;
this.AmbientViewingEnvironment = other.AmbientViewingEnvironment;
this.ReferenceViewingEnvironment = other.ReferenceViewingEnvironment;
this.NominalDiffuseWhite = other.NominalDiffuseWhite;
}
/// <summary>
@ -71,6 +74,24 @@ public class HeifMetadata : IFormatMetadata<HeifMetadata>
/// </summary>
public HeifContentColorVolume? ContentColorVolume { get; set; }
/// <summary>
/// Gets or sets the nominal ambient viewing environment for the primary image, or <see langword="null"/>
/// when it is not available.
/// </summary>
public HeifAmbientViewingEnvironment? AmbientViewingEnvironment { get; set; }
/// <summary>
/// Gets or sets the reference mastering environment for the primary image, or <see langword="null"/> when
/// it is not available.
/// </summary>
public HeifReferenceViewingEnvironment? ReferenceViewingEnvironment { get; set; }
/// <summary>
/// Gets or sets the nominal diffuse-white description for the primary image, or <see langword="null"/> when
/// it is not available.
/// </summary>
public HeifNominalDiffuseWhite? NominalDiffuseWhite { get; set; }
/// <inheritdoc/>
public static HeifMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) => new()
{

64
src/ImageSharp/Formats/Heif/HeifNominalDiffuseWhite.cs

@ -0,0 +1,64 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary>
/// Describes the nominal diffuse-white luminance of a HEIF image.
/// </summary>
public readonly struct HeifNominalDiffuseWhite : IEquatable<HeifNominalDiffuseWhite>
{
/// <summary>
/// Initializes a new instance of the <see cref="HeifNominalDiffuseWhite"/> struct.
/// </summary>
/// <param name="luminance">
/// The nominal diffuse-white luminance in candelas per square metre, or <see langword="null"/> to use the
/// standard default.
/// </param>
public HeifNominalDiffuseWhite(double? luminance) => this.Luminance = luminance;
/// <summary>
/// Gets the nominal diffuse-white luminance in candelas per square metre, or <see langword="null"/> when the
/// image requests the standard default.
/// </summary>
public double? Luminance { get; }
/// <summary>
/// Compares two nominal diffuse-white descriptions for equality.
/// </summary>
/// <param name="left">The first nominal diffuse-white description.</param>
/// <param name="right">The second nominal diffuse-white description.</param>
/// <returns><see langword="true"/> when both descriptions specify the same luminance behavior.</returns>
public static bool operator ==(HeifNominalDiffuseWhite left, HeifNominalDiffuseWhite right)
=> left.Equals(right);
/// <summary>
/// Compares two nominal diffuse-white descriptions for inequality.
/// </summary>
/// <param name="left">The first nominal diffuse-white description.</param>
/// <param name="right">The second nominal diffuse-white description.</param>
/// <returns><see langword="true"/> when the descriptions specify different luminance behavior.</returns>
public static bool operator !=(HeifNominalDiffuseWhite left, HeifNominalDiffuseWhite right)
=> !left.Equals(right);
/// <summary>
/// Determines whether the specified object is a nominal diffuse-white description with the same value.
/// </summary>
/// <param name="obj">The object to compare with this value.</param>
/// <returns><see langword="true"/> when <paramref name="obj"/> specifies the same luminance behavior.</returns>
public override bool Equals(object? obj)
=> obj is HeifNominalDiffuseWhite other && this.Equals(other);
/// <summary>
/// Determines whether the specified nominal diffuse-white description has the same value as this value.
/// </summary>
/// <param name="other">The nominal diffuse-white description to compare with this value.</param>
/// <returns><see langword="true"/> when both descriptions specify the same luminance behavior.</returns>
public bool Equals(HeifNominalDiffuseWhite other) => this.Luminance.Equals(other.Luminance);
/// <summary>
/// Returns a hash code for this nominal diffuse-white description.
/// </summary>
/// <returns>A hash code derived from the luminance behavior.</returns>
public override int GetHashCode() => this.Luminance.GetHashCode();
}

99
src/ImageSharp/Formats/Heif/HeifReferenceViewingEnvironment.cs

@ -0,0 +1,99 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.ColorProfiles;
namespace SixLabors.ImageSharp.Formats.Heif;
/// <summary>
/// Describes the display surround and periphery in which a HEIF image was mastered.
/// </summary>
public readonly struct HeifReferenceViewingEnvironment : IEquatable<HeifReferenceViewingEnvironment>
{
/// <summary>
/// Initializes a new instance of the <see cref="HeifReferenceViewingEnvironment"/> struct.
/// </summary>
/// <param name="surroundLuminance">The luminance of the area immediately surrounding the display.</param>
/// <param name="surroundLight">The CIE 1931 chromaticity coordinates of the surround light.</param>
/// <param name="peripheryLuminance">The luminance of the environment outside the display surround.</param>
/// <param name="peripheryLight">The CIE 1931 chromaticity coordinates of the periphery light.</param>
public HeifReferenceViewingEnvironment(
double surroundLuminance,
CieXyChromaticityCoordinates surroundLight,
double peripheryLuminance,
CieXyChromaticityCoordinates peripheryLight)
{
this.SurroundLuminance = surroundLuminance;
this.SurroundLight = surroundLight;
this.PeripheryLuminance = peripheryLuminance;
this.PeripheryLight = peripheryLight;
}
/// <summary>
/// Gets the luminance of the area immediately surrounding the display in candelas per square metre.
/// </summary>
public double SurroundLuminance { get; }
/// <summary>
/// Gets the CIE 1931 chromaticity coordinates of the surround light.
/// </summary>
public CieXyChromaticityCoordinates SurroundLight { get; }
/// <summary>
/// Gets the luminance of the environment outside the display surround in candelas per square metre.
/// </summary>
public double PeripheryLuminance { get; }
/// <summary>
/// Gets the CIE 1931 chromaticity coordinates of the periphery light.
/// </summary>
public CieXyChromaticityCoordinates PeripheryLight { get; }
/// <summary>
/// Compares two reference viewing environments for equality.
/// </summary>
/// <param name="left">The first reference viewing environment.</param>
/// <param name="right">The second reference viewing environment.</param>
/// <returns><see langword="true"/> when every surround and periphery value is equal.</returns>
public static bool operator ==(HeifReferenceViewingEnvironment left, HeifReferenceViewingEnvironment right)
=> left.Equals(right);
/// <summary>
/// Compares two reference viewing environments for inequality.
/// </summary>
/// <param name="left">The first reference viewing environment.</param>
/// <param name="right">The second reference viewing environment.</param>
/// <returns><see langword="true"/> when any surround or periphery value differs.</returns>
public static bool operator !=(HeifReferenceViewingEnvironment left, HeifReferenceViewingEnvironment right)
=> !left.Equals(right);
/// <summary>
/// Determines whether the specified object is a reference viewing environment 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 environment values.</returns>
public override bool Equals(object? obj)
=> obj is HeifReferenceViewingEnvironment other && this.Equals(other);
/// <summary>
/// Determines whether the specified reference viewing environment has the same values as this value.
/// </summary>
/// <param name="other">The reference viewing environment to compare with this value.</param>
/// <returns><see langword="true"/> when every surround and periphery value is equal.</returns>
public bool Equals(HeifReferenceViewingEnvironment other)
=> this.SurroundLuminance.Equals(other.SurroundLuminance)
&& this.SurroundLight.Equals(other.SurroundLight)
&& this.PeripheryLuminance.Equals(other.PeripheryLuminance)
&& this.PeripheryLight.Equals(other.PeripheryLight);
/// <summary>
/// Returns a hash code for this reference viewing environment.
/// </summary>
/// <returns>A hash code derived from the surround and periphery values.</returns>
public override int GetHashCode()
=> HashCode.Combine(
this.SurroundLuminance,
this.SurroundLight,
this.PeripheryLuminance,
this.PeripheryLight);
}
Loading…
Cancel
Save