mirror of https://github.com/mpv-player/mpv
csputils: replace more primitives with pl_
We can go deeper, but need to stop somewhere to not reimplement vo_gpu using libplacebo...
This commit is contained in:
parent
47be5ad4aa
commit
475f76dc6d
|
@ -94,7 +94,7 @@ static void add_dvd_streams(demuxer_t *demuxer)
|
||||||
|
|
||||||
// emulate the extradata
|
// emulate the extradata
|
||||||
struct mp_csp_params csp = MP_CSP_PARAMS_DEFAULTS;
|
struct mp_csp_params csp = MP_CSP_PARAMS_DEFAULTS;
|
||||||
struct mp_cmat cmatrix;
|
struct pl_transform3x3 cmatrix;
|
||||||
mp_get_csp_matrix(&csp, &cmatrix);
|
mp_get_csp_matrix(&csp, &cmatrix);
|
||||||
|
|
||||||
char *s = talloc_strdup(sh, "");
|
char *s = talloc_strdup(sh, "");
|
||||||
|
|
|
@ -967,14 +967,14 @@ static void mangle_colors(struct sd *sd, struct sub_bitmaps *parts)
|
||||||
struct mp_csp_params vs_params = MP_CSP_PARAMS_DEFAULTS;
|
struct mp_csp_params vs_params = MP_CSP_PARAMS_DEFAULTS;
|
||||||
vs_params.repr.sys = csp;
|
vs_params.repr.sys = csp;
|
||||||
vs_params.repr.levels = levels;
|
vs_params.repr.levels = levels;
|
||||||
struct mp_cmat vs_yuv2rgb, vs_rgb2yuv;
|
struct pl_transform3x3 vs_yuv2rgb;
|
||||||
mp_get_csp_matrix(&vs_params, &vs_yuv2rgb);
|
mp_get_csp_matrix(&vs_params, &vs_yuv2rgb);
|
||||||
mp_invert_cmat(&vs_rgb2yuv, &vs_yuv2rgb);
|
pl_transform3x3_invert(&vs_yuv2rgb);
|
||||||
|
|
||||||
// Proper conversion to RGB
|
// Proper conversion to RGB
|
||||||
struct mp_csp_params rgb_params = MP_CSP_PARAMS_DEFAULTS;
|
struct mp_csp_params rgb_params = MP_CSP_PARAMS_DEFAULTS;
|
||||||
rgb_params.color = params.color;
|
rgb_params.color = params.color;
|
||||||
struct mp_cmat vs2rgb;
|
struct pl_transform3x3 vs2rgb;
|
||||||
mp_get_csp_matrix(&rgb_params, &vs2rgb);
|
mp_get_csp_matrix(&rgb_params, &vs2rgb);
|
||||||
|
|
||||||
for (int n = 0; n < parts->num_parts; n++) {
|
for (int n = 0; n < parts->num_parts; n++) {
|
||||||
|
@ -985,7 +985,7 @@ static void mangle_colors(struct sd *sd, struct sub_bitmaps *parts)
|
||||||
int b = (color >> 8u) & 0xff;
|
int b = (color >> 8u) & 0xff;
|
||||||
int a = 0xff - (color & 0xff);
|
int a = 0xff - (color & 0xff);
|
||||||
int rgb[3] = {r, g, b}, yuv[3];
|
int rgb[3] = {r, g, b}, yuv[3];
|
||||||
mp_map_fixp_color(&vs_rgb2yuv, 8, rgb, 8, yuv);
|
mp_map_fixp_color(&vs_yuv2rgb, 8, rgb, 8, yuv);
|
||||||
mp_map_fixp_color(&vs2rgb, 8, yuv, 8, rgb);
|
mp_map_fixp_color(&vs2rgb, 8, yuv, 8, rgb);
|
||||||
sb->libass.color = MP_ASS_RGBA(rgb[0], rgb[1], rgb[2], a);
|
sb->libass.color = MP_ASS_RGBA(rgb[0], rgb[1], rgb[2], a);
|
||||||
}
|
}
|
||||||
|
|
430
video/csputils.c
430
video/csputils.c
|
@ -3,8 +3,6 @@
|
||||||
*
|
*
|
||||||
* Copyleft (C) 2009 Reimar Döffinger <Reimar.Doeffinger@gmx.de>
|
* Copyleft (C) 2009 Reimar Döffinger <Reimar.Doeffinger@gmx.de>
|
||||||
*
|
*
|
||||||
* mp_invert_cmat based on DarkPlaces engine (relicensed from GPL to LGPL)
|
|
||||||
*
|
|
||||||
* This file is part of mpv.
|
* This file is part of mpv.
|
||||||
*
|
*
|
||||||
* mpv is free software; you can redistribute it and/or
|
* mpv is free software; you can redistribute it and/or
|
||||||
|
@ -170,260 +168,17 @@ enum pl_color_primaries mp_csp_guess_primaries(int width, int height)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void mp_invert_matrix3x3(float m[3][3])
|
// LMS<-XYZ revised matrix from CIECAM97, based on a linear transform and
|
||||||
{
|
// normalized for equal energy on monochrome inputs
|
||||||
float m00 = m[0][0], m01 = m[0][1], m02 = m[0][2],
|
static const pl_matrix3x3 m_cat97 = {{
|
||||||
m10 = m[1][0], m11 = m[1][1], m12 = m[1][2],
|
{ 0.8562, 0.3372, -0.1934 },
|
||||||
m20 = m[2][0], m21 = m[2][1], m22 = m[2][2];
|
{ -0.8360, 1.8327, 0.0033 },
|
||||||
|
{ 0.0357, -0.0469, 1.0112 },
|
||||||
// calculate the adjoint
|
}};
|
||||||
m[0][0] = (m11 * m22 - m21 * m12);
|
|
||||||
m[0][1] = -(m01 * m22 - m21 * m02);
|
|
||||||
m[0][2] = (m01 * m12 - m11 * m02);
|
|
||||||
m[1][0] = -(m10 * m22 - m20 * m12);
|
|
||||||
m[1][1] = (m00 * m22 - m20 * m02);
|
|
||||||
m[1][2] = -(m00 * m12 - m10 * m02);
|
|
||||||
m[2][0] = (m10 * m21 - m20 * m11);
|
|
||||||
m[2][1] = -(m00 * m21 - m20 * m01);
|
|
||||||
m[2][2] = (m00 * m11 - m10 * m01);
|
|
||||||
|
|
||||||
// calculate the determinant (as inverse == 1/det * adjoint,
|
|
||||||
// adjoint * m == identity * det, so this calculates the det)
|
|
||||||
float det = m00 * m[0][0] + m10 * m[0][1] + m20 * m[0][2];
|
|
||||||
det = 1.0f / det;
|
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++) {
|
|
||||||
for (int j = 0; j < 3; j++)
|
|
||||||
m[i][j] *= det;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A := A * B
|
|
||||||
static void mp_mul_matrix3x3(float a[3][3], float b[3][3])
|
|
||||||
{
|
|
||||||
float a00 = a[0][0], a01 = a[0][1], a02 = a[0][2],
|
|
||||||
a10 = a[1][0], a11 = a[1][1], a12 = a[1][2],
|
|
||||||
a20 = a[2][0], a21 = a[2][1], a22 = a[2][2];
|
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++) {
|
|
||||||
a[0][i] = a00 * b[0][i] + a01 * b[1][i] + a02 * b[2][i];
|
|
||||||
a[1][i] = a10 * b[0][i] + a11 * b[1][i] + a12 * b[2][i];
|
|
||||||
a[2][i] = a20 * b[0][i] + a21 * b[1][i] + a22 * b[2][i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// return the primaries associated with a certain mp_csp_primaries val
|
|
||||||
struct mp_csp_primaries mp_get_csp_primaries(enum pl_color_primaries spc)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
Values from: ITU-R Recommendations BT.470-6, BT.601-7, BT.709-5, BT.2020-0
|
|
||||||
|
|
||||||
https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.470-6-199811-S!!PDF-E.pdf
|
|
||||||
https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.601-7-201103-I!!PDF-E.pdf
|
|
||||||
https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.709-5-200204-I!!PDF-E.pdf
|
|
||||||
https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.2020-0-201208-I!!PDF-E.pdf
|
|
||||||
|
|
||||||
Other colorspaces from https://en.wikipedia.org/wiki/RGB_color_space#Specifications
|
|
||||||
*/
|
|
||||||
|
|
||||||
// CIE standard illuminant series
|
|
||||||
static const struct mp_csp_col_xy
|
|
||||||
d50 = {0.34577, 0.35850},
|
|
||||||
d65 = {0.31271, 0.32902},
|
|
||||||
c = {0.31006, 0.31616},
|
|
||||||
dci = {0.31400, 0.35100},
|
|
||||||
e = {1.0/3.0, 1.0/3.0};
|
|
||||||
|
|
||||||
switch (spc) {
|
|
||||||
case PL_COLOR_PRIM_BT_470M:
|
|
||||||
return (struct mp_csp_primaries) {
|
|
||||||
.red = {0.670, 0.330},
|
|
||||||
.green = {0.210, 0.710},
|
|
||||||
.blue = {0.140, 0.080},
|
|
||||||
.white = c
|
|
||||||
};
|
|
||||||
case PL_COLOR_PRIM_BT_601_525:
|
|
||||||
return (struct mp_csp_primaries) {
|
|
||||||
.red = {0.630, 0.340},
|
|
||||||
.green = {0.310, 0.595},
|
|
||||||
.blue = {0.155, 0.070},
|
|
||||||
.white = d65
|
|
||||||
};
|
|
||||||
case PL_COLOR_PRIM_BT_601_625:
|
|
||||||
return (struct mp_csp_primaries) {
|
|
||||||
.red = {0.640, 0.330},
|
|
||||||
.green = {0.290, 0.600},
|
|
||||||
.blue = {0.150, 0.060},
|
|
||||||
.white = d65
|
|
||||||
};
|
|
||||||
// This is the default assumption if no colorspace information could
|
|
||||||
// be determined, eg. for files which have no video channel.
|
|
||||||
case PL_COLOR_PRIM_UNKNOWN:
|
|
||||||
case PL_COLOR_PRIM_BT_709:
|
|
||||||
return (struct mp_csp_primaries) {
|
|
||||||
.red = {0.640, 0.330},
|
|
||||||
.green = {0.300, 0.600},
|
|
||||||
.blue = {0.150, 0.060},
|
|
||||||
.white = d65
|
|
||||||
};
|
|
||||||
case PL_COLOR_PRIM_BT_2020:
|
|
||||||
return (struct mp_csp_primaries) {
|
|
||||||
.red = {0.708, 0.292},
|
|
||||||
.green = {0.170, 0.797},
|
|
||||||
.blue = {0.131, 0.046},
|
|
||||||
.white = d65
|
|
||||||
};
|
|
||||||
case PL_COLOR_PRIM_APPLE:
|
|
||||||
return (struct mp_csp_primaries) {
|
|
||||||
.red = {0.625, 0.340},
|
|
||||||
.green = {0.280, 0.595},
|
|
||||||
.blue = {0.115, 0.070},
|
|
||||||
.white = d65
|
|
||||||
};
|
|
||||||
case PL_COLOR_PRIM_ADOBE:
|
|
||||||
return (struct mp_csp_primaries) {
|
|
||||||
.red = {0.640, 0.330},
|
|
||||||
.green = {0.210, 0.710},
|
|
||||||
.blue = {0.150, 0.060},
|
|
||||||
.white = d65
|
|
||||||
};
|
|
||||||
case PL_COLOR_PRIM_PRO_PHOTO:
|
|
||||||
return (struct mp_csp_primaries) {
|
|
||||||
.red = {0.7347, 0.2653},
|
|
||||||
.green = {0.1596, 0.8404},
|
|
||||||
.blue = {0.0366, 0.0001},
|
|
||||||
.white = d50
|
|
||||||
};
|
|
||||||
case PL_COLOR_PRIM_CIE_1931:
|
|
||||||
return (struct mp_csp_primaries) {
|
|
||||||
.red = {0.7347, 0.2653},
|
|
||||||
.green = {0.2738, 0.7174},
|
|
||||||
.blue = {0.1666, 0.0089},
|
|
||||||
.white = e
|
|
||||||
};
|
|
||||||
// From SMPTE RP 431-2 and 432-1
|
|
||||||
case PL_COLOR_PRIM_DCI_P3:
|
|
||||||
case PL_COLOR_PRIM_DISPLAY_P3:
|
|
||||||
return (struct mp_csp_primaries) {
|
|
||||||
.red = {0.680, 0.320},
|
|
||||||
.green = {0.265, 0.690},
|
|
||||||
.blue = {0.150, 0.060},
|
|
||||||
.white = spc == PL_COLOR_PRIM_DCI_P3 ? dci : d65
|
|
||||||
};
|
|
||||||
// From Panasonic VARICAM reference manual
|
|
||||||
case PL_COLOR_PRIM_V_GAMUT:
|
|
||||||
return (struct mp_csp_primaries) {
|
|
||||||
.red = {0.730, 0.280},
|
|
||||||
.green = {0.165, 0.840},
|
|
||||||
.blue = {0.100, -0.03},
|
|
||||||
.white = d65
|
|
||||||
};
|
|
||||||
// From Sony S-Log reference manual
|
|
||||||
case PL_COLOR_PRIM_S_GAMUT:
|
|
||||||
return (struct mp_csp_primaries) {
|
|
||||||
.red = {0.730, 0.280},
|
|
||||||
.green = {0.140, 0.855},
|
|
||||||
.blue = {0.100, -0.05},
|
|
||||||
.white = d65
|
|
||||||
};
|
|
||||||
// from EBU Tech. 3213-E
|
|
||||||
case PL_COLOR_PRIM_EBU_3213:
|
|
||||||
return (struct mp_csp_primaries) {
|
|
||||||
.red = {0.630, 0.340},
|
|
||||||
.green = {0.295, 0.605},
|
|
||||||
.blue = {0.155, 0.077},
|
|
||||||
.white = d65
|
|
||||||
};
|
|
||||||
// From H.273, traditional film with Illuminant C
|
|
||||||
case PL_COLOR_PRIM_FILM_C:
|
|
||||||
return (struct mp_csp_primaries) {
|
|
||||||
.red = {0.681, 0.319},
|
|
||||||
.green = {0.243, 0.692},
|
|
||||||
.blue = {0.145, 0.049},
|
|
||||||
.white = c
|
|
||||||
};
|
|
||||||
// From libplacebo source code
|
|
||||||
case PL_COLOR_PRIM_ACES_AP0:
|
|
||||||
return (struct mp_csp_primaries) {
|
|
||||||
.red = {0.7347, 0.2653},
|
|
||||||
.green = {0.0000, 1.0000},
|
|
||||||
.blue = {0.0001, -0.0770},
|
|
||||||
.white = {0.32168, 0.33767},
|
|
||||||
};
|
|
||||||
// From libplacebo source code
|
|
||||||
case PL_COLOR_PRIM_ACES_AP1:
|
|
||||||
return (struct mp_csp_primaries) {
|
|
||||||
.red = {0.713, 0.293},
|
|
||||||
.green = {0.165, 0.830},
|
|
||||||
.blue = {0.128, 0.044},
|
|
||||||
.white = {0.32168, 0.33767},
|
|
||||||
};
|
|
||||||
default:
|
|
||||||
return (struct mp_csp_primaries) {{0}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the nominal peak for a given colorspace, relative to the reference white
|
|
||||||
// level. In other words, this returns the brightest encodable value that can
|
|
||||||
// be represented by a given transfer curve.
|
|
||||||
float mp_trc_nom_peak(enum pl_color_transfer trc)
|
|
||||||
{
|
|
||||||
switch (trc) {
|
|
||||||
case PL_COLOR_TRC_PQ: return 10000.0 / MP_REF_WHITE;
|
|
||||||
case PL_COLOR_TRC_HLG: return 12.0 / MP_REF_WHITE_HLG;
|
|
||||||
case PL_COLOR_TRC_V_LOG: return 46.0855;
|
|
||||||
case PL_COLOR_TRC_S_LOG1: return 6.52;
|
|
||||||
case PL_COLOR_TRC_S_LOG2: return 9.212;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool mp_trc_is_hdr(enum pl_color_transfer trc)
|
|
||||||
{
|
|
||||||
return mp_trc_nom_peak(trc) > 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute the RGB/XYZ matrix as described here:
|
|
||||||
// http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
|
|
||||||
void mp_get_rgb2xyz_matrix(struct mp_csp_primaries space, float m[3][3])
|
|
||||||
{
|
|
||||||
float S[3], X[4], Z[4];
|
|
||||||
|
|
||||||
// Convert from CIE xyY to XYZ. Note that Y=1 holds true for all primaries
|
|
||||||
X[0] = space.red.x / space.red.y;
|
|
||||||
X[1] = space.green.x / space.green.y;
|
|
||||||
X[2] = space.blue.x / space.blue.y;
|
|
||||||
X[3] = space.white.x / space.white.y;
|
|
||||||
|
|
||||||
Z[0] = (1 - space.red.x - space.red.y) / space.red.y;
|
|
||||||
Z[1] = (1 - space.green.x - space.green.y) / space.green.y;
|
|
||||||
Z[2] = (1 - space.blue.x - space.blue.y) / space.blue.y;
|
|
||||||
Z[3] = (1 - space.white.x - space.white.y) / space.white.y;
|
|
||||||
|
|
||||||
// S = XYZ^-1 * W
|
|
||||||
for (int i = 0; i < 3; i++) {
|
|
||||||
m[0][i] = X[i];
|
|
||||||
m[1][i] = 1;
|
|
||||||
m[2][i] = Z[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
mp_invert_matrix3x3(m);
|
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++)
|
|
||||||
S[i] = m[i][0] * X[3] + m[i][1] * 1 + m[i][2] * Z[3];
|
|
||||||
|
|
||||||
// M = [Sc * XYZc]
|
|
||||||
for (int i = 0; i < 3; i++) {
|
|
||||||
m[0][i] = S[i] * X[i];
|
|
||||||
m[1][i] = S[i] * 1;
|
|
||||||
m[2][i] = S[i] * Z[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// M := M * XYZd<-XYZs
|
// M := M * XYZd<-XYZs
|
||||||
static void mp_apply_chromatic_adaptation(struct mp_csp_col_xy src,
|
static void apply_chromatic_adaptation(struct pl_cie_xy src,
|
||||||
struct mp_csp_col_xy dest, float m[3][3])
|
struct pl_cie_xy dest, pl_matrix3x3 *mat)
|
||||||
{
|
{
|
||||||
// If the white points are nearly identical, this is a wasteful identity
|
// If the white points are nearly identical, this is a wasteful identity
|
||||||
// operation.
|
// operation.
|
||||||
|
@ -432,98 +187,33 @@ static void mp_apply_chromatic_adaptation(struct mp_csp_col_xy src,
|
||||||
|
|
||||||
// XYZd<-XYZs = Ma^-1 * (I*[Cd/Cs]) * Ma
|
// XYZd<-XYZs = Ma^-1 * (I*[Cd/Cs]) * Ma
|
||||||
// http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html
|
// http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html
|
||||||
float C[3][2], tmp[3][3] = {{0}};
|
// For Ma, we use the CIECAM97 revised (linear) matrix
|
||||||
|
float C[3][2];
|
||||||
// Ma = Bradford matrix, arguably most popular method in use today.
|
|
||||||
// This is derived experimentally and thus hard-coded.
|
|
||||||
float bradford[3][3] = {
|
|
||||||
{ 0.8951, 0.2664, -0.1614 },
|
|
||||||
{ -0.7502, 1.7135, 0.0367 },
|
|
||||||
{ 0.0389, -0.0685, 1.0296 },
|
|
||||||
};
|
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
// source cone
|
// source cone
|
||||||
C[i][0] = bradford[i][0] * mp_xy_X(src)
|
C[i][0] = m_cat97.m[i][0] * pl_cie_X(src)
|
||||||
+ bradford[i][1] * 1
|
+ m_cat97.m[i][1] * 1
|
||||||
+ bradford[i][2] * mp_xy_Z(src);
|
+ m_cat97.m[i][2] * pl_cie_Z(src);
|
||||||
|
|
||||||
// dest cone
|
// dest cone
|
||||||
C[i][1] = bradford[i][0] * mp_xy_X(dest)
|
C[i][1] = m_cat97.m[i][0] * pl_cie_X(dest)
|
||||||
+ bradford[i][1] * 1
|
+ m_cat97.m[i][1] * 1
|
||||||
+ bradford[i][2] * mp_xy_Z(dest);
|
+ m_cat97.m[i][2] * pl_cie_Z(dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
// tmp := I * [Cd/Cs] * Ma
|
// tmp := I * [Cd/Cs] * Ma
|
||||||
|
pl_matrix3x3 tmp = {0};
|
||||||
for (int i = 0; i < 3; i++)
|
for (int i = 0; i < 3; i++)
|
||||||
tmp[i][i] = C[i][1] / C[i][0];
|
tmp.m[i][i] = C[i][1] / C[i][0];
|
||||||
|
|
||||||
mp_mul_matrix3x3(tmp, bradford);
|
pl_matrix3x3_mul(&tmp, &m_cat97);
|
||||||
|
|
||||||
// M := M * Ma^-1 * tmp
|
// M := M * Ma^-1 * tmp
|
||||||
mp_invert_matrix3x3(bradford);
|
pl_matrix3x3 ma_inv = m_cat97;
|
||||||
mp_mul_matrix3x3(m, bradford);
|
pl_matrix3x3_invert(&ma_inv);
|
||||||
mp_mul_matrix3x3(m, tmp);
|
pl_matrix3x3_mul(mat, &ma_inv);
|
||||||
}
|
pl_matrix3x3_mul(mat, &tmp);
|
||||||
|
|
||||||
// get the coefficients of the source -> dest cms matrix
|
|
||||||
void mp_get_cms_matrix(struct mp_csp_primaries src, struct mp_csp_primaries dest,
|
|
||||||
enum mp_render_intent intent, float m[3][3])
|
|
||||||
{
|
|
||||||
float tmp[3][3];
|
|
||||||
|
|
||||||
// In saturation mapping, we don't care about accuracy and just want
|
|
||||||
// primaries to map to primaries, making this an identity transformation.
|
|
||||||
if (intent == MP_INTENT_SATURATION) {
|
|
||||||
for (int i = 0; i < 3; i++)
|
|
||||||
m[i][i] = 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// RGBd<-RGBs = RGBd<-XYZd * XYZd<-XYZs * XYZs<-RGBs
|
|
||||||
// Equations from: http://www.brucelindbloom.com/index.html?Math.html
|
|
||||||
// Note: Perceptual is treated like relative colorimetric. There's no
|
|
||||||
// definition for perceptual other than "make it look good".
|
|
||||||
|
|
||||||
// RGBd<-XYZd, inverted from XYZd<-RGBd
|
|
||||||
mp_get_rgb2xyz_matrix(dest, m);
|
|
||||||
mp_invert_matrix3x3(m);
|
|
||||||
|
|
||||||
// Chromatic adaptation, except in absolute colorimetric intent
|
|
||||||
if (intent != MP_INTENT_ABSOLUTE_COLORIMETRIC)
|
|
||||||
mp_apply_chromatic_adaptation(src.white, dest.white, m);
|
|
||||||
|
|
||||||
// XYZs<-RGBs
|
|
||||||
mp_get_rgb2xyz_matrix(src, tmp);
|
|
||||||
mp_mul_matrix3x3(m, tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the coefficients of an ST 428-1 xyz -> rgb conversion matrix
|
|
||||||
// intent = the rendering intent used to convert to the target primaries
|
|
||||||
static void mp_get_xyz2rgb_coeffs(struct mp_csp_params *params,
|
|
||||||
enum mp_render_intent intent, struct mp_cmat *m)
|
|
||||||
{
|
|
||||||
// Convert to DCI-P3
|
|
||||||
struct mp_csp_primaries prim = mp_get_csp_primaries(PL_COLOR_PRIM_DCI_P3);
|
|
||||||
float brightness = params->brightness;
|
|
||||||
mp_get_rgb2xyz_matrix(prim, m->m);
|
|
||||||
mp_invert_matrix3x3(m->m);
|
|
||||||
|
|
||||||
// All non-absolute mappings want to map source white to target white
|
|
||||||
if (intent != MP_INTENT_ABSOLUTE_COLORIMETRIC) {
|
|
||||||
// SMPTE EG 432-1 Annex H defines the white point as equal energy
|
|
||||||
static const struct mp_csp_col_xy smpte432 = {1.0/3.0, 1.0/3.0};
|
|
||||||
mp_apply_chromatic_adaptation(smpte432, prim.white, m->m);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Since this outputs linear RGB rather than companded RGB, we
|
|
||||||
// want to linearize any brightness additions. 2 is a reasonable
|
|
||||||
// approximation for any sort of gamma function that could be in use.
|
|
||||||
// As this is an aesthetic setting only, any exact values do not matter.
|
|
||||||
brightness *= fabs(brightness);
|
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++)
|
|
||||||
m->c[i] = brightness;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get multiplication factor required if image data is fit within the LSBs of a
|
// Get multiplication factor required if image data is fit within the LSBs of a
|
||||||
|
@ -608,19 +298,19 @@ void mp_get_csp_uint_mul(enum pl_color_system csp, enum pl_color_levels levels,
|
||||||
* Under these conditions the given parameters lr, lg, lb uniquely
|
* Under these conditions the given parameters lr, lg, lb uniquely
|
||||||
* determine the mapping of Y, U, V to R, G, B.
|
* determine the mapping of Y, U, V to R, G, B.
|
||||||
*/
|
*/
|
||||||
static void luma_coeffs(struct mp_cmat *mat, float lr, float lg, float lb)
|
static void luma_coeffs(struct pl_transform3x3 *mat, float lr, float lg, float lb)
|
||||||
{
|
{
|
||||||
assert(fabs(lr+lg+lb - 1) < 1e-6);
|
assert(fabs(lr+lg+lb - 1) < 1e-6);
|
||||||
*mat = (struct mp_cmat) {
|
*mat = (struct pl_transform3x3) {
|
||||||
{ {1, 0, 2 * (1-lr) },
|
{ {{1, 0, 2 * (1-lr) },
|
||||||
{1, -2 * (1-lb) * lb/lg, -2 * (1-lr) * lr/lg },
|
{1, -2 * (1-lb) * lb/lg, -2 * (1-lr) * lr/lg },
|
||||||
{1, 2 * (1-lb), 0 } },
|
{1, 2 * (1-lb), 0 }} },
|
||||||
// Constant coefficients (mat->c) not set here
|
// Constant coefficients (mat->c) not set here
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the coefficients of the yuv -> rgb conversion matrix
|
// get the coefficients of the yuv -> rgb conversion matrix
|
||||||
void mp_get_csp_matrix(struct mp_csp_params *params, struct mp_cmat *m)
|
void mp_get_csp_matrix(struct mp_csp_params *params, struct pl_transform3x3 *m)
|
||||||
{
|
{
|
||||||
enum pl_color_system colorspace = params->repr.sys;
|
enum pl_color_system colorspace = params->repr.sys;
|
||||||
if (colorspace <= PL_COLOR_SYSTEM_UNKNOWN || colorspace >= PL_COLOR_SYSTEM_COUNT)
|
if (colorspace <= PL_COLOR_SYSTEM_UNKNOWN || colorspace >= PL_COLOR_SYSTEM_COUNT)
|
||||||
|
@ -639,29 +329,30 @@ void mp_get_csp_matrix(struct mp_csp_params *params, struct mp_cmat *m)
|
||||||
// If this clips on any VO, a constant 0.5 coefficient can be added
|
// If this clips on any VO, a constant 0.5 coefficient can be added
|
||||||
// to the chroma channels to normalize them into [0,1]. This is not
|
// to the chroma channels to normalize them into [0,1]. This is not
|
||||||
// currently needed by anything, though.
|
// currently needed by anything, though.
|
||||||
*m = (struct mp_cmat){{{0, 0, 1}, {1, 0, 0}, {0, 1, 0}}};
|
*m = (struct pl_transform3x3){{{{0, 0, 1}, {1, 0, 0}, {0, 1, 0}}}};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PL_COLOR_SYSTEM_RGB: {
|
case PL_COLOR_SYSTEM_RGB: {
|
||||||
*m = (struct mp_cmat){{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}};
|
*m = (struct pl_transform3x3){{{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}}};
|
||||||
levels_in = -1;
|
levels_in = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PL_COLOR_SYSTEM_XYZ: {
|
case PL_COLOR_SYSTEM_XYZ: {
|
||||||
// The vo should probably not be using a matrix generated by this
|
// For lack of anything saner to do, just assume the caller wants
|
||||||
// function for XYZ sources, but if it does, let's just convert it to
|
// DCI-P3 primaries, which is a reasonable assumption.
|
||||||
// an equivalent RGB space based on the colorimetry metadata it
|
const struct pl_raw_primaries *dst = pl_raw_primaries_get(PL_COLOR_PRIM_DCI_P3);
|
||||||
// provided in mp_csp_params. (At the risk of clipping, if the
|
pl_matrix3x3 mat = pl_get_xyz2rgb_matrix(dst);
|
||||||
// chosen primaries are too small to fit the actual data)
|
// DCDM X'Y'Z' is expected to have equal energy white point (EG 432-1 Annex H)
|
||||||
mp_get_xyz2rgb_coeffs(params, MP_INTENT_RELATIVE_COLORIMETRIC, m);
|
apply_chromatic_adaptation((struct pl_cie_xy){1.0/3.0, 1.0/3.0}, dst->white, &mat);
|
||||||
|
*m = (struct pl_transform3x3) { .mat = mat };
|
||||||
levels_in = -1;
|
levels_in = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PL_COLOR_SYSTEM_YCGCO: {
|
case PL_COLOR_SYSTEM_YCGCO: {
|
||||||
*m = (struct mp_cmat) {
|
*m = (struct pl_transform3x3) {
|
||||||
{{1, -1, 1},
|
{{{1, -1, 1},
|
||||||
{1, 1, 0},
|
{1, 1, 0},
|
||||||
{1, -1, -1}},
|
{1, -1, -1}}},
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -680,9 +371,9 @@ void mp_get_csp_matrix(struct mp_csp_params *params, struct mp_cmat *m)
|
||||||
float huecos = params->gray ? 0 : params->saturation * cos(params->hue);
|
float huecos = params->gray ? 0 : params->saturation * cos(params->hue);
|
||||||
float huesin = params->gray ? 0 : params->saturation * sin(params->hue);
|
float huesin = params->gray ? 0 : params->saturation * sin(params->hue);
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
float u = m->m[i][1], v = m->m[i][2];
|
float u = m->mat.m[i][1], v = m->mat.m[i][2];
|
||||||
m->m[i][1] = huecos * u - huesin * v;
|
m->mat.m[i][1] = huecos * u - huesin * v;
|
||||||
m->m[i][2] = huesin * u + huecos * v;
|
m->mat.m[i][2] = huesin * u + huecos * v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -728,13 +419,13 @@ void mp_get_csp_matrix(struct mp_csp_params *params, struct mp_cmat *m)
|
||||||
cmul *= params->contrast;
|
cmul *= params->contrast;
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
m->m[i][0] *= ymul;
|
m->mat.m[i][0] *= ymul;
|
||||||
m->m[i][1] *= cmul;
|
m->mat.m[i][1] *= cmul;
|
||||||
m->m[i][2] *= cmul;
|
m->mat.m[i][2] *= cmul;
|
||||||
// Set c so that Y=umin,UV=cmid maps to RGB=min (black to black),
|
// Set c so that Y=umin,UV=cmid maps to RGB=min (black to black),
|
||||||
// also add brightness offset (black lift)
|
// also add brightness offset (black lift)
|
||||||
m->c[i] = rgblev.min - m->m[i][0] * yuvlev.ymin
|
m->c[i] = rgblev.min - m->mat.m[i][0] * yuvlev.ymin
|
||||||
- (m->m[i][1] + m->m[i][2]) * yuvlev.cmid
|
- (m->mat.m[i][1] + m->mat.m[i][2]) * yuvlev.cmid
|
||||||
+ params->brightness;
|
+ params->brightness;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -822,32 +513,17 @@ void mp_csp_equalizer_state_get(struct mp_csp_equalizer_state *state,
|
||||||
mp_csp_copy_equalizer_values(params, opts);
|
mp_csp_copy_equalizer_values(params, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mp_invert_cmat(struct mp_cmat *out, struct mp_cmat *in)
|
|
||||||
{
|
|
||||||
*out = *in;
|
|
||||||
mp_invert_matrix3x3(out->m);
|
|
||||||
|
|
||||||
// fix the constant coefficient
|
|
||||||
// rgb = M * yuv + C
|
|
||||||
// M^-1 * rgb = yuv + M^-1 * C
|
|
||||||
// yuv = M^-1 * rgb - M^-1 * C
|
|
||||||
// ^^^^^^^^^^
|
|
||||||
out->c[0] = -(out->m[0][0] * in->c[0] + out->m[0][1] * in->c[1] + out->m[0][2] * in->c[2]);
|
|
||||||
out->c[1] = -(out->m[1][0] * in->c[0] + out->m[1][1] * in->c[1] + out->m[1][2] * in->c[2]);
|
|
||||||
out->c[2] = -(out->m[2][0] * in->c[0] + out->m[2][1] * in->c[1] + out->m[2][2] * in->c[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Multiply the color in c with the given matrix.
|
// Multiply the color in c with the given matrix.
|
||||||
// i/o is {R, G, B} or {Y, U, V} (depending on input/output and matrix), using
|
// i/o is {R, G, B} or {Y, U, V} (depending on input/output and matrix), using
|
||||||
// a fixed point representation with the given number of bits (so for bits==8,
|
// a fixed point representation with the given number of bits (so for bits==8,
|
||||||
// [0,255] maps to [0,1]). The output is clipped to the range as needed.
|
// [0,255] maps to [0,1]). The output is clipped to the range as needed.
|
||||||
void mp_map_fixp_color(struct mp_cmat *matrix, int ibits, int in[3],
|
void mp_map_fixp_color(struct pl_transform3x3 *matrix, int ibits, int in[3],
|
||||||
int obits, int out[3])
|
int obits, int out[3])
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
double val = matrix->c[i];
|
double val = matrix->c[i];
|
||||||
for (int x = 0; x < 3; x++)
|
for (int x = 0; x < 3; x++)
|
||||||
val += matrix->m[i][x] * in[x] / ((1 << ibits) - 1);
|
val += matrix->mat.m[i][x] * in[x] / ((1 << ibits) - 1);
|
||||||
int ival = lrint(val * ((1 << obits) - 1));
|
int ival = lrint(val * ((1 << obits) - 1));
|
||||||
out[i] = av_clip(ival, 0, (1 << obits) - 1);
|
out[i] = av_clip(ival, 0, (1 << obits) - 1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,15 +46,6 @@ enum mp_csp_light {
|
||||||
|
|
||||||
extern const struct m_opt_choice_alternatives mp_csp_light_names[];
|
extern const struct m_opt_choice_alternatives mp_csp_light_names[];
|
||||||
|
|
||||||
// These constants are based on the ICC specification (Table 23) and match
|
|
||||||
// up with the API of LittleCMS, which treats them as integers.
|
|
||||||
enum mp_render_intent {
|
|
||||||
MP_INTENT_PERCEPTUAL = 0,
|
|
||||||
MP_INTENT_RELATIVE_COLORIMETRIC = 1,
|
|
||||||
MP_INTENT_SATURATION = 2,
|
|
||||||
MP_INTENT_ABSOLUTE_COLORIMETRIC = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
// The numeric values (except -1) match the Matroska StereoMode element value.
|
// The numeric values (except -1) match the Matroska StereoMode element value.
|
||||||
enum mp_stereo3d_mode {
|
enum mp_stereo3d_mode {
|
||||||
MP_STEREO3D_INVALID = -1,
|
MP_STEREO3D_INVALID = -1,
|
||||||
|
@ -123,59 +114,15 @@ bool mp_csp_equalizer_state_changed(struct mp_csp_equalizer_state *state);
|
||||||
void mp_csp_equalizer_state_get(struct mp_csp_equalizer_state *state,
|
void mp_csp_equalizer_state_get(struct mp_csp_equalizer_state *state,
|
||||||
struct mp_csp_params *params);
|
struct mp_csp_params *params);
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum pl_color_system mp_csp_guess_colorspace(int width, int height);
|
enum pl_color_system mp_csp_guess_colorspace(int width, int height);
|
||||||
enum pl_color_primaries mp_csp_guess_primaries(int width, int height);
|
enum pl_color_primaries mp_csp_guess_primaries(int width, int height);
|
||||||
|
|
||||||
struct mp_csp_primaries mp_get_csp_primaries(enum pl_color_primaries csp);
|
|
||||||
float mp_trc_nom_peak(enum pl_color_transfer trc);
|
|
||||||
bool mp_trc_is_hdr(enum pl_color_transfer trc);
|
|
||||||
|
|
||||||
/* Color conversion matrix: RGB = m * YUV + c
|
|
||||||
* m is in row-major matrix, with m[row][col], e.g.:
|
|
||||||
* [ a11 a12 a13 ] float m[3][3] = { { a11, a12, a13 },
|
|
||||||
* [ a21 a22 a23 ] { a21, a22, a23 },
|
|
||||||
* [ a31 a32 a33 ] { a31, a32, a33 } };
|
|
||||||
* This is accessed as e.g.: m[2-1][1-1] = a21
|
|
||||||
* In particular, each row contains all the coefficients for one of R, G, B,
|
|
||||||
* while each column contains all the coefficients for one of Y, U, V:
|
|
||||||
* m[r,g,b][y,u,v] = ...
|
|
||||||
* The matrix could also be viewed as group of 3 vectors, e.g. the 1st column
|
|
||||||
* is the Y vector (1, 1, 1), the 2nd is the U vector, the 3rd the V vector.
|
|
||||||
* The matrix might also be used for other conversions and colorspaces.
|
|
||||||
*/
|
|
||||||
struct mp_cmat {
|
|
||||||
float m[3][3];
|
|
||||||
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]);
|
|
||||||
|
|
||||||
double mp_get_csp_mul(enum pl_color_system csp, int input_bits, int texture_bits);
|
double mp_get_csp_mul(enum pl_color_system csp, int input_bits, int texture_bits);
|
||||||
void mp_get_csp_uint_mul(enum pl_color_system csp, enum pl_color_levels levels,
|
void mp_get_csp_uint_mul(enum pl_color_system csp, enum pl_color_levels levels,
|
||||||
int bits, int component, double *out_m, double *out_o);
|
int bits, int component, double *out_m, double *out_o);
|
||||||
void mp_get_csp_matrix(struct mp_csp_params *params, struct mp_cmat *out);
|
void mp_get_csp_matrix(struct mp_csp_params *params, struct pl_transform3x3 *out);
|
||||||
|
|
||||||
void mp_invert_matrix3x3(float m[3][3]);
|
void mp_map_fixp_color(struct pl_transform3x3 *matrix, int ibits, int in[3],
|
||||||
void mp_invert_cmat(struct mp_cmat *out, struct mp_cmat *in);
|
|
||||||
void mp_map_fixp_color(struct mp_cmat *matrix, int ibits, int in[3],
|
|
||||||
int obits, int out[3]);
|
int obits, int out[3]);
|
||||||
|
|
||||||
#endif /* MPLAYER_CSPUTILS_H */
|
#endif /* MPLAYER_CSPUTILS_H */
|
||||||
|
|
|
@ -961,11 +961,11 @@ void mp_image_params_guess_csp(struct mp_image_params *params)
|
||||||
} else {
|
} else {
|
||||||
// If the signal peak is unknown, we're forced to pick the TRC's
|
// If the signal peak is unknown, we're forced to pick the TRC's
|
||||||
// nominal range as the signal peak to prevent clipping
|
// nominal range as the signal peak to prevent clipping
|
||||||
params->color.hdr.max_luma = mp_trc_nom_peak(params->color.transfer) * MP_REF_WHITE;
|
params->color.hdr.max_luma = pl_color_transfer_nominal_peak(params->color.transfer) * MP_REF_WHITE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mp_trc_is_hdr(params->color.transfer)) {
|
if (!pl_color_space_is_hdr(¶ms->color)) {
|
||||||
// Some clips have leftover HDR metadata after conversion to SDR, so to
|
// Some clips have leftover HDR metadata after conversion to SDR, so to
|
||||||
// avoid blowing up the tone mapping code, strip/sanitize it
|
// avoid blowing up the tone mapping code, strip/sanitize it
|
||||||
params->color.hdr = pl_hdr_metadata_empty;
|
params->color.hdr = pl_hdr_metadata_empty;
|
||||||
|
|
|
@ -197,12 +197,12 @@ static cmsHPROFILE get_vid_profile(struct gl_lcms *p, cmsContext cms,
|
||||||
|
|
||||||
// The input profile for the transformation is dependent on the video
|
// The input profile for the transformation is dependent on the video
|
||||||
// primaries and transfer characteristics
|
// primaries and transfer characteristics
|
||||||
struct mp_csp_primaries csp = mp_get_csp_primaries(prim);
|
const struct pl_raw_primaries *csp = pl_raw_primaries_get(prim);
|
||||||
cmsCIExyY wp_xyY = {csp.white.x, csp.white.y, 1.0};
|
cmsCIExyY wp_xyY = {csp->white.x, csp->white.y, 1.0};
|
||||||
cmsCIExyYTRIPLE prim_xyY = {
|
cmsCIExyYTRIPLE prim_xyY = {
|
||||||
.Red = {csp.red.x, csp.red.y, 1.0},
|
.Red = {csp->red.x, csp->red.y, 1.0},
|
||||||
.Green = {csp.green.x, csp.green.y, 1.0},
|
.Green = {csp->green.x, csp->green.y, 1.0},
|
||||||
.Blue = {csp.blue.x, csp.blue.y, 1.0},
|
.Blue = {csp->blue.x, csp->blue.y, 1.0},
|
||||||
};
|
};
|
||||||
|
|
||||||
cmsToneCurve *tonecurve[3] = {0};
|
cmsToneCurve *tonecurve[3] = {0};
|
||||||
|
@ -242,7 +242,7 @@ static cmsHPROFILE get_vid_profile(struct gl_lcms *p, cmsContext cms,
|
||||||
// function. Relative colorimetric is used since we want to
|
// function. Relative colorimetric is used since we want to
|
||||||
// approximate the BT.1886 to the target device's actual black
|
// approximate the BT.1886 to the target device's actual black
|
||||||
// point even in e.g. perceptual mode
|
// point even in e.g. perceptual mode
|
||||||
const int intent = MP_INTENT_RELATIVE_COLORIMETRIC;
|
const int intent = PL_INTENT_RELATIVE_COLORIMETRIC;
|
||||||
cmsCIEXYZ bp_XYZ;
|
cmsCIEXYZ bp_XYZ;
|
||||||
if (!cmsDetectBlackPoint(&bp_XYZ, disp_profile, intent, 0))
|
if (!cmsDetectBlackPoint(&bp_XYZ, disp_profile, intent, 0))
|
||||||
return false;
|
return false;
|
||||||
|
@ -519,7 +519,7 @@ const struct m_sub_options mp_icc_conf = {
|
||||||
.size = sizeof(struct mp_icc_opts),
|
.size = sizeof(struct mp_icc_opts),
|
||||||
.defaults = &(const struct mp_icc_opts) {
|
.defaults = &(const struct mp_icc_opts) {
|
||||||
.size_str = "auto",
|
.size_str = "auto",
|
||||||
.intent = MP_INTENT_RELATIVE_COLORIMETRIC,
|
.intent = PL_INTENT_RELATIVE_COLORIMETRIC,
|
||||||
.use_embedded = true,
|
.use_embedded = true,
|
||||||
.cache = true,
|
.cache = true,
|
||||||
},
|
},
|
||||||
|
|
|
@ -2344,9 +2344,9 @@ static void pass_convert_yuv(struct gl_video *p)
|
||||||
|
|
||||||
// Conversion to RGB. For RGB itself, this still applies e.g. brightness
|
// Conversion to RGB. For RGB itself, this still applies e.g. brightness
|
||||||
// and contrast controls, or expansion of e.g. LSB-packed 10 bit data.
|
// and contrast controls, or expansion of e.g. LSB-packed 10 bit data.
|
||||||
struct mp_cmat m = {{{0}}};
|
struct pl_transform3x3 m = {0};
|
||||||
mp_get_csp_matrix(&cparams, &m);
|
mp_get_csp_matrix(&cparams, &m);
|
||||||
gl_sc_uniform_mat3(sc, "colormatrix", true, &m.m[0][0]);
|
gl_sc_uniform_mat3(sc, "colormatrix", true, &m.mat.m[0][0]);
|
||||||
gl_sc_uniform_vec3(sc, "colormatrix_c", m.c);
|
gl_sc_uniform_vec3(sc, "colormatrix_c", m.c);
|
||||||
|
|
||||||
GLSL(color.rgb = mat3(colormatrix) * color.rgb + colormatrix_c;)
|
GLSL(color.rgb = mat3(colormatrix) * color.rgb + colormatrix_c;)
|
||||||
|
@ -2482,7 +2482,7 @@ static void pass_scale_main(struct gl_video *p)
|
||||||
// Linear light downscaling results in nasty artifacts for HDR curves
|
// Linear light downscaling results in nasty artifacts for HDR curves
|
||||||
// due to the potentially extreme brightness differences severely
|
// due to the potentially extreme brightness differences severely
|
||||||
// compounding any ringing. So just scale in gamma light instead.
|
// compounding any ringing. So just scale in gamma light instead.
|
||||||
if (mp_trc_is_hdr(p->image_params.color.transfer))
|
if (pl_color_space_is_hdr(&p->image_params.color))
|
||||||
use_linear = false;
|
use_linear = false;
|
||||||
} else if (upscaling) {
|
} else if (upscaling) {
|
||||||
use_linear = p->opts.linear_upscaling || p->opts.sigmoid_upscaling;
|
use_linear = p->opts.linear_upscaling || p->opts.sigmoid_upscaling;
|
||||||
|
@ -2592,7 +2592,7 @@ static void pass_colormanage(struct gl_video *p, struct pl_color_space src,
|
||||||
// limitation reasons, so we use a gamma 2.2 input curve here instead.
|
// limitation reasons, so we use a gamma 2.2 input curve here instead.
|
||||||
// We could pick any value we want here, the difference is just coding
|
// We could pick any value we want here, the difference is just coding
|
||||||
// efficiency.
|
// efficiency.
|
||||||
if (mp_trc_is_hdr(trc_orig))
|
if (pl_color_space_is_hdr(&p->image_params.color))
|
||||||
trc_orig = PL_COLOR_TRC_GAMMA22;
|
trc_orig = PL_COLOR_TRC_GAMMA22;
|
||||||
|
|
||||||
if (gl_video_get_lut3d(p, prim_orig, trc_orig)) {
|
if (gl_video_get_lut3d(p, prim_orig, trc_orig)) {
|
||||||
|
@ -2629,7 +2629,7 @@ static void pass_colormanage(struct gl_video *p, struct pl_color_space src,
|
||||||
// Avoid outputting linear light or HDR content "by default". For these
|
// 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
|
// just pick gamma 2.2 as a default, since it's a good estimate for
|
||||||
// the response of typical displays
|
// the response of typical displays
|
||||||
if (dst.transfer == PL_COLOR_TRC_LINEAR || mp_trc_is_hdr(dst.transfer))
|
if (dst.transfer == PL_COLOR_TRC_LINEAR || pl_color_space_is_hdr(&dst))
|
||||||
dst.transfer = PL_COLOR_TRC_GAMMA22;
|
dst.transfer = PL_COLOR_TRC_GAMMA22;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2637,9 +2637,9 @@ static void pass_colormanage(struct gl_video *p, struct pl_color_space src,
|
||||||
// it from the chosen transfer function. Also normalize the src peak, in
|
// it from the chosen transfer function. Also normalize the src peak, in
|
||||||
// case it was unknown
|
// case it was unknown
|
||||||
if (!dst.hdr.max_luma)
|
if (!dst.hdr.max_luma)
|
||||||
dst.hdr.max_luma = mp_trc_nom_peak(dst.transfer) * MP_REF_WHITE;
|
dst.hdr.max_luma = pl_color_transfer_nominal_peak(dst.transfer) * MP_REF_WHITE;
|
||||||
if (!src.hdr.max_luma)
|
if (!src.hdr.max_luma)
|
||||||
src.hdr.max_luma = mp_trc_nom_peak(src.transfer) * MP_REF_WHITE;
|
src.hdr.max_luma = pl_color_transfer_nominal_peak(src.transfer) * MP_REF_WHITE;
|
||||||
|
|
||||||
// Whitelist supported modes
|
// Whitelist supported modes
|
||||||
switch (p->opts.tone_map.curve) {
|
switch (p->opts.tone_map.curve) {
|
||||||
|
@ -2671,7 +2671,7 @@ static void pass_colormanage(struct gl_video *p, struct pl_color_space src,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct gl_tone_map_opts tone_map = p->opts.tone_map;
|
struct gl_tone_map_opts tone_map = p->opts.tone_map;
|
||||||
bool detect_peak = tone_map.compute_peak >= 0 && mp_trc_is_hdr(src.transfer)
|
bool detect_peak = tone_map.compute_peak >= 0 && pl_color_space_is_hdr(&src)
|
||||||
&& src.hdr.max_luma > dst.hdr.max_luma;
|
&& src.hdr.max_luma > dst.hdr.max_luma;
|
||||||
|
|
||||||
if (detect_peak && !p->hdr_peak_ssbo) {
|
if (detect_peak && !p->hdr_peak_ssbo) {
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
|
#include <libplacebo/colorspace.h>
|
||||||
|
|
||||||
#include "video_shaders.h"
|
#include "video_shaders.h"
|
||||||
#include "video.h"
|
#include "video.h"
|
||||||
|
|
||||||
|
@ -337,7 +339,7 @@ static const float SLOG_A = 0.432699,
|
||||||
//
|
//
|
||||||
// These functions always output to a normalized scale of [0,1], for
|
// These functions always output to a normalized scale of [0,1], for
|
||||||
// convenience of the video.c code that calls it. To get the values in an
|
// convenience of the video.c code that calls it. To get the values in an
|
||||||
// absolute scale, multiply the result by `mp_trc_nom_peak(trc)`
|
// absolute scale, multiply the result by `pl_color_transfer_nominal_peak(trc)`
|
||||||
void pass_linearize(struct gl_shader_cache *sc, enum pl_color_transfer trc)
|
void pass_linearize(struct gl_shader_cache *sc, enum pl_color_transfer trc)
|
||||||
{
|
{
|
||||||
if (trc == PL_COLOR_TRC_LINEAR)
|
if (trc == PL_COLOR_TRC_LINEAR)
|
||||||
|
@ -430,7 +432,7 @@ void pass_linearize(struct gl_shader_cache *sc, enum pl_color_transfer trc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rescale to prevent clipping on non-float textures
|
// Rescale to prevent clipping on non-float textures
|
||||||
GLSLF("color.rgb *= vec3(1.0/%f);\n", mp_trc_nom_peak(trc));
|
GLSLF("color.rgb *= vec3(1.0/%f);\n", pl_color_transfer_nominal_peak(trc));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delinearize (compress), given a TRC as output. This corresponds to the
|
// Delinearize (compress), given a TRC as output. This corresponds to the
|
||||||
|
@ -445,7 +447,7 @@ void pass_delinearize(struct gl_shader_cache *sc, enum pl_color_transfer trc)
|
||||||
|
|
||||||
GLSLF("// delinearize\n");
|
GLSLF("// delinearize\n");
|
||||||
GLSL(color.rgb = clamp(color.rgb, 0.0, 1.0);)
|
GLSL(color.rgb = clamp(color.rgb, 0.0, 1.0);)
|
||||||
GLSLF("color.rgb *= vec3(%f);\n", mp_trc_nom_peak(trc));
|
GLSLF("color.rgb *= vec3(%f);\n", pl_color_transfer_nominal_peak(trc));
|
||||||
|
|
||||||
switch (trc) {
|
switch (trc) {
|
||||||
case PL_COLOR_TRC_SRGB:
|
case PL_COLOR_TRC_SRGB:
|
||||||
|
@ -842,11 +844,10 @@ void pass_color_map(struct gl_shader_cache *sc, bool is_linear,
|
||||||
|
|
||||||
// Some operations need access to the video's luma coefficients, so make
|
// Some operations need access to the video's luma coefficients, so make
|
||||||
// them available
|
// them available
|
||||||
float rgb2xyz[3][3];
|
pl_matrix3x3 rgb2xyz = pl_get_rgb2xyz_matrix(pl_raw_primaries_get(src.primaries));
|
||||||
mp_get_rgb2xyz_matrix(mp_get_csp_primaries(src.primaries), rgb2xyz);
|
gl_sc_uniform_vec3(sc, "src_luma", rgb2xyz.m[1]);
|
||||||
gl_sc_uniform_vec3(sc, "src_luma", rgb2xyz[1]);
|
rgb2xyz = pl_get_rgb2xyz_matrix(pl_raw_primaries_get(dst.primaries));
|
||||||
mp_get_rgb2xyz_matrix(mp_get_csp_primaries(dst.primaries), rgb2xyz);
|
gl_sc_uniform_vec3(sc, "dst_luma", rgb2xyz.m[1]);
|
||||||
gl_sc_uniform_vec3(sc, "dst_luma", rgb2xyz[1]);
|
|
||||||
|
|
||||||
bool need_ootf = src_light != dst_light;
|
bool need_ootf = src_light != dst_light;
|
||||||
if (src_light == MP_CSP_LIGHT_SCENE_HLG && src.hdr.max_luma != dst.hdr.max_luma)
|
if (src_light == MP_CSP_LIGHT_SCENE_HLG && src.hdr.max_luma != dst.hdr.max_luma)
|
||||||
|
@ -867,7 +868,7 @@ void pass_color_map(struct gl_shader_cache *sc, bool is_linear,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pre-scale the incoming values into an absolute scale
|
// Pre-scale the incoming values into an absolute scale
|
||||||
GLSLF("color.rgb *= vec3(%f);\n", mp_trc_nom_peak(src.transfer));
|
GLSLF("color.rgb *= vec3(%f);\n", pl_color_transfer_nominal_peak(src.transfer));
|
||||||
|
|
||||||
if (need_ootf)
|
if (need_ootf)
|
||||||
pass_ootf(sc, src_light, src.hdr.max_luma / MP_REF_WHITE);
|
pass_ootf(sc, src_light, src.hdr.max_luma / MP_REF_WHITE);
|
||||||
|
@ -880,11 +881,11 @@ void pass_color_map(struct gl_shader_cache *sc, bool is_linear,
|
||||||
|
|
||||||
// Adapt to the right colorspace if necessary
|
// Adapt to the right colorspace if necessary
|
||||||
if (src.primaries != dst.primaries) {
|
if (src.primaries != dst.primaries) {
|
||||||
struct mp_csp_primaries csp_src = mp_get_csp_primaries(src.primaries),
|
const struct pl_raw_primaries *csp_src = pl_raw_primaries_get(src.primaries),
|
||||||
csp_dst = mp_get_csp_primaries(dst.primaries);
|
*csp_dst = pl_raw_primaries_get(dst.primaries);
|
||||||
float m[3][3] = {{0}};
|
pl_matrix3x3 m = pl_get_color_mapping_matrix(csp_src, csp_dst,
|
||||||
mp_get_cms_matrix(csp_src, csp_dst, MP_INTENT_RELATIVE_COLORIMETRIC, m);
|
PL_INTENT_RELATIVE_COLORIMETRIC);
|
||||||
gl_sc_uniform_mat3(sc, "cms_matrix", true, &m[0][0]);
|
gl_sc_uniform_mat3(sc, "cms_matrix", true, &m.m[0][0]);
|
||||||
GLSL(color.rgb = cms_matrix * color.rgb;)
|
GLSL(color.rgb = cms_matrix * color.rgb;)
|
||||||
|
|
||||||
if (!opts->gamut_mode || opts->gamut_mode == GAMUT_DESATURATE) {
|
if (!opts->gamut_mode || opts->gamut_mode == GAMUT_DESATURATE) {
|
||||||
|
@ -907,8 +908,8 @@ void pass_color_map(struct gl_shader_cache *sc, bool is_linear,
|
||||||
// For SDR, we normalize to the chosen signal peak. For HDR, we normalize
|
// For SDR, we normalize to the chosen signal peak. For HDR, we normalize
|
||||||
// to the encoding range of the transfer function.
|
// to the encoding range of the transfer function.
|
||||||
float dst_range = dst.hdr.max_luma / MP_REF_WHITE;
|
float dst_range = dst.hdr.max_luma / MP_REF_WHITE;
|
||||||
if (mp_trc_is_hdr(dst.transfer))
|
if (pl_color_space_is_hdr(&dst))
|
||||||
dst_range = mp_trc_nom_peak(dst.transfer);
|
dst_range = pl_color_transfer_nominal_peak(dst.transfer);
|
||||||
|
|
||||||
GLSLF("color.rgb *= vec3(%f);\n", 1.0 / dst_range);
|
GLSLF("color.rgb *= vec3(%f);\n", 1.0 / dst_range);
|
||||||
|
|
||||||
|
@ -1009,7 +1010,7 @@ void pass_sample_deband(struct gl_shader_cache *sc, struct deband_opts *opts,
|
||||||
GLSL(noise.z = rand(h); h = permute(h);)
|
GLSL(noise.z = rand(h); h = permute(h);)
|
||||||
|
|
||||||
// Noise is scaled to the signal level to prevent extreme noise for HDR
|
// Noise is scaled to the signal level to prevent extreme noise for HDR
|
||||||
float gain = opts->grain/8192.0 / mp_trc_nom_peak(trc);
|
float gain = opts->grain/8192.0 / pl_color_transfer_nominal_peak(trc);
|
||||||
GLSLF("color.xyz += %f * (noise - vec3(0.5));\n", gain);
|
GLSLF("color.xyz += %f * (noise - vec3(0.5));\n", gain);
|
||||||
GLSLF("}\n");
|
GLSLF("}\n");
|
||||||
}
|
}
|
||||||
|
|
|
@ -193,7 +193,7 @@ static int create_vdp_mixer(struct mp_vdpau_mixer *mixer,
|
||||||
if (!opts->chroma_deint)
|
if (!opts->chroma_deint)
|
||||||
SET_VIDEO_ATTR(SKIP_CHROMA_DEINTERLACE, uint8_t, 1);
|
SET_VIDEO_ATTR(SKIP_CHROMA_DEINTERLACE, uint8_t, 1);
|
||||||
|
|
||||||
struct mp_cmat yuv2rgb;
|
struct pl_transform3x3 yuv2rgb;
|
||||||
VdpCSCMatrix matrix;
|
VdpCSCMatrix matrix;
|
||||||
|
|
||||||
struct mp_csp_params cparams = MP_CSP_PARAMS_DEFAULTS;
|
struct mp_csp_params cparams = MP_CSP_PARAMS_DEFAULTS;
|
||||||
|
@ -204,7 +204,7 @@ static int create_vdp_mixer(struct mp_vdpau_mixer *mixer,
|
||||||
|
|
||||||
for (int r = 0; r < 3; r++) {
|
for (int r = 0; r < 3; r++) {
|
||||||
for (int c = 0; c < 3; c++)
|
for (int c = 0; c < 3; c++)
|
||||||
matrix[r][c] = yuv2rgb.m[r][c];
|
matrix[r][c] = yuv2rgb.mat.m[r][c];
|
||||||
matrix[r][3] = yuv2rgb.c[r];
|
matrix[r][3] = yuv2rgb.c[r];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -551,7 +551,7 @@ static bool mp_zimg_state_init(struct mp_zimg_context *ctx,
|
||||||
params.allow_approximate_gamma = 1;
|
params.allow_approximate_gamma = 1;
|
||||||
|
|
||||||
// leave at default for SDR, which means 100 cd/m^2 for zimg
|
// leave at default for SDR, which means 100 cd/m^2 for zimg
|
||||||
if (ctx->dst.color.hdr.max_luma > 0 && mp_trc_is_hdr(ctx->dst.color.transfer))
|
if (ctx->dst.color.hdr.max_luma > 0 && pl_color_space_is_hdr(&ctx->dst.color))
|
||||||
params.nominal_peak_luminance = ctx->dst.color.hdr.max_luma;
|
params.nominal_peak_luminance = ctx->dst.color.hdr.max_luma;
|
||||||
|
|
||||||
st->graph = zimg_filter_graph_build(&src_fmt, &dst_fmt, ¶ms);
|
st->graph = zimg_filter_graph_build(&src_fmt, &dst_fmt, ¶ms);
|
||||||
|
|
Loading…
Reference in New Issue