*/
#include "libavutil/avassert.h"
+#include "libavutil/mem_internal.h"
#include "libavutil/opt.h"
#include "libavutil/pixdesc.h"
#include "libavutil/pixfmt.h"
#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
[AVCOL_TRC_GAMMA28] = { 1.0, 0.0, 1.0 / 2.8, 0.0 },
[AVCOL_TRC_SMPTE170M] = { 1.099, 0.018, 0.45, 4.5 },
[AVCOL_TRC_SMPTE240M] = { 1.1115, 0.0228, 0.45, 4.0 },
+ [AVCOL_TRC_LINEAR] = { 1.0, 0.0, 1.0, 0.0 },
[AVCOL_TRC_IEC61966_2_1] = { 1.055, 0.0031308, 1.0 / 2.4, 12.92 },
[AVCOL_TRC_IEC61966_2_4] = { 1.099, 0.018, 0.45, 4.5 },
[AVCOL_TRC_BT2020_10] = { 1.099, 0.018, 0.45, 4.5 },
};
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)
s->delin_lut[n] = av_clip_int16(lrint(d * 28672.0));
// linearize
- if (v <= -in_beta) {
+ if (v <= -in_beta * in_delta) {
l = -pow((1.0 - in_alpha - v) * in_ialpha, in_igamma);
- } else if (v < in_beta) {
+ } else if (v < in_beta * in_delta) {
l = v * in_idelta;
} else {
l = pow((v + in_alpha - 1.0) * in_ialpha, in_igamma);
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,
}
}
-struct ThreadData {
+typedef struct ThreadData {
AVFrame *in, *out;
ptrdiff_t in_linesize[3], out_linesize[3];
int in_ss_h, out_ss_h;
-};
+} ThreadData;
static int convert(AVFilterContext *ctx, void *data, int job_nr, int n_jobs)
{
- struct ThreadData *td = data;
+ const ThreadData *td = data;
ColorSpaceContext *s = ctx->priv;
uint8_t *in_data[3], *out_data[3];
int16_t *rgb[3];
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] =
return 0;
}
-static int init(AVFilterContext *ctx)
+static av_cold int init(AVFilterContext *ctx)
{
ColorSpaceContext *s = ctx->priv;
int res;
ptrdiff_t rgb_stride = FFALIGN(in->width * sizeof(int16_t), 32);
unsigned rgb_sz = rgb_stride * in->height;
- struct ThreadData td;
+ ThreadData td;
if (!out) {
av_frame_free(&in);
res = av_frame_copy_props(out, in);
if (res < 0) {
av_frame_free(&in);
+ av_frame_free(&out);
return res;
}
!s->dither_scratch_base[1][0] || !s->dither_scratch_base[1][1] ||
!s->dither_scratch_base[2][0] || !s->dither_scratch_base[2][1]) {
uninit(ctx);
+ av_frame_free(&in);
+ av_frame_free(&out);
return AVERROR(ENOMEM);
}
s->rgb_sz = rgb_sz;
}
res = create_filtergraph(ctx, in, out);
- if (res < 0)
+ if (res < 0) {
+ av_frame_free(&in);
+ av_frame_free(&out);
return res;
+ }
s->rgb_stride = rgb_stride / sizeof(int16_t);
td.in = in;
td.out = out;
td.out_ss_h = av_pix_fmt_desc_get(out->format)->log2_chroma_h;
if (s->yuv2yuv_passthrough) {
res = av_frame_copy(out, in);
- if (res < 0)
+ if (res < 0) {
+ av_frame_free(&in);
+ av_frame_free(&out);
return res;
+ }
} else {
ctx->internal->execute(ctx, convert, &td, NULL,
FFMIN((in->height + 1) >> 1, ff_filter_get_nb_threads(ctx)));
return AVERROR(ENOMEM);
if (s->user_format == AV_PIX_FMT_NONE)
return ff_set_common_formats(ctx, formats);
- res = ff_formats_ref(formats, &ctx->inputs[0]->out_formats);
+ res = ff_formats_ref(formats, &ctx->inputs[0]->outcfg.formats);
if (res < 0)
return res;
formats = NULL;
if (res < 0)
return res;
- return ff_formats_ref(formats, &ctx->outputs[0]->in_formats);
+ return ff_formats_ref(formats, &ctx->outputs[0]->incfg.formats);
}
static int config_props(AVFilterLink *outlink)
ENUM("smpte432", AVCOL_PRI_SMPTE432, "prm"),
ENUM("bt2020", AVCOL_PRI_BT2020, "prm"),
ENUM("jedec-p22", AVCOL_PRI_JEDEC_P22, "prm"),
+ ENUM("ebu3213", AVCOL_PRI_EBU3213, "prm"),
{ "trc", "Output transfer characteristics",
OFFSET(user_trc), AV_OPT_TYPE_INT, { .i64 = AVCOL_TRC_UNSPECIFIED },
ENUM("gamma28", AVCOL_TRC_GAMMA28, "trc"),
ENUM("smpte170m", AVCOL_TRC_SMPTE170M, "trc"),
ENUM("smpte240m", AVCOL_TRC_SMPTE240M, "trc"),
+ ENUM("linear", AVCOL_TRC_LINEAR, "trc"),
ENUM("srgb", AVCOL_TRC_IEC61966_2_1, "trc"),
ENUM("iec61966-2-1", AVCOL_TRC_IEC61966_2_1, "trc"),
ENUM("xvycc", AVCOL_TRC_IEC61966_2_4, "trc"),