Skip to main content

zenmeta/
display.rs

1//! Retention checks for a pipeline retaining a gain-map rendition.
2//! Extract vendor/XMP parameters into [`GainMapParams`] before calling this.
3//! Container writers must regenerate gain-map discovery/parameters separately
4//! from source XMP, and filter metadata inside encoded auxiliary images too.
5use zencodec::{GainMapParams, Metadata, MetadataPolicy};
6
7/// Retention would change rendering, or the gain-map parameters are invalid.
8#[derive(Debug)]
9#[non_exhaustive]
10pub enum Error {
11    /// Invalid typed parameters; preserve the diagnostic from the gain-map parser.
12    GainMap(zencodec::gainmap::GainMapParseError),
13    /// A rendering field was removed without an accompanying pixel conversion.
14    Removed(&'static str),
15}
16impl core::fmt::Display for Error {
17    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
18        match self {
19            Self::GainMap(e) => write!(f, "invalid gain map: {e}"),
20            Self::Removed(field) => {
21                write!(f, "retention would remove display information: {field}")
22            }
23        }
24    }
25}
26impl core::error::Error for Error {
27    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
28        match self {
29            Self::GainMap(e) => Some(e),
30            _ => None,
31        }
32    }
33}
34/// Filter source metadata while retaining a gain map with unchanged base pixels.
35///
36/// This does not parse vendor data, scan pixels, copy gain-map storage, certify
37/// arbitrary ICC contents, or sanitize containers. It couples an explicit policy
38/// with already extracted parameters and refuses removal of known render signals.
39/// An SDR fallback or orientation bake is a separate, explicit pixel operation.
40/// The target codec must serialize `params` anew, even when source XMP is dropped.
41pub fn filter_for_gain_map(
42    meta: &Metadata,
43    params: &GainMapParams,
44    policy: &MetadataPolicy,
45) -> Result<Metadata, Error> {
46    params.validate().map_err(Error::GainMap)?;
47    let out = crate::metadata::filter(meta, policy);
48    if out.orientation != meta.orientation {
49        return Err(Error::Removed("orientation"));
50    }
51    if out.cicp != meta.cicp {
52        return Err(Error::Removed("CICP"));
53    }
54    if out.content_light_level != meta.content_light_level
55        || out.mastering_display != meta.mastering_display
56        || out.diffuse_white != meta.diffuse_white
57    {
58        return Err(Error::Removed("HDR luminance"));
59    }
60    if meta.icc_profile.is_some()
61        && out.icc_profile.is_none()
62        && !meta
63            .icc_profile
64            .as_ref()
65            .is_some_and(|icc| zenpixels::icc::is_common_srgb(icc))
66    {
67        return Err(Error::Removed("ICC profile"));
68    }
69    Ok(out)
70}