diff --git a/meson.build b/meson.build index 5d573c34..8f29f910 100644 --- a/meson.build +++ b/meson.build @@ -12,6 +12,7 @@ project('libplacebo', ['c', 'cpp'], 5, # API version { + '242': 'add `pl_hdr_metadata.scene_max/avg` and `pl_hdr_metadata.ootf`', '241': 'add `pl_plane_data.swapped`', '240': 'add `PL_COLOR_TRC_ST428`', '239': 'add `pl_fmt.planes` and `pl_tex.planes`', diff --git a/src/colorspace.c b/src/colorspace.c index d3b8e7eb..ccf82ce2 100644 --- a/src/colorspace.c +++ b/src/colorspace.c @@ -287,6 +287,16 @@ const struct pl_hdr_metadata pl_hdr_metadata_hdr10 ={ .max_fall = 0, // unknown }; +static inline bool pl_hdr_bezier_equal(const struct pl_hdr_bezier *a, + const struct pl_hdr_bezier *b) +{ + return a->target_luma == b->target_luma && + a->knee_x == b->knee_x && + a->knee_y == b->knee_y && + a->num_anchors == b->num_anchors && + !memcmp(a->anchors, b->anchors, sizeof(a->anchors[0]) * a->num_anchors); +} + bool pl_hdr_metadata_equal(const struct pl_hdr_metadata *a, const struct pl_hdr_metadata *b) { @@ -294,7 +304,12 @@ bool pl_hdr_metadata_equal(const struct pl_hdr_metadata *a, a->min_luma == b->min_luma && a->max_luma == b->max_luma && a->max_cll == b->max_cll && - a->max_fall == b->max_fall; + a->max_fall == b->max_fall && + a->scene_max[0] == b->scene_max[0] && + a->scene_max[1] == b->scene_max[1] && + a->scene_max[2] == b->scene_max[2] && + a->scene_avg == b->scene_avg && + pl_hdr_bezier_equal(&a->ootf, &b->ootf); } void pl_hdr_metadata_merge(struct pl_hdr_metadata *orig, @@ -309,6 +324,12 @@ void pl_hdr_metadata_merge(struct pl_hdr_metadata *orig, orig->max_cll = update->max_cll; if (!orig->max_fall) orig->max_fall = update->max_fall; + if (!orig->scene_max[1]) + memcpy(orig->scene_max, update->scene_max, sizeof(orig->scene_max)); + if (!orig->scene_avg) + orig->scene_avg = update->scene_avg; + if (!orig->ootf.target_luma) + orig->ootf = update->ootf; } const struct pl_color_space pl_color_space_unknown = {0}; @@ -409,6 +430,10 @@ void pl_color_space_infer(struct pl_color_space *space) space->hdr.min_luma = space->sig_floor * PL_COLOR_SDR_WHITE; space->sig_floor = 0; } + if (space->sig_avg) { + space->hdr.scene_avg = space->sig_avg * PL_COLOR_SDR_WHITE; + space->sig_avg = 0; + } reinfer_peaks: if (space->hdr.max_luma < 1 || space->hdr.max_luma > 10000) { @@ -448,6 +473,7 @@ void pl_color_space_infer(struct pl_color_space *space) if (space->sig_scale && !pl_color_transfer_is_hdr(space->transfer)) { space->hdr.max_luma *= space->sig_scale; space->hdr.min_luma *= space->sig_scale; + space->hdr.scene_avg *= space->sig_scale; space->sig_scale = 0; } diff --git a/src/include/libplacebo/colorspace.h b/src/include/libplacebo/colorspace.h index 4fb8b787..9769194f 100644 --- a/src/include/libplacebo/colorspace.h +++ b/src/include/libplacebo/colorspace.h @@ -328,17 +328,30 @@ void pl_raw_primaries_merge(struct pl_raw_primaries *orig, // Returns the raw primaries for a given color space. const struct pl_raw_primaries *pl_raw_primaries_get(enum pl_color_primaries prim); +// Bezier curve for HDR metadata +struct pl_hdr_bezier { + float target_luma; // target luminance (cd/m²) for this OOTF + float knee_x, knee_y; // cross-over knee point (0-1) + float anchors[15]; // intermediate bezier curve control points (0-1) + uint8_t num_anchors; +}; + // Represents raw HDR metadata as defined by SMPTE 2086 / CTA 861.3, which is // often attached to HDR sources and can be forwarded to HDR-capable displays, // or used to guide the libplacebo built-in tone mapping. struct pl_hdr_metadata { // Mastering display metadata. This is used for tone-mapping. - struct pl_raw_primaries prim; // mastering display primaries - float min_luma, max_luma; // min/max luminance (in cd/m²) + struct pl_raw_primaries prim; // mastering display primaries + float min_luma, max_luma; // min/max luminance (in cd/m²) // Content light level. This is ignored by libplacebo itself. - float max_cll; // max content light level (in cd/m²) - float max_fall; // max frame average light level (in cd/m²) + float max_cll; // max content light level (in cd/m²) + float max_fall; // max frame average light level (in cd/m²) + + // HDR10+ dynamic metadata (per-scene) + float scene_max[3]; // maxSCL in cd/m² per component (RGB) + float scene_avg; // average of maxRGB in cd/m² + struct pl_hdr_bezier ootf; // reference OOTF (optional) }; extern const struct pl_hdr_metadata pl_hdr_metadata_empty; // equal to {0} @@ -389,7 +402,7 @@ struct pl_color_space { // Deprecated fields enum pl_color_light light PL_DEPRECATED; // ignored float sig_peak PL_DEPRECATED; // replaced by `hdr.max_luma` - float sig_avg PL_DEPRECATED; // ignored + float sig_avg PL_DEPRECATED; // replaced by `hdr.scene_avg` float sig_floor PL_DEPRECATED; // replaced by `hdr.min_luma` float sig_scale PL_DEPRECATED; // merged into `hdr.max/min_luma` };