| /* | |
| * Copyright 2025 Google LLC | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #ifndef SkHdrMetadata_DEFINED | |
| #define SkHdrMetadata_DEFINED | |
| #include "include/core/SkColorSpace.h" | |
| #include "include/core/SkData.h" | |
| #include "include/private/SkAPI.h" | |
| #include <algorithm> | |
| #include <cmath> | |
| #include <memory> | |
| #include <optional> | |
| #include <vector> | |
| class SkColorFilter; | |
| class SkData; | |
| class SkString; | |
| namespace skhdr { | |
| /** | |
| * Content light level metadata. | |
| * The semantics of this metadata is defined in: | |
| * ANSI/CTA-861-H A DTV Profile for Uncompressed High Speed Digital Interfaces | |
| * Annex P Calculation of MaxCLL and MaxFALL | |
| * Slightly different semantics for this metadata are defined in: | |
| * Portable Network Graphics (PNG) Specification (Third Edition) | |
| * 11.3.2.8 cLLI Content Light Level Information | |
| * https://www.w3.org/TR/png-3/#cLLI-chunk | |
| * This metadata should only be used in ways that work with both semantics. | |
| */ | |
| struct SK_API ContentLightLevelInformation { | |
| float fMaxCLL = 0.f; | |
| float fMaxFALL = 0.f; | |
| /** | |
| * Decode from the binary encoding listed at: | |
| * AV1 Bitstream & Decoding Process Specification Version 1.0.0 Errata 1 | |
| * https://aomediacodec.github.io/av1-spec/av1-spec.pdf | |
| * 5.8.3 Metadata high dynamic range content light level syntax | |
| * This encoding is equivalent to: | |
| * ITU-T H.265 (V10) (07/2024) | |
| * D.2.35 Content light level information SEI message syntax | |
| * Return false if parsing fails. | |
| */ | |
| bool parse(const SkData* data); | |
| /** | |
| * Serialize to the encoding used by parse(). | |
| */ | |
| sk_sp<SkData> serialize() const; | |
| /** | |
| * Helper functions for accessing members as uint16_t, which is often needed when interacting | |
| * with libraries using the CTA semantics. | |
| */ | |
| static ContentLightLevelInformation MakeUint16(uint16_t maxCLL, uint16_t maxFALL) { | |
| return { .fMaxCLL = static_cast<float>(maxCLL), .fMaxFALL = static_cast<float>(maxFALL) }; | |
| } | |
| uint16_t getUint16MaxCLL() const { | |
| return static_cast<uint16_t>(std::clamp(std::round(fMaxCLL), 0.f, 65535.f)); | |
| } | |
| uint16_t getUint16MaxFALL() const { | |
| return static_cast<uint16_t>(std::clamp(std::round(fMaxFALL), 0.f, 65535.f)); | |
| } | |
| /** | |
| * Decode from the binary encoding listed at: | |
| * Portable Network Graphics (PNG) Specification (Third Edition) | |
| * 11.3.2.8 cLLI Content Light Level Information | |
| * https://www.w3.org/TR/png-3/#cLLI-chunk | |
| * This encoding is not equivalent to the encoding used by parse(). | |
| * Return false if parsing fails. | |
| */ | |
| bool parsePngChunk(const SkData* data); | |
| /** | |
| * Serialize to the encoding used by parsePngChunk(). | |
| */ | |
| sk_sp<SkData> serializePngChunk() const; | |
| /** | |
| * Return a human-readable description. | |
| */ | |
| SkString toString() const; | |
| bool operator==(const ContentLightLevelInformation& other) const; | |
| bool operator!=(const ContentLightLevelInformation& other) const { | |
| return !(*this == other); | |
| } | |
| }; | |
| /** | |
| * Mastering display color volume metadata. | |
| * The semantics of this metadata is defined in: | |
| * SMPTE ST 2086:2018 Mastering Display Color Volume Metadata Supporting | |
| * High Luminance and Wide Color Gamut Images | |
| */ | |
| struct SK_API MasteringDisplayColorVolume { | |
| SkColorSpacePrimaries fDisplayPrimaries = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; | |
| float fMaximumDisplayMasteringLuminance = 0.f; | |
| float fMinimumDisplayMasteringLuminance = 0.f; | |
| /** | |
| * The encoding as defined in: | |
| * AV1 Bitstream & Decoding Process Specification Version 1.0.0 Errata 1 | |
| * https://aomediacodec.github.io/av1-spec/av1-spec.pdf | |
| * 5.8.4 Metadata high dynamic range mastering display color volume syntax | |
| * This encoding is equivalent to: | |
| * ITU-T H.265 (V10) (07/2024) | |
| * D.2.35 Content light level information SEI message syntax | |
| * This encoding is also equivalent to: | |
| * Portable Network Graphics (PNG) Specification (Third Edition) | |
| * 11.3.2.7 mDCV Mastering Display Color Volume | |
| * https://www.w3.org/TR/png-3/#mDCV-chunk | |
| * Return false if parsing fails. | |
| */ | |
| bool parse(const SkData* data); | |
| /** | |
| * Serialize to the encoding used by parse(). | |
| */ | |
| sk_sp<SkData> serialize() const; | |
| /** | |
| * Return a human-readable description. | |
| */ | |
| SkString toString() const; | |
| bool operator==(const MasteringDisplayColorVolume& other) const; | |
| bool operator!=(const MasteringDisplayColorVolume& other) const { | |
| return !(*this == other); | |
| } | |
| }; | |
| /** | |
| * Adaptive Global Tone Map | |
| * This structure contains the metadata items from the ColorVolumeTransform metadata group | |
| * in Clause 7.1: Metadata set of SMPTE ST 2094-50: Dynamic metadata for color volume transform | |
| * Application #5 | |
| * https://github.com/SMPTE/st2094-50 | |
| * WARNING: This specification is still a DRAFT and is subject to change before publication. | |
| */ | |
| struct SK_API AdaptiveGlobalToneMap { | |
| // A GainCurve metadata group. | |
| struct SK_API GainCurve { | |
| // Structure holding one entry of the GainCurveControlPointX, GainCurveControlPointY, and | |
| // GainCurveControlPointM metadata items. | |
| struct ControlPoint { | |
| float fX = 0.f; | |
| float fY = 0.f; | |
| float fM = 0.f; | |
| bool operator==(const ControlPoint& other) const { | |
| return fX == other.fX && fY == other.fY && fM == other.fM; | |
| } | |
| }; | |
| // The size of this vector is the value of the GainCurveNumControlPoints metadata item. | |
| static constexpr size_t kMinNumControlPoints = 1u; | |
| static constexpr size_t kMaxNumControlPoints = 32u; | |
| std::vector<ControlPoint> fControlPoints = {}; | |
| bool operator==(const GainCurve& other) const { | |
| return fControlPoints == other.fControlPoints; | |
| } | |
| }; | |
| // A ComponentMix metadata group. | |
| struct SK_API ComponentMixingFunction { | |
| // The ComponentMixRed/Green/Blue/Max/Min/Component metadata items. | |
| float fRed = 0.f; | |
| float fGreen = 0.f; | |
| float fBlue = 0.f; | |
| float fMax = 0.f; | |
| float fMin = 0.f; | |
| float fComponent = 0.f; | |
| bool operator==(const ComponentMixingFunction& other) const { | |
| return fRed == other.fRed && fGreen == other.fGreen && fBlue == other.fBlue && | |
| fMax == other.fMax && fMin == other.fMin && fComponent == other.fComponent; | |
| } | |
| }; | |
| // A ColorGainFunction metadata group. | |
| struct SK_API ColorGainFunction { | |
| // The ComponentMix metadata group. | |
| ComponentMixingFunction fComponentMixing = {}; | |
| // The GainCurve metadata group. | |
| GainCurve fGainCurve = {}; | |
| bool operator==(const ColorGainFunction& other) const { | |
| return fComponentMixing == other.fComponentMixing && fGainCurve == other.fGainCurve; | |
| } | |
| }; | |
| // Structure holding the metadata items and groups for an alternate image. | |
| struct SK_API AlternateImage { | |
| // The AlternateHdrHeadroom metadata item. | |
| float fHdrHeadroom = 0.f; | |
| // The ColorGainFunction metadata group. | |
| ColorGainFunction fColorGainFunction = {}; | |
| bool operator==(const AlternateImage& other) const { | |
| return fHdrHeadroom == other.fHdrHeadroom && fColorGainFunction == other.fColorGainFunction; | |
| } | |
| }; | |
| // HeadroomAdaptiveToneMap metadata group. | |
| struct SK_API HeadroomAdaptiveToneMap { | |
| // The BaselineHdrHeadroom metadata item. | |
| float fBaselineHdrHeadroom = 0.f; | |
| // The GainApplicationSpaceColorPrimaries metadata item. | |
| SkColorSpacePrimaries fGainApplicationSpacePrimaries = | |
| {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; | |
| // The size of this vector is the NumAlternateImages metadata item. | |
| static constexpr size_t kMaxNumAlternateImages = 4u; | |
| std::vector<AlternateImage> fAlternateImages = {}; | |
| bool operator==(const HeadroomAdaptiveToneMap& other) const { | |
| return fBaselineHdrHeadroom == other.fBaselineHdrHeadroom && | |
| fGainApplicationSpacePrimaries == other.fGainApplicationSpacePrimaries && | |
| fAlternateImages == other.fAlternateImages; | |
| } | |
| }; | |
| // The default value for the HdrReferenceWhite metadata item. | |
| static constexpr float kDefaultHdrReferenceWhite = 203.f; | |
| // The HdrReferenceWhite metadata item. | |
| float fHdrReferenceWhite = kDefaultHdrReferenceWhite; | |
| // The HeadroomAdaptiveToneMap metadata group. | |
| std::optional<HeadroomAdaptiveToneMap> fHeadroomAdaptiveToneMap = std::nullopt; | |
| /** | |
| * Decode from the binary encoding in Annex C. | |
| */ | |
| bool parse(const SkData* data); | |
| /** | |
| * Serialize to the encoding used by parse(). | |
| */ | |
| sk_sp<SkData> serialize() const; | |
| /** | |
| * Return a human-readable description. | |
| */ | |
| SkString toString() const; | |
| /** | |
| * Return true if this metadata satisfies all normative constraints. | |
| */ | |
| bool isValid() const; | |
| bool operator==(const AdaptiveGlobalToneMap& other) const { | |
| return fHdrReferenceWhite == other.fHdrReferenceWhite && | |
| fHeadroomAdaptiveToneMap == other.fHeadroomAdaptiveToneMap; | |
| } | |
| }; | |
| /** | |
| * Structure containing all HDR metadata that can be attached to an image or video frame. | |
| */ | |
| class SK_API Metadata { | |
| public: | |
| /** | |
| * Return a container with no metadata. | |
| */ | |
| static Metadata MakeEmpty(); | |
| /** | |
| * If there does not exists Content Light Level Information metadata, then return false. | |
| * Otherwise return true and if `clli` is non-nullptr then write the metadata to `clli`. | |
| */ | |
| bool getContentLightLevelInformation(ContentLightLevelInformation* clli) const; | |
| /** | |
| * Set the Content Light Level Information metadata. | |
| */ | |
| void setContentLightLevelInformation(const ContentLightLevelInformation& clli); | |
| /** | |
| * If there does not exists Mastering Display Color Volume metadata, then return false. | |
| * Otherwise return true and if `mdcv` is non-nullptr then write the metadata to `mdcv`. | |
| */ | |
| bool getMasteringDisplayColorVolume(MasteringDisplayColorVolume* mdcv) const; | |
| /** | |
| * Set the Mastering Display Color Volume metadata. | |
| */ | |
| void setMasteringDisplayColorVolume(const MasteringDisplayColorVolume& mdcv); | |
| /** | |
| * If there does not exists Adaptive Global Tone Map metadata, then return false. | |
| * Otherwise return true and if `agtm` is non-nullptr then write the metadata to `agtm`. | |
| */ | |
| bool getAdaptiveGlobalToneMap(AdaptiveGlobalToneMap* agtm) const; | |
| /** | |
| * Set the Adaptive Global Tone Map metadata. | |
| */ | |
| void setAdaptiveGlobalToneMap(const AdaptiveGlobalToneMap& agtm); | |
| /** | |
| * Return the serialized Adaptive Global Tone Mapping metadata, or nullptr if none has been set. | |
| */ | |
| sk_sp<const SkData> getSerializedAgtm() const; | |
| /** | |
| * Set the serialized Adaptive Global Tone Mapping metadata. | |
| */ | |
| void setSerializedAgtm(sk_sp<const SkData>); | |
| /** | |
| * Return a human-readable description. | |
| */ | |
| SkString toString() const; | |
| /** | |
| * Return the SkColorFilter to tone map to the specified targeted HDR headroom. | |
| * | |
| * The `inputColorSpace` parameter is the color space of the input image that will be | |
| * used with the image. If it is PQ or HLG, then the color filter will effectively | |
| * reinterpret the image as having the HDR reference white parameter indicated in the | |
| * metadata. | |
| * | |
| * If `inputColorSpace` is PQ or HLG, then a default tone mapping will be provided, | |
| * inferring the baseline HDR headroom from the CLLI or MDCV metadata, if present. | |
| */ | |
| sk_sp<SkColorFilter> makeToneMapColorFilter( | |
| float targetedHdrHeadroom, const SkColorSpace* inputColorSpace = nullptr) const; | |
| bool operator==(const Metadata& other) const; | |
| bool operator!=(const Metadata& other) const { | |
| return !(*this == other); | |
| } | |
| private: | |
| std::optional<ContentLightLevelInformation> fContentLightLevelInformation; | |
| std::optional<MasteringDisplayColorVolume> fMasteringDisplayColorVolume; | |
| std::optional<AdaptiveGlobalToneMap> fAdaptiveGlobalToneMap; | |
| }; | |
| } // namespace skhdr | |
| #endif |