blob: 448da7f12d06c486a2b7570b61a46ed004d3816a [file] [edit]
/**
* Copyright (c) 2026 Collabora Ltd.
*
* SPDX-License-Identifier: MIT
*/
#ifndef U_YCBCR_H
#define U_YCBCR_H
/* BT.601 coefficients */
static const float util_ycbcr_bt601_coeffs[3] = {
0.299f, 0.587f, 0.114f
};
/* BT.701 coefficients */
static const float util_ycbcr_bt709_coeffs[3] = {
0.2126f, 0.7152f, 0.0722f
};
/* BT.2020 coefficients */
static const float util_ycbcr_bt2020_coeffs[3] = {
0.2627f, 0.6780f, 0.0593f
};
/* SMPTE 240M coefficients */
static const float util_ycbcr_smpte240m_coeffs[3] = {
0.2122f, 0.7013f, 0.0865f
};
static inline void
util_get_ycbcr_to_rgb_matrix(float m[3][4], const float coeffs[3])
{
/**
* Sets up a 3x4 matrix that computes:
*
* R = Y + e * Cr
* G = Y - (a * e / b) * Cr - (c * d / b) * Cb
* B = Y + d * Cb
*/
float a = coeffs[0];
float b = coeffs[1];
float c = coeffs[2];
float d = 2 - 2 * c;
float e = 2 - 2 * a;
float f = 1.0f / b;
m[0][0] = 1; m[0][1] = 0; m[0][2] = e; m[0][3] = 0;
m[1][0] = 1; m[1][1] = -c * d * f; m[1][2] = -a * e * f; m[1][3] = 0;
m[2][0] = 1; m[2][1] = d; m[2][2] = 0; m[2][3] = 0;
}
static inline void
util_get_rgb_to_ycbcr_matrix(float m[3][4], const float coeffs[3])
{
/**
* Sets up a 3x4 matrix that computes:
*
* Y = a * R + b * G + c * B
* Cb = (B - Y) / d
* Cr = (R - Y) / e
*/
float a = coeffs[0];
float b = coeffs[1];
float c = coeffs[2];
float d = 0.5f / (c - 1);
float e = 0.5f / (a - 1);
m[0][0] = a; m[0][1] = b; m[0][2] = c; m[0][3] = 0;
m[1][0] = d * a; m[1][1] = d * b; m[1][2] = 0.5f; m[1][3] = 0;
m[2][0] = 0.5f; m[2][1] = e * b; m[2][2] = e * c; m[2][3] = 0;
}
static inline float
util_get_full_range_chroma_bias(unsigned bpc)
{
return -(1 << (bpc - 1)) / ((1 << bpc) - 1.0f);
}
static inline void
util_get_full_range_coeffs(float out[3][2], const unsigned bpc[3])
{
out[0][0] = 1; out[0][1] = 0;
out[1][0] = 1; out[1][1] = util_get_full_range_chroma_bias(bpc[1]);
out[2][0] = 1; out[2][1] = util_get_full_range_chroma_bias(bpc[2]);
}
static inline float
util_get_narrow_range(unsigned bpc)
{
return 1 - 1.0f / (1 << bpc);
}
static inline float
util_get_narrow_range_luma_factor(unsigned bpc)
{
return util_get_narrow_range(bpc) * (256.0f / 219);
}
static inline float
util_get_narrow_range_chroma_factor(unsigned bpc)
{
return util_get_narrow_range(bpc) * (256.0f / 224);
}
static inline void
util_get_narrow_range_coeffs(float out[3][2], const unsigned bpc[3])
{
float y_factor = util_get_narrow_range_luma_factor(bpc[0]);
float cb_factor = util_get_narrow_range_chroma_factor(bpc[1]);
float cr_factor = util_get_narrow_range_chroma_factor(bpc[2]);
float y_bias = -16.0f / 219;
float c_bias = -128.0f / 224;
out[0][0] = y_factor; out[0][1] = y_bias;
out[1][0] = cb_factor; out[1][1] = c_bias;
out[2][0] = cr_factor; out[2][1] = c_bias;
}
static inline void
util_get_identity_range_coeffs(float out[3][2])
{
out[0][0] = out[1][0] = out[2][0] = 1.0f;
out[0][1] = out[1][1] = out[2][1] = 0.0f;
}
static inline void
util_ycbcr_adjust_from_range(float mat[3][4],
const float range[3][2])
{
for (int i = 0; i < 3; ++i) {
mat[i][3] = range[0][1] * mat[i][0] +
range[1][1] * mat[i][1] +
range[2][1] * mat[i][2] +
mat[i][3];
mat[i][0] = mat[i][0] * range[0][0];
mat[i][1] = mat[i][1] * range[1][0];
mat[i][2] = mat[i][2] * range[2][0];
}
}
static inline void
util_ycbcr_adjust_to_range(float mat[3][4],
const float range[3][2])
{
for (int i = 0; i < 3; ++i) {
float tmp = 1.0f / range[i][0];
mat[i][0] = mat[i][0] * tmp;
mat[i][1] = mat[i][1] * tmp;
mat[i][2] = mat[i][2] * tmp;
mat[i][3] -= range[i][1] * tmp;
}
}
#endif /* U_YCBCR_H */