Hi,

patch attached.

There is bug in format negotiation, xyz12 format is preferred over
others when using yuv inputs.

The hacky xyz12 swscale output sucks.
From d2712eec65842b3423cb4eff012d718d8c5c714f Mon Sep 17 00:00:00 2001
From: Paul B Mahol <one...@gmail.com>
Date: Mon, 1 Feb 2016 21:05:16 +0100
Subject: [PATCH] avfilter: add chromascope filter

Signed-off-by: Paul B Mahol <one...@gmail.com>
---
 libavfilter/Makefile         |    1 +
 libavfilter/allfilters.c     |    1 +
 libavfilter/vf_chromascope.c | 1079 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1081 insertions(+)
 create mode 100644 libavfilter/vf_chromascope.c

diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 10c2e0b..d6660cd 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -121,6 +121,7 @@ OBJS-$(CONFIG_BLEND_FILTER)                  += vf_blend.o dualinput.o framesync
 OBJS-$(CONFIG_BOXBLUR_FILTER)                += vf_boxblur.o
 OBJS-$(CONFIG_BWDIF_FILTER)                  += vf_bwdif.o
 OBJS-$(CONFIG_CHROMAKEY_FILTER)              += vf_chromakey.o
+OBJS-$(CONFIG_CHROMASCOPE_FILTER)            += vf_chromascope.o
 OBJS-$(CONFIG_CODECVIEW_FILTER)              += vf_codecview.o
 OBJS-$(CONFIG_COLORBALANCE_FILTER)           += vf_colorbalance.o
 OBJS-$(CONFIG_COLORCHANNELMIXER_FILTER)      += vf_colorchannelmixer.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index ed52649..65b024c 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -142,6 +142,7 @@ void avfilter_register_all(void)
     REGISTER_FILTER(BOXBLUR,        boxblur,        vf);
     REGISTER_FILTER(BWDIF,          bwdif,          vf);
     REGISTER_FILTER(CHROMAKEY,      chromakey,      vf);
+    REGISTER_FILTER(CHROMASCOPE,    chromascope,    vf);
     REGISTER_FILTER(CODECVIEW,      codecview,      vf);
     REGISTER_FILTER(COLORBALANCE,   colorbalance,   vf);
     REGISTER_FILTER(COLORCHANNELMIXER, colorchannelmixer, vf);
diff --git a/libavfilter/vf_chromascope.c b/libavfilter/vf_chromascope.c
new file mode 100644
index 0000000..63594b3
--- /dev/null
+++ b/libavfilter/vf_chromascope.c
@@ -0,0 +1,1079 @@
+/*
+ * Copyright (c) 2000 John Walker
+ * Copyright (c) 2016 Paul B Mahol
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/avassert.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/opt.h"
+#include "libavutil/parseutils.h"
+#include "libavutil/pixdesc.h"
+#include "avfilter.h"
+#include "formats.h"
+#include "internal.h"
+#include "video.h"
+
+enum ColorsSystems {
+    NTSCsystem,
+    EBUsystem,
+    SMPTEsystem,
+    HDTVsystem,
+    APPLEsystem,
+    CIE1931system,
+    Rec709system,
+    Rec2020system,
+    NB_CS
+};
+
+typedef struct ChromascopeContext {
+    const AVClass *class;
+    int color_system;
+    int size;
+    int show_white;
+    int showBlack;
+    int full_chart;
+    int correct_gamma;
+    int luv;
+    float intensity;
+    int background;
+
+    double m[3][3];
+    AVFrame *f;
+} ChromascopeContext;
+
+#define OFFSET(x) offsetof(ChromascopeContext, x)
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
+
+static const AVOption chromascope_options[] = {
+    { "system",    "set chromascope color system", OFFSET(color_system), AV_OPT_TYPE_INT, {.i64=Rec709system}, 0, NB_CS-1, FLAGS, "system" },
+      { "ntsc",    NULL, 0, AV_OPT_TYPE_CONST, {.i64=NTSCsystem},    0, 0, FLAGS, "system" },
+      { "ebu",     NULL, 0, AV_OPT_TYPE_CONST, {.i64=EBUsystem},     0, 0, FLAGS, "system" },
+      { "smpte",   NULL, 0, AV_OPT_TYPE_CONST, {.i64=SMPTEsystem},   0, 0, FLAGS, "system" },
+      { "hdtv",    NULL, 0, AV_OPT_TYPE_CONST, {.i64=HDTVsystem},    0, 0, FLAGS, "system" },
+      { "apple",   NULL, 0, AV_OPT_TYPE_CONST, {.i64=APPLEsystem},   0, 0, FLAGS, "system" },
+      { "cie1931", NULL, 0, AV_OPT_TYPE_CONST, {.i64=CIE1931system}, 0, 0, FLAGS, "system" },
+      { "rec709",  NULL, 0, AV_OPT_TYPE_CONST, {.i64=Rec709system},  0, 0, FLAGS, "system" },
+      { "rec2020", NULL, 0, AV_OPT_TYPE_CONST, {.i64=Rec2020system}, 0, 0, FLAGS, "system" },
+    { "size",      "set chromascope size", OFFSET(size), AV_OPT_TYPE_INT, {.i64=512}, 256, 8192, FLAGS },
+    { "s",         "set chromascope size", OFFSET(size), AV_OPT_TYPE_INT, {.i64=512}, 256, 8192, FLAGS },
+    { "intensity", "set chromascope intensity", OFFSET(intensity), AV_OPT_TYPE_FLOAT, {.dbl=0.001}, 0, 1, FLAGS },
+    { "i",         "set chromascope intensity", OFFSET(intensity), AV_OPT_TYPE_FLOAT, {.dbl=0.001}, 0, 1, FLAGS },
+    { "fullchart", NULL, OFFSET(full_chart), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
+    { "correctgamma", NULL, OFFSET(correct_gamma), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, FLAGS },
+    { "showwhite", NULL, OFFSET(show_white), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
+    { "luv",      "CIE 1976 Lu'v'", OFFSET(luv), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
+    { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(chromascope);
+
+static const enum AVPixelFormat in_pix_fmts[] = {
+    AV_PIX_FMT_RGBA,
+    AV_PIX_FMT_XYZ12,
+    AV_PIX_FMT_NONE
+};
+
+static const enum AVPixelFormat out_pix_fmts[] = {
+    AV_PIX_FMT_RGBA64,
+    AV_PIX_FMT_NONE
+};
+
+static int query_formats(AVFilterContext *ctx)
+{
+    int ret;
+
+    if ((ret = ff_formats_ref(ff_make_format_list(in_pix_fmts), &ctx->inputs[0]->out_formats)) < 0)
+        return ret;
+
+    if ((ret = ff_formats_ref(ff_make_format_list(out_pix_fmts), &ctx->outputs[0]->in_formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    ChromascopeContext *s = outlink->src->priv;
+
+    outlink->h = outlink->w = s->size;
+    outlink->sample_aspect_ratio = (AVRational){1,1};
+    return 0;
+}
+
+/* A  color  system is defined by the CIE x and y  coordinates of its
+   three primary illuminants and the x and y coordinates of the  white
+   point. */
+
+struct ColorSystem {
+    const char *name;                 /* Color system name */
+    double xRed, yRed,                /* Red primary illuminant */
+           xGreen, yGreen,            /* Green primary illuminant */
+           xBlue, yBlue,              /* Blue primary illuminant */
+           xWhite, yWhite,            /* White point */
+           gamma;             /* gamma of nonlinear correction */
+};
+
+/* The  following  table  gives  the  CIE  color  matching  functions
+   \bar{x}(\lambda),  \bar{y}(\lambda),  and   \bar{z}(\lambda),   for
+   wavelengths  \lambda  at 5 nanometre increments from 380 nm through
+   780 nm.  This table is used in conjunction with  Planck's  law  for
+   the  energy spectrum of a black body at a given temperature to plot
+   the black body curve on the CIE chart. */
+
+static float const cie_color_match[][3] = {
+    { 0.0014, 0.0000, 0.0065 },       /* 380 nm */
+    { 0.0022, 0.0001, 0.0105 },
+    { 0.0042, 0.0001, 0.0201 },
+    { 0.0076, 0.0002, 0.0362 },
+    { 0.0143, 0.0004, 0.0679 },
+    { 0.0232, 0.0006, 0.1102 },
+    { 0.0435, 0.0012, 0.2074 },
+    { 0.0776, 0.0022, 0.3713 },
+    { 0.1344, 0.0040, 0.6456 },
+    { 0.2148, 0.0073, 1.0391 },
+    { 0.2839, 0.0116, 1.3856 },
+    { 0.3285, 0.0168, 1.6230 },
+    { 0.3483, 0.0230, 1.7471 },
+    { 0.3481, 0.0298, 1.7826 },
+    { 0.3362, 0.0380, 1.7721 },
+    { 0.3187, 0.0480, 1.7441 },
+    { 0.2908, 0.0600, 1.6692 },
+    { 0.2511, 0.0739, 1.5281 },
+    { 0.1954, 0.0910, 1.2876 },
+    { 0.1421, 0.1126, 1.0419 },
+    { 0.0956, 0.1390, 0.8130 },
+    { 0.0580, 0.1693, 0.6162 },
+    { 0.0320, 0.2080, 0.4652 },
+    { 0.0147, 0.2586, 0.3533 },
+    { 0.0049, 0.3230, 0.2720 },
+    { 0.0024, 0.4073, 0.2123 },
+    { 0.0093, 0.5030, 0.1582 },
+    { 0.0291, 0.6082, 0.1117 },
+    { 0.0633, 0.7100, 0.0782 },
+    { 0.1096, 0.7932, 0.0573 },
+    { 0.1655, 0.8620, 0.0422 },
+    { 0.2257, 0.9149, 0.0298 },
+    { 0.2904, 0.9540, 0.0203 },
+    { 0.3597, 0.9803, 0.0134 },
+    { 0.4334, 0.9950, 0.0087 },
+    { 0.5121, 1.0000, 0.0057 },
+    { 0.5945, 0.9950, 0.0039 },
+    { 0.6784, 0.9786, 0.0027 },
+    { 0.7621, 0.9520, 0.0021 },
+    { 0.8425, 0.9154, 0.0018 },
+    { 0.9163, 0.8700, 0.0017 },
+    { 0.9786, 0.8163, 0.0014 },
+    { 1.0263, 0.7570, 0.0011 },
+    { 1.0567, 0.6949, 0.0010 },
+    { 1.0622, 0.6310, 0.0008 },
+    { 1.0456, 0.5668, 0.0006 },
+    { 1.0026, 0.5030, 0.0003 },
+    { 0.9384, 0.4412, 0.0002 },
+    { 0.8544, 0.3810, 0.0002 },
+    { 0.7514, 0.3210, 0.0001 },
+    { 0.6424, 0.2650, 0.0000 },
+    { 0.5419, 0.2170, 0.0000 },
+    { 0.4479, 0.1750, 0.0000 },
+    { 0.3608, 0.1382, 0.0000 },
+    { 0.2835, 0.1070, 0.0000 },
+    { 0.2187, 0.0816, 0.0000 },
+    { 0.1649, 0.0610, 0.0000 },
+    { 0.1212, 0.0446, 0.0000 },
+    { 0.0874, 0.0320, 0.0000 },
+    { 0.0636, 0.0232, 0.0000 },
+    { 0.0468, 0.0170, 0.0000 },
+    { 0.0329, 0.0119, 0.0000 },
+    { 0.0227, 0.0082, 0.0000 },
+    { 0.0158, 0.0057, 0.0000 },
+    { 0.0114, 0.0041, 0.0000 },
+    { 0.0081, 0.0029, 0.0000 },
+    { 0.0058, 0.0021, 0.0000 },
+    { 0.0041, 0.0015, 0.0000 },
+    { 0.0029, 0.0010, 0.0000 },
+    { 0.0020, 0.0007, 0.0000 },
+    { 0.0014, 0.0005, 0.0000 },
+    { 0.0010, 0.0004, 0.0000 },
+    { 0.0007, 0.0002, 0.0000 },
+    { 0.0005, 0.0002, 0.0000 },
+    { 0.0003, 0.0001, 0.0000 },
+    { 0.0002, 0.0001, 0.0000 },
+    { 0.0002, 0.0001, 0.0000 },
+    { 0.0001, 0.0000, 0.0000 },
+    { 0.0001, 0.0000, 0.0000 },
+    { 0.0001, 0.0000, 0.0000 },
+    { 0.0000, 0.0000, 0.0000 }        /* 780 nm */
+};
+
+/* The following table gives the  spectral  chromaticity  co-ordinates
+   x(\lambda) and y(\lambda) for wavelengths in 5 nanometre increments
+   from 380 nm through  780  nm.   These  co-ordinates  represent  the
+   position in the CIE x-y space of pure spectral colors of the given
+   wavelength, and  thus  define  the  outline  of  the  CIE  "tongue"
+   diagram. */
+
+static float spectral_chromaticity[81][3] = {
+    { 0.1741, 0.0050 },               /* 380 nm */
+    { 0.1740, 0.0050 },
+    { 0.1738, 0.0049 },
+    { 0.1736, 0.0049 },
+    { 0.1733, 0.0048 },
+    { 0.1730, 0.0048 },
+    { 0.1726, 0.0048 },
+    { 0.1721, 0.0048 },
+    { 0.1714, 0.0051 },
+    { 0.1703, 0.0058 },
+    { 0.1689, 0.0069 },
+    { 0.1669, 0.0086 },
+    { 0.1644, 0.0109 },
+    { 0.1611, 0.0138 },
+    { 0.1566, 0.0177 },
+    { 0.1510, 0.0227 },
+    { 0.1440, 0.0297 },
+    { 0.1355, 0.0399 },
+    { 0.1241, 0.0578 },
+    { 0.1096, 0.0868 },
+    { 0.0913, 0.1327 },
+    { 0.0687, 0.2007 },
+    { 0.0454, 0.2950 },
+    { 0.0235, 0.4127 },
+    { 0.0082, 0.5384 },
+    { 0.0039, 0.6548 },
+    { 0.0139, 0.7502 },
+    { 0.0389, 0.8120 },
+    { 0.0743, 0.8338 },
+    { 0.1142, 0.8262 },
+    { 0.1547, 0.8059 },
+    { 0.1929, 0.7816 },
+    { 0.2296, 0.7543 },
+    { 0.2658, 0.7243 },
+    { 0.3016, 0.6923 },
+    { 0.3373, 0.6589 },
+    { 0.3731, 0.6245 },
+    { 0.4087, 0.5896 },
+    { 0.4441, 0.5547 },
+    { 0.4788, 0.5202 },
+    { 0.5125, 0.4866 },
+    { 0.5448, 0.4544 },
+    { 0.5752, 0.4242 },
+    { 0.6029, 0.3965 },
+    { 0.6270, 0.3725 },
+    { 0.6482, 0.3514 },
+    { 0.6658, 0.3340 },
+    { 0.6801, 0.3197 },
+    { 0.6915, 0.3083 },
+    { 0.7006, 0.2993 },
+    { 0.7079, 0.2920 },
+    { 0.7140, 0.2859 },
+    { 0.7190, 0.2809 },
+    { 0.7230, 0.2770 },
+    { 0.7260, 0.2740 },
+    { 0.7283, 0.2717 },
+    { 0.7300, 0.2700 },
+    { 0.7311, 0.2689 },
+    { 0.7320, 0.2680 },
+    { 0.7327, 0.2673 },
+    { 0.7334, 0.2666 },
+    { 0.7340, 0.2660 },
+    { 0.7344, 0.2656 },
+    { 0.7346, 0.2654 },
+    { 0.7347, 0.2653 },
+    { 0.7347, 0.2653 },
+    { 0.7347, 0.2653 },
+    { 0.7347, 0.2653 },
+    { 0.7347, 0.2653 },
+    { 0.7347, 0.2653 },
+    { 0.7347, 0.2653 },
+    { 0.7347, 0.2653 },
+    { 0.7347, 0.2653 },
+    { 0.7347, 0.2653 },
+    { 0.7347, 0.2653 },
+    { 0.7347, 0.2653 },
+    { 0.7347, 0.2653 },
+    { 0.7347, 0.2653 },
+    { 0.7347, 0.2653 },
+    { 0.7347, 0.2653 },
+    { 0.7347, 0.2653 }                /* 780 nm */
+};
+
+
+/* Standard white point chromaticities. */
+
+#define C     0.31006, 0.31616
+#define E     1.0/3.0, 1.0/3.0
+#define D65   0.31271, 0.32902
+
+/* Gamma of nonlinear correction.
+   See Charles Poynton's ColorFAQ Item 45 and GammaFAQ Item 6 at
+   http://www.inforamp.net/~poynton/ColorFAQ.html
+   http://www.inforamp.net/~poynton/GammaFAQ.html
+*/
+
+#define GAMMA_REC709    0.      /* Rec. 709 */
+
+static const struct ColorSystem color_systems[] = {
+    [NTSCsystem] = { // also BT 470M
+        "NTSC",
+        0.67,  0.33,  0.21,  0.71,  0.14,  0.08,
+        C, GAMMA_REC709
+    },
+    [EBUsystem] = {
+        "EBU (PAL/SECAM)", // also BT 601_625
+        0.64,  0.33,  0.29,  0.60,  0.15,  0.06,
+        D65, GAMMA_REC709
+    },
+    [SMPTEsystem] = { // also BT 601_525
+        "SMPTE",
+        0.630, 0.340, 0.310, 0.595, 0.155, 0.070,
+        D65, GAMMA_REC709
+    },
+    [HDTVsystem] = {
+        "HDTV",
+        0.670, 0.330, 0.210, 0.710, 0.150, 0.060,
+        D65, GAMMA_REC709
+    },
+    [APPLEsystem] = {
+        "Apple",
+        0.625, 0.340, 0.280, 0.595, 0.115, 0.070,
+        D65, GAMMA_REC709
+    },
+    [CIE1931system] = {
+        "CIE 1931",
+        0.7347, 0.2653, 0.2738, 0.7174, 0.1666, 0.0089,
+        E, GAMMA_REC709
+    },
+    [Rec709system] = {
+        "CIE REC 709",
+        0.64,  0.33,  0.30,  0.60,  0.15,  0.06,
+        D65, GAMMA_REC709
+    },
+    [Rec2020system] = {
+        "CIE REC 2020",
+        0.708,  0.292,  0.170,  0.797,  0.131,  0.046,
+        D65, GAMMA_REC709
+    },
+};
+
+static struct ColorSystem CustomSystem = {
+    "Custom",
+    0.64,  0.33,  0.30,  0.60,  0.15,  0.06,
+    D65, GAMMA_REC709
+};
+
+static void
+upvp_to_xy(double   const up,
+           double   const vp,
+           double * const xc,
+           double * const yc)
+{
+/*----------------------------------------------------------------------------
+    Given 1976 coordinates u', v', determine 1931 chromaticities x, y
+-----------------------------------------------------------------------------*/
+    *xc = 9*up / (6*up - 16*vp + 12);
+    *yc = 4*vp / (6*up - 16*vp + 12);
+}
+
+static void
+xy_to_upvp(double xc,
+           double yc,
+           double * const up,
+           double * const vp)
+{
+/*----------------------------------------------------------------------------
+    Given 1931 chromaticities x, y, determine 1976 coordinates u', v'
+-----------------------------------------------------------------------------*/
+    *up = 4*xc / (- 2*xc + 12*yc + 3);
+    *vp = 9*yc / (- 2*xc + 12*yc + 3);
+}
+
+static void
+xyz_to_rgb(const struct ColorSystem * const cs,
+           double xc,
+           double yc,
+           double zc,
+           double * const r,
+           double * const g,
+           double * const b)
+{
+/*----------------------------------------------------------------------------
+    Given  an additive tricolor system CS, defined by the CIE x and y
+    chromaticities of its three primaries (z is derived  trivially  as
+    1-(x+y)),  and  a  desired chromaticity (XC, YC, ZC) in CIE space,
+    determine the contribution of each primary in a linear combination
+    which   sums  to  the  desired  chromaticity.   If  the  requested
+    chromaticity falls outside the  Maxwell  triangle  (color  gamut)
+    formed  by the three primaries, one of the r, g, or b weights will
+    be negative.
+
+    Caller can use constrain_rgb() to desaturate an outside-gamut
+    color to the closest representation within the available
+    gamut.
+-----------------------------------------------------------------------------*/
+    double xr, yr, zr, xg, yg, zg, xb, yb, zb;
+    double xw, yw, zw;
+    double rx, ry, rz, gx, gy, gz, bx, by, bz;
+    double rw, gw, bw;
+
+    xr = cs->xRed;    yr = cs->yRed;    zr = 1 - (xr + yr);
+    xg = cs->xGreen;  yg = cs->yGreen;  zg = 1 - (xg + yg);
+    xb = cs->xBlue;   yb = cs->yBlue;   zb = 1 - (xb + yb);
+
+    xw = cs->xWhite;  yw = cs->yWhite;  zw = 1 - (xw + yw);
+
+    /* xyz -> rgb matrix, before scaling to white. */
+    rx = yg*zb - yb*zg;  ry = xb*zg - xg*zb;  rz = xg*yb - xb*yg;
+    gx = yb*zr - yr*zb;  gy = xr*zb - xb*zr;  gz = xb*yr - xr*yb;
+    bx = yr*zg - yg*zr;  by = xg*zr - xr*zg;  bz = xr*yg - xg*yr;
+
+    /* White scaling factors.
+       Dividing by yw scales the white luminance to unity, as conventional. */
+    rw = (rx*xw + ry*yw + rz*zw) / yw;
+    gw = (gx*xw + gy*yw + gz*zw) / yw;
+    bw = (bx*xw + by*yw + bz*zw) / yw;
+
+    /* xyz -> rgb matrix, correctly scaled to white. */
+    rx = rx / rw;  ry = ry / rw;  rz = rz / rw;
+    gx = gx / gw;  gy = gy / gw;  gz = gz / gw;
+    bx = bx / bw;  by = by / bw;  bz = bz / bw;
+
+    /* rgb of the desired point */
+    *r = rx*xc + ry*yc + rz*zc;
+    *g = gx*xc + gy*yc + gz*zc;
+    *b = bx*xc + by*yc + bz*zc;
+}
+
+static void invert_matrix3x3(double m[3][3])
+{
+    int i, j;
+    double 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)
+    double det = m00 * m[0][0] + m10 * m[0][1] + m20 * m[0][2];
+    det = 1.0 / det;
+
+    for (i = 0; i < 3; i++) {
+        for (j = 0; j < 3; j++)
+            m[i][j] *= det;
+    }
+}
+
+static void get_rgb2xyz_matrix(struct ColorSystem system, double m[3][3])
+{
+    double S[3], X[4], Z[4];
+    int i;
+
+    // Convert from CIE xyY to XYZ. Note that Y=1 holds true for all primaries
+    X[0] = system.xRed   / system.yRed;
+    X[1] = system.xGreen / system.yGreen;
+    X[2] = system.xBlue  / system.yBlue;
+    X[3] = system.xWhite / system.yWhite;
+
+    Z[0] = (1 - system.xRed   - system.yRed)   / system.yRed;
+    Z[1] = (1 - system.xGreen - system.yGreen) / system.yGreen;
+    Z[2] = (1 - system.xBlue  - system.yBlue)  / system.yBlue;
+    Z[3] = (1 - system.xWhite - system.yWhite) / system.yWhite;
+
+    // S = XYZ^-1 * W
+    for (i = 0; i < 3; i++) {
+        m[0][i] = X[i];
+        m[1][i] = 1;
+        m[2][i] = Z[i];
+    }
+
+    invert_matrix3x3(m);
+
+    for (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 (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];
+    }
+}
+
+static void
+rgb_to_xyz(double rc,
+           double gc,
+           double bc,
+           double * const x,
+           double * const y,
+           double * const z,
+           const double m[3][3])
+{
+    double sum;
+
+    *x = m[0][0] * rc + m[0][1] * gc + m[0][2] * bc;
+    *y = m[1][0] * rc + m[1][1] * gc + m[1][2] * bc;
+    *z = m[2][0] * rc + m[2][1] * gc + m[2][2] * bc;
+
+    sum = *x + *y + *z;
+
+    *x = *x / sum;
+    *y = *y / sum;
+}
+
+static int
+constrain_rgb(double * const r,
+              double * const g,
+              double * const b)
+{
+/*----------------------------------------------------------------------------
+    If  the  requested RGB shade contains a negative weight for one of
+    the primaries, it lies outside the color  gamut  accessible  from
+    the  given  triple  of  primaries.  Desaturate it by adding white,
+    equal quantities of R, G, and B, enough to make RGB all positive.
+-----------------------------------------------------------------------------*/
+    double w;
+
+    /* Amount of white needed is w = - min(0, *r, *g, *b) */
+    w = (0 < *r) ? 0 : *r;
+    w = (w < *g) ? w : *g;
+    w = (w < *b) ? w : *b;
+    w = - w;
+
+    /* Add just enough white to make r, g, b all positive. */
+    if (w > 0) {
+        *r += w;  *g += w; *b += w;
+
+        return 1;                     /* Color modified to fit RGB gamut */
+    }
+
+    return 0;                         /* Color within RGB gamut */
+}
+
+static void
+gamma_correct(const struct ColorSystem * const cs,
+              double *                   const c)
+{
+/*----------------------------------------------------------------------------
+    Transform linear RGB values to nonlinear RGB values.
+
+    Rec. 709 is ITU-R Recommendation BT. 709 (1990)
+    ``Basic Parameter Values for the HDTV Standard for the Studio and for
+    International Programme Exchange'', formerly CCIR Rec. 709.
+
+    For details see
+       http://www.inforamp.net/~poynton/ColorFAQ.html
+       http://www.inforamp.net/~poynton/GammaFAQ.html
+-----------------------------------------------------------------------------*/
+    double gamma;
+    double cc;
+
+    gamma = cs->gamma;
+
+    if (gamma == 0.) {
+        /* Rec. 709 gamma correction. */
+        cc = 0.018;
+        if (*c < cc) {
+            *c *= (1.099 * pow(cc, 0.45) - 0.099) / cc;
+        } else {
+            *c = 1.099 * pow(*c, 0.45) - 0.099;
+        }
+    } else {
+    /* Nonlinear color = (Linear color)^(1/gamma) */
+        *c = pow(*c, 1./gamma);
+    }
+}
+
+
+
+static void
+gamma_correct_rgb(const struct ColorSystem * const cs,
+                  double * const r,
+                  double * const g,
+                  double * const b)
+{
+    gamma_correct(cs, r);
+    gamma_correct(cs, g);
+    gamma_correct(cs, b);
+}
+
+/* Sz(X) is the displacement in pixels of a displacement of X normalized
+   distance units.  (A normalized distance unit is 1/512 of the smaller
+   dimension of the canvas)
+*/
+#define Sz(x) (((x) * (int)FFMIN(w, h)) / 512)
+
+static void
+computeMonochromeColorLocation(
+    double waveLength,
+    int    w,
+    int    h,
+    int    upvp,
+    int *  xP,
+    int *  yP)
+{
+    const int ix = (waveLength - 380) / 5;
+    const double px = spectral_chromaticity[ix][0];
+    const double py = spectral_chromaticity[ix][1];
+
+    if (upvp) {
+        double up, vp;
+        xy_to_upvp(px, py, &up, &vp);
+        *xP = up * (w - 1);
+        *yP = (h - 1) - vp * (h - 1);
+    } else {
+        *xP = px * (w - 1);
+        *yP = (h - 1) - py * (h - 1);
+    }
+}
+
+static void
+find_tongue(uint16_t*      pixels,
+            int      const w,
+            int      const linesize,
+            int      const row,
+            int *    const presentP,
+            int *    const leftEdgeP,
+            int *    const rightEdgeP)
+{
+    int i;
+
+    for (i = 0; i < w && pixels[row * linesize + i * 4 + 0] == 0; i++)
+        ;
+
+    if (i >= w) {
+        *presentP = 0;
+    } else {
+        int j;
+        int const leftEdge = i;
+
+        *presentP = 1;
+
+        for (j = w - 1; j >= leftEdge && pixels[row * linesize + j * 4 + 0] == 0; j--)
+            ;
+
+        *rightEdgeP = j;
+        *leftEdgeP = leftEdge;
+    }
+}
+
+static void draw_line(uint16_t *const pixels, int linesize,
+                      int x0, int y0, int x1, int y1,
+                      int w, int h,
+                      const uint16_t *const rgbcolor)
+{
+    int dx  = FFABS(x1 - x0), sx = x0 < x1 ? 1 : -1;
+    int dy  = FFABS(y1 - y0), sy = y0 < y1 ? 1 : -1;
+    int err = (dx > dy ? dx : -dy) / 2, e2;
+
+    for (;;) {
+        pixels[y0 * linesize + x0 * 4 + 0] = rgbcolor[0];
+        pixels[y0 * linesize + x0 * 4 + 1] = rgbcolor[1];
+        pixels[y0 * linesize + x0 * 4 + 2] = rgbcolor[2];
+        pixels[y0 * linesize + x0 * 4 + 3] = rgbcolor[3];
+
+        if (x0 == x1 && y0 == y1)
+            break;
+
+        e2 = err;
+
+        if (e2 >-dx) {
+            err -= dy;
+            x0 += sx;
+        }
+
+        if (e2 < dy) {
+            err += dx;
+            y0 += sy;
+        }
+    }
+}
+
+static void draw_rline(uint16_t *const pixels, int linesize,
+                       int x0, int y0, int x1, int y1,
+                       int w, int h)
+{
+    int dx  = FFABS(x1 - x0), sx = x0 < x1 ? 1 : -1;
+    int dy  = FFABS(y1 - y0), sy = y0 < y1 ? 1 : -1;
+    int err = (dx > dy ? dx : -dy) / 2, e2;
+
+    for (;;) {
+        pixels[y0 * linesize + x0 * 4 + 0] = 65535 - pixels[y0 * linesize + x0 * 4 + 0];
+        pixels[y0 * linesize + x0 * 4 + 1] = 65535 - pixels[y0 * linesize + x0 * 4 + 1];
+        pixels[y0 * linesize + x0 * 4 + 2] = 65535 - pixels[y0 * linesize + x0 * 4 + 2];
+        pixels[y0 * linesize + x0 * 4 + 3] = 65535;
+
+        if (x0 == x1 && y0 == y1)
+            break;
+
+        e2 = err;
+
+        if (e2 >-dx) {
+            err -= dy;
+            x0 += sx;
+        }
+
+        if (e2 < dy) {
+            err += dx;
+            y0 += sy;
+        }
+    }
+}
+
+static void
+tongue_outline(uint16_t* const pixels,
+               int       const linesize,
+               int       const w,
+               int       const h,
+               uint16_t  const maxval,
+               int       const upvp,
+               int       const xBias,
+               int       const yBias)
+{
+    const uint16_t rgbcolor[4] = { 65535, 65535, 65535, 65535 };
+    int wavelength;
+    int lx, ly;
+    int fx, fy;
+
+    for (wavelength = 380; wavelength <= 700; wavelength += 5) {
+        int icx, icy;
+
+        computeMonochromeColorLocation(wavelength, w, h, upvp,
+                                       &icx, &icy);
+
+        if (wavelength > 380)
+            draw_line(pixels, linesize, lx, ly, icx, icy, w, h, rgbcolor);
+        else {
+            fx = icx;
+            fy = icy;
+        }
+        lx = icx;
+        ly = icy;
+    }
+    draw_line(pixels, linesize, lx, ly, fx, fy, w, h, rgbcolor);
+}
+
+static void
+fill_in_tongue(uint16_t*                  const pixels,
+               int                        const linesize,
+               int                        const w,
+               int                        const h,
+               uint16_t                   const maxval,
+               const struct ColorSystem * const cs,
+               int                        const upvp,
+               int                        const xBias,
+               int                        const yBias,
+               int                        const highlight_gamut,
+               int                        const correct_gamma)
+{
+    int y;
+
+    /* Scan the image line by line and  fill  the  tongue  outline
+       with the RGB values determined by the color system for the x-y
+       co-ordinates within the tongue.
+    */
+
+    for (y = 0; y < h; ++y) {
+        int  present;  /* There is some tongue on this line */
+        int leftEdge; /* x position of leftmost pixel in tongue on this line */
+        int rightEdge; /* same, but rightmost */
+
+        find_tongue(pixels, w, linesize, y, &present, &leftEdge, &rightEdge);
+
+        if (present) {
+            int x;
+
+            for (x = leftEdge; x <= rightEdge; ++x) {
+                double cx, cy, cz, jr, jg, jb, jmax;
+                int r, g, b, mx;
+
+                if (upvp) {
+                    double up, vp;
+                    up = ((double) x) / (w - 1);
+                    vp = 1.0 - ((double) y) / (h - 1);
+                    upvp_to_xy(up, vp, &cx, &cy);
+                    cz = 1.0 - (cx + cy);
+                } else {
+                    cx = ((double) x) / (w - 1);
+                    cy = 1.0 - ((double) y) / (h - 1);
+                    cz = 1.0 - (cx + cy);
+                }
+
+                xyz_to_rgb(cs, cx, cy, cz, &jr, &jg, &jb);
+
+                mx = maxval;
+
+                /* Check whether the requested color  is  within  the
+                   gamut  achievable with the given color system.  If
+                   not, draw it in a reduced  intensity,  interpolated
+                   by desaturation to the closest within-gamut color. */
+
+                if (constrain_rgb(&jr, &jg, &jb))
+                    mx = highlight_gamut ? maxval : ((maxval + 1) * 3) / 4;
+
+                /* Scale to max(rgb) = 1. */
+                jmax = FFMAX(jr, FFMAX(jg, jb));
+                if (jmax > 0) {
+                    jr = jr / jmax;
+                    jg = jg / jmax;
+                    jb = jb / jmax;
+                }
+                /* gamma correct from linear rgb to nonlinear rgb. */
+                if (correct_gamma)
+                    gamma_correct_rgb(cs, &jr, &jg, &jb);
+                r = mx * jr;
+                g = mx * jg;
+                b = mx * jb;
+                pixels[y * linesize + x * 4 + 0] = r;
+                pixels[y * linesize + x * 4 + 1] = g;
+                pixels[y * linesize + x * 4 + 2] = b;
+                pixels[y * linesize + x * 4 + 3] = 65535;
+            }
+        }
+    }
+}
+
+static void
+plot_white_point(uint16_t*      pixels,
+                 int      const linesize,
+                 int      const w,
+                 int      const h,
+                 int      const maxval,
+                 int      const color_system,
+                 int      const upvp)
+{
+    const struct ColorSystem *cs = &color_systems[color_system];
+    int wx, wy;
+
+    if (upvp) {
+        double wup, wvp;
+        xy_to_upvp(cs->xWhite, cs->yWhite, &wup, &wvp);
+        wx = wup;
+        wy = wvp;
+        wx = (w - 1) * wup;
+        wy = (h - 1) - ((int) ((h - 1) * wvp));
+    } else {
+        wx = (w - 1) * cs->xWhite;
+        wy = (h - 1) - ((int) ((h - 1) * cs->yWhite));
+    }
+
+    draw_rline(pixels, linesize,
+               wx + Sz(3), wy, wx + Sz(10), wy,
+               w, h);
+    draw_rline(pixels, linesize,
+               wx - Sz(3), wy, wx - Sz(10), wy,
+               w, h);
+    draw_rline(pixels, linesize,
+               wx, wy + Sz(3), wx, wy + Sz(10),
+               w, h);
+    draw_rline(pixels, linesize,
+               wx, wy - Sz(3), wx, wy - Sz(10),
+               w, h);
+}
+
+static int draw_background(AVFilterContext *ctx)
+{
+    ChromascopeContext *s = ctx->priv;
+    const struct ColorSystem *cs = &color_systems[s->color_system];
+    AVFilterLink *outlink = ctx->outputs[0];
+    int w = s->size;
+    int h = s->size;
+    uint16_t *pixels;
+
+    if ((s->f = ff_get_video_buffer(outlink, outlink->w, outlink->h)) == NULL)
+        return AVERROR(ENOMEM);
+    pixels = (uint16_t *)s->f->data[0];
+
+    tongue_outline(pixels, s->f->linesize[0] / 2, w, h, 65535, s->luv, 0, 0);
+
+    fill_in_tongue(pixels, s->f->linesize[0] / 2, w, h, 65535, cs, s->luv, 0, 0,
+                   s->full_chart, s->correct_gamma);
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *in)
+{
+    AVFilterContext *ctx  = inlink->dst;
+    ChromascopeContext *s = ctx->priv;
+    AVFilterLink *outlink = ctx->outputs[0];
+    int i = s->intensity * 65535;
+    int w = s->size;
+    int h = s->size;
+    AVFrame *out;
+    int ret, x, y;
+
+    out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
+    if (!out) {
+        av_frame_free(&in);
+        return AVERROR(ENOMEM);
+    }
+    out->pts = in->pts;
+
+    if (!s->background) {
+        ret = draw_background(ctx);
+        if (ret < 0)
+            return ret;
+        s->background = 1;
+    }
+    for (y = 0; y < outlink->h; y++) {
+        memset(out->data[0] + y * out->linesize[0], 0, outlink->w * 8);
+    }
+
+    if (in->format == AV_PIX_FMT_RGBA) {
+        for (y = 0; y < inlink->h; y++) {
+            for (x = 0; x < inlink->w; x++) {
+                const uint8_t* src = in->data[0] + in->linesize[0] * y + x * 4;
+                double r = src[0] / 255.;
+                double g = src[1] / 255.;
+                double b = src[2] / 255.;
+                double cx, cy, cz;
+                uint16_t *dst;
+                int wx, wy;
+
+                rgb_to_xyz(r, g, b, &cx, &cy, &cz, s->m);
+                if (s->luv) {
+                    double up, vp;
+                    xy_to_upvp(cx, cy, &up, &vp);
+                    cx = up;
+                    cy = vp;
+                }
+                wx = (w - 1) * cx;
+                wy = (h - 1) - ((h - 1) * cy);
+
+                if (wx < 0 || wx >= w ||
+                    wy < 0 || wy >= h)
+                    continue;
+
+                dst = (uint16_t *)(out->data[0] + wy * out->linesize[0] + wx * 8 + 0);
+                dst[0] = FFMIN(dst[0] + i, 65535);
+                dst[1] = FFMIN(dst[1] + i, 65535);
+                dst[2] = FFMIN(dst[2] + i, 65535);
+                dst[3] = 65535;
+            }
+        }
+    } else if (in->format == AV_PIX_FMT_XYZ12) {
+        for (y = 0; y < inlink->h; y++) {
+            for (x = 0; x < inlink->w; x++) {
+                const uint16_t* src = (uint16_t *)(in->data[0] + in->linesize[0] * y + x * 6);
+                double cx = src[0];
+                double cy = src[1];
+                double cz = src[2];
+                double sum = cx + cy + cz;
+                uint16_t *dst;
+                int wx, wy;
+
+                cx = cx / sum;
+                cy = cy / sum;
+
+                if (s->luv) {
+                    double up, vp;
+                    xy_to_upvp(cx, cy, &up, &vp);
+                    cx = up;
+                    cy = vp;
+                }
+                wx = (w - 1) * cx;
+                wy = (h - 1) - ((h - 1) * cy);
+
+                if (wx < 0 || wx >= w ||
+                    wy < 0 || wy >= h)
+                    continue;
+
+                dst = (uint16_t *)(out->data[0] + wy * out->linesize[0] + wx * 8 + 0);
+                dst[0] = FFMIN(dst[0] + i, 65535);
+                dst[1] = FFMIN(dst[1] + i, 65535);
+                dst[2] = FFMIN(dst[2] + i, 65535);
+                dst[3] = 65535;
+            }
+        }
+    }
+
+    for (y = 0; y < outlink->h; y++) {
+        uint16_t *dst = (uint16_t *)(out->data[0] + y * out->linesize[0]);
+        uint16_t *src = (uint16_t *)(s->f->data[0] + y * s->f->linesize[0]);
+        for (x = 0; x < outlink->w; x++) {
+            if (dst[x * 4 + 0] == 0) {
+                dst[x * 4 + 0] = src[x * 4 + 0];
+                dst[x * 4 + 1] = src[x * 4 + 1];
+                dst[x * 4 + 2] = src[x * 4 + 2];
+                dst[x * 4 + 3] = src[x * 4 + 3];
+            }
+        }
+    }
+
+    if (s->show_white)
+        plot_white_point((uint16_t *)out->data[0], out->linesize[0] / 2,
+                         outlink->w, outlink->h, 65535,
+                         s->color_system, s->luv);
+
+    av_frame_free(&in);
+    return ff_filter_frame(outlink, out);
+}
+
+static void av_cold uninit(AVFilterContext *ctx)
+{
+    ChromascopeContext *s = ctx->priv;
+
+    av_frame_free(&s->f);
+}
+
+static int config_input(AVFilterLink *inlink)
+{
+    ChromascopeContext *s = inlink->dst->priv;
+
+    get_rgb2xyz_matrix(color_systems[s->color_system], s->m);
+
+    return 0;
+}
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .filter_frame = filter_frame,
+        .config_props = config_input,
+    },
+    { NULL }
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .config_props = config_output,
+    },
+    { NULL }
+};
+
+AVFilter ff_vf_chromascope = {
+    .name          = "chromascope",
+    .description   = NULL_IF_CONFIG_SMALL("Video chromascope."),
+    .priv_size     = sizeof(ChromascopeContext),
+    .priv_class    = &chromascope_class,
+    .query_formats = query_formats,
+    .uninit        = uninit,
+    .inputs        = inputs,
+    .outputs       = outputs,
+};
-- 
1.9.1

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
http://ffmpeg.org/mailman/listinfo/ffmpeg-devel

Reply via email to