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
query_formats(AVFilterContext * ctx)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
config_output(AVFilterLink * outlink)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
uv_to_xy(double const u,double const v,double * const xc,double * const yc)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
upvp_to_xy(double const up,double const vp,double * const xc,double * const yc)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
xy_to_upvp(double xc,double yc,double * const up,double * const vp)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
xy_to_uv(double xc,double yc,double * const u,double * const v)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
xyz_to_rgb(const double m[3][3],double xc,double yc,double zc,double * const r,double * const g,double * const b)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
invert_matrix3x3(double in[3][3],double out[3][3])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
get_rgb2xyz_matrix(struct ColorSystem system,double m[3][3])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
rgb_to_xy(double rc,double gc,double bc,double * const x,double * const y,double * const z,const double m[3][3])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 if (sum == 0)
853 sum = 1;
854 *x = *x / sum;
855 *y = *y / sum;
856 }
857
858 static int
constrain_rgb(double * const r,double * const g,double * const b)859 constrain_rgb(double * const r,
860 double * const g,
861 double * const b)
862 {
863 /*----------------------------------------------------------------------------
864 If the requested RGB shade contains a negative weight for one of
865 the primaries, it lies outside the color gamut accessible from
866 the given triple of primaries. Desaturate it by adding white,
867 equal quantities of R, G, and B, enough to make RGB all positive.
868 -----------------------------------------------------------------------------*/
869 double w;
870
871 /* Amount of white needed is w = - min(0, *r, *g, *b) */
872 w = (0 < *r) ? 0 : *r;
873 w = (w < *g) ? w : *g;
874 w = (w < *b) ? w : *b;
875 w = - w;
876
877 /* Add just enough white to make r, g, b all positive. */
878 if (w > 0) {
879 *r += w; *g += w; *b += w;
880
881 return 1; /* Color modified to fit RGB gamut */
882 }
883
884 return 0; /* Color within RGB gamut */
885 }
886
887 static void
gamma_correct(const struct ColorSystem * const cs,double * const c)888 gamma_correct(const struct ColorSystem * const cs,
889 double * const c)
890 {
891 /*----------------------------------------------------------------------------
892 Transform linear RGB values to nonlinear RGB values.
893
894 Rec. 709 is ITU-R Recommendation BT. 709 (1990)
895 ``Basic Parameter Values for the HDTV Standard for the Studio and for
896 International Programme Exchange'', formerly CCIR Rec. 709.
897
898 For details see
899 http://www.inforamp.net/~poynton/ColorFAQ.html
900 http://www.inforamp.net/~poynton/GammaFAQ.html
901 -----------------------------------------------------------------------------*/
902 double gamma;
903 double cc;
904
905 gamma = cs->gamma;
906
907 if (gamma == 0.) {
908 /* Rec. 709 gamma correction. */
909 cc = 0.018;
910 if (*c < cc) {
911 *c *= (1.099 * pow(cc, 0.45) - 0.099) / cc;
912 } else {
913 *c = 1.099 * pow(*c, 0.45) - 0.099;
914 }
915 } else {
916 /* Nonlinear color = (Linear color)^(1/gamma) */
917 *c = pow(*c, 1./gamma);
918 }
919 }
920
921
922
923 static void
gamma_correct_rgb(const struct ColorSystem * const cs,double * const r,double * const g,double * const b)924 gamma_correct_rgb(const struct ColorSystem * const cs,
925 double * const r,
926 double * const g,
927 double * const b)
928 {
929 gamma_correct(cs, r);
930 gamma_correct(cs, g);
931 gamma_correct(cs, b);
932 }
933
934 /* Sz(X) is the displacement in pixels of a displacement of X normalized
935 distance units. (A normalized distance unit is 1/512 of the smaller
936 dimension of the canvas)
937 */
938 #define Sz(x) (((x) * (int)FFMIN(w, h)) / 512)
939
940 static void
monochrome_color_location(double waveLength,int w,int h,int cie,int * xP,int * yP)941 monochrome_color_location(double waveLength, int w, int h,
942 int cie, int *xP, int *yP)
943 {
944 const int ix = waveLength - 360;
945 const double pX = spectral_chromaticity[ix][0];
946 const double pY = spectral_chromaticity[ix][1];
947 const double pZ = spectral_chromaticity[ix][2];
948 const double px = pX / (pX + pY + pZ);
949 const double py = pY / (pX + pY + pZ);
950
951 if (cie == LUV) {
952 double up, vp;
953
954 xy_to_upvp(px, py, &up, &vp);
955 *xP = up * (w - 1);
956 *yP = (h - 1) - vp * (h - 1);
957 } else if (cie == UCS) {
958 double u, v;
959
960 xy_to_uv(px, py, &u, &v);
961 *xP = u * (w - 1);
962 *yP = (h - 1) - v * (h - 1);
963 } else if (cie == XYY) {
964 *xP = px * (w - 1);
965 *yP = (h - 1) - py * (h - 1);
966 } else {
967 av_assert0(0);
968 }
969 }
970
971 static void
find_tongue(uint16_t * const pixels,int const w,int const linesize,int const row,int * const presentP,int * const leftEdgeP,int * const rightEdgeP)972 find_tongue(uint16_t* const pixels,
973 int const w,
974 int const linesize,
975 int const row,
976 int * const presentP,
977 int * const leftEdgeP,
978 int * const rightEdgeP)
979 {
980 int i;
981
982 for (i = 0; i < w && pixels[row * linesize + i * 4 + 0] == 0; i++)
983 ;
984
985 if (i >= w) {
986 *presentP = 0;
987 } else {
988 int j;
989 int const leftEdge = i;
990
991 *presentP = 1;
992
993 for (j = w - 1; j >= leftEdge && pixels[row * linesize + j * 4 + 0] == 0; j--)
994 ;
995
996 *rightEdgeP = j;
997 *leftEdgeP = leftEdge;
998 }
999 }
1000
draw_line(uint16_t * const pixels,int linesize,int x0,int y0,int x1,int y1,int w,int h,const uint16_t * const rgbcolor)1001 static void draw_line(uint16_t *const pixels, int linesize,
1002 int x0, int y0, int x1, int y1,
1003 int w, int h,
1004 const uint16_t *const rgbcolor)
1005 {
1006 int dx = FFABS(x1 - x0), sx = x0 < x1 ? 1 : -1;
1007 int dy = FFABS(y1 - y0), sy = y0 < y1 ? 1 : -1;
1008 int err = (dx > dy ? dx : -dy) / 2, e2;
1009
1010 for (;;) {
1011 pixels[y0 * linesize + x0 * 4 + 0] = rgbcolor[0];
1012 pixels[y0 * linesize + x0 * 4 + 1] = rgbcolor[1];
1013 pixels[y0 * linesize + x0 * 4 + 2] = rgbcolor[2];
1014 pixels[y0 * linesize + x0 * 4 + 3] = rgbcolor[3];
1015
1016 if (x0 == x1 && y0 == y1)
1017 break;
1018
1019 e2 = err;
1020
1021 if (e2 >-dx) {
1022 err -= dy;
1023 x0 += sx;
1024 }
1025
1026 if (e2 < dy) {
1027 err += dx;
1028 y0 += sy;
1029 }
1030 }
1031 }
1032
draw_rline(uint16_t * const pixels,int linesize,int x0,int y0,int x1,int y1,int w,int h)1033 static void draw_rline(uint16_t *const pixels, int linesize,
1034 int x0, int y0, int x1, int y1,
1035 int w, int h)
1036 {
1037 int dx = FFABS(x1 - x0), sx = x0 < x1 ? 1 : -1;
1038 int dy = FFABS(y1 - y0), sy = y0 < y1 ? 1 : -1;
1039 int err = (dx > dy ? dx : -dy) / 2, e2;
1040
1041 for (;;) {
1042 pixels[y0 * linesize + x0 * 4 + 0] = 65535 - pixels[y0 * linesize + x0 * 4 + 0];
1043 pixels[y0 * linesize + x0 * 4 + 1] = 65535 - pixels[y0 * linesize + x0 * 4 + 1];
1044 pixels[y0 * linesize + x0 * 4 + 2] = 65535 - pixels[y0 * linesize + x0 * 4 + 2];
1045 pixels[y0 * linesize + x0 * 4 + 3] = 65535;
1046
1047 if (x0 == x1 && y0 == y1)
1048 break;
1049
1050 e2 = err;
1051
1052 if (e2 >-dx) {
1053 err -= dy;
1054 x0 += sx;
1055 }
1056
1057 if (e2 < dy) {
1058 err += dx;
1059 y0 += sy;
1060 }
1061 }
1062 }
1063
1064 static void
tongue_outline(uint16_t * const pixels,int const linesize,int const w,int const h,uint16_t const maxval,int const cie)1065 tongue_outline(uint16_t* const pixels,
1066 int const linesize,
1067 int const w,
1068 int const h,
1069 uint16_t const maxval,
1070 int const cie)
1071 {
1072 const uint16_t rgbcolor[4] = { maxval, maxval, maxval, maxval };
1073 int wavelength;
1074 int lx, ly;
1075 int fx, fy;
1076
1077 for (wavelength = 360; wavelength <= 830; wavelength++) {
1078 int icx, icy;
1079
1080 monochrome_color_location(wavelength, w, h, cie,
1081 &icx, &icy);
1082
1083 if (wavelength > 360)
1084 draw_line(pixels, linesize, lx, ly, icx, icy, w, h, rgbcolor);
1085 else {
1086 fx = icx;
1087 fy = icy;
1088 }
1089 lx = icx;
1090 ly = icy;
1091 }
1092 draw_line(pixels, linesize, lx, ly, fx, fy, w, h, rgbcolor);
1093 }
1094
1095 static void
fill_in_tongue(uint16_t * const pixels,int const linesize,int const w,int const h,uint16_t const maxval,const struct ColorSystem * const cs,double const m[3][3],int const cie,int const correct_gamma,float const contrast)1096 fill_in_tongue(uint16_t* const pixels,
1097 int const linesize,
1098 int const w,
1099 int const h,
1100 uint16_t const maxval,
1101 const struct ColorSystem * const cs,
1102 double const m[3][3],
1103 int const cie,
1104 int const correct_gamma,
1105 float const contrast)
1106 {
1107 int y;
1108
1109 /* Scan the image line by line and fill the tongue outline
1110 with the RGB values determined by the color system for the x-y
1111 co-ordinates within the tongue.
1112 */
1113
1114 for (y = 0; y < h; ++y) {
1115 int present; /* There is some tongue on this line */
1116 int leftEdge; /* x position of leftmost pixel in tongue on this line */
1117 int rightEdge; /* same, but rightmost */
1118
1119 find_tongue(pixels, w, linesize, y, &present, &leftEdge, &rightEdge);
1120
1121 if (present) {
1122 int x;
1123
1124 for (x = leftEdge; x <= rightEdge; ++x) {
1125 double cx, cy, cz, jr, jg, jb, jmax;
1126 int r, g, b, mx = maxval;
1127
1128 if (cie == LUV) {
1129 double up, vp;
1130 up = ((double) x) / (w - 1);
1131 vp = 1.0 - ((double) y) / (h - 1);
1132 upvp_to_xy(up, vp, &cx, &cy);
1133 cz = 1.0 - (cx + cy);
1134 } else if (cie == UCS) {
1135 double u, v;
1136 u = ((double) x) / (w - 1);
1137 v = 1.0 - ((double) y) / (h - 1);
1138 uv_to_xy(u, v, &cx, &cy);
1139 cz = 1.0 - (cx + cy);
1140 } else if (cie == XYY) {
1141 cx = ((double) x) / (w - 1);
1142 cy = 1.0 - ((double) y) / (h - 1);
1143 cz = 1.0 - (cx + cy);
1144 } else {
1145 av_assert0(0);
1146 }
1147
1148 xyz_to_rgb(m, cx, cy, cz, &jr, &jg, &jb);
1149
1150 /* Check whether the requested color is within the
1151 gamut achievable with the given color system. If
1152 not, draw it in a reduced intensity, interpolated
1153 by desaturation to the closest within-gamut color. */
1154
1155 if (constrain_rgb(&jr, &jg, &jb))
1156 mx *= contrast;
1157
1158 jmax = FFMAX3(jr, jg, jb);
1159 if (jmax > 0) {
1160 jr = jr / jmax;
1161 jg = jg / jmax;
1162 jb = jb / jmax;
1163 }
1164 /* gamma correct from linear rgb to nonlinear rgb. */
1165 if (correct_gamma)
1166 gamma_correct_rgb(cs, &jr, &jg, &jb);
1167 r = mx * jr;
1168 g = mx * jg;
1169 b = mx * jb;
1170 pixels[y * linesize + x * 4 + 0] = r;
1171 pixels[y * linesize + x * 4 + 1] = g;
1172 pixels[y * linesize + x * 4 + 2] = b;
1173 pixels[y * linesize + x * 4 + 3] = 65535;
1174 }
1175 }
1176 }
1177 }
1178
1179 static void
plot_white_point(uint16_t * pixels,int const linesize,int const w,int const h,int const maxval,int const color_system,int const cie)1180 plot_white_point(uint16_t* pixels,
1181 int const linesize,
1182 int const w,
1183 int const h,
1184 int const maxval,
1185 int const color_system,
1186 int const cie)
1187 {
1188 const struct ColorSystem *cs = &color_systems[color_system];
1189 int wx, wy;
1190
1191 if (cie == LUV) {
1192 double wup, wvp;
1193 xy_to_upvp(cs->xWhite, cs->yWhite, &wup, &wvp);
1194 wx = (w - 1) * wup;
1195 wy = (h - 1) - ((int) ((h - 1) * wvp));
1196 } else if (cie == UCS) {
1197 double wu, wv;
1198 xy_to_uv(cs->xWhite, cs->yWhite, &wu, &wv);
1199 wx = (w - 1) * wu;
1200 wy = (h - 1) - ((int) ((h - 1) * wv));
1201 } else if (cie == XYY) {
1202 wx = (w - 1) * cs->xWhite;
1203 wy = (h - 1) - ((int) ((h - 1) * cs->yWhite));
1204 } else {
1205 av_assert0(0);
1206 }
1207
1208 draw_rline(pixels, linesize,
1209 wx + Sz(3), wy, wx + Sz(10), wy,
1210 w, h);
1211 draw_rline(pixels, linesize,
1212 wx - Sz(3), wy, wx - Sz(10), wy,
1213 w, h);
1214 draw_rline(pixels, linesize,
1215 wx, wy + Sz(3), wx, wy + Sz(10),
1216 w, h);
1217 draw_rline(pixels, linesize,
1218 wx, wy - Sz(3), wx, wy - Sz(10),
1219 w, h);
1220 }
1221
draw_background(AVFilterContext * ctx)1222 static int draw_background(AVFilterContext *ctx)
1223 {
1224 CiescopeContext *s = ctx->priv;
1225 const struct ColorSystem *cs = &color_systems[s->color_system];
1226 AVFilterLink *outlink = ctx->outputs[0];
1227 int w = s->size;
1228 int h = s->size;
1229 uint16_t *pixels;
1230
1231 if ((s->f = ff_get_video_buffer(outlink, outlink->w, outlink->h)) == NULL)
1232 return AVERROR(ENOMEM);
1233 pixels = (uint16_t *)s->f->data[0];
1234
1235 tongue_outline(pixels, s->f->linesize[0] / 2, w, h, 65535, s->cie);
1236
1237 fill_in_tongue(pixels, s->f->linesize[0] / 2, w, h, 65535, cs, (const double (*)[3])s->i, s->cie,
1238 s->correct_gamma, s->contrast);
1239
1240 return 0;
1241 }
1242
filter_rgb48(AVFilterContext * ctx,AVFrame * in,double * cx,double * cy,int x,int y)1243 static void filter_rgb48(AVFilterContext *ctx, AVFrame *in, double *cx, double *cy, int x, int y)
1244 {
1245 CiescopeContext *s = ctx->priv;
1246 const uint16_t* src = (const uint16_t*)(in->data[0] + in->linesize[0] * y + x * 6);
1247 double r = src[0] / 65535.;
1248 double g = src[1] / 65535.;
1249 double b = src[2] / 65535.;
1250 double cz;
1251
1252 rgb_to_xy(r, g, b, cx, cy, &cz, (const double (*)[3])s->m);
1253 }
1254
filter_rgba64(AVFilterContext * ctx,AVFrame * in,double * cx,double * cy,int x,int y)1255 static void filter_rgba64(AVFilterContext *ctx, AVFrame *in, double *cx, double *cy, int x, int y)
1256 {
1257 CiescopeContext *s = ctx->priv;
1258 const uint16_t* src = (const uint16_t*)(in->data[0] + in->linesize[0] * y + x * 8);
1259 double r = src[0] / 65535.;
1260 double g = src[1] / 65535.;
1261 double b = src[2] / 65535.;
1262 double cz;
1263
1264 rgb_to_xy(r, g, b, cx, cy, &cz, (const double (*)[3])s->m);
1265 }
1266
filter_rgb24(AVFilterContext * ctx,AVFrame * in,double * cx,double * cy,int x,int y)1267 static void filter_rgb24(AVFilterContext *ctx, AVFrame *in, double *cx, double *cy, int x, int y)
1268 {
1269 CiescopeContext *s = ctx->priv;
1270 const uint8_t* src = in->data[0] + in->linesize[0] * y + x * 3;
1271 double r = src[0] / 255.;
1272 double g = src[1] / 255.;
1273 double b = src[2] / 255.;
1274 double cz;
1275
1276 rgb_to_xy(r, g, b, cx, cy, &cz, (const double (*)[3])s->m);
1277 }
1278
filter_rgba(AVFilterContext * ctx,AVFrame * in,double * cx,double * cy,int x,int y)1279 static void filter_rgba(AVFilterContext *ctx, AVFrame *in, double *cx, double *cy, int x, int y)
1280 {
1281 CiescopeContext *s = ctx->priv;
1282 const uint8_t* src = in->data[0] + in->linesize[0] * y + x * 4;
1283 double r = src[0] / 255.;
1284 double g = src[1] / 255.;
1285 double b = src[2] / 255.;
1286 double cz;
1287
1288 rgb_to_xy(r, g, b, cx, cy, &cz, (const double (*)[3])s->m);
1289 }
1290
filter_xyz(AVFilterContext * ctx,AVFrame * in,double * cx,double * cy,int x,int y)1291 static void filter_xyz(AVFilterContext *ctx, AVFrame *in, double *cx, double *cy, int x, int y)
1292 {
1293 CiescopeContext *s = ctx->priv;
1294 const uint16_t* src = (uint16_t *)(in->data[0] + in->linesize[0] * y + x * 6);
1295 double lx = s->log2lin[src[0]];
1296 double ly = s->log2lin[src[1]];
1297 double lz = s->log2lin[src[2]];
1298 double sum = lx + ly + lz;
1299
1300 if (sum == 0)
1301 sum = 1;
1302 *cx = lx / sum;
1303 *cy = ly / sum;
1304 }
1305
plot_gamuts(uint16_t * pixels,int linesize,int w,int h,int cie,int gamuts)1306 static void plot_gamuts(uint16_t *pixels, int linesize, int w, int h,
1307 int cie, int gamuts)
1308 {
1309 int i;
1310
1311 for (i = 0; i < NB_CS; i++) {
1312 const struct ColorSystem *cs = &color_systems[i];
1313 int rx, ry, gx, gy, bx, by;
1314
1315 if (!((1 << i) & gamuts))
1316 continue;
1317 if (cie == LUV) {
1318 double wup, wvp;
1319 xy_to_upvp(cs->xRed, cs->yRed, &wup, &wvp);
1320 rx = (w - 1) * wup;
1321 ry = (h - 1) - ((int) ((h - 1) * wvp));
1322 xy_to_upvp(cs->xGreen, cs->yGreen, &wup, &wvp);
1323 gx = (w - 1) * wup;
1324 gy = (h - 1) - ((int) ((h - 1) * wvp));
1325 xy_to_upvp(cs->xBlue, cs->yBlue, &wup, &wvp);
1326 bx = (w - 1) * wup;
1327 by = (h - 1) - ((int) ((h - 1) * wvp));
1328 } else if (cie == UCS) {
1329 double wu, wv;
1330 xy_to_uv(cs->xRed, cs->yRed, &wu, &wv);
1331 rx = (w - 1) * wu;
1332 ry = (h - 1) - ((int) ((h - 1) * wv));
1333 xy_to_uv(cs->xGreen, cs->yGreen, &wu, &wv);
1334 gx = (w - 1) * wu;
1335 gy = (h - 1) - ((int) ((h - 1) * wv));
1336 xy_to_uv(cs->xBlue, cs->yBlue, &wu, &wv);
1337 bx = (w - 1) * wu;
1338 by = (h - 1) - ((int) ((h - 1) * wv));
1339 } else if (cie == XYY) {
1340 rx = (w - 1) * cs->xRed;
1341 ry = (h - 1) - ((int) ((h - 1) * cs->yRed));
1342 gx = (w - 1) * cs->xGreen;
1343 gy = (h - 1) - ((int) ((h - 1) * cs->yGreen));
1344 bx = (w - 1) * cs->xBlue;
1345 by = (h - 1) - ((int) ((h - 1) * cs->yBlue));
1346 } else {
1347 av_assert0(0);
1348 }
1349
1350 draw_rline(pixels, linesize, rx, ry, gx, gy, w, h);
1351 draw_rline(pixels, linesize, gx, gy, bx, by, w, h);
1352 draw_rline(pixels, linesize, bx, by, rx, ry, w, h);
1353 }
1354 }
1355
filter_frame(AVFilterLink * inlink,AVFrame * in)1356 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
1357 {
1358 AVFilterContext *ctx = inlink->dst;
1359 CiescopeContext *s = ctx->priv;
1360 AVFilterLink *outlink = ctx->outputs[0];
1361 int i = s->intensity * 65535;
1362 int w = outlink->w;
1363 int h = outlink->h;
1364 AVFrame *out;
1365 int ret, x, y;
1366
1367 out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
1368 if (!out) {
1369 av_frame_free(&in);
1370 return AVERROR(ENOMEM);
1371 }
1372 out->pts = in->pts;
1373
1374 if (!s->background) {
1375 ret = draw_background(ctx);
1376 if (ret < 0) {
1377 av_frame_free(&out);
1378 return ret;
1379 }
1380 s->background = 1;
1381 }
1382 for (y = 0; y < outlink->h; y++) {
1383 memset(out->data[0] + y * out->linesize[0], 0, outlink->w * 8);
1384 }
1385
1386 for (y = 0; y < in->height; y++) {
1387 for (x = 0; x < in->width; x++) {
1388 double cx, cy;
1389 uint16_t *dst;
1390 int wx, wy;
1391
1392 s->filter(ctx, in, &cx, &cy, x, y);
1393
1394 if (s->cie == LUV) {
1395 double up, vp;
1396 xy_to_upvp(cx, cy, &up, &vp);
1397 cx = up;
1398 cy = vp;
1399 } else if (s->cie == UCS) {
1400 double u, v;
1401 xy_to_uv(cx, cy, &u, &v);
1402 cx = u;
1403 cy = v;
1404 }
1405
1406 wx = (w - 1) * cx;
1407 wy = (h - 1) - ((h - 1) * cy);
1408
1409 if (wx < 0 || wx >= w ||
1410 wy < 0 || wy >= h)
1411 continue;
1412
1413 dst = (uint16_t *)(out->data[0] + wy * out->linesize[0] + wx * 8 + 0);
1414 dst[0] = FFMIN(dst[0] + i, 65535);
1415 dst[1] = FFMIN(dst[1] + i, 65535);
1416 dst[2] = FFMIN(dst[2] + i, 65535);
1417 dst[3] = 65535;
1418 }
1419 }
1420
1421 for (y = 0; y < outlink->h; y++) {
1422 uint16_t *dst = (uint16_t *)(out->data[0] + y * out->linesize[0]);
1423 const uint16_t *src = (const uint16_t *)(s->f->data[0] + y * s->f->linesize[0]);
1424 for (x = 0; x < outlink->w; x++) {
1425 const int xx = x * 4;
1426 if (dst[xx + 3] == 0) {
1427 dst[xx + 0] = src[xx + 0];
1428 dst[xx + 1] = src[xx + 1];
1429 dst[xx + 2] = src[xx + 2];
1430 dst[xx + 3] = src[xx + 3];
1431 }
1432 }
1433 }
1434
1435 if (s->show_white)
1436 plot_white_point((uint16_t *)out->data[0], out->linesize[0] / 2,
1437 outlink->w, outlink->h, 65535,
1438 s->color_system, s->cie);
1439
1440 plot_gamuts((uint16_t *)out->data[0], out->linesize[0] / 2,
1441 outlink->w, outlink->h,
1442 s->cie, s->gamuts);
1443
1444 av_frame_free(&in);
1445 return ff_filter_frame(outlink, out);
1446 }
1447
uninit(AVFilterContext * ctx)1448 static void av_cold uninit(AVFilterContext *ctx)
1449 {
1450 CiescopeContext *s = ctx->priv;
1451
1452 av_frame_free(&s->f);
1453 }
1454
config_input(AVFilterLink * inlink)1455 static int config_input(AVFilterLink *inlink)
1456 {
1457 CiescopeContext *s = inlink->dst->priv;
1458 int i;
1459
1460 get_rgb2xyz_matrix(color_systems[s->color_system], s->m);
1461 invert_matrix3x3(s->m, s->i);
1462
1463 switch (inlink->format) {
1464 case AV_PIX_FMT_RGB24:
1465 s->filter = filter_rgb24;
1466 break;
1467 case AV_PIX_FMT_RGBA:
1468 s->filter = filter_rgba;
1469 break;
1470 case AV_PIX_FMT_RGB48:
1471 s->filter = filter_rgb48;
1472 break;
1473 case AV_PIX_FMT_RGBA64:
1474 s->filter = filter_rgba64;
1475 break;
1476 case AV_PIX_FMT_XYZ12:
1477 s->filter = filter_xyz;
1478 for (i = 0; i < 65536; i++)
1479 s->log2lin[i] = pow(i / 65535., s->igamma) * 65535.;
1480 break;
1481 default:
1482 av_assert0(0);
1483 }
1484
1485 return 0;
1486 }
1487
1488 static const AVFilterPad inputs[] = {
1489 {
1490 .name = "default",
1491 .type = AVMEDIA_TYPE_VIDEO,
1492 .filter_frame = filter_frame,
1493 .config_props = config_input,
1494 },
1495 { NULL }
1496 };
1497
1498 static const AVFilterPad outputs[] = {
1499 {
1500 .name = "default",
1501 .type = AVMEDIA_TYPE_VIDEO,
1502 .config_props = config_output,
1503 },
1504 { NULL }
1505 };
1506
1507 AVFilter ff_vf_ciescope = {
1508 .name = "ciescope",
1509 .description = NULL_IF_CONFIG_SMALL("Video CIE scope."),
1510 .priv_size = sizeof(CiescopeContext),
1511 .priv_class = &ciescope_class,
1512 .query_formats = query_formats,
1513 .uninit = uninit,
1514 .inputs = inputs,
1515 .outputs = outputs,
1516 };
1517