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:
Kacper Michajłow 2023-11-04 06:27:38 +01:00 committed by Dudemanguy
parent 47be5ad4aa
commit 475f76dc6d
10 changed files with 98 additions and 474 deletions

View File

@ -94,7 +94,7 @@ static void add_dvd_streams(demuxer_t *demuxer)
// emulate the extradata
struct mp_csp_params csp = MP_CSP_PARAMS_DEFAULTS;
struct mp_cmat cmatrix;
struct pl_transform3x3 cmatrix;
mp_get_csp_matrix(&csp, &cmatrix);
char *s = talloc_strdup(sh, "");

View File

@ -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;
vs_params.repr.sys = csp;
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_invert_cmat(&vs_rgb2yuv, &vs_yuv2rgb);
pl_transform3x3_invert(&vs_yuv2rgb);
// Proper conversion to RGB
struct mp_csp_params rgb_params = MP_CSP_PARAMS_DEFAULTS;
rgb_params.color = params.color;
struct mp_cmat vs2rgb;
struct pl_transform3x3 vs2rgb;
mp_get_csp_matrix(&rgb_params, &vs2rgb);
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 a = 0xff - (color & 0xff);
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);
sb->libass.color = MP_ASS_RGBA(rgb[0], rgb[1], rgb[2], a);
}

View File

@ -3,8 +3,6 @@
*
* 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.
*
* 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])
{
float m00 = m[0][0], m01 = m[0][1], m02 = m[0][2],
m10 = m[1][0], m11 = m[1][1], m12 = m[1][2],
m20 = m[2][0], m21 = m[2][1], m22 = m[2][2];
// 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];
}
}
// LMS<-XYZ revised matrix from CIECAM97, based on a linear transform and
// normalized for equal energy on monochrome inputs
static const pl_matrix3x3 m_cat97 = {{
{ 0.8562, 0.3372, -0.1934 },
{ -0.8360, 1.8327, 0.0033 },
{ 0.0357, -0.0469, 1.0112 },
}};
// M := M * XYZd<-XYZs
static void mp_apply_chromatic_adaptation(struct mp_csp_col_xy src,
struct mp_csp_col_xy dest, float m[3][3])
static void apply_chromatic_adaptation(struct pl_cie_xy src,
struct pl_cie_xy dest, pl_matrix3x3 *mat)
{
// If the white points are nearly identical, this is a wasteful identity
// 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
// http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html
float C[3][2], tmp[3][3] = {{0}};
// 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 Ma, we use the CIECAM97 revised (linear) matrix
float C[3][2];
for (int i = 0; i < 3; i++) {
// source cone
C[i][0] = bradford[i][0] * mp_xy_X(src)
+ bradford[i][1] * 1
+ bradford[i][2] * mp_xy_Z(src);
C[i][0] = m_cat97.m[i][0] * pl_cie_X(src)
+ m_cat97.m[i][1] * 1
+ m_cat97.m[i][2] * pl_cie_Z(src);
// dest cone
C[i][1] = bradford[i][0] * mp_xy_X(dest)
+ bradford[i][1] * 1
+ bradford[i][2] * mp_xy_Z(dest);
C[i][1] = m_cat97.m[i][0] * pl_cie_X(dest)
+ m_cat97.m[i][1] * 1
+ m_cat97.m[i][2] * pl_cie_Z(dest);
}
// tmp := I * [Cd/Cs] * Ma
pl_matrix3x3 tmp = {0};
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
mp_invert_matrix3x3(bradford);
mp_mul_matrix3x3(m, bradford);
mp_mul_matrix3x3(m, 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;
pl_matrix3x3 ma_inv = m_cat97;
pl_matrix3x3_invert(&ma_inv);
pl_matrix3x3_mul(mat, &ma_inv);
pl_matrix3x3_mul(mat, &tmp);
}
// 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
* 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);
*mat = (struct mp_cmat) {
{ {1, 0, 2 * (1-lr) },
*mat = (struct pl_transform3x3) {
{ {{1, 0, 2 * (1-lr) },
{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
};
}
// 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;
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
// to the chroma channels to normalize them into [0,1]. This is not
// 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;
}
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;
break;
}
case PL_COLOR_SYSTEM_XYZ: {
// The vo should probably not be using a matrix generated by this
// function for XYZ sources, but if it does, let's just convert it to
// an equivalent RGB space based on the colorimetry metadata it
// provided in mp_csp_params. (At the risk of clipping, if the
// chosen primaries are too small to fit the actual data)
mp_get_xyz2rgb_coeffs(params, MP_INTENT_RELATIVE_COLORIMETRIC, m);
// For lack of anything saner to do, just assume the caller wants
// DCI-P3 primaries, which is a reasonable assumption.
const struct pl_raw_primaries *dst = pl_raw_primaries_get(PL_COLOR_PRIM_DCI_P3);
pl_matrix3x3 mat = pl_get_xyz2rgb_matrix(dst);
// DCDM X'Y'Z' is expected to have equal energy white point (EG 432-1 Annex H)
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;
break;
}
case PL_COLOR_SYSTEM_YCGCO: {
*m = (struct mp_cmat) {
{{1, -1, 1},
*m = (struct pl_transform3x3) {
{{{1, -1, 1},
{1, 1, 0},
{1, -1, -1}},
{1, -1, -1}}},
};
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 huesin = params->gray ? 0 : params->saturation * sin(params->hue);
for (int i = 0; i < 3; i++) {
float u = m->m[i][1], v = m->m[i][2];
m->m[i][1] = huecos * u - huesin * v;
m->m[i][2] = huesin * u + huecos * v;
float u = m->mat.m[i][1], v = m->mat.m[i][2];
m->mat.m[i][1] = huecos * u - huesin * 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;
for (int i = 0; i < 3; i++) {
m->m[i][0] *= ymul;
m->m[i][1] *= cmul;
m->m[i][2] *= cmul;
m->mat.m[i][0] *= ymul;
m->mat.m[i][1] *= cmul;
m->mat.m[i][2] *= cmul;
// Set c so that Y=umin,UV=cmid maps to RGB=min (black to black),
// also add brightness offset (black lift)
m->c[i] = rgblev.min - m->m[i][0] * yuvlev.ymin
- (m->m[i][1] + m->m[i][2]) * yuvlev.cmid
m->c[i] = rgblev.min - m->mat.m[i][0] * yuvlev.ymin
- (m->mat.m[i][1] + m->mat.m[i][2]) * yuvlev.cmid
+ 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);
}
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.
// 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,
// [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])
{
for (int i = 0; i < 3; i++) {
double val = matrix->c[i];
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));
out[i] = av_clip(ival, 0, (1 << obits) - 1);
}

View File

@ -46,15 +46,6 @@ enum mp_csp_light {
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.
enum mp_stereo3d_mode {
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,
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_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);
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);
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_invert_cmat(struct mp_cmat *out, struct mp_cmat *in);
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]);
#endif /* MPLAYER_CSPUTILS_H */

View File

@ -961,11 +961,11 @@ void mp_image_params_guess_csp(struct mp_image_params *params)
} else {
// If the signal peak is unknown, we're forced to pick the TRC's
// 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(&params->color)) {
// Some clips have leftover HDR metadata after conversion to SDR, so to
// avoid blowing up the tone mapping code, strip/sanitize it
params->color.hdr = pl_hdr_metadata_empty;

View File

@ -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
// primaries and transfer characteristics
struct mp_csp_primaries csp = mp_get_csp_primaries(prim);
cmsCIExyY wp_xyY = {csp.white.x, csp.white.y, 1.0};
const struct pl_raw_primaries *csp = pl_raw_primaries_get(prim);
cmsCIExyY wp_xyY = {csp->white.x, csp->white.y, 1.0};
cmsCIExyYTRIPLE prim_xyY = {
.Red = {csp.red.x, csp.red.y, 1.0},
.Green = {csp.green.x, csp.green.y, 1.0},
.Blue = {csp.blue.x, csp.blue.y, 1.0},
.Red = {csp->red.x, csp->red.y, 1.0},
.Green = {csp->green.x, csp->green.y, 1.0},
.Blue = {csp->blue.x, csp->blue.y, 1.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
// approximate the BT.1886 to the target device's actual black
// point even in e.g. perceptual mode
const int intent = MP_INTENT_RELATIVE_COLORIMETRIC;
const int intent = PL_INTENT_RELATIVE_COLORIMETRIC;
cmsCIEXYZ bp_XYZ;
if (!cmsDetectBlackPoint(&bp_XYZ, disp_profile, intent, 0))
return false;
@ -519,7 +519,7 @@ const struct m_sub_options mp_icc_conf = {
.size = sizeof(struct mp_icc_opts),
.defaults = &(const struct mp_icc_opts) {
.size_str = "auto",
.intent = MP_INTENT_RELATIVE_COLORIMETRIC,
.intent = PL_INTENT_RELATIVE_COLORIMETRIC,
.use_embedded = true,
.cache = true,
},

View File

@ -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
// 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);
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);
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
// due to the potentially extreme brightness differences severely
// 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;
} else if (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.
// We could pick any value we want here, the difference is just coding
// efficiency.
if (mp_trc_is_hdr(trc_orig))
if (pl_color_space_is_hdr(&p->image_params.color))
trc_orig = PL_COLOR_TRC_GAMMA22;
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
// just pick gamma 2.2 as a default, since it's a good estimate for
// 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;
}
@ -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
// case it was unknown
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)
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
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;
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;
if (detect_peak && !p->hdr_peak_ssbo) {

View File

@ -17,6 +17,8 @@
#include <math.h>
#include <libplacebo/colorspace.h>
#include "video_shaders.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
// 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)
{
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
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
@ -445,7 +447,7 @@ void pass_delinearize(struct gl_shader_cache *sc, enum pl_color_transfer trc)
GLSLF("// delinearize\n");
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) {
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
// them available
float rgb2xyz[3][3];
mp_get_rgb2xyz_matrix(mp_get_csp_primaries(src.primaries), rgb2xyz);
gl_sc_uniform_vec3(sc, "src_luma", rgb2xyz[1]);
mp_get_rgb2xyz_matrix(mp_get_csp_primaries(dst.primaries), rgb2xyz);
gl_sc_uniform_vec3(sc, "dst_luma", rgb2xyz[1]);
pl_matrix3x3 rgb2xyz = pl_get_rgb2xyz_matrix(pl_raw_primaries_get(src.primaries));
gl_sc_uniform_vec3(sc, "src_luma", rgb2xyz.m[1]);
rgb2xyz = pl_get_rgb2xyz_matrix(pl_raw_primaries_get(dst.primaries));
gl_sc_uniform_vec3(sc, "dst_luma", rgb2xyz.m[1]);
bool need_ootf = src_light != dst_light;
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
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)
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
if (src.primaries != dst.primaries) {
struct mp_csp_primaries csp_src = mp_get_csp_primaries(src.primaries),
csp_dst = mp_get_csp_primaries(dst.primaries);
float m[3][3] = {{0}};
mp_get_cms_matrix(csp_src, csp_dst, MP_INTENT_RELATIVE_COLORIMETRIC, m);
gl_sc_uniform_mat3(sc, "cms_matrix", true, &m[0][0]);
const struct pl_raw_primaries *csp_src = pl_raw_primaries_get(src.primaries),
*csp_dst = pl_raw_primaries_get(dst.primaries);
pl_matrix3x3 m = pl_get_color_mapping_matrix(csp_src, csp_dst,
PL_INTENT_RELATIVE_COLORIMETRIC);
gl_sc_uniform_mat3(sc, "cms_matrix", true, &m.m[0][0]);
GLSL(color.rgb = cms_matrix * color.rgb;)
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
// to the encoding range of the transfer function.
float dst_range = dst.hdr.max_luma / MP_REF_WHITE;
if (mp_trc_is_hdr(dst.transfer))
dst_range = mp_trc_nom_peak(dst.transfer);
if (pl_color_space_is_hdr(&dst))
dst_range = pl_color_transfer_nominal_peak(dst.transfer);
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);)
// 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("}\n");
}

View File

@ -193,7 +193,7 @@ static int create_vdp_mixer(struct mp_vdpau_mixer *mixer,
if (!opts->chroma_deint)
SET_VIDEO_ATTR(SKIP_CHROMA_DEINTERLACE, uint8_t, 1);
struct mp_cmat yuv2rgb;
struct pl_transform3x3 yuv2rgb;
VdpCSCMatrix matrix;
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 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];
}

View File

@ -551,7 +551,7 @@ static bool mp_zimg_state_init(struct mp_zimg_context *ctx,
params.allow_approximate_gamma = 1;
// 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;
st->graph = zimg_filter_graph_build(&src_fmt, &dst_fmt, &params);