summaryrefslogtreecommitdiffstats
path: root/video/out
diff options
context:
space:
mode:
authorNiklas Haas <git@nand.wakku.to>2014-03-26 01:46:38 +0100
committerwm4 <wm4@nowhere>2014-06-22 19:00:38 +0200
commit70f50ddc5e97020d64ea0702748a00eddebc2473 (patch)
treef115668aa97cf11770459a169cc777ffca113840 /video/out
parent86d3d11a68510764504a2a3c5987ab8e059d6df5 (diff)
downloadmpv-70f50ddc5e97020d64ea0702748a00eddebc2473.tar.bz2
mpv-70f50ddc5e97020d64ea0702748a00eddebc2473.tar.xz
video: Add support for non-BT.709 primaries
This add support for reading primary information from lavc, categorized into BT.601-525, BT.601-625, BT.709 and BT.2020; and passes it on to the vo. In vo_opengl, we always generate the 3dlut against the wider BT.2020 and transform our source into this colorspace in the shader.
Diffstat (limited to 'video/out')
-rw-r--r--video/out/gl_lcms.c20
-rw-r--r--video/out/gl_video.c17
-rw-r--r--video/out/gl_video_shaders.glsl34
3 files changed, 55 insertions, 16 deletions
diff --git a/video/out/gl_lcms.c b/video/out/gl_lcms.c
index 75388ada05..b1967ebf0e 100644
--- a/video/out/gl_lcms.c
+++ b/video/out/gl_lcms.c
@@ -46,7 +46,7 @@ static bool parse_3dlut_size(const char *arg, int *p1, int *p2, int *p3)
return false;
for (int n = 0; n < 3; n++) {
int s = ((int[]) { *p1, *p2, *p3 })[n];
- if (s < 2 || s > 256 || ((s - 1) & s))
+ if (s < 2 || s > 512 || ((s - 1) & s))
return false;
}
return true;
@@ -135,8 +135,8 @@ struct lut3d *mp_load_icc(struct mp_icc_opts *opts, struct mp_log *log,
char *cache_info =
// Gamma is included in the header to help uniquely identify it,
// because we may change the parameter in the future or make it
- // customizable.
- talloc_asprintf(tmp, "intent=%d, size=%dx%dx%d, gamma=2.4",
+ // customizable, same for the primaries.
+ talloc_asprintf(tmp, "intent=%d, size=%dx%dx%d, gamma=2.4, prim=bt2020\n",
opts->intent, s_r, s_g, s_b);
// check cache
@@ -165,17 +165,19 @@ struct lut3d *mp_load_icc(struct mp_icc_opts *opts, struct mp_log *log,
if (!profile)
goto error_exit;
- cmsCIExyY d65 = {0.3127, 0.3290, 1.0};
- static const cmsCIExyYTRIPLE bt709prim = {
- .Red = {0.64, 0.33, 1.0},
- .Green = {0.30, 0.60, 1.0},
- .Blue = {0.15, 0.06, 1.0},
+ // We always generate the 3DLUT against BT.2020, and transform into this
+ // space inside the shader if the source differs.
+ static const cmsCIExyY d65 = {0.3127, 0.3290, 1.0};
+ static const cmsCIExyYTRIPLE bt2020prim = {
+ .Red = {0.708, 0.292, 1.0},
+ .Green = {0.170, 0.797, 1.0},
+ .Blue = {0.131, 0.046, 1.0},
};
// 2.4 is arbitrarily used as a gamma compression factor for the 3DLUT,
// reducing artifacts due to rounding errors on wide gamut profiles
cmsToneCurve *tonecurve = cmsBuildGamma(cms, 2.4);
- cmsHPROFILE vid_profile = cmsCreateRGBProfileTHR(cms, &d65, &bt709prim,
+ cmsHPROFILE vid_profile = cmsCreateRGBProfileTHR(cms, &d65, &bt2020prim,
(cmsToneCurve*[3]){tonecurve, tonecurve, tonecurve});
cmsFreeToneCurve(tonecurve);
cmsHTRANSFORM trafo = cmsCreateTransformTHR(cms, vid_profile, TYPE_RGB_16,
diff --git a/video/out/gl_video.c b/video/out/gl_video.c
index 9ba5c54284..e50cef3686 100644
--- a/video/out/gl_video.c
+++ b/video/out/gl_video.c
@@ -639,6 +639,13 @@ static void update_uniforms(struct gl_video *p, GLuint program)
gl->Uniform1i(gl->GetUniformLocation(program, "lut_3d"), TEXUNIT_3DLUT);
+ loc = gl->GetUniformLocation(program, "cms_matrix");
+ if (loc >= 0) {
+ float cms_matrix[3][3] = {{0}};
+ mp_get_cms_matrix(p->image_params.primaries, cms_matrix);
+ gl->UniformMatrix3fv(loc, 1, GL_TRUE, &cms_matrix[0][0]);
+ }
+
for (int n = 0; n < 2; n++) {
const char *lut = p->scalers[n].lut_name;
if (lut)
@@ -840,6 +847,9 @@ static void compile_shaders(struct gl_video *p)
char *header = talloc_asprintf(tmp, "#version %d\n%s%s", gl->glsl_version,
shader_prelude, PRELUDE_END);
+ bool use_cms = p->opts.srgb || p->use_lut_3d;
+ bool use_cms_matrix = use_cms && (p->image_params.primaries != MP_CSP_PRIM_BT_2020);
+
if (p->gl_target == GL_TEXTURE_RECTANGLE) {
shader_def(&header, "VIDEO_SAMPLER", "sampler2DRect");
shader_def_opt(&header, "USE_RECTANGLE", true);
@@ -852,8 +862,8 @@ static void compile_shaders(struct gl_video *p)
shader_def_opt(&header, "USE_ALPHA", p->has_alpha);
char *header_osd = talloc_strdup(tmp, header);
- shader_def_opt(&header_osd, "USE_OSD_LINEAR_CONV", p->opts.srgb ||
- p->use_lut_3d);
+ shader_def_opt(&header_osd, "USE_OSD_LINEAR_CONV", use_cms);
+ shader_def_opt(&header_osd, "USE_OSD_CMS_MATRIX", use_cms_matrix);
shader_def_opt(&header_osd, "USE_OSD_3DLUT", p->use_lut_3d);
// 3DLUT overrides SRGB
shader_def_opt(&header_osd, "USE_OSD_SRGB", !p->use_lut_3d && p->opts.srgb);
@@ -888,7 +898,7 @@ static void compile_shaders(struct gl_video *p)
// Linear light scaling is only enabled when either color correction
// option (3dlut or srgb) is enabled, otherwise scaling is done in the
// source space.
- bool convert_to_linear_gamma = !p->is_linear_rgb && (p->opts.srgb || p->use_lut_3d);
+ bool convert_to_linear_gamma = !p->is_linear_rgb && use_cms;
if (p->image_format == IMGFMT_NV12 || p->image_format == IMGFMT_NV21) {
shader_def(&header_conv, "USE_CONV", "CONV_NV12");
@@ -912,6 +922,7 @@ static void compile_shaders(struct gl_video *p)
shader_def(&header_conv, "USE_ALPHA_BLEND", "1");
shader_def_opt(&header_final, "USE_GAMMA_POW", p->opts.gamma > 0);
+ shader_def_opt(&header_final, "USE_CMS_MATRIX", use_cms_matrix);
shader_def_opt(&header_final, "USE_3DLUT", p->use_lut_3d);
// 3DLUT overrides SRGB
shader_def_opt(&header_final, "USE_SRGB", p->opts.srgb && !p->use_lut_3d);
diff --git a/video/out/gl_video_shaders.glsl b/video/out/gl_video_shaders.glsl
index b0058f8ca6..a6ecc2eeeb 100644
--- a/video/out/gl_video_shaders.glsl
+++ b/video/out/gl_video_shaders.glsl
@@ -56,6 +56,13 @@ vec3 bt2020_expand(vec3 v)
}
#endif
+// Constant matrix for conversion from BT.2020 to sRGB
+const mat3 srgb_matrix = mat3(
+ 1.6604910, -0.1245505, -0.0181508,
+ -0.5876411, 1.1328999, -0.1005789,
+ -0.0728499, -0.0083494, 1.1187297
+);
+
#!section vertex_all
#if __VERSION__ < 130
@@ -66,6 +73,7 @@ vec3 bt2020_expand(vec3 v)
uniform mat3 transform;
uniform sampler3D lut_3d;
+uniform mat3 cms_matrix; // transformation from file's gamut to bt.2020
in vec2 vertex_position;
in vec4 vertex_color;
@@ -89,12 +97,17 @@ void main() {
// NOTE: This always applies the true BT2020, maybe we need to use
// approx-gamma here too?
#endif
+#ifdef USE_OSD_CMS_MATRIX
+ // Convert to the right target gamut first (to BT.709 for sRGB,
+ // and to BT.2020 for 3DLUT).
+ color.rgb = clamp(cms_matrix * color.rgb, 0, 1);
+#endif
#ifdef USE_OSD_3DLUT
color.rgb = pow(color.rgb, vec3(1/2.4)); // linear -> 2.4 3DLUT space
color = vec4(texture3D(lut_3d, color.rgb).rgb, color.a);
#endif
#ifdef USE_OSD_SRGB
- color.rgb = srgb_compand(color.rgb);
+ color.rgb = srgb_compand(clamp(srgb_matrix * color.rgb, 0, 1));
#endif
texcoord = vertex_texcoord;
@@ -136,6 +149,7 @@ uniform sampler2D lut_l_2d;
uniform sampler3D lut_3d;
uniform sampler2D dither;
uniform mat4x3 colormatrix;
+uniform mat3 cms_matrix;
uniform mat2 dither_trafo;
uniform vec3 inv_gamma;
uniform float input_gamma;
@@ -406,16 +420,28 @@ void main() {
// User-defined gamma correction factor (via the gamma sub-option)
color = pow(color, inv_gamma);
#endif
+#ifdef USE_CMS_MATRIX
+ // Convert to the right target gamut first (to BT.709 for sRGB,
+ // and to BT.2020 for 3DLUT).
+ color = cms_matrix * color;
+#endif
#ifdef USE_3DLUT
// For the 3DLUT we are arbitrarily using 2.4 as input gamma to reduce
// the amount of rounding errors, so we pull up to that space first and
// then pass it through the 3D texture.
- color = pow(color, vec3(1/2.4));
+ //
+ // The value is clamped to [0,1] first because the gamma function is not
+ // well-defined outside it. This should not be a problem because the 3dlut
+ // is not defined for values outside its boundaries either way, and no
+ // media can possibly exceed its BT.2020 source gamut either way due to
+ // that being the biggest taggable color space. This is just to avoid
+ // numerical quirks like -1e-30 turning into NaN.
+ color = pow(clamp(color, 0, 1), vec3(1/2.4));
color = texture3D(lut_3d, color).rgb;
#endif
#ifdef USE_SRGB
- // Compand from the linear scaling gamma to the sRGB output gamma
- color = srgb_compand(color.rgb);
+ // Adapt and compand from the linear BT2020 source to the sRGB output
+ color = srgb_compand(clamp(srgb_matrix * color, 0, 1));
#endif
#ifdef USE_DITHER
vec2 dither_pos = gl_FragCoord.xy / dither_size;