diff --git a/video/csputils.c b/video/csputils.c index 9db7bb54a9..5b5c4e1def 100644 --- a/video/csputils.c +++ b/video/csputils.c @@ -479,7 +479,7 @@ bool mp_trc_is_hdr(enum mp_csp_trc trc) // Compute the RGB/XYZ matrix as described here: // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html -static void mp_get_rgb2xyz_matrix(struct mp_csp_primaries space, float m[3][3]) +void mp_get_rgb2xyz_matrix(struct mp_csp_primaries space, float m[3][3]) { float S[3], X[4], Z[4]; @@ -537,14 +537,14 @@ static void mp_apply_chromatic_adaptation(struct mp_csp_col_xy src, for (int i = 0; i < 3; i++) { // source cone - C[i][0] = bradford[i][0] * src.x / src.y + C[i][0] = bradford[i][0] * mp_xy_X(src) + bradford[i][1] * 1 - + bradford[i][2] * (1 - src.x - src.y) / src.y; + + bradford[i][2] * mp_xy_Z(src); // dest cone - C[i][1] = bradford[i][0] * dest.x / dest.y + C[i][1] = bradford[i][0] * mp_xy_X(dest) + bradford[i][1] * 1 - + bradford[i][2] * (1 - dest.x - dest.y) / dest.y; + + bradford[i][2] * mp_xy_Z(dest); } // tmp := I * [Cd/Cs] * Ma diff --git a/video/csputils.h b/video/csputils.h index 9e81c90ebf..8b63ac3905 100644 --- a/video/csputils.h +++ b/video/csputils.h @@ -218,6 +218,14 @@ struct mp_csp_col_xy { float x, y; }; +static inline float mp_xy_X(struct mp_csp_col_xy xy) { + return xy.x / xy.y; +} + +static inline float mp_xy_Z(struct mp_csp_col_xy xy) { + return (1 - xy.x - xy.y) / xy.y; +} + struct mp_csp_primaries { struct mp_csp_col_xy red, green, blue, white; }; @@ -268,6 +276,7 @@ struct mp_cmat { float c[3]; }; +void mp_get_rgb2xyz_matrix(struct mp_csp_primaries space, float m[3][3]); void mp_get_cms_matrix(struct mp_csp_primaries src, struct mp_csp_primaries dest, enum mp_render_intent intent, float cms_matrix[3][3]); diff --git a/video/out/opengl/video_shaders.c b/video/out/opengl/video_shaders.c index f56c687903..26f751afb2 100644 --- a/video/out/opengl/video_shaders.c +++ b/video/out/opengl/video_shaders.c @@ -548,8 +548,26 @@ void pass_color_map(struct gl_shader_cache *sc, // Tone map to prevent clipping when the source signal peak exceeds the // encodable range - if (src.sig_peak > dst_range) + if (src.sig_peak > dst_range) { + // Convert to linear, relative XYZ before tone mapping to preserve + // channel balance better + struct mp_csp_primaries prim = mp_get_csp_primaries(src.primaries); + float rgb2xyz[3][3]; + mp_get_rgb2xyz_matrix(prim, rgb2xyz); + gl_sc_uniform_mat3(sc, "rgb2xyz", true, &rgb2xyz[0][0]); + mp_invert_matrix3x3(rgb2xyz); + gl_sc_uniform_mat3(sc, "xyz2rgb", true, &rgb2xyz[0][0]); + // White balance, calculated from the relative XYZ coefficients of + // the white point. Failing to multiply in this difference causes + // the tone mapping process to shift the color temperature. + gl_sc_uniform_vec2(sc, "balance", (float[]){mp_xy_X(prim.white), + mp_xy_Z(prim.white)}); + GLSL(color.xyz = rgb2xyz * color.rgb;) + GLSL(color.xz /= balance;) pass_tone_map(sc, src.sig_peak / dst_range, algo, tone_mapping_param); + GLSL(color.xz *= balance;) + GLSL(color.rgb = xyz2rgb * color.xyz;) + } // Adapt to the right colorspace if necessary if (src.primaries != dst.primaries) {