#include "formats.h"
#include "internal.h"
#include "video.h"
+#include "colorspace.h"
enum DitherMode {
DITHER_NONE,
struct ColorPrimaries {
enum Whitepoint wp;
- double xr, yr, xg, yg, xb, yb;
+ struct PrimaryCoefficients coeff;
};
struct TransferCharacteristics {
double alpha, beta, gamma, delta;
};
-struct LumaCoefficients {
- double cr, cg, cb;
-};
-
-struct WhitepointCoefficients {
- double xw, yw;
-};
-
typedef struct ColorSpaceContext {
const AVClass *class;
// FIXME dithering if bitdepth goes down?
// FIXME bitexact for fate integration?
-static const double ycgco_matrix[3][3] =
-{
- { 0.25, 0.5, 0.25 },
- { -0.25, 0.5, -0.25 },
- { 0.5, 0, -0.5 },
-};
-
-static const double gbr_matrix[3][3] =
-{
- { 0, 1, 0 },
- { 0, -0.5, 0.5 },
- { 0.5, -0.5, 0 },
-};
-
-/*
- * All constants explained in e.g. https://linuxtv.org/downloads/v4l-dvb-apis/ch02s06.html
- * The older ones (bt470bg/m) are also explained in their respective ITU docs
- * (e.g. https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.470-5-199802-S!!PDF-E.pdf)
- * whereas the newer ones can typically be copied directly from wikipedia :)
- */
-static const struct LumaCoefficients luma_coefficients[AVCOL_SPC_NB] = {
- [AVCOL_SPC_FCC] = { 0.30, 0.59, 0.11 },
- [AVCOL_SPC_BT470BG] = { 0.299, 0.587, 0.114 },
- [AVCOL_SPC_SMPTE170M] = { 0.299, 0.587, 0.114 },
- [AVCOL_SPC_BT709] = { 0.2126, 0.7152, 0.0722 },
- [AVCOL_SPC_SMPTE240M] = { 0.212, 0.701, 0.087 },
- [AVCOL_SPC_YCOCG] = { 0.25, 0.5, 0.25 },
- [AVCOL_SPC_RGB] = { 1, 1, 1 },
- [AVCOL_SPC_BT2020_NCL] = { 0.2627, 0.6780, 0.0593 },
- [AVCOL_SPC_BT2020_CL] = { 0.2627, 0.6780, 0.0593 },
-};
-
-static const struct LumaCoefficients *get_luma_coefficients(enum AVColorSpace csp)
-{
- const struct LumaCoefficients *coeffs;
-
- if (csp >= AVCOL_SPC_NB)
- return NULL;
- coeffs = &luma_coefficients[csp];
- if (!coeffs->cr)
- return NULL;
-
- return coeffs;
-}
-
-static void fill_rgb2yuv_table(const struct LumaCoefficients *coeffs,
- double rgb2yuv[3][3])
-{
- double bscale, rscale;
-
- // special ycgco matrix
- if (coeffs->cr == 0.25 && coeffs->cg == 0.5 && coeffs->cb == 0.25) {
- memcpy(rgb2yuv, ycgco_matrix, sizeof(double) * 9);
- return;
- } else if (coeffs->cr == 1 && coeffs->cg == 1 && coeffs->cb == 1) {
- memcpy(rgb2yuv, gbr_matrix, sizeof(double) * 9);
- return;
- }
-
- rgb2yuv[0][0] = coeffs->cr;
- rgb2yuv[0][1] = coeffs->cg;
- rgb2yuv[0][2] = coeffs->cb;
- bscale = 0.5 / (coeffs->cb - 1.0);
- rscale = 0.5 / (coeffs->cr - 1.0);
- rgb2yuv[1][0] = bscale * coeffs->cr;
- rgb2yuv[1][1] = bscale * coeffs->cg;
- rgb2yuv[1][2] = 0.5;
- rgb2yuv[2][0] = 0.5;
- rgb2yuv[2][1] = rscale * coeffs->cg;
- rgb2yuv[2][2] = rscale * coeffs->cb;
-}
-
// FIXME I'm pretty sure gamma22/28 also have a linear toe slope, but I can't
// find any actual tables that document their real values...
// See http://www.13thmonkey.org/~boris/gammacorrection/ first graph why it matters
};
static const struct ColorPrimaries color_primaries[AVCOL_PRI_NB] = {
- [AVCOL_PRI_BT709] = { WP_D65, 0.640, 0.330, 0.300, 0.600, 0.150, 0.060 },
- [AVCOL_PRI_BT470M] = { WP_C, 0.670, 0.330, 0.210, 0.710, 0.140, 0.080 },
- [AVCOL_PRI_BT470BG] = { WP_D65, 0.640, 0.330, 0.290, 0.600, 0.150, 0.060,},
- [AVCOL_PRI_SMPTE170M] = { WP_D65, 0.630, 0.340, 0.310, 0.595, 0.155, 0.070 },
- [AVCOL_PRI_SMPTE240M] = { WP_D65, 0.630, 0.340, 0.310, 0.595, 0.155, 0.070 },
- [AVCOL_PRI_SMPTE428] = { WP_E, 0.735, 0.265, 0.274, 0.718, 0.167, 0.009 },
- [AVCOL_PRI_SMPTE431] = { WP_DCI, 0.680, 0.320, 0.265, 0.690, 0.150, 0.060 },
- [AVCOL_PRI_SMPTE432] = { WP_D65, 0.680, 0.320, 0.265, 0.690, 0.150, 0.060 },
- [AVCOL_PRI_FILM] = { WP_C, 0.681, 0.319, 0.243, 0.692, 0.145, 0.049 },
- [AVCOL_PRI_BT2020] = { WP_D65, 0.708, 0.292, 0.170, 0.797, 0.131, 0.046 },
- [AVCOL_PRI_JEDEC_P22] = { WP_D65, 0.630, 0.340, 0.295, 0.605, 0.155, 0.077 },
+ [AVCOL_PRI_BT709] = { WP_D65, { 0.640, 0.330, 0.300, 0.600, 0.150, 0.060 } },
+ [AVCOL_PRI_BT470M] = { WP_C, { 0.670, 0.330, 0.210, 0.710, 0.140, 0.080 } },
+ [AVCOL_PRI_BT470BG] = { WP_D65, { 0.640, 0.330, 0.290, 0.600, 0.150, 0.060 } },
+ [AVCOL_PRI_SMPTE170M] = { WP_D65, { 0.630, 0.340, 0.310, 0.595, 0.155, 0.070 } },
+ [AVCOL_PRI_SMPTE240M] = { WP_D65, { 0.630, 0.340, 0.310, 0.595, 0.155, 0.070 } },
+ [AVCOL_PRI_SMPTE428] = { WP_E, { 0.735, 0.265, 0.274, 0.718, 0.167, 0.009 } },
+ [AVCOL_PRI_SMPTE431] = { WP_DCI, { 0.680, 0.320, 0.265, 0.690, 0.150, 0.060 } },
+ [AVCOL_PRI_SMPTE432] = { WP_D65, { 0.680, 0.320, 0.265, 0.690, 0.150, 0.060 } },
+ [AVCOL_PRI_FILM] = { WP_C, { 0.681, 0.319, 0.243, 0.692, 0.145, 0.049 } },
+ [AVCOL_PRI_BT2020] = { WP_D65, { 0.708, 0.292, 0.170, 0.797, 0.131, 0.046 } },
+ [AVCOL_PRI_JEDEC_P22] = { WP_D65, { 0.630, 0.340, 0.295, 0.605, 0.155, 0.077 } },
};
static const struct ColorPrimaries *get_color_primaries(enum AVColorPrimaries prm)
{
- const struct ColorPrimaries *coeffs;
+ const struct ColorPrimaries *p;
if (prm >= AVCOL_PRI_NB)
return NULL;
- coeffs = &color_primaries[prm];
- if (!coeffs->xr)
+ p = &color_primaries[prm];
+ if (!p->coeff.xr)
return NULL;
- return coeffs;
-}
-
-static void invert_matrix3x3(const double in[3][3], double out[3][3])
-{
- double m00 = in[0][0], m01 = in[0][1], m02 = in[0][2],
- m10 = in[1][0], m11 = in[1][1], m12 = in[1][2],
- m20 = in[2][0], m21 = in[2][1], m22 = in[2][2];
- int i, j;
- double det;
-
- out[0][0] = (m11 * m22 - m21 * m12);
- out[0][1] = -(m01 * m22 - m21 * m02);
- out[0][2] = (m01 * m12 - m11 * m02);
- out[1][0] = -(m10 * m22 - m20 * m12);
- out[1][1] = (m00 * m22 - m20 * m02);
- out[1][2] = -(m00 * m12 - m10 * m02);
- out[2][0] = (m10 * m21 - m20 * m11);
- out[2][1] = -(m00 * m21 - m20 * m01);
- out[2][2] = (m00 * m11 - m10 * m01);
-
- det = m00 * out[0][0] + m10 * out[0][1] + m20 * out[0][2];
- det = 1.0 / det;
-
- for (i = 0; i < 3; i++) {
- for (j = 0; j < 3; j++)
- out[i][j] *= det;
- }
+ return p;
}
static int fill_gamma_table(ColorSpaceContext *s)
return 0;
}
-/*
- * see e.g. http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
- */
-static void fill_rgb2xyz_table(const struct ColorPrimaries *coeffs,
- double rgb2xyz[3][3])
-{
- const struct WhitepointCoefficients *wp = &whitepoint_coefficients[coeffs->wp];
- double i[3][3], sr, sg, sb, zw;
-
- rgb2xyz[0][0] = coeffs->xr / coeffs->yr;
- rgb2xyz[0][1] = coeffs->xg / coeffs->yg;
- rgb2xyz[0][2] = coeffs->xb / coeffs->yb;
- rgb2xyz[1][0] = rgb2xyz[1][1] = rgb2xyz[1][2] = 1.0;
- rgb2xyz[2][0] = (1.0 - coeffs->xr - coeffs->yr) / coeffs->yr;
- rgb2xyz[2][1] = (1.0 - coeffs->xg - coeffs->yg) / coeffs->yg;
- rgb2xyz[2][2] = (1.0 - coeffs->xb - coeffs->yb) / coeffs->yb;
- invert_matrix3x3(rgb2xyz, i);
- zw = 1.0 - wp->xw - wp->yw;
- sr = i[0][0] * wp->xw + i[0][1] * wp->yw + i[0][2] * zw;
- sg = i[1][0] * wp->xw + i[1][1] * wp->yw + i[1][2] * zw;
- sb = i[2][0] * wp->xw + i[2][1] * wp->yw + i[2][2] * zw;
- rgb2xyz[0][0] *= sr;
- rgb2xyz[0][1] *= sg;
- rgb2xyz[0][2] *= sb;
- rgb2xyz[1][0] *= sr;
- rgb2xyz[1][1] *= sg;
- rgb2xyz[1][2] *= sb;
- rgb2xyz[2][0] *= sr;
- rgb2xyz[2][1] *= sg;
- rgb2xyz[2][2] *= sb;
-}
-
-static void mul3x3(double dst[3][3], const double src1[3][3], const double src2[3][3])
-{
- int m, n;
-
- for (m = 0; m < 3; m++)
- for (n = 0; n < 3; n++)
- dst[m][n] = src2[m][0] * src1[0][n] +
- src2[m][1] * src1[1][n] +
- src2[m][2] * src1[2][n];
-}
-
/*
* See http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html
* This function uses the Bradford mechanism.
double mai[3][3], fac[3][3], tmp[3][3];
double rs, gs, bs, rd, gd, bd;
- invert_matrix3x3(ma, mai);
+ ff_matrix_invert_3x3(ma, mai);
rs = ma[0][0] * wp_src->xw + ma[0][1] * wp_src->yw + ma[0][2] * zw_src;
gs = ma[1][0] * wp_src->xw + ma[1][1] * wp_src->yw + ma[1][2] * zw_src;
bs = ma[2][0] * wp_src->xw + ma[2][1] * wp_src->yw + ma[2][2] * zw_src;
fac[1][1] = gd / gs;
fac[2][2] = bd / bs;
fac[0][1] = fac[0][2] = fac[1][0] = fac[1][2] = fac[2][0] = fac[2][1] = 0.0;
- mul3x3(tmp, ma, fac);
- mul3x3(out, tmp, mai);
+ ff_matrix_mul_3x3(tmp, ma, fac);
+ ff_matrix_mul_3x3(out, tmp, mai);
}
static void apply_lut(int16_t *buf[3], ptrdiff_t stride,
s->yuv2yuv(out_data, td->out_linesize, in_data, td->in_linesize, w, h,
s->yuv2yuv_coeffs, s->yuv_offset);
} else {
- // FIXME maybe (for caching effciency) do pipeline per-line instead of
+ // FIXME maybe (for caching efficiency) do pipeline per-line instead of
// full buffer per function? (Or, since yuv2rgb requires 2 lines: per
// 2 lines, for yuv420.)
/*
sizeof(*s->in_primaries));
if (!s->lrgb2lrgb_passthrough) {
double rgb2xyz[3][3], xyz2rgb[3][3], rgb2rgb[3][3];
+ const struct WhitepointCoefficients *wp_out, *wp_in;
- fill_rgb2xyz_table(s->out_primaries, rgb2xyz);
- invert_matrix3x3(rgb2xyz, xyz2rgb);
- fill_rgb2xyz_table(s->in_primaries, rgb2xyz);
+ wp_out = &whitepoint_coefficients[s->out_primaries->wp];
+ wp_in = &whitepoint_coefficients[s->in_primaries->wp];
+ ff_fill_rgb2xyz_table(&s->out_primaries->coeff, wp_out, rgb2xyz);
+ ff_matrix_invert_3x3(rgb2xyz, xyz2rgb);
+ ff_fill_rgb2xyz_table(&s->in_primaries->coeff, wp_in, rgb2xyz);
if (s->out_primaries->wp != s->in_primaries->wp &&
s->wp_adapt != WP_ADAPT_IDENTITY) {
double wpconv[3][3], tmp[3][3];
fill_whitepoint_conv_table(wpconv, s->wp_adapt, s->in_primaries->wp,
s->out_primaries->wp);
- mul3x3(tmp, rgb2xyz, wpconv);
- mul3x3(rgb2rgb, tmp, xyz2rgb);
+ ff_matrix_mul_3x3(tmp, rgb2xyz, wpconv);
+ ff_matrix_mul_3x3(rgb2rgb, tmp, xyz2rgb);
} else {
- mul3x3(rgb2rgb, rgb2xyz, xyz2rgb);
+ ff_matrix_mul_3x3(rgb2rgb, rgb2xyz, xyz2rgb);
}
for (m = 0; m < 3; m++)
for (n = 0; n < 3; n++) {
s->in_rng = in->color_range;
if (s->user_irng != AVCOL_RANGE_UNSPECIFIED)
s->in_rng = s->user_irng;
- s->in_lumacoef = get_luma_coefficients(s->in_csp);
+ s->in_lumacoef = ff_get_luma_coefficients(s->in_csp);
if (!s->in_lumacoef) {
av_log(ctx, AV_LOG_ERROR,
"Unsupported input colorspace %d (%s)\n",
if (!s->out_lumacoef) {
s->out_csp = out->colorspace;
s->out_rng = out->color_range;
- s->out_lumacoef = get_luma_coefficients(s->out_csp);
+ s->out_lumacoef = ff_get_luma_coefficients(s->out_csp);
if (!s->out_lumacoef) {
if (s->out_csp == AVCOL_SPC_UNSPECIFIED) {
if (s->user_all == CS_UNSPECIFIED) {
}
for (n = 0; n < 8; n++)
s->yuv_offset[0][n] = off;
- fill_rgb2yuv_table(s->in_lumacoef, rgb2yuv);
- invert_matrix3x3(rgb2yuv, yuv2rgb);
+ ff_fill_rgb2yuv_table(s->in_lumacoef, rgb2yuv);
+ ff_matrix_invert_3x3(rgb2yuv, yuv2rgb);
bits = 1 << (in_desc->comp[0].depth - 1);
for (n = 0; n < 3; n++) {
for (in_rng = s->in_y_rng, m = 0; m < 3; m++, in_rng = s->in_uv_rng) {
}
for (n = 0; n < 8; n++)
s->yuv_offset[1][n] = off;
- fill_rgb2yuv_table(s->out_lumacoef, rgb2yuv);
+ ff_fill_rgb2yuv_table(s->out_lumacoef, rgb2yuv);
bits = 1 << (29 - out_desc->comp[0].depth);
for (out_rng = s->out_y_rng, n = 0; n < 3; n++, out_rng = s->out_uv_rng) {
for (m = 0; m < 3; m++) {
double yuv2yuv[3][3];
int in_rng, out_rng;
- mul3x3(yuv2yuv, yuv2rgb, rgb2yuv);
+ ff_matrix_mul_3x3(yuv2yuv, yuv2rgb, rgb2yuv);
for (out_rng = s->out_y_rng, m = 0; m < 3; m++, out_rng = s->out_uv_rng) {
for (in_rng = s->in_y_rng, n = 0; n < 3; n++, in_rng = s->in_uv_rng) {
s->yuv2yuv_coeffs[m][n][0] =