GCC Code Coverage Report
Directory: ../../../ffmpeg/ Exec Total Coverage
File: src/libavfilter/vf_ciescope.c Lines: 0 398 0.0 %
Date: 2020-10-23 17:01:47 Branches: 0 158 0.0 %

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