Browse code

avfilter: add ciescope filter

Signed-off-by: Paul B Mahol <onemda@gmail.com>

Paul B Mahol authored on 2016/02/02 05:05:16
Showing 6 changed files
... ...
@@ -9,6 +9,7 @@ version <next>:
9 9
 - firequalizer filter
10 10
 - datascope filter
11 11
 - bench and abench filters
12
+- ciescope filter
12 13
 
13 14
 
14 15
 version 3.0:
... ...
@@ -4553,6 +4553,61 @@ ffmpeg -f lavfi -i color=c=black:s=1280x720 -i video.mp4 -shortest -filter_compl
4553 4553
 @end example
4554 4554
 @end itemize
4555 4555
 
4556
+@section ciescope
4557
+
4558
+Display CIE color diagram with pixels overlaid onto it.
4559
+
4560
+The filter acccepts the following options:
4561
+
4562
+@table @option
4563
+@item system
4564
+Set color system.
4565
+
4566
+@table @samp
4567
+@item ntsc, 470m
4568
+@item ebu, 470bg
4569
+@item smpte
4570
+@item 240m
4571
+@item apple
4572
+@item widergb
4573
+@item cie1931
4574
+@item rec709, hdtv
4575
+@item uhdtv, rec2020
4576
+@end table
4577
+
4578
+@item cie
4579
+Set CIE system.
4580
+
4581
+@table @samp
4582
+@item xyy
4583
+@item ucs
4584
+@item luv
4585
+@end table
4586
+
4587
+@item gamuts
4588
+Set what gamuts to draw.
4589
+
4590
+See @code{system} option for avaiable values.
4591
+
4592
+@item size, s
4593
+Set ciescope size, by default set to 512.
4594
+
4595
+@item intensity, i
4596
+Set intensity used to map input pixel values to CIE diagram.
4597
+
4598
+@item contrast
4599
+Set contrast used to draw tongue colors that are out of active color system gamut.
4600
+
4601
+@item corrgamma
4602
+Correct gamma displayed on scope, by default enabled.
4603
+
4604
+@item showwhite
4605
+Show white point on CIE diagram, by default disabled.
4606
+
4607
+@item gamma
4608
+Set input gamma. Used only with XYZ input color space.
4609
+@end table
4610
+
4556 4611
 @section codecview
4557 4612
 
4558 4613
 Visualize information exported by some codecs.
... ...
@@ -124,6 +124,7 @@ OBJS-$(CONFIG_BLEND_FILTER)                  += vf_blend.o dualinput.o framesync
124 124
 OBJS-$(CONFIG_BOXBLUR_FILTER)                += vf_boxblur.o
125 125
 OBJS-$(CONFIG_BWDIF_FILTER)                  += vf_bwdif.o
126 126
 OBJS-$(CONFIG_CHROMAKEY_FILTER)              += vf_chromakey.o
127
+OBJS-$(CONFIG_CIESCOPE_FILTER)               += vf_ciescope.o
127 128
 OBJS-$(CONFIG_CODECVIEW_FILTER)              += vf_codecview.o
128 129
 OBJS-$(CONFIG_COLORBALANCE_FILTER)           += vf_colorbalance.o
129 130
 OBJS-$(CONFIG_COLORCHANNELMIXER_FILTER)      += vf_colorchannelmixer.o
... ...
@@ -145,6 +145,7 @@ void avfilter_register_all(void)
145 145
     REGISTER_FILTER(BOXBLUR,        boxblur,        vf);
146 146
     REGISTER_FILTER(BWDIF,          bwdif,          vf);
147 147
     REGISTER_FILTER(CHROMAKEY,      chromakey,      vf);
148
+    REGISTER_FILTER(CIESCOPE,       ciescope,       vf);
148 149
     REGISTER_FILTER(CODECVIEW,      codecview,      vf);
149 150
     REGISTER_FILTER(COLORBALANCE,   colorbalance,   vf);
150 151
     REGISTER_FILTER(COLORCHANNELMIXER, colorchannelmixer, vf);
... ...
@@ -30,7 +30,7 @@
30 30
 #include "libavutil/version.h"
31 31
 
32 32
 #define LIBAVFILTER_VERSION_MAJOR   6
33
-#define LIBAVFILTER_VERSION_MINOR  38
33
+#define LIBAVFILTER_VERSION_MINOR  39
34 34
 #define LIBAVFILTER_VERSION_MICRO 100
35 35
 
36 36
 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
37 37
new file mode 100644
... ...
@@ -0,0 +1,1509 @@
0
+/*
1
+ * Copyright (c) 2000 John Walker
2
+ * Copyright (c) 2016 Paul B Mahol
3
+ *
4
+ * This file is part of FFmpeg.
5
+ *
6
+ * FFmpeg is free software; you can redistribute it and/or
7
+ * modify it under the terms of the GNU Lesser General Public
8
+ * License as published by the Free Software Foundation; either
9
+ * version 2.1 of the License, or (at your option) any later version.
10
+ *
11
+ * FFmpeg is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
+ * Lesser General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU Lesser General Public
17
+ * License along with FFmpeg; if not, write to the Free Software
18
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
+ */
20
+
21
+#include "libavutil/avassert.h"
22
+#include "libavutil/intreadwrite.h"
23
+#include "libavutil/opt.h"
24
+#include "libavutil/parseutils.h"
25
+#include "libavutil/pixdesc.h"
26
+#include "avfilter.h"
27
+#include "formats.h"
28
+#include "internal.h"
29
+#include "video.h"
30
+
31
+enum CieSystem {
32
+    XYY,
33
+    UCS,
34
+    LUV,
35
+    NB_CIE
36
+};
37
+
38
+enum ColorsSystems {
39
+    NTSCsystem,
40
+    EBUsystem,
41
+    SMPTEsystem,
42
+    SMPTE240Msystem,
43
+    APPLEsystem,
44
+    wRGBsystem,
45
+    CIE1931system,
46
+    Rec709system,
47
+    Rec2020system,
48
+    NB_CS
49
+};
50
+
51
+typedef struct CiescopeContext {
52
+    const AVClass *class;
53
+    int color_system;
54
+    unsigned gamuts;
55
+    int size;
56
+    int show_white;
57
+    int correct_gamma;
58
+    int cie;
59
+    float intensity;
60
+    float contrast;
61
+    int background;
62
+
63
+    double log2lin[65536];
64
+    double igamma;
65
+    double i[3][3];
66
+    double m[3][3];
67
+    AVFrame *f;
68
+    void (*filter)(AVFilterContext *ctx, AVFrame *in, double *cx, double *cy, int x, int y);
69
+} CiescopeContext;
70
+
71
+#define OFFSET(x) offsetof(CiescopeContext, x)
72
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
73
+
74
+static const AVOption ciescope_options[] = {
75
+    { "system",     "set color system", OFFSET(color_system), AV_OPT_TYPE_INT, {.i64=Rec709system}, 0, NB_CS-1, FLAGS, "system" },
76
+    {   "ntsc",       "NTSC 1953 Y'I'O' (ITU-R BT.470 System M)", 0, AV_OPT_TYPE_CONST, {.i64=NTSCsystem},     0, 0, FLAGS, "system" },
77
+    {   "470m",       "NTSC 1953 Y'I'O' (ITU-R BT.470 System M)", 0, AV_OPT_TYPE_CONST, {.i64=NTSCsystem},     0, 0, FLAGS, "system" },
78
+    {   "ebu",        "EBU Y'U'V' (PAL/SECAM) (ITU-R BT.470 System B, G)", 0, AV_OPT_TYPE_CONST, {.i64=EBUsystem},      0, 0, FLAGS, "system" },
79
+    {   "470bg",      "EBU Y'U'V' (PAL/SECAM) (ITU-R BT.470 System B, G)", 0, AV_OPT_TYPE_CONST, {.i64=EBUsystem},      0, 0, FLAGS, "system" },
80
+    {   "smpte",      "SMPTE-C RGB",            0, AV_OPT_TYPE_CONST, {.i64=SMPTEsystem},    0, 0, FLAGS, "system" },
81
+    {   "240m",       "SMPTE-240M Y'PbPr",      0, AV_OPT_TYPE_CONST, {.i64=SMPTE240Msystem},0, 0, FLAGS, "system" },
82
+    {   "apple",      "Apple RGB",              0, AV_OPT_TYPE_CONST, {.i64=APPLEsystem},    0, 0, FLAGS, "system" },
83
+    {   "widergb",    "Adobe Wide Gamut RGB",   0, AV_OPT_TYPE_CONST, {.i64=wRGBsystem},     0, 0, FLAGS, "system" },
84
+    {   "cie1931",    "CIE 1931 RGB",           0, AV_OPT_TYPE_CONST, {.i64=CIE1931system},  0, 0, FLAGS, "system" },
85
+    {   "hdtv",       "ITU.BT-709 Y'CbCr",      0, AV_OPT_TYPE_CONST, {.i64=Rec709system},   0, 0, FLAGS, "system" },
86
+    {   "rec709",     "ITU.BT-709 Y'CbCr",      0, AV_OPT_TYPE_CONST, {.i64=Rec709system},   0, 0, FLAGS, "system" },
87
+    {   "uhdtv",      "ITU-R.BT-2020",          0, AV_OPT_TYPE_CONST, {.i64=Rec2020system},  0, 0, FLAGS, "system" },
88
+    {   "rec2020",    "ITU-R.BT-2020",          0, AV_OPT_TYPE_CONST, {.i64=Rec2020system},  0, 0, FLAGS, "system" },
89
+    { "cie",        "set cie system", OFFSET(cie), AV_OPT_TYPE_INT,   {.i64=XYY}, 0, NB_CIE-1, FLAGS, "cie" },
90
+    {   "xyy",      "CIE 1931 xyY", 0, AV_OPT_TYPE_CONST, {.i64=XYY}, 0, 0, FLAGS, "cie" },
91
+    {   "ucs",      "CIE 1960 UCS", 0, AV_OPT_TYPE_CONST, {.i64=UCS}, 0, 0, FLAGS, "cie" },
92
+    {   "luv",      "CIE 1976 Luv", 0, AV_OPT_TYPE_CONST, {.i64=LUV}, 0, 0, FLAGS, "cie" },
93
+    { "gamuts",     "set what gamuts to draw", OFFSET(gamuts), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, 0xFFF, FLAGS, "gamuts" },
94
+    {   "ntsc",     NULL, 0, AV_OPT_TYPE_CONST, {.i64=1<<NTSCsystem},      0, 0, FLAGS, "gamuts" },
95
+    {   "470m",     NULL, 0, AV_OPT_TYPE_CONST, {.i64=1<<NTSCsystem},      0, 0, FLAGS, "gamuts" },
96
+    {   "ebu",      NULL, 0, AV_OPT_TYPE_CONST, {.i64=1<<EBUsystem},       0, 0, FLAGS, "gamuts" },
97
+    {   "470bg",    NULL, 0, AV_OPT_TYPE_CONST, {.i64=1<<EBUsystem},       0, 0, FLAGS, "gamuts" },
98
+    {   "smpte",    NULL, 0, AV_OPT_TYPE_CONST, {.i64=1<<SMPTEsystem},     0, 0, FLAGS, "gamuts" },
99
+    {   "240m",     NULL, 0, AV_OPT_TYPE_CONST, {.i64=1<<SMPTE240Msystem}, 0, 0, FLAGS, "gamuts" },
100
+    {   "apple",    NULL, 0, AV_OPT_TYPE_CONST, {.i64=1<<APPLEsystem},     0, 0, FLAGS, "gamuts" },
101
+    {   "widergb",  NULL, 0, AV_OPT_TYPE_CONST, {.i64=1<<wRGBsystem},      0, 0, FLAGS, "gamuts" },
102
+    {   "cie1931",  NULL, 0, AV_OPT_TYPE_CONST, {.i64=1<<CIE1931system},   0, 0, FLAGS, "gamuts" },
103
+    {   "hdtv",     NULL, 0, AV_OPT_TYPE_CONST, {.i64=1<<Rec709system},    0, 0, FLAGS, "gamuts" },
104
+    {   "rec709",   NULL, 0, AV_OPT_TYPE_CONST, {.i64=1<<Rec709system},    0, 0, FLAGS, "gamuts" },
105
+    {   "uhdtv",    NULL, 0, AV_OPT_TYPE_CONST, {.i64=1<<Rec2020system},   0, 0, FLAGS, "gamuts" },
106
+    {   "rec2020",  NULL, 0, AV_OPT_TYPE_CONST, {.i64=1<<Rec2020system},   0, 0, FLAGS, "gamuts" },
107
+    { "size",       "set ciescope size", OFFSET(size), AV_OPT_TYPE_INT, {.i64=512}, 256, 8192, FLAGS },
108
+    { "s",          "set ciescope size", OFFSET(size), AV_OPT_TYPE_INT, {.i64=512}, 256, 8192, FLAGS },
109
+    { "intensity",  "set ciescope intensity", OFFSET(intensity), AV_OPT_TYPE_FLOAT, {.dbl=0.001}, 0, 1, FLAGS },
110
+    { "i",          "set ciescope intensity", OFFSET(intensity), AV_OPT_TYPE_FLOAT, {.dbl=0.001}, 0, 1, FLAGS },
111
+    { "contrast",   NULL, OFFSET(contrast), AV_OPT_TYPE_FLOAT, {.dbl=0.75},  0, 1, FLAGS },
112
+    { "corrgamma",  NULL, OFFSET(correct_gamma), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, FLAGS },
113
+    { "showwhite",  NULL, OFFSET(show_white), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
114
+    { "gamma",      NULL, OFFSET(igamma), AV_OPT_TYPE_DOUBLE, {.dbl=2.6}, 0.1, 6, FLAGS },
115
+    { NULL }
116
+};
117
+
118
+AVFILTER_DEFINE_CLASS(ciescope);
119
+
120
+static const enum AVPixelFormat in_pix_fmts[] = {
121
+    AV_PIX_FMT_RGB24,
122
+    AV_PIX_FMT_RGBA,
123
+    AV_PIX_FMT_RGB48,
124
+    AV_PIX_FMT_RGBA64,
125
+    AV_PIX_FMT_XYZ12,
126
+    AV_PIX_FMT_NONE
127
+};
128
+
129
+static const enum AVPixelFormat out_pix_fmts[] = {
130
+    AV_PIX_FMT_RGBA64,
131
+    AV_PIX_FMT_NONE
132
+};
133
+
134
+static int query_formats(AVFilterContext *ctx)
135
+{
136
+    int ret;
137
+
138
+    if ((ret = ff_formats_ref(ff_make_format_list(in_pix_fmts), &ctx->inputs[0]->out_formats)) < 0)
139
+        return ret;
140
+
141
+    if ((ret = ff_formats_ref(ff_make_format_list(out_pix_fmts), &ctx->outputs[0]->in_formats)) < 0)
142
+        return ret;
143
+
144
+    return 0;
145
+}
146
+
147
+static int config_output(AVFilterLink *outlink)
148
+{
149
+    CiescopeContext *s = outlink->src->priv;
150
+
151
+    outlink->h = outlink->w = s->size;
152
+    outlink->sample_aspect_ratio = (AVRational){1,1};
153
+
154
+    return 0;
155
+}
156
+
157
+/* A  color  system is defined by the CIE x and y  coordinates of its
158
+   three primary illuminants and the x and y coordinates of the  white
159
+   point. */
160
+
161
+struct ColorSystem {
162
+    double xRed, yRed,                /* Red primary illuminant */
163
+           xGreen, yGreen,            /* Green primary illuminant */
164
+           xBlue, yBlue,              /* Blue primary illuminant */
165
+           xWhite, yWhite,            /* White point */
166
+           gamma;             /* gamma of nonlinear correction */
167
+};
168
+
169
+static float const spectral_chromaticity[][3] = {
170
+    { 0.175560, 0.005294, 0.819146 },
171
+    { 0.175483, 0.005286, 0.819231 },
172
+    { 0.175400, 0.005279, 0.819321 },
173
+    { 0.175317, 0.005271, 0.819412 },
174
+    { 0.175237, 0.005263, 0.819500 },
175
+    { 0.175161, 0.005256, 0.819582 },
176
+    { 0.175088, 0.005247, 0.819665 },
177
+    { 0.175015, 0.005236, 0.819749 },
178
+    { 0.174945, 0.005226, 0.819829 },
179
+    { 0.174880, 0.005221, 0.819899 },
180
+    { 0.174821, 0.005221, 0.819959 },
181
+    { 0.174770, 0.005229, 0.820001 },
182
+    { 0.174722, 0.005238, 0.820040 },
183
+    { 0.174665, 0.005236, 0.820098 },
184
+    { 0.174595, 0.005218, 0.820187 },
185
+    { 0.174510, 0.005182, 0.820309 },
186
+    { 0.174409, 0.005127, 0.820464 },
187
+    { 0.174308, 0.005068, 0.820624 },
188
+    { 0.174222, 0.005017, 0.820761 },
189
+    { 0.174156, 0.004981, 0.820863 },
190
+    { 0.174112, 0.004964, 0.820924 },
191
+    { 0.174088, 0.004964, 0.820948 },
192
+    { 0.174073, 0.004973, 0.820955 },
193
+    { 0.174057, 0.004982, 0.820961 },
194
+    { 0.174036, 0.004986, 0.820978 },
195
+    { 0.174008, 0.004981, 0.821012 },
196
+    { 0.173972, 0.004964, 0.821064 },
197
+    { 0.173932, 0.004943, 0.821125 },
198
+    { 0.173889, 0.004926, 0.821185 },
199
+    { 0.173845, 0.004916, 0.821239 },
200
+    { 0.173801, 0.004915, 0.821284 },
201
+    { 0.173754, 0.004925, 0.821321 },
202
+    { 0.173705, 0.004937, 0.821358 },
203
+    { 0.173655, 0.004944, 0.821401 },
204
+    { 0.173606, 0.004940, 0.821454 },
205
+    { 0.173560, 0.004923, 0.821517 },
206
+    { 0.173514, 0.004895, 0.821590 },
207
+    { 0.173468, 0.004865, 0.821667 },
208
+    { 0.173424, 0.004836, 0.821740 },
209
+    { 0.173380, 0.004813, 0.821807 },
210
+    { 0.173337, 0.004797, 0.821866 },
211
+    { 0.173291, 0.004786, 0.821923 },
212
+    { 0.173238, 0.004779, 0.821983 },
213
+    { 0.173174, 0.004775, 0.822051 },
214
+    { 0.173101, 0.004774, 0.822125 },
215
+    { 0.173021, 0.004775, 0.822204 },
216
+    { 0.172934, 0.004781, 0.822285 },
217
+    { 0.172843, 0.004791, 0.822366 },
218
+    { 0.172751, 0.004799, 0.822450 },
219
+    { 0.172662, 0.004802, 0.822536 },
220
+    { 0.172577, 0.004799, 0.822624 },
221
+    { 0.172489, 0.004795, 0.822715 },
222
+    { 0.172396, 0.004796, 0.822808 },
223
+    { 0.172296, 0.004803, 0.822901 },
224
+    { 0.172192, 0.004815, 0.822993 },
225
+    { 0.172087, 0.004833, 0.823081 },
226
+    { 0.171982, 0.004855, 0.823163 },
227
+    { 0.171871, 0.004889, 0.823240 },
228
+    { 0.171741, 0.004939, 0.823319 },
229
+    { 0.171587, 0.005010, 0.823402 },
230
+    { 0.171407, 0.005102, 0.823490 },
231
+    { 0.171206, 0.005211, 0.823583 },
232
+    { 0.170993, 0.005334, 0.823674 },
233
+    { 0.170771, 0.005470, 0.823759 },
234
+    { 0.170541, 0.005621, 0.823838 },
235
+    { 0.170301, 0.005789, 0.823911 },
236
+    { 0.170050, 0.005974, 0.823976 },
237
+    { 0.169786, 0.006177, 0.824037 },
238
+    { 0.169505, 0.006398, 0.824097 },
239
+    { 0.169203, 0.006639, 0.824158 },
240
+    { 0.168878, 0.006900, 0.824222 },
241
+    { 0.168525, 0.007184, 0.824291 },
242
+    { 0.168146, 0.007491, 0.824363 },
243
+    { 0.167746, 0.007821, 0.824433 },
244
+    { 0.167328, 0.008175, 0.824496 },
245
+    { 0.166895, 0.008556, 0.824549 },
246
+    { 0.166446, 0.008964, 0.824589 },
247
+    { 0.165977, 0.009402, 0.824622 },
248
+    { 0.165483, 0.009865, 0.824652 },
249
+    { 0.164963, 0.010351, 0.824687 },
250
+    { 0.164412, 0.010858, 0.824731 },
251
+    { 0.163828, 0.011385, 0.824787 },
252
+    { 0.163210, 0.011937, 0.824853 },
253
+    { 0.162552, 0.012520, 0.824928 },
254
+    { 0.161851, 0.013137, 0.825011 },
255
+    { 0.161105, 0.013793, 0.825102 },
256
+    { 0.160310, 0.014491, 0.825199 },
257
+    { 0.159466, 0.015232, 0.825302 },
258
+    { 0.158573, 0.016015, 0.825412 },
259
+    { 0.157631, 0.016840, 0.825529 },
260
+    { 0.156641, 0.017705, 0.825654 },
261
+    { 0.155605, 0.018609, 0.825786 },
262
+    { 0.154525, 0.019556, 0.825920 },
263
+    { 0.153397, 0.020554, 0.826049 },
264
+    { 0.152219, 0.021612, 0.826169 },
265
+    { 0.150985, 0.022740, 0.826274 },
266
+    { 0.149691, 0.023950, 0.826359 },
267
+    { 0.148337, 0.025247, 0.826416 },
268
+    { 0.146928, 0.026635, 0.826437 },
269
+    { 0.145468, 0.028118, 0.826413 },
270
+    { 0.143960, 0.029703, 0.826337 },
271
+    { 0.142405, 0.031394, 0.826201 },
272
+    { 0.140796, 0.033213, 0.825991 },
273
+    { 0.139121, 0.035201, 0.825679 },
274
+    { 0.137364, 0.037403, 0.825233 },
275
+    { 0.135503, 0.039879, 0.824618 },
276
+    { 0.133509, 0.042692, 0.823798 },
277
+    { 0.131371, 0.045876, 0.822753 },
278
+    { 0.129086, 0.049450, 0.821464 },
279
+    { 0.126662, 0.053426, 0.819912 },
280
+    { 0.124118, 0.057803, 0.818079 },
281
+    { 0.121469, 0.062588, 0.815944 },
282
+    { 0.118701, 0.067830, 0.813468 },
283
+    { 0.115807, 0.073581, 0.810612 },
284
+    { 0.112776, 0.079896, 0.807328 },
285
+    { 0.109594, 0.086843, 0.803563 },
286
+    { 0.106261, 0.094486, 0.799253 },
287
+    { 0.102776, 0.102864, 0.794360 },
288
+    { 0.099128, 0.112007, 0.788865 },
289
+    { 0.095304, 0.121945, 0.782751 },
290
+    { 0.091294, 0.132702, 0.776004 },
291
+    { 0.087082, 0.144317, 0.768601 },
292
+    { 0.082680, 0.156866, 0.760455 },
293
+    { 0.078116, 0.170420, 0.751464 },
294
+    { 0.073437, 0.185032, 0.741531 },
295
+    { 0.068706, 0.200723, 0.730571 },
296
+    { 0.063993, 0.217468, 0.718539 },
297
+    { 0.059316, 0.235254, 0.705430 },
298
+    { 0.054667, 0.254096, 0.691238 },
299
+    { 0.050031, 0.274002, 0.675967 },
300
+    { 0.045391, 0.294976, 0.659633 },
301
+    { 0.040757, 0.316981, 0.642262 },
302
+    { 0.036195, 0.339900, 0.623905 },
303
+    { 0.031756, 0.363598, 0.604646 },
304
+    { 0.027494, 0.387921, 0.584584 },
305
+    { 0.023460, 0.412703, 0.563837 },
306
+    { 0.019705, 0.437756, 0.542539 },
307
+    { 0.016268, 0.462955, 0.520777 },
308
+    { 0.013183, 0.488207, 0.498610 },
309
+    { 0.010476, 0.513404, 0.476120 },
310
+    { 0.008168, 0.538423, 0.453409 },
311
+    { 0.006285, 0.563068, 0.430647 },
312
+    { 0.004875, 0.587116, 0.408008 },
313
+    { 0.003982, 0.610447, 0.385570 },
314
+    { 0.003636, 0.633011, 0.363352 },
315
+    { 0.003859, 0.654823, 0.341318 },
316
+    { 0.004646, 0.675898, 0.319456 },
317
+    { 0.006011, 0.696120, 0.297869 },
318
+    { 0.007988, 0.715342, 0.276670 },
319
+    { 0.010603, 0.733413, 0.255984 },
320
+    { 0.013870, 0.750186, 0.235943 },
321
+    { 0.017766, 0.765612, 0.216622 },
322
+    { 0.022244, 0.779630, 0.198126 },
323
+    { 0.027273, 0.792104, 0.180623 },
324
+    { 0.032820, 0.802926, 0.164254 },
325
+    { 0.038852, 0.812016, 0.149132 },
326
+    { 0.045328, 0.819391, 0.135281 },
327
+    { 0.052177, 0.825164, 0.122660 },
328
+    { 0.059326, 0.829426, 0.111249 },
329
+    { 0.066716, 0.832274, 0.101010 },
330
+    { 0.074302, 0.833803, 0.091894 },
331
+    { 0.082053, 0.834090, 0.083856 },
332
+    { 0.089942, 0.833289, 0.076769 },
333
+    { 0.097940, 0.831593, 0.070468 },
334
+    { 0.106021, 0.829178, 0.064801 },
335
+    { 0.114161, 0.826207, 0.059632 },
336
+    { 0.122347, 0.822770, 0.054882 },
337
+    { 0.130546, 0.818928, 0.050526 },
338
+    { 0.138702, 0.814774, 0.046523 },
339
+    { 0.146773, 0.810395, 0.042832 },
340
+    { 0.154722, 0.805864, 0.039414 },
341
+    { 0.162535, 0.801238, 0.036226 },
342
+    { 0.170237, 0.796519, 0.033244 },
343
+    { 0.177850, 0.791687, 0.030464 },
344
+    { 0.185391, 0.786728, 0.027881 },
345
+    { 0.192876, 0.781629, 0.025495 },
346
+    { 0.200309, 0.776399, 0.023292 },
347
+    { 0.207690, 0.771055, 0.021255 },
348
+    { 0.215030, 0.765595, 0.019375 },
349
+    { 0.222337, 0.760020, 0.017643 },
350
+    { 0.229620, 0.754329, 0.016051 },
351
+    { 0.236885, 0.748524, 0.014591 },
352
+    { 0.244133, 0.742614, 0.013253 },
353
+    { 0.251363, 0.736606, 0.012031 },
354
+    { 0.258578, 0.730507, 0.010916 },
355
+    { 0.265775, 0.724324, 0.009901 },
356
+    { 0.272958, 0.718062, 0.008980 },
357
+    { 0.280129, 0.711725, 0.008146 },
358
+    { 0.287292, 0.705316, 0.007391 },
359
+    { 0.294450, 0.698842, 0.006708 },
360
+    { 0.301604, 0.692308, 0.006088 },
361
+    { 0.308760, 0.685712, 0.005528 },
362
+    { 0.315914, 0.679063, 0.005022 },
363
+    { 0.323066, 0.672367, 0.004566 },
364
+    { 0.330216, 0.665628, 0.004156 },
365
+    { 0.337363, 0.658848, 0.003788 },
366
+    { 0.344513, 0.652028, 0.003459 },
367
+    { 0.351664, 0.645172, 0.003163 },
368
+    { 0.358814, 0.638287, 0.002899 },
369
+    { 0.365959, 0.631379, 0.002662 },
370
+    { 0.373102, 0.624451, 0.002448 },
371
+    { 0.380244, 0.617502, 0.002254 },
372
+    { 0.387379, 0.610542, 0.002079 },
373
+    { 0.394507, 0.603571, 0.001922 },
374
+    { 0.401626, 0.596592, 0.001782 },
375
+    { 0.408736, 0.589607, 0.001657 },
376
+    { 0.415836, 0.582618, 0.001546 },
377
+    { 0.422921, 0.575631, 0.001448 },
378
+    { 0.429989, 0.568649, 0.001362 },
379
+    { 0.437036, 0.561676, 0.001288 },
380
+    { 0.444062, 0.554714, 0.001224 },
381
+    { 0.451065, 0.547766, 0.001169 },
382
+    { 0.458041, 0.540837, 0.001123 },
383
+    { 0.464986, 0.533930, 0.001084 },
384
+    { 0.471899, 0.527051, 0.001051 },
385
+    { 0.478775, 0.520202, 0.001023 },
386
+    { 0.485612, 0.513389, 0.001000 },
387
+    { 0.492405, 0.506615, 0.000980 },
388
+    { 0.499151, 0.499887, 0.000962 },
389
+    { 0.505845, 0.493211, 0.000944 },
390
+    { 0.512486, 0.486591, 0.000923 },
391
+    { 0.519073, 0.480029, 0.000899 },
392
+    { 0.525600, 0.473527, 0.000872 },
393
+    { 0.532066, 0.467091, 0.000843 },
394
+    { 0.538463, 0.460725, 0.000812 },
395
+    { 0.544787, 0.454434, 0.000779 },
396
+    { 0.551031, 0.448225, 0.000744 },
397
+    { 0.557193, 0.442099, 0.000708 },
398
+    { 0.563269, 0.436058, 0.000673 },
399
+    { 0.569257, 0.430102, 0.000641 },
400
+    { 0.575151, 0.424232, 0.000616 },
401
+    { 0.580953, 0.418447, 0.000601 },
402
+    { 0.586650, 0.412758, 0.000591 },
403
+    { 0.592225, 0.407190, 0.000586 },
404
+    { 0.597658, 0.401762, 0.000580 },
405
+    { 0.602933, 0.396497, 0.000571 },
406
+    { 0.608035, 0.391409, 0.000556 },
407
+    { 0.612977, 0.386486, 0.000537 },
408
+    { 0.617779, 0.381706, 0.000516 },
409
+    { 0.622459, 0.377047, 0.000493 },
410
+    { 0.627037, 0.372491, 0.000472 },
411
+    { 0.631521, 0.368026, 0.000453 },
412
+    { 0.635900, 0.363665, 0.000435 },
413
+    { 0.640156, 0.359428, 0.000416 },
414
+    { 0.644273, 0.355331, 0.000396 },
415
+    { 0.648233, 0.351395, 0.000372 },
416
+    { 0.652028, 0.347628, 0.000344 },
417
+    { 0.655669, 0.344018, 0.000313 },
418
+    { 0.659166, 0.340553, 0.000281 },
419
+    { 0.662528, 0.337221, 0.000251 },
420
+    { 0.665764, 0.334011, 0.000226 },
421
+    { 0.668874, 0.330919, 0.000207 },
422
+    { 0.671859, 0.327947, 0.000194 },
423
+    { 0.674720, 0.325095, 0.000185 },
424
+    { 0.677459, 0.322362, 0.000179 },
425
+    { 0.680079, 0.319747, 0.000174 },
426
+    { 0.682582, 0.317249, 0.000170 },
427
+    { 0.684971, 0.314863, 0.000167 },
428
+    { 0.687250, 0.312586, 0.000164 },
429
+    { 0.689426, 0.310414, 0.000160 },
430
+    { 0.691504, 0.308342, 0.000154 },
431
+    { 0.693490, 0.306366, 0.000145 },
432
+    { 0.695389, 0.304479, 0.000133 },
433
+    { 0.697206, 0.302675, 0.000119 },
434
+    { 0.698944, 0.300950, 0.000106 },
435
+    { 0.700606, 0.299301, 0.000093 },
436
+    { 0.702193, 0.297725, 0.000083 },
437
+    { 0.703709, 0.296217, 0.000074 },
438
+    { 0.705163, 0.294770, 0.000067 },
439
+    { 0.706563, 0.293376, 0.000061 },
440
+    { 0.707918, 0.292027, 0.000055 },
441
+    { 0.709231, 0.290719, 0.000050 },
442
+    { 0.710500, 0.289453, 0.000047 },
443
+    { 0.711724, 0.288232, 0.000044 },
444
+    { 0.712901, 0.287057, 0.000041 },
445
+    { 0.714032, 0.285929, 0.000040 },
446
+    { 0.715117, 0.284845, 0.000038 },
447
+    { 0.716159, 0.283804, 0.000036 },
448
+    { 0.717159, 0.282806, 0.000035 },
449
+    { 0.718116, 0.281850, 0.000034 },
450
+    { 0.719033, 0.280935, 0.000032 },
451
+    { 0.719912, 0.280058, 0.000030 },
452
+    { 0.720753, 0.279219, 0.000028 },
453
+    { 0.721555, 0.278420, 0.000026 },
454
+    { 0.722315, 0.277662, 0.000023 },
455
+    { 0.723032, 0.276948, 0.000020 },
456
+    { 0.723702, 0.276282, 0.000016 },
457
+    { 0.724328, 0.275660, 0.000012 },
458
+    { 0.724914, 0.275078, 0.000007 },
459
+    { 0.725467, 0.274530, 0.000003 },
460
+    { 0.725992, 0.274008, 0.000000 },
461
+    { 0.726495, 0.273505, 0.000000 },
462
+    { 0.726975, 0.273025, 0.000000 },
463
+    { 0.727432, 0.272568, 0.000000 },
464
+    { 0.727864, 0.272136, 0.000000 },
465
+    { 0.728272, 0.271728, 0.000000 },
466
+    { 0.728656, 0.271344, 0.000000 },
467
+    { 0.729020, 0.270980, 0.000000 },
468
+    { 0.729361, 0.270639, 0.000000 },
469
+    { 0.729678, 0.270322, 0.000000 },
470
+    { 0.729969, 0.270031, 0.000000 },
471
+    { 0.730234, 0.269766, 0.000000 },
472
+    { 0.730474, 0.269526, 0.000000 },
473
+    { 0.730693, 0.269307, 0.000000 },
474
+    { 0.730896, 0.269104, 0.000000 },
475
+    { 0.731089, 0.268911, 0.000000 },
476
+    { 0.731280, 0.268720, 0.000000 },
477
+    { 0.731467, 0.268533, 0.000000 },
478
+    { 0.731650, 0.268350, 0.000000 },
479
+    { 0.731826, 0.268174, 0.000000 },
480
+    { 0.731993, 0.268007, 0.000000 },
481
+    { 0.732150, 0.267850, 0.000000 },
482
+    { 0.732300, 0.267700, 0.000000 },
483
+    { 0.732443, 0.267557, 0.000000 },
484
+    { 0.732581, 0.267419, 0.000000 },
485
+    { 0.732719, 0.267281, 0.000000 },
486
+    { 0.732859, 0.267141, 0.000000 },
487
+    { 0.733000, 0.267000, 0.000000 },
488
+    { 0.733142, 0.266858, 0.000000 },
489
+    { 0.733281, 0.266719, 0.000000 },
490
+    { 0.733417, 0.266583, 0.000000 },
491
+    { 0.733551, 0.266449, 0.000000 },
492
+    { 0.733683, 0.266317, 0.000000 },
493
+    { 0.733813, 0.266187, 0.000000 },
494
+    { 0.733936, 0.266064, 0.000000 },
495
+    { 0.734047, 0.265953, 0.000000 },
496
+    { 0.734143, 0.265857, 0.000000 },
497
+    { 0.734221, 0.265779, 0.000000 },
498
+    { 0.734286, 0.265714, 0.000000 },
499
+    { 0.734341, 0.265659, 0.000000 },
500
+    { 0.734390, 0.265610, 0.000000 },
501
+    { 0.734438, 0.265562, 0.000000 },
502
+    { 0.734482, 0.265518, 0.000000 },
503
+    { 0.734523, 0.265477, 0.000000 },
504
+    { 0.734560, 0.265440, 0.000000 },
505
+    { 0.734592, 0.265408, 0.000000 },
506
+    { 0.734621, 0.265379, 0.000000 },
507
+    { 0.734649, 0.265351, 0.000000 },
508
+    { 0.734673, 0.265327, 0.000000 },
509
+    { 0.734690, 0.265310, 0.000000 },
510
+    { 0.734690, 0.265310, 0.000000 },
511
+    { 0.734690, 0.265310, 0.000000 },
512
+    { 0.734690, 0.265310, 0.000000 },
513
+    { 0.734690, 0.265310, 0.000000 },
514
+    { 0.734690, 0.265310, 0.000000 },
515
+    { 0.734690, 0.265310, 0.000000 },
516
+    { 0.734690, 0.265310, 0.000000 },
517
+    { 0.734690, 0.265310, 0.000000 },
518
+    { 0.734690, 0.265310, 0.000000 },
519
+    { 0.734690, 0.265310, 0.000000 },
520
+    { 0.734690, 0.265310, 0.000000 },
521
+    { 0.734690, 0.265310, 0.000000 },
522
+    { 0.734690, 0.265310, 0.000000 },
523
+    { 0.734690, 0.265310, 0.000000 },
524
+    { 0.734690, 0.265310, 0.000000 },
525
+    { 0.734690, 0.265310, 0.000000 },
526
+    { 0.734690, 0.265310, 0.000000 },
527
+    { 0.734690, 0.265310, 0.000000 },
528
+    { 0.734690, 0.265310, 0.000000 },
529
+    { 0.734690, 0.265310, 0.000000 },
530
+    { 0.734690, 0.265310, 0.000000 },
531
+    { 0.734690, 0.265310, 0.000000 },
532
+    { 0.734690, 0.265310, 0.000000 },
533
+    { 0.734690, 0.265310, 0.000000 },
534
+    { 0.734690, 0.265310, 0.000000 },
535
+    { 0.734690, 0.265310, 0.000000 },
536
+    { 0.734690, 0.265310, 0.000000 },
537
+    { 0.734690, 0.265310, 0.000000 },
538
+    { 0.734690, 0.265310, 0.000000 },
539
+    { 0.734690, 0.265310, 0.000000 },
540
+    { 0.734690, 0.265310, 0.000000 },
541
+    { 0.734690, 0.265310, 0.000000 },
542
+    { 0.734690, 0.265310, 0.000000 },
543
+    { 0.734690, 0.265310, 0.000000 },
544
+    { 0.734690, 0.265310, 0.000000 },
545
+    { 0.734690, 0.265310, 0.000000 },
546
+    { 0.734690, 0.265310, 0.000000 },
547
+    { 0.734690, 0.265310, 0.000000 },
548
+    { 0.734690, 0.265310, 0.000000 },
549
+    { 0.734690, 0.265310, 0.000000 },
550
+    { 0.734690, 0.265310, 0.000000 },
551
+    { 0.734690, 0.265310, 0.000000 },
552
+    { 0.734690, 0.265310, 0.000000 },
553
+    { 0.734690, 0.265310, 0.000000 },
554
+    { 0.734690, 0.265310, 0.000000 },
555
+    { 0.734690, 0.265310, 0.000000 },
556
+    { 0.734690, 0.265310, 0.000000 },
557
+    { 0.734690, 0.265310, 0.000000 },
558
+    { 0.734690, 0.265310, 0.000000 },
559
+    { 0.734690, 0.265310, 0.000000 },
560
+    { 0.734690, 0.265310, 0.000000 },
561
+    { 0.734690, 0.265310, 0.000000 },
562
+    { 0.734690, 0.265310, 0.000000 },
563
+    { 0.734690, 0.265310, 0.000000 },
564
+    { 0.734690, 0.265310, 0.000000 },
565
+    { 0.734690, 0.265310, 0.000000 },
566
+    { 0.734690, 0.265310, 0.000000 },
567
+    { 0.734690, 0.265310, 0.000000 },
568
+    { 0.734690, 0.265310, 0.000000 },
569
+    { 0.734690, 0.265310, 0.000000 },
570
+    { 0.734690, 0.265310, 0.000000 },
571
+    { 0.734690, 0.265310, 0.000000 },
572
+    { 0.734690, 0.265310, 0.000000 },
573
+    { 0.734690, 0.265310, 0.000000 },
574
+    { 0.734690, 0.265310, 0.000000 },
575
+    { 0.734690, 0.265310, 0.000000 },
576
+    { 0.734690, 0.265310, 0.000000 },
577
+    { 0.734690, 0.265310, 0.000000 },
578
+    { 0.734690, 0.265310, 0.000000 },
579
+    { 0.734690, 0.265310, 0.000000 },
580
+    { 0.734690, 0.265310, 0.000000 },
581
+    { 0.734690, 0.265310, 0.000000 },
582
+    { 0.734690, 0.265310, 0.000000 },
583
+    { 0.734690, 0.265310, 0.000000 },
584
+    { 0.734690, 0.265310, 0.000000 },
585
+    { 0.734690, 0.265310, 0.000000 },
586
+    { 0.734690, 0.265310, 0.000000 },
587
+    { 0.734690, 0.265310, 0.000000 },
588
+    { 0.734690, 0.265310, 0.000000 },
589
+    { 0.734690, 0.265310, 0.000000 },
590
+    { 0.734690, 0.265310, 0.000000 },
591
+    { 0.734690, 0.265310, 0.000000 },
592
+    { 0.734690, 0.265310, 0.000000 },
593
+    { 0.734690, 0.265310, 0.000000 },
594
+    { 0.734690, 0.265310, 0.000000 },
595
+    { 0.734690, 0.265310, 0.000000 },
596
+    { 0.734690, 0.265310, 0.000000 },
597
+    { 0.734690, 0.265310, 0.000000 },
598
+    { 0.734690, 0.265310, 0.000000 },
599
+    { 0.734690, 0.265310, 0.000000 },
600
+    { 0.734690, 0.265310, 0.000000 },
601
+    { 0.734690, 0.265310, 0.000000 },
602
+    { 0.734690, 0.265310, 0.000000 },
603
+    { 0.734690, 0.265310, 0.000000 },
604
+    { 0.734690, 0.265310, 0.000000 },
605
+    { 0.734690, 0.265310, 0.000000 },
606
+    { 0.734690, 0.265310, 0.000000 },
607
+    { 0.734690, 0.265310, 0.000000 },
608
+    { 0.734690, 0.265310, 0.000000 },
609
+    { 0.734690, 0.265310, 0.000000 },
610
+    { 0.734690, 0.265310, 0.000000 },
611
+    { 0.734690, 0.265310, 0.000000 },
612
+    { 0.734690, 0.265310, 0.000000 },
613
+    { 0.734690, 0.265310, 0.000000 },
614
+    { 0.734690, 0.265310, 0.000000 },
615
+    { 0.734690, 0.265310, 0.000000 },
616
+    { 0.734690, 0.265310, 0.000000 },
617
+    { 0.734690, 0.265310, 0.000000 },
618
+    { 0.734690, 0.265310, 0.000000 },
619
+    { 0.734690, 0.265310, 0.000000 },
620
+    { 0.734690, 0.265310, 0.000000 },
621
+    { 0.734690, 0.265310, 0.000000 },
622
+    { 0.734690, 0.265310, 0.000000 },
623
+    { 0.734690, 0.265310, 0.000000 },
624
+    { 0.734690, 0.265310, 0.000000 },
625
+    { 0.734690, 0.265310, 0.000000 },
626
+    { 0.734690, 0.265310, 0.000000 },
627
+    { 0.734690, 0.265310, 0.000000 },
628
+    { 0.734690, 0.265310, 0.000000 },
629
+    { 0.734690, 0.265310, 0.000000 },
630
+    { 0.734690, 0.265310, 0.000000 },
631
+    { 0.734690, 0.265310, 0.000000 },
632
+    { 0.734690, 0.265310, 0.000000 },
633
+    { 0.734690, 0.265310, 0.000000 },
634
+    { 0.734690, 0.265310, 0.000000 },
635
+    { 0.734690, 0.265310, 0.000000 },
636
+    { 0.734690, 0.265310, 0.000000 },
637
+    { 0.734690, 0.265310, 0.000000 },
638
+    { 0.734690, 0.265310, 0.000000 },
639
+    { 0.734690, 0.265310, 0.000000 },
640
+    { 0.734690, 0.265310, 0.000000 },
641
+};
642
+
643
+
644
+/* Standard white point chromaticities. */
645
+
646
+#define C     0.310063, 0.316158
647
+#define E     1.0/3.0, 1.0/3.0
648
+#define D50   0.34570, 0.3585
649
+#define D65   0.312713, 0.329016
650
+
651
+/* Gamma of nonlinear correction.
652
+   See Charles Poynton's ColorFAQ Item 45 and GammaFAQ Item 6 at
653
+   http://www.inforamp.net/~poynton/ColorFAQ.html
654
+   http://www.inforamp.net/~poynton/GammaFAQ.html
655
+*/
656
+
657
+#define GAMMA_REC709    0.      /* Rec. 709 */
658
+
659
+static const struct ColorSystem color_systems[] = {
660
+    [NTSCsystem] = {
661
+        0.67,  0.33,  0.21,  0.71,  0.14,  0.08,
662
+        C, GAMMA_REC709
663
+    },
664
+    [EBUsystem] = {
665
+        0.64,  0.33,  0.29,  0.60,  0.15,  0.06,
666
+        D65, GAMMA_REC709
667
+    },
668
+    [SMPTEsystem] = {
669
+        0.630, 0.340, 0.310, 0.595, 0.155, 0.070,
670
+        D65, GAMMA_REC709
671
+    },
672
+    [SMPTE240Msystem] = {
673
+        0.670, 0.330, 0.210, 0.710, 0.150, 0.060,
674
+        D65, GAMMA_REC709
675
+    },
676
+    [APPLEsystem] = {
677
+        0.625, 0.340, 0.280, 0.595, 0.115, 0.070,
678
+        D65, GAMMA_REC709
679
+    },
680
+    [wRGBsystem] = {
681
+        0.7347, 0.2653, 0.1152, 0.8264, 0.1566, 0.0177,
682
+        D50, GAMMA_REC709
683
+    },
684
+    [CIE1931system] = {
685
+        0.7347, 0.2653, 0.2738, 0.7174, 0.1666, 0.0089,
686
+        E, GAMMA_REC709
687
+    },
688
+    [Rec709system] = {
689
+        0.64,  0.33,  0.30,  0.60,  0.15,  0.06,
690
+        D65, GAMMA_REC709
691
+    },
692
+    [Rec2020system] = {
693
+        0.708,  0.292,  0.170,  0.797,  0.131,  0.046,
694
+        D65, GAMMA_REC709
695
+    },
696
+};
697
+
698
+/*
699
+static struct ColorSystem CustomSystem = {
700
+    "Custom",
701
+    0.64,  0.33,  0.30,  0.60,  0.15,  0.06,
702
+    D65, GAMMA_REC709
703
+};
704
+*/
705
+
706
+static void
707
+uv_to_xy(double   const u,
708
+         double   const v,
709
+         double * const xc,
710
+         double * const yc)
711
+{
712
+/*
713
+    Given 1970 coordinates u, v, determine 1931 chromaticities x, y
714
+*/
715
+    *xc = 3*u / (2*u - 8*v + 4);
716
+    *yc = 2*v / (2*u - 8*v + 4);
717
+}
718
+
719
+static void
720
+upvp_to_xy(double   const up,
721
+           double   const vp,
722
+           double * const xc,
723
+           double * const yc)
724
+{
725
+/*
726
+    Given 1976 coordinates u', v', determine 1931 chromaticities x, y
727
+*/
728
+    *xc = 9*up / (6*up - 16*vp + 12);
729
+    *yc = 4*vp / (6*up - 16*vp + 12);
730
+}
731
+
732
+static void
733
+xy_to_upvp(double xc,
734
+           double yc,
735
+           double * const up,
736
+           double * const vp)
737
+{
738
+/*
739
+    Given 1931 chromaticities x, y, determine 1976 coordinates u', v'
740
+*/
741
+    *up = 4*xc / (- 2*xc + 12*yc + 3);
742
+    *vp = 9*yc / (- 2*xc + 12*yc + 3);
743
+}
744
+
745
+static void
746
+xy_to_uv(double xc,
747
+         double yc,
748
+         double * const u,
749
+         double * const v)
750
+{
751
+/*
752
+    Given 1931 chromaticities x, y, determine 1960 coordinates u, v
753
+*/
754
+    *u = 4*xc / (- 2*xc + 12*yc + 3);
755
+    *v = 6*yc / (- 2*xc + 12*yc + 3);
756
+}
757
+
758
+static void
759
+xyz_to_rgb(const double m[3][3],
760
+           double xc, double yc, double zc,
761
+           double * const r, double * const g, double * const b)
762
+{
763
+    *r = m[0][0]*xc + m[0][1]*yc + m[0][2]*zc;
764
+    *g = m[1][0]*xc + m[1][1]*yc + m[1][2]*zc;
765
+    *b = m[2][0]*xc + m[2][1]*yc + m[2][2]*zc;
766
+}
767
+
768
+static void invert_matrix3x3(double in[3][3], double out[3][3])
769
+{
770
+    double m00 = in[0][0], m01 = in[0][1], m02 = in[0][2],
771
+           m10 = in[1][0], m11 = in[1][1], m12 = in[1][2],
772
+           m20 = in[2][0], m21 = in[2][1], m22 = in[2][2];
773
+    int i, j;
774
+
775
+    out[0][0] =  (m11 * m22 - m21 * m12);
776
+    out[0][1] = -(m01 * m22 - m21 * m02);
777
+    out[0][2] =  (m01 * m12 - m11 * m02);
778
+    out[1][0] = -(m10 * m22 - m20 * m12);
779
+    out[1][1] =  (m00 * m22 - m20 * m02);
780
+    out[1][2] = -(m00 * m12 - m10 * m02);
781
+    out[2][0] =  (m10 * m21 - m20 * m11);
782
+    out[2][1] = -(m00 * m21 - m20 * m01);
783
+    out[2][2] =  (m00 * m11 - m10 * m01);
784
+
785
+    double det = m00 * out[0][0] + m10 * out[0][1] + m20 * out[0][2];
786
+    det = 1.0 / det;
787
+
788
+    for (i = 0; i < 3; i++) {
789
+        for (j = 0; j < 3; j++)
790
+            out[i][j] *= det;
791
+    }
792
+}
793
+
794
+static void get_rgb2xyz_matrix(struct ColorSystem system, double m[3][3])
795
+{
796
+    double S[3], X[4], Z[4];
797
+    int i;
798
+
799
+    X[0] = system.xRed   / system.yRed;
800
+    X[1] = system.xGreen / system.yGreen;
801
+    X[2] = system.xBlue  / system.yBlue;
802
+    X[3] = system.xWhite / system.yWhite;
803
+
804
+    Z[0] = (1 - system.xRed   - system.yRed)   / system.yRed;
805
+    Z[1] = (1 - system.xGreen - system.yGreen) / system.yGreen;
806
+    Z[2] = (1 - system.xBlue  - system.yBlue)  / system.yBlue;
807
+    Z[3] = (1 - system.xWhite - system.yWhite) / system.yWhite;
808
+
809
+    for (i = 0; i < 3; i++) {
810
+        m[0][i] = X[i];
811
+        m[1][i] = 1;
812
+        m[2][i] = Z[i];
813
+    }
814
+
815
+    invert_matrix3x3(m, m);
816
+
817
+    for (i = 0; i < 3; i++)
818
+        S[i] = m[i][0] * X[3] + m[i][1] * 1 + m[i][2] * Z[3];
819
+
820
+    for (i = 0; i < 3; i++) {
821
+        m[0][i] = S[i] * X[i];
822
+        m[1][i] = S[i] * 1;
823
+        m[2][i] = S[i] * Z[i];
824
+    }
825
+}
826
+
827
+static void
828
+rgb_to_xy(double rc,
829
+          double gc,
830
+          double bc,
831
+          double * const x,
832
+          double * const y,
833
+          double * const z,
834
+          const double m[3][3])
835
+{
836
+    double sum;
837
+
838
+    *x = m[0][0] * rc + m[0][1] * gc + m[0][2] * bc;
839
+    *y = m[1][0] * rc + m[1][1] * gc + m[1][2] * bc;
840
+    *z = m[2][0] * rc + m[2][1] * gc + m[2][2] * bc;
841
+
842
+    sum = *x + *y + *z;
843
+
844
+    *x = *x / sum;
845
+    *y = *y / sum;
846
+}
847
+
848
+static int
849
+constrain_rgb(double * const r,
850
+              double * const g,
851
+              double * const b)
852
+{
853
+/*----------------------------------------------------------------------------
854
+    If  the  requested RGB shade contains a negative weight for one of
855
+    the primaries, it lies outside the color  gamut  accessible  from
856
+    the  given  triple  of  primaries.  Desaturate it by adding white,
857
+    equal quantities of R, G, and B, enough to make RGB all positive.
858
+-----------------------------------------------------------------------------*/
859
+    double w;
860
+
861
+    /* Amount of white needed is w = - min(0, *r, *g, *b) */
862
+    w = (0 < *r) ? 0 : *r;
863
+    w = (w < *g) ? w : *g;
864
+    w = (w < *b) ? w : *b;
865
+    w = - w;
866
+
867
+    /* Add just enough white to make r, g, b all positive. */
868
+    if (w > 0) {
869
+        *r += w;  *g += w; *b += w;
870
+
871
+        return 1;                     /* Color modified to fit RGB gamut */
872
+    }
873
+
874
+    return 0;                         /* Color within RGB gamut */
875
+}
876
+
877
+static void
878
+gamma_correct(const struct ColorSystem * const cs,
879
+              double *                   const c)
880
+{
881
+/*----------------------------------------------------------------------------
882
+    Transform linear RGB values to nonlinear RGB values.
883
+
884
+    Rec. 709 is ITU-R Recommendation BT. 709 (1990)
885
+    ``Basic Parameter Values for the HDTV Standard for the Studio and for
886
+    International Programme Exchange'', formerly CCIR Rec. 709.
887
+
888
+    For details see
889
+       http://www.inforamp.net/~poynton/ColorFAQ.html
890
+       http://www.inforamp.net/~poynton/GammaFAQ.html
891
+-----------------------------------------------------------------------------*/
892
+    double gamma;
893
+    double cc;
894
+
895
+    gamma = cs->gamma;
896
+
897
+    if (gamma == 0.) {
898
+        /* Rec. 709 gamma correction. */
899
+        cc = 0.018;
900
+        if (*c < cc) {
901
+            *c *= (1.099 * pow(cc, 0.45) - 0.099) / cc;
902
+        } else {
903
+            *c = 1.099 * pow(*c, 0.45) - 0.099;
904
+        }
905
+    } else {
906
+    /* Nonlinear color = (Linear color)^(1/gamma) */
907
+        *c = pow(*c, 1./gamma);
908
+    }
909
+}
910
+
911
+
912
+
913
+static void
914
+gamma_correct_rgb(const struct ColorSystem * const cs,
915
+                  double * const r,
916
+                  double * const g,
917
+                  double * const b)
918
+{
919
+    gamma_correct(cs, r);
920
+    gamma_correct(cs, g);
921
+    gamma_correct(cs, b);
922
+}
923
+
924
+/* Sz(X) is the displacement in pixels of a displacement of X normalized
925
+   distance units.  (A normalized distance unit is 1/512 of the smaller
926
+   dimension of the canvas)
927
+*/
928
+#define Sz(x) (((x) * (int)FFMIN(w, h)) / 512)
929
+
930
+static void
931
+monochrome_color_location(double waveLength, int w, int h,
932
+                          int cie, int *xP, int *yP)
933
+{
934
+    const int ix = waveLength - 360;
935
+    const double pX = spectral_chromaticity[ix][0];
936
+    const double pY = spectral_chromaticity[ix][1];
937
+    const double pZ = spectral_chromaticity[ix][2];
938
+    const double px = pX / (pX + pY + pZ);
939
+    const double py = pY / (pX + pY + pZ);
940
+
941
+    if (cie == LUV) {
942
+        double up, vp;
943
+
944
+        xy_to_upvp(px, py, &up, &vp);
945
+        *xP = up * (w - 1);
946
+        *yP = (h - 1) - vp * (h - 1);
947
+    } else if (cie == UCS) {
948
+        double u, v;
949
+
950
+        xy_to_uv(px, py, &u, &v);
951
+        *xP = u * (w - 1);
952
+        *yP = (h - 1) - v * (h - 1);
953
+    } else if (cie == XYY) {
954
+        *xP = px * (w - 1);
955
+        *yP = (h - 1) - py * (h - 1);
956
+    } else {
957
+        av_assert0(0);
958
+    }
959
+}
960
+
961
+static void
962
+find_tongue(uint16_t* const pixels,
963
+            int       const w,
964
+            int       const linesize,
965
+            int       const row,
966
+            int *     const presentP,
967
+            int *     const leftEdgeP,
968
+            int *     const rightEdgeP)
969
+{
970
+    int i;
971
+
972
+    for (i = 0; i < w && pixels[row * linesize + i * 4 + 0] == 0; i++)
973
+        ;
974
+
975
+    if (i >= w) {
976
+        *presentP = 0;
977
+    } else {
978
+        int j;
979
+        int const leftEdge = i;
980
+
981
+        *presentP = 1;
982
+
983
+        for (j = w - 1; j >= leftEdge && pixels[row * linesize + j * 4 + 0] == 0; j--)
984
+            ;
985
+
986
+        *rightEdgeP = j;
987
+        *leftEdgeP = leftEdge;
988
+    }
989
+}
990
+
991
+static void draw_line(uint16_t *const pixels, int linesize,
992
+                      int x0, int y0, int x1, int y1,
993
+                      int w, int h,
994
+                      const uint16_t *const rgbcolor)
995
+{
996
+    int dx  = FFABS(x1 - x0), sx = x0 < x1 ? 1 : -1;
997
+    int dy  = FFABS(y1 - y0), sy = y0 < y1 ? 1 : -1;
998
+    int err = (dx > dy ? dx : -dy) / 2, e2;
999
+
1000
+    for (;;) {
1001
+        pixels[y0 * linesize + x0 * 4 + 0] = rgbcolor[0];
1002
+        pixels[y0 * linesize + x0 * 4 + 1] = rgbcolor[1];
1003
+        pixels[y0 * linesize + x0 * 4 + 2] = rgbcolor[2];
1004
+        pixels[y0 * linesize + x0 * 4 + 3] = rgbcolor[3];
1005
+
1006
+        if (x0 == x1 && y0 == y1)
1007
+            break;
1008
+
1009
+        e2 = err;
1010
+
1011
+        if (e2 >-dx) {
1012
+            err -= dy;
1013
+            x0 += sx;
1014
+        }
1015
+
1016
+        if (e2 < dy) {
1017
+            err += dx;
1018
+            y0 += sy;
1019
+        }
1020
+    }
1021
+}
1022
+
1023
+static void draw_rline(uint16_t *const pixels, int linesize,
1024
+                       int x0, int y0, int x1, int y1,
1025
+                       int w, int h)
1026
+{
1027
+    int dx  = FFABS(x1 - x0), sx = x0 < x1 ? 1 : -1;
1028
+    int dy  = FFABS(y1 - y0), sy = y0 < y1 ? 1 : -1;
1029
+    int err = (dx > dy ? dx : -dy) / 2, e2;
1030
+
1031
+    for (;;) {
1032
+        pixels[y0 * linesize + x0 * 4 + 0] = 65535 - pixels[y0 * linesize + x0 * 4 + 0];
1033
+        pixels[y0 * linesize + x0 * 4 + 1] = 65535 - pixels[y0 * linesize + x0 * 4 + 1];
1034
+        pixels[y0 * linesize + x0 * 4 + 2] = 65535 - pixels[y0 * linesize + x0 * 4 + 2];
1035
+        pixels[y0 * linesize + x0 * 4 + 3] = 65535;
1036
+
1037
+        if (x0 == x1 && y0 == y1)
1038
+            break;
1039
+
1040
+        e2 = err;
1041
+
1042
+        if (e2 >-dx) {
1043
+            err -= dy;
1044
+            x0 += sx;
1045
+        }
1046
+
1047
+        if (e2 < dy) {
1048
+            err += dx;
1049
+            y0 += sy;
1050
+        }
1051
+    }
1052
+}
1053
+
1054
+static void
1055
+tongue_outline(uint16_t* const pixels,
1056
+               int       const linesize,
1057
+               int       const w,
1058
+               int       const h,
1059
+               uint16_t  const maxval,
1060
+               int       const cie)
1061
+{
1062
+    const uint16_t rgbcolor[4] = { maxval, maxval, maxval, maxval };
1063
+    int wavelength;
1064
+    int lx, ly;
1065
+    int fx, fy;
1066
+
1067
+    for (wavelength = 360; wavelength <= 830; wavelength++) {
1068
+        int icx, icy;
1069
+
1070
+        monochrome_color_location(wavelength, w, h, cie,
1071
+                                  &icx, &icy);
1072
+
1073
+        if (wavelength > 360)
1074
+            draw_line(pixels, linesize, lx, ly, icx, icy, w, h, rgbcolor);
1075
+        else {
1076
+            fx = icx;
1077
+            fy = icy;
1078
+        }
1079
+        lx = icx;
1080
+        ly = icy;
1081
+    }
1082
+    draw_line(pixels, linesize, lx, ly, fx, fy, w, h, rgbcolor);
1083
+}
1084
+
1085
+static void
1086
+fill_in_tongue(uint16_t*                  const pixels,
1087
+               int                        const linesize,
1088
+               int                        const w,
1089
+               int                        const h,
1090
+               uint16_t                   const maxval,
1091
+               const struct ColorSystem * const cs,
1092
+               double                     const m[3][3],
1093
+               int                        const cie,
1094
+               int                        const correct_gamma,
1095
+               float                      const contrast)
1096
+{
1097
+    int y;
1098
+
1099
+    /* Scan the image line by line and  fill  the  tongue  outline
1100
+       with the RGB values determined by the color system for the x-y
1101
+       co-ordinates within the tongue.
1102
+    */
1103
+
1104
+    for (y = 0; y < h; ++y) {
1105
+        int  present;  /* There is some tongue on this line */
1106
+        int leftEdge; /* x position of leftmost pixel in tongue on this line */
1107
+        int rightEdge; /* same, but rightmost */
1108
+
1109
+        find_tongue(pixels, w, linesize, y, &present, &leftEdge, &rightEdge);
1110
+
1111
+        if (present) {
1112
+            int x;
1113
+
1114
+            for (x = leftEdge; x <= rightEdge; ++x) {
1115
+                double cx, cy, cz, jr, jg, jb, jmax;
1116
+                int r, g, b, mx = maxval;
1117
+
1118
+                if (cie == LUV) {
1119
+                    double up, vp;
1120
+                    up = ((double) x) / (w - 1);
1121
+                    vp = 1.0 - ((double) y) / (h - 1);
1122
+                    upvp_to_xy(up, vp, &cx, &cy);
1123
+                    cz = 1.0 - (cx + cy);
1124
+                } else if (cie == UCS) {
1125
+                    double u, v;
1126
+                    u = ((double) x) / (w - 1);
1127
+                    v = 1.0 - ((double) y) / (h - 1);
1128
+                    uv_to_xy(u, v, &cx, &cy);
1129
+                    cz = 1.0 - (cx + cy);
1130
+                } else if (cie == XYY) {
1131
+                    cx = ((double) x) / (w - 1);
1132
+                    cy = 1.0 - ((double) y) / (h - 1);
1133
+                    cz = 1.0 - (cx + cy);
1134
+                } else {
1135
+                    av_assert0(0);
1136
+                }
1137
+
1138
+                xyz_to_rgb(m, cx, cy, cz, &jr, &jg, &jb);
1139
+
1140
+                /* Check whether the requested color  is  within  the
1141
+                   gamut  achievable with the given color system.  If
1142
+                   not, draw it in a reduced  intensity,  interpolated
1143
+                   by desaturation to the closest within-gamut color. */
1144
+
1145
+                if (constrain_rgb(&jr, &jg, &jb))
1146
+                    mx *= contrast;
1147
+
1148
+                jmax = FFMAX3(jr, jg, jb);
1149
+                if (jmax > 0) {
1150
+                    jr = jr / jmax;
1151
+                    jg = jg / jmax;
1152
+                    jb = jb / jmax;
1153
+                }
1154
+                /* gamma correct from linear rgb to nonlinear rgb. */
1155
+                if (correct_gamma)
1156
+                    gamma_correct_rgb(cs, &jr, &jg, &jb);
1157
+                r = mx * jr;
1158
+                g = mx * jg;
1159
+                b = mx * jb;
1160
+                pixels[y * linesize + x * 4 + 0] = r;
1161
+                pixels[y * linesize + x * 4 + 1] = g;
1162
+                pixels[y * linesize + x * 4 + 2] = b;
1163
+                pixels[y * linesize + x * 4 + 3] = 65535;
1164
+            }
1165
+        }
1166
+    }
1167
+}
1168
+
1169
+static void
1170
+plot_white_point(uint16_t*      pixels,
1171
+                 int      const linesize,
1172
+                 int      const w,
1173
+                 int      const h,
1174
+                 int      const maxval,
1175
+                 int      const color_system,
1176
+                 int      const cie)
1177
+{
1178
+    const struct ColorSystem *cs = &color_systems[color_system];
1179
+    int wx, wy;
1180
+
1181
+    if (cie == LUV) {
1182
+        double wup, wvp;
1183
+        xy_to_upvp(cs->xWhite, cs->yWhite, &wup, &wvp);
1184
+        wx = wup;
1185
+        wy = wvp;
1186
+        wx = (w - 1) * wup;
1187
+        wy = (h - 1) - ((int) ((h - 1) * wvp));
1188
+    } else if (cie == UCS) {
1189
+        double wu, wv;
1190
+        xy_to_uv(cs->xWhite, cs->yWhite, &wu, &wv);
1191
+        wx = wu;
1192
+        wy = wv;
1193
+        wx = (w - 1) * wu;
1194
+        wy = (h - 1) - ((int) ((h - 1) * wv));
1195
+    } else if (cie == XYY) {
1196
+        wx = (w - 1) * cs->xWhite;
1197
+        wy = (h - 1) - ((int) ((h - 1) * cs->yWhite));
1198
+    } else {
1199
+        av_assert0(0);
1200
+    }
1201
+
1202
+    draw_rline(pixels, linesize,
1203
+               wx + Sz(3), wy, wx + Sz(10), wy,
1204
+               w, h);
1205
+    draw_rline(pixels, linesize,
1206
+               wx - Sz(3), wy, wx - Sz(10), wy,
1207
+               w, h);
1208
+    draw_rline(pixels, linesize,
1209
+               wx, wy + Sz(3), wx, wy + Sz(10),
1210
+               w, h);
1211
+    draw_rline(pixels, linesize,
1212
+               wx, wy - Sz(3), wx, wy - Sz(10),
1213
+               w, h);
1214
+}
1215
+
1216
+static int draw_background(AVFilterContext *ctx)
1217
+{
1218
+    CiescopeContext *s = ctx->priv;
1219
+    const struct ColorSystem *cs = &color_systems[s->color_system];
1220
+    AVFilterLink *outlink = ctx->outputs[0];
1221
+    int w = s->size;
1222
+    int h = s->size;
1223
+    uint16_t *pixels;
1224
+
1225
+    if ((s->f = ff_get_video_buffer(outlink, outlink->w, outlink->h)) == NULL)
1226
+        return AVERROR(ENOMEM);
1227
+    pixels = (uint16_t *)s->f->data[0];
1228
+
1229
+    tongue_outline(pixels, s->f->linesize[0] / 2, w, h, 65535, s->cie);
1230
+
1231
+    fill_in_tongue(pixels, s->f->linesize[0] / 2, w, h, 65535, cs, s->i, s->cie,
1232
+                   s->correct_gamma, s->contrast);
1233
+
1234
+    return 0;
1235
+}
1236
+
1237
+static void filter_rgb48(AVFilterContext *ctx, AVFrame *in, double *cx, double *cy, int x, int y)
1238
+{
1239
+    CiescopeContext *s = ctx->priv;
1240
+    const uint16_t* src = (const uint16_t*)(in->data[0] + in->linesize[0] * y + x * 6);
1241
+    double r = src[0] / 65535.;
1242
+    double g = src[1] / 65535.;
1243
+    double b = src[2] / 65535.;
1244
+    double cz;
1245
+
1246
+    rgb_to_xy(r, g, b, cx, cy, &cz, s->m);
1247
+}
1248
+
1249
+static void filter_rgba64(AVFilterContext *ctx, AVFrame *in, double *cx, double *cy, int x, int y)
1250
+{
1251
+    CiescopeContext *s = ctx->priv;
1252
+    const uint16_t* src = (const uint16_t*)(in->data[0] + in->linesize[0] * y + x * 8);
1253
+    double r = src[0] / 65535.;
1254
+    double g = src[1] / 65535.;
1255
+    double b = src[2] / 65535.;
1256
+    double cz;
1257
+
1258
+    rgb_to_xy(r, g, b, cx, cy, &cz, s->m);
1259
+}
1260
+
1261
+static void filter_rgb24(AVFilterContext *ctx, AVFrame *in, double *cx, double *cy, int x, int y)
1262
+{
1263
+    CiescopeContext *s = ctx->priv;
1264
+    const uint8_t* src = in->data[0] + in->linesize[0] * y + x * 3;
1265
+    double r = src[0] / 255.;
1266
+    double g = src[1] / 255.;
1267
+    double b = src[2] / 255.;
1268
+    double cz;
1269
+
1270
+    rgb_to_xy(r, g, b, cx, cy, &cz, s->m);
1271
+}
1272
+
1273
+static void filter_rgba(AVFilterContext *ctx, AVFrame *in, double *cx, double *cy, int x, int y)
1274
+{
1275
+    CiescopeContext *s = ctx->priv;
1276
+    const uint8_t* src = in->data[0] + in->linesize[0] * y + x * 4;
1277
+    double r = src[0] / 255.;
1278
+    double g = src[1] / 255.;
1279
+    double b = src[2] / 255.;
1280
+    double cz;
1281
+
1282
+    rgb_to_xy(r, g, b, cx, cy, &cz, s->m);
1283
+}
1284
+
1285
+static void filter_xyz(AVFilterContext *ctx, AVFrame *in, double *cx, double *cy, int x, int y)
1286
+{
1287
+    CiescopeContext *s = ctx->priv;
1288
+    const uint16_t* src = (uint16_t *)(in->data[0] + in->linesize[0] * y + x * 6);
1289
+    double lx = s->log2lin[src[0]];
1290
+    double ly = s->log2lin[src[1]];
1291
+    double lz = s->log2lin[src[2]];
1292
+    double sum = lx + ly + lz;
1293
+
1294
+    if (sum == 0)
1295
+        sum = 1;
1296
+    *cx = lx / sum;
1297
+    *cy = ly / sum;
1298
+}
1299
+
1300
+static void plot_gamuts(uint16_t *pixels, int linesize, int w, int h,
1301
+                        int cie, int gamuts)
1302
+{
1303
+    int i;
1304
+
1305
+    for (i = 0; i < NB_CS; i++) {
1306
+        const struct ColorSystem *cs = &color_systems[i];
1307
+        int rx, ry, gx, gy, bx, by;
1308
+
1309
+        if (!((1 << i) & gamuts))
1310
+            continue;
1311
+        if (cie == LUV) {
1312
+            double wup, wvp;
1313
+            xy_to_upvp(cs->xRed, cs->yRed, &wup, &wvp);
1314
+            rx = (w - 1) * wup;
1315
+            ry = (h - 1) - ((int) ((h - 1) * wvp));
1316
+            xy_to_upvp(cs->xGreen, cs->yGreen, &wup, &wvp);
1317
+            gx = (w - 1) * wup;
1318
+            gy = (h - 1) - ((int) ((h - 1) * wvp));
1319
+            xy_to_upvp(cs->xBlue, cs->yBlue, &wup, &wvp);
1320
+            bx = (w - 1) * wup;
1321
+            by = (h - 1) - ((int) ((h - 1) * wvp));
1322
+        } else if (cie == UCS) {
1323
+            double wu, wv;
1324
+            xy_to_uv(cs->xRed, cs->yRed, &wu, &wv);
1325
+            rx = (w - 1) * wu;
1326
+            ry = (h - 1) - ((int) ((h - 1) * wv));
1327
+            xy_to_uv(cs->xGreen, cs->yGreen, &wu, &wv);
1328
+            gx = (w - 1) * wu;
1329
+            gy = (h - 1) - ((int) ((h - 1) * wv));
1330
+            xy_to_uv(cs->xBlue, cs->yBlue, &wu, &wv);
1331
+            bx = (w - 1) * wu;
1332
+            by = (h - 1) - ((int) ((h - 1) * wv));
1333
+        } else if (cie == XYY) {
1334
+            rx = (w - 1) * cs->xRed;
1335
+            ry = (h - 1) - ((int) ((h - 1) * cs->yRed));
1336
+            gx = (w - 1) * cs->xGreen;
1337
+            gy = (h - 1) - ((int) ((h - 1) * cs->yGreen));
1338
+            bx = (w - 1) * cs->xBlue;
1339
+            by = (h - 1) - ((int) ((h - 1) * cs->yBlue));
1340
+        } else {
1341
+            av_assert0(0);
1342
+        }
1343
+
1344
+        draw_rline(pixels, linesize, rx, ry, gx, gy, w, h);
1345
+        draw_rline(pixels, linesize, gx, gy, bx, by, w, h);
1346
+        draw_rline(pixels, linesize, bx, by, rx, ry, w, h);
1347
+    }
1348
+}
1349
+
1350
+static int filter_frame(AVFilterLink *inlink, AVFrame *in)
1351
+{
1352
+    AVFilterContext *ctx  = inlink->dst;
1353
+    CiescopeContext *s = ctx->priv;
1354
+    AVFilterLink *outlink = ctx->outputs[0];
1355
+    int i = s->intensity * 65535;
1356
+    int w = outlink->w;
1357
+    int h = outlink->h;
1358
+    AVFrame *out;
1359
+    int ret, x, y;
1360
+
1361
+    out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
1362
+    if (!out) {
1363
+        av_frame_free(&in);
1364
+        return AVERROR(ENOMEM);
1365
+    }
1366
+    out->pts = in->pts;
1367
+
1368
+    if (!s->background) {
1369
+        ret = draw_background(ctx);
1370
+        if (ret < 0)
1371
+            return ret;
1372
+        s->background = 1;
1373
+    }
1374
+    for (y = 0; y < outlink->h; y++) {
1375
+        memset(out->data[0] + y * out->linesize[0], 0, outlink->w * 8);
1376
+    }
1377
+
1378
+    for (y = 0; y < in->height; y++) {
1379
+        for (x = 0; x < in->width; x++) {
1380
+            double cx, cy;
1381
+            uint16_t *dst;
1382
+            int wx, wy;
1383
+
1384
+            s->filter(ctx, in, &cx, &cy, x, y);
1385
+
1386
+            if (s->cie == LUV) {
1387
+                double up, vp;
1388
+                xy_to_upvp(cx, cy, &up, &vp);
1389
+                cx = up;
1390
+                cy = vp;
1391
+            } else if (s->cie == UCS) {
1392
+                double u, v;
1393
+                xy_to_uv(cx, cy, &u, &v);
1394
+                cx = u;
1395
+                cy = v;
1396
+            }
1397
+
1398
+            wx = (w - 1) * cx;
1399
+            wy = (h - 1) - ((h - 1) * cy);
1400
+
1401
+            if (wx < 0 || wx >= w ||
1402
+                wy < 0 || wy >= h)
1403
+                continue;
1404
+
1405
+            dst = (uint16_t *)(out->data[0] + wy * out->linesize[0] + wx * 8 + 0);
1406
+            dst[0] = FFMIN(dst[0] + i, 65535);
1407
+            dst[1] = FFMIN(dst[1] + i, 65535);
1408
+            dst[2] = FFMIN(dst[2] + i, 65535);
1409
+            dst[3] = 65535;
1410
+        }
1411
+    }
1412
+
1413
+    for (y = 0; y < outlink->h; y++) {
1414
+        uint16_t *dst = (uint16_t *)(out->data[0] + y * out->linesize[0]);
1415
+        const uint16_t *src = (const uint16_t *)(s->f->data[0] + y * s->f->linesize[0]);
1416
+        for (x = 0; x < outlink->w; x++) {
1417
+            const int xx = x * 4;
1418
+            if (dst[xx + 3] == 0) {
1419
+                dst[xx + 0] = src[xx + 0];
1420
+                dst[xx + 1] = src[xx + 1];
1421
+                dst[xx + 2] = src[xx + 2];
1422
+                dst[xx + 3] = src[xx + 3];
1423
+            }
1424
+        }
1425
+    }
1426
+
1427
+    if (s->show_white)
1428
+        plot_white_point((uint16_t *)out->data[0], out->linesize[0] / 2,
1429
+                         outlink->w, outlink->h, 65535,
1430
+                         s->color_system, s->cie);
1431
+
1432
+    plot_gamuts((uint16_t *)out->data[0], out->linesize[0] / 2,
1433
+                outlink->w, outlink->h,
1434
+                s->cie, s->gamuts);
1435
+
1436
+    av_frame_free(&in);
1437
+    return ff_filter_frame(outlink, out);
1438
+}
1439
+
1440
+static void av_cold uninit(AVFilterContext *ctx)
1441
+{
1442
+    CiescopeContext *s = ctx->priv;
1443
+
1444
+    av_frame_free(&s->f);
1445
+}
1446
+
1447
+static int config_input(AVFilterLink *inlink)
1448
+{
1449
+    CiescopeContext *s = inlink->dst->priv;
1450
+    int i;
1451
+
1452
+    get_rgb2xyz_matrix(color_systems[s->color_system], s->m);
1453
+    invert_matrix3x3(s->m, s->i);
1454
+
1455
+    switch (inlink->format) {
1456
+    case AV_PIX_FMT_RGB24:
1457
+        s->filter = filter_rgb24;
1458
+        break;
1459
+    case AV_PIX_FMT_RGBA:
1460
+        s->filter = filter_rgba;
1461
+        break;
1462
+    case AV_PIX_FMT_RGB48:
1463
+        s->filter = filter_rgb48;
1464
+        break;
1465
+    case AV_PIX_FMT_RGBA64:
1466
+        s->filter = filter_rgba64;
1467
+        break;
1468
+    case AV_PIX_FMT_XYZ12:
1469
+        s->filter = filter_xyz;
1470
+        for (i = 0; i < 65536; i++)
1471
+            s->log2lin[i] = pow(i / 65535., s->igamma) * 65535.;
1472
+        break;
1473
+    default:
1474
+        av_assert0(0);
1475
+    }
1476
+
1477
+    return 0;
1478
+}
1479
+
1480
+static const AVFilterPad inputs[] = {
1481
+    {
1482
+        .name         = "default",
1483
+        .type         = AVMEDIA_TYPE_VIDEO,
1484
+        .filter_frame = filter_frame,
1485
+        .config_props = config_input,
1486
+    },
1487
+    { NULL }
1488
+};
1489
+
1490
+static const AVFilterPad outputs[] = {
1491
+    {
1492
+        .name         = "default",
1493
+        .type         = AVMEDIA_TYPE_VIDEO,
1494
+        .config_props = config_output,
1495
+    },
1496
+    { NULL }
1497
+};
1498
+
1499
+AVFilter ff_vf_ciescope = {
1500
+    .name          = "ciescope",
1501
+    .description   = NULL_IF_CONFIG_SMALL("Video CIE scope."),
1502
+    .priv_size     = sizeof(CiescopeContext),
1503
+    .priv_class    = &ciescope_class,
1504
+    .query_formats = query_formats,
1505
+    .uninit        = uninit,
1506
+    .inputs        = inputs,
1507
+    .outputs       = outputs,
1508
+};