summaryrefslogtreecommitdiffstats
path: root/video/out/opengl/video.c
diff options
context:
space:
mode:
Diffstat (limited to 'video/out/opengl/video.c')
-rw-r--r--video/out/opengl/video.c130
1 files changed, 61 insertions, 69 deletions
diff --git a/video/out/opengl/video.c b/video/out/opengl/video.c
index 59dd64cb65..a4cc6cfac8 100644
--- a/video/out/opengl/video.c
+++ b/video/out/opengl/video.c
@@ -2158,19 +2158,29 @@ static void pass_scale_main(struct gl_video *p)
}
}
-// Adapts the colors from the given color space to the display device's native
-// gamut.
-static void pass_colormanage(struct gl_video *p, float peak_src,
- enum mp_csp_prim prim_src,
- enum mp_csp_trc trc_src)
-{
- GLSLF("// color management\n");
- enum mp_csp_trc trc_dst = p->opts.target_trc;
- enum mp_csp_prim prim_dst = p->opts.target_prim;
- float peak_dst = p->opts.target_brightness;
+// Adapts the colors to the right output color space. (Final pass during
+// rendering)
+// If OSD is true, ignore any changes that may have been made to the video
+// by previous passes (i.e. linear scaling)
+static void pass_colormanage(struct gl_video *p, struct mp_colorspace src, bool osd)
+{
+ struct mp_colorspace ref = src;
+
+ if (p->use_linear && !osd)
+ src.gamma = MP_CSP_TRC_LINEAR;
+
+ // Figure out the target color space from the options, or auto-guess if
+ // none were set
+ struct mp_colorspace dst = {
+ .gamma = p->opts.target_trc,
+ .primaries = p->opts.target_prim,
+ .nom_peak = mp_csp_trc_nom_peak(p->opts.target_trc, p->opts.target_brightness),
+ };
if (p->use_lut_3d) {
- // The 3DLUT is always generated against the original source space
+ // The 3DLUT is always generated against the video's original source
+ // space, *not* the reference space. (To avoid having to regenerate
+ // the 3DLUT for the OSD on every frame)
enum mp_csp_prim prim_orig = p->image_params.color.primaries;
enum mp_csp_trc trc_orig = p->image_params.color.gamma;
@@ -2186,87 +2196,66 @@ static void pass_colormanage(struct gl_video *p, float peak_src,
}
if (gl_video_get_lut3d(p, prim_orig, trc_orig)) {
- prim_dst = prim_orig;
- trc_dst = trc_orig;
+ dst.primaries = prim_orig;
+ dst.gamma = trc_orig;
}
}
- if (prim_dst == MP_CSP_PRIM_AUTO) {
+ if (dst.primaries == MP_CSP_PRIM_AUTO) {
// The vast majority of people are on sRGB or BT.709 displays, so pick
// this as the default output color space.
- prim_dst = MP_CSP_PRIM_BT_709;
+ dst.primaries = MP_CSP_PRIM_BT_709;
- if (p->image_params.color.primaries == MP_CSP_PRIM_BT_601_525 ||
- p->image_params.color.primaries == MP_CSP_PRIM_BT_601_625)
+ if (ref.primaries == MP_CSP_PRIM_BT_601_525 ||
+ ref.primaries == MP_CSP_PRIM_BT_601_625)
{
// Since we auto-pick BT.601 and BT.709 based on the dimensions,
// combined with the fact that they're very similar to begin with,
// and to avoid confusing the average user, just don't adapt BT.601
// content automatically at all.
- prim_dst = p->image_params.color.primaries;
+ dst.primaries = ref.gamma;
}
}
- if (trc_dst == MP_CSP_TRC_AUTO) {
+ if (dst.gamma == MP_CSP_TRC_AUTO) {
// Most people seem to complain when the image is darker or brighter
// than what they're "used to", so just avoid changing the gamma
// altogether by default. The only exceptions to this rule apply to
// very unusual TRCs, which even hardcode technoluddites would probably
// not enjoy viewing unaltered.
- trc_dst = p->image_params.color.gamma;
+ dst.gamma = ref.gamma;
// Avoid outputting linear light or HDR content "by default". For these
// just pick gamma 2.2 as a default, since it's a good estimate for
// the response of typical displays
- if (trc_dst == MP_CSP_TRC_LINEAR || mp_trc_is_hdr(trc_dst))
- trc_dst = MP_CSP_TRC_GAMMA22;
+ if (dst.gamma == MP_CSP_TRC_LINEAR || mp_trc_is_hdr(dst.gamma))
+ dst.gamma = MP_CSP_TRC_GAMMA22;
}
- if (!peak_src) {
- // If the source has no information known, it's display-referred
- // (and should be treated relative to the specified desired peak_dst)
- peak_src = peak_dst * mp_csp_trc_rel_peak(p->image_params.color.gamma);
- }
-
- // All operations from here on require linear light as a starting point,
- // so we linearize even if trc_src == trc_dst when one of the other
- // operations needs it
- bool need_gamma = trc_src != trc_dst || prim_src != prim_dst ||
- peak_src != peak_dst;
- if (need_gamma)
- pass_linearize(p->sc, trc_src);
-
- // Adapt and tone map for a different reference peak brightness
- if (peak_src != peak_dst)
- {
- GLSLF("// HDR tone mapping\n");
- float rel_peak = peak_src / peak_dst;
- // Normalize such that 1 is the target brightness (and values above
- // 1 are out of range)
- GLSLF("color.rgb *= vec3(%f);\n", rel_peak);
- // Tone map back down to the range [0,1]
- pass_tone_map(p->sc, rel_peak, p->opts.hdr_tone_mapping,
- p->opts.tone_mapping_param);
+ // For the src peaks, the correct brightness metadata may be present for
+ // sig_peak, nom_peak, both, or neither. To handle everything in a generic
+ // way, it's important to never automatically infer a sig_peak that is
+ // below the nom_peak (since we don't know what bits the image contains,
+ // doing so would potentially badly clip). The only time in which this
+ // may be the case is when the mastering metadata explicitly says so, i.e.
+ // the sig_peak was already set. So to simplify the logic as much as
+ // possible, make sure the nom_peak is present and correct first, and just
+ // set sig_peak = nom_peak if missing.
+ if (!src.nom_peak) {
+ // For display-referred colorspaces, we treat it as relative to
+ // target_brightness
+ src.nom_peak = mp_csp_trc_nom_peak(src.gamma, p->opts.target_brightness);
}
- // Adapt to the right colorspace if necessary
- if (prim_src != prim_dst) {
- struct mp_csp_primaries csp_src = mp_get_csp_primaries(prim_src),
- csp_dst = mp_get_csp_primaries(prim_dst);
- float m[3][3] = {{0}};
- mp_get_cms_matrix(csp_src, csp_dst, MP_INTENT_RELATIVE_COLORIMETRIC, m);
- gl_sc_uniform_mat3(p->sc, "cms_matrix", true, &m[0][0]);
- GLSL(color.rgb = cms_matrix * color.rgb;)
- }
+ if (!src.sig_peak)
+ src.sig_peak = src.nom_peak;
- if (need_gamma) {
- // If the target encoding function has a fixed peak, we need to
- // un-normalize back to the encoding signal range
- if (trc_dst == MP_CSP_TRC_SMPTE_ST2084)
- GLSLF("color.rgb *= vec3(%f);\n", peak_dst / 10000);
+ MP_DBG(p, "HDR src nom: %f sig: %f, dst: %f\n",
+ src.nom_peak, src.sig_peak, dst.nom_peak);
- pass_delinearize(p->sc, trc_dst);
- }
+ // Adapt from src to dst as necessary
+ pass_color_map(p->sc, src, dst, p->opts.hdr_tone_mapping,
+ p->opts.tone_mapping_param);
if (p->use_lut_3d) {
gl_sc_uniform_sampler(p->sc, "lut_3d", GL_TEXTURE_3D, TEXUNIT_3DLUT);
@@ -2407,11 +2396,15 @@ static void pass_draw_osd(struct gl_video *p, int draw_flags, double pts,
default:
abort();
}
- // Subtitle color management, they're assumed to be display-referred
- // sRGB by default
+ // When subtitles need to be color managed, assume they're in sRGB
+ // (for lack of anything saner to do)
if (cms) {
- pass_colormanage(p, p->opts.target_brightness,
- MP_CSP_PRIM_BT_709, MP_CSP_TRC_SRGB);
+ static const struct mp_colorspace csp_srgb = {
+ .primaries = MP_CSP_PRIM_BT_709,
+ .gamma = MP_CSP_TRC_SRGB,
+ };
+
+ pass_colormanage(p, csp_srgb, true);
}
gl_sc_set_vao(p->sc, mpgl_osd_get_vao(p->osd));
gl_sc_gen_shader_and_reset(p->sc);
@@ -2542,8 +2535,7 @@ static void pass_draw_to_screen(struct gl_video *p, int fbo)
GLSL(color.rgb = pow(color.rgb, vec3(user_gamma));)
}
- pass_colormanage(p, p->image_params.color.peak, p->image_params.color.primaries,
- p->use_linear ? MP_CSP_TRC_LINEAR : p->image_params.color.gamma);
+ pass_colormanage(p, p->image_params.color, false);
// Draw checkerboard pattern to indicate transparency
if (p->has_alpha && p->opts.alpha_mode == ALPHA_BLEND_TILES) {