2 * Copyright (c) 2000 John Walker
3 * Copyright (c) 2016 Paul B Mahol
5 * This file is part of FFmpeg.
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.
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.
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
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"
53 typedef struct CiescopeContext {
65 double log2lin[65536];
70 void (*filter)(AVFilterContext *ctx, AVFrame *in, double *cx, double *cy, int x, int y);
73 #define OFFSET(x) offsetof(CiescopeContext, x)
74 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
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 },
122 AVFILTER_DEFINE_CLASS(ciescope);
124 static const enum AVPixelFormat in_pix_fmts[] = {
133 static const enum AVPixelFormat out_pix_fmts[] = {
138 static int query_formats(AVFilterContext *ctx)
142 if ((ret = ff_formats_ref(ff_make_format_list(in_pix_fmts), &ctx->inputs[0]->outcfg.formats)) < 0)
145 if ((ret = ff_formats_ref(ff_make_format_list(out_pix_fmts), &ctx->outputs[0]->incfg.formats)) < 0)
151 static int config_output(AVFilterLink *outlink)
153 CiescopeContext *s = outlink->src->priv;
155 outlink->h = outlink->w = s->size;
156 outlink->sample_aspect_ratio = (AVRational){1,1};
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
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 */
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 },
648 /* Standard white point chromaticities. */
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
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
661 #define GAMMA_REC709 0. /* Rec. 709 */
663 static const struct ColorSystem color_systems[] = {
665 0.67, 0.33, 0.21, 0.71, 0.14, 0.08,
669 0.64, 0.33, 0.29, 0.60, 0.15, 0.06,
673 0.630, 0.340, 0.310, 0.595, 0.155, 0.070,
676 [SMPTE240Msystem] = {
677 0.670, 0.330, 0.210, 0.710, 0.150, 0.060,
681 0.625, 0.340, 0.280, 0.595, 0.115, 0.070,
685 0.7347, 0.2653, 0.1152, 0.8264, 0.1566, 0.0177,
689 0.7347, 0.2653, 0.2738, 0.7174, 0.1666, 0.0089,
693 0.64, 0.33, 0.30, 0.60, 0.15, 0.06,
697 0.708, 0.292, 0.170, 0.797, 0.131, 0.046,
701 0.680, 0.320, 0.265, 0.690, 0.150, 0.060,
702 0.314, 0.351, GAMMA_REC709
707 static struct ColorSystem CustomSystem = {
709 0.64, 0.33, 0.30, 0.60, 0.15, 0.06,
715 uv_to_xy(double const u,
721 Given 1970 coordinates u, v, determine 1931 chromaticities x, y
723 *xc = 3*u / (2*u - 8*v + 4);
724 *yc = 2*v / (2*u - 8*v + 4);
728 upvp_to_xy(double const up,
734 Given 1976 coordinates u', v', determine 1931 chromaticities x, y
736 *xc = 9*up / (6*up - 16*vp + 12);
737 *yc = 4*vp / (6*up - 16*vp + 12);
741 xy_to_upvp(double xc,
747 Given 1931 chromaticities x, y, determine 1976 coordinates u', v'
749 *up = 4*xc / (- 2*xc + 12*yc + 3);
750 *vp = 9*yc / (- 2*xc + 12*yc + 3);
760 Given 1931 chromaticities x, y, determine 1960 coordinates u, v
762 *u = 4*xc / (- 2*xc + 12*yc + 3);
763 *v = 6*yc / (- 2*xc + 12*yc + 3);
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)
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;
776 static void invert_matrix3x3(double in[3][3], double out[3][3])
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];
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);
794 det = m00 * out[0][0] + m10 * out[0][1] + m20 * out[0][2];
797 for (i = 0; i < 3; i++) {
798 for (j = 0; j < 3; j++)
803 static void get_rgb2xyz_matrix(struct ColorSystem system, double m[3][3])
805 double S[3], X[4], Z[4];
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;
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;
818 for (i = 0; i < 3; i++) {
824 invert_matrix3x3(m, m);
826 for (i = 0; i < 3; i++)
827 S[i] = m[i][0] * X[3] + m[i][1] * 1 + m[i][2] * Z[3];
829 for (i = 0; i < 3; i++) {
830 m[0][i] = S[i] * X[i];
832 m[2][i] = S[i] * Z[i];
843 const double m[3][3])
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;
858 constrain_rgb(double * const r,
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 -----------------------------------------------------------------------------*/
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;
876 /* Add just enough white to make r, g, b all positive. */
878 *r += w; *g += w; *b += w;
880 return 1; /* Color modified to fit RGB gamut */
883 return 0; /* Color within RGB gamut */
887 gamma_correct(const struct ColorSystem * const cs,
890 /*----------------------------------------------------------------------------
891 Transform linear RGB values to nonlinear RGB values.
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.
898 http://www.inforamp.net/~poynton/ColorFAQ.html
899 http://www.inforamp.net/~poynton/GammaFAQ.html
900 -----------------------------------------------------------------------------*/
907 /* Rec. 709 gamma correction. */
910 *c *= (1.099 * pow(cc, 0.45) - 0.099) / cc;
912 *c = 1.099 * pow(*c, 0.45) - 0.099;
915 /* Nonlinear color = (Linear color)^(1/gamma) */
916 *c = pow(*c, 1./gamma);
923 gamma_correct_rgb(const struct ColorSystem * const cs,
928 gamma_correct(cs, r);
929 gamma_correct(cs, g);
930 gamma_correct(cs, b);
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)
937 #define Sz(x) (((x) * (int)FFMIN(w, h)) / 512)
940 monochrome_color_location(double waveLength, int w, int h,
941 int cie, int *xP, int *yP)
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);
953 xy_to_upvp(px, py, &up, &vp);
955 *yP = (h - 1) - vp * (h - 1);
956 } else if (cie == UCS) {
959 xy_to_uv(px, py, &u, &v);
961 *yP = (h - 1) - v * (h - 1);
962 } else if (cie == XYY) {
964 *yP = (h - 1) - py * (h - 1);
971 find_tongue(uint16_t* const pixels,
975 int * const presentP,
976 int * const leftEdgeP,
977 int * const rightEdgeP)
981 for (i = 0; i < w && pixels[row * linesize + i * 4 + 0] == 0; i++)
988 int const leftEdge = i;
992 for (j = w - 1; j >= leftEdge && pixels[row * linesize + j * 4 + 0] == 0; j--)
996 *leftEdgeP = leftEdge;
1000 static void draw_line(uint16_t *const pixels, int linesize,
1001 int x0, int y0, int x1, int y1,
1003 const uint16_t *const rgbcolor)
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;
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];
1015 if (x0 == x1 && y0 == y1)
1032 static void draw_rline(uint16_t *const pixels, int linesize,
1033 int x0, int y0, int x1, int y1,
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;
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;
1046 if (x0 == x1 && y0 == y1)
1064 tongue_outline(uint16_t* const pixels,
1068 uint16_t const maxval,
1071 const uint16_t rgbcolor[4] = { maxval, maxval, maxval, maxval };
1076 for (wavelength = 360; wavelength <= 830; wavelength++) {
1079 monochrome_color_location(wavelength, w, h, cie,
1082 if (wavelength > 360)
1083 draw_line(pixels, linesize, lx, ly, icx, icy, w, h, rgbcolor);
1091 draw_line(pixels, linesize, lx, ly, fx, fy, w, h, rgbcolor);
1095 fill_in_tongue(uint16_t* const pixels,
1099 uint16_t const maxval,
1100 const struct ColorSystem * const cs,
1101 double const m[3][3],
1103 int const correct_gamma,
1104 float const contrast)
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.
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 */
1118 find_tongue(pixels, w, linesize, y, &present, &leftEdge, &rightEdge);
1123 for (x = leftEdge; x <= rightEdge; ++x) {
1124 double cx, cy, cz, jr, jg, jb, jmax;
1125 int r, g, b, mx = maxval;
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) {
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);
1147 xyz_to_rgb(m, cx, cy, cz, &jr, &jg, &jb);
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. */
1154 if (constrain_rgb(&jr, &jg, &jb))
1157 jmax = FFMAX3(jr, jg, jb);
1163 /* gamma correct from linear rgb to nonlinear rgb. */
1165 gamma_correct_rgb(cs, &jr, &jg, &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;
1179 plot_white_point(uint16_t* pixels,
1184 int const color_system,
1187 const struct ColorSystem *cs = &color_systems[color_system];
1192 xy_to_upvp(cs->xWhite, cs->yWhite, &wup, &wvp);
1194 wy = (h - 1) - ((int) ((h - 1) * wvp));
1195 } else if (cie == UCS) {
1197 xy_to_uv(cs->xWhite, cs->yWhite, &wu, &wv);
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));
1207 draw_rline(pixels, linesize,
1208 wx + Sz(3), wy, wx + Sz(10), wy,
1210 draw_rline(pixels, linesize,
1211 wx - Sz(3), wy, wx - Sz(10), wy,
1213 draw_rline(pixels, linesize,
1214 wx, wy + Sz(3), wx, wy + Sz(10),
1216 draw_rline(pixels, linesize,
1217 wx, wy - Sz(3), wx, wy - Sz(10),
1221 static int draw_background(AVFilterContext *ctx)
1223 CiescopeContext *s = ctx->priv;
1224 const struct ColorSystem *cs = &color_systems[s->color_system];
1225 AVFilterLink *outlink = ctx->outputs[0];
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];
1234 tongue_outline(pixels, s->f->linesize[0] / 2, w, h, 65535, s->cie);
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);
1242 static void filter_rgb48(AVFilterContext *ctx, AVFrame *in, double *cx, double *cy, int x, int y)
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.;
1251 rgb_to_xy(r, g, b, cx, cy, &cz, (const double (*)[3])s->m);
1254 static void filter_rgba64(AVFilterContext *ctx, AVFrame *in, double *cx, double *cy, int x, int y)
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.;
1263 rgb_to_xy(r, g, b, cx, cy, &cz, (const double (*)[3])s->m);
1266 static void filter_rgb24(AVFilterContext *ctx, AVFrame *in, double *cx, double *cy, int x, int y)
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.;
1275 rgb_to_xy(r, g, b, cx, cy, &cz, (const double (*)[3])s->m);
1278 static void filter_rgba(AVFilterContext *ctx, AVFrame *in, double *cx, double *cy, int x, int y)
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.;
1287 rgb_to_xy(r, g, b, cx, cy, &cz, (const double (*)[3])s->m);
1290 static void filter_xyz(AVFilterContext *ctx, AVFrame *in, double *cx, double *cy, int x, int y)
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;
1305 static void plot_gamuts(uint16_t *pixels, int linesize, int w, int h,
1306 int cie, int gamuts)
1310 for (i = 0; i < NB_CS; i++) {
1311 const struct ColorSystem *cs = &color_systems[i];
1312 int rx, ry, gx, gy, bx, by;
1314 if (!((1 << i) & gamuts))
1318 xy_to_upvp(cs->xRed, cs->yRed, &wup, &wvp);
1320 ry = (h - 1) - ((int) ((h - 1) * wvp));
1321 xy_to_upvp(cs->xGreen, cs->yGreen, &wup, &wvp);
1323 gy = (h - 1) - ((int) ((h - 1) * wvp));
1324 xy_to_upvp(cs->xBlue, cs->yBlue, &wup, &wvp);
1326 by = (h - 1) - ((int) ((h - 1) * wvp));
1327 } else if (cie == UCS) {
1329 xy_to_uv(cs->xRed, cs->yRed, &wu, &wv);
1331 ry = (h - 1) - ((int) ((h - 1) * wv));
1332 xy_to_uv(cs->xGreen, cs->yGreen, &wu, &wv);
1334 gy = (h - 1) - ((int) ((h - 1) * wv));
1335 xy_to_uv(cs->xBlue, cs->yBlue, &wu, &wv);
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));
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);
1355 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
1357 AVFilterContext *ctx = inlink->dst;
1358 CiescopeContext *s = ctx->priv;
1359 AVFilterLink *outlink = ctx->outputs[0];
1360 int i = s->intensity * 65535;
1366 out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
1369 return AVERROR(ENOMEM);
1373 if (!s->background) {
1374 ret = draw_background(ctx);
1376 av_frame_free(&out);
1381 for (y = 0; y < outlink->h; y++) {
1382 memset(out->data[0] + y * out->linesize[0], 0, outlink->w * 8);
1385 for (y = 0; y < in->height; y++) {
1386 for (x = 0; x < in->width; x++) {
1391 s->filter(ctx, in, &cx, &cy, x, y);
1393 if (s->cie == LUV) {
1395 xy_to_upvp(cx, cy, &up, &vp);
1398 } else if (s->cie == UCS) {
1400 xy_to_uv(cx, cy, &u, &v);
1406 wy = (h - 1) - ((h - 1) * cy);
1408 if (wx < 0 || wx >= w ||
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);
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];
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);
1439 plot_gamuts((uint16_t *)out->data[0], out->linesize[0] / 2,
1440 outlink->w, outlink->h,
1444 return ff_filter_frame(outlink, out);
1447 static void av_cold uninit(AVFilterContext *ctx)
1449 CiescopeContext *s = ctx->priv;
1451 av_frame_free(&s->f);
1454 static int config_input(AVFilterLink *inlink)
1456 CiescopeContext *s = inlink->dst->priv;
1459 get_rgb2xyz_matrix(color_systems[s->color_system], s->m);
1460 invert_matrix3x3(s->m, s->i);
1462 switch (inlink->format) {
1463 case AV_PIX_FMT_RGB24:
1464 s->filter = filter_rgb24;
1466 case AV_PIX_FMT_RGBA:
1467 s->filter = filter_rgba;
1469 case AV_PIX_FMT_RGB48:
1470 s->filter = filter_rgb48;
1472 case AV_PIX_FMT_RGBA64:
1473 s->filter = filter_rgba64;
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.;
1487 static const AVFilterPad inputs[] = {
1490 .type = AVMEDIA_TYPE_VIDEO,
1491 .filter_frame = filter_frame,
1492 .config_props = config_input,
1497 static const AVFilterPad outputs[] = {
1500 .type = AVMEDIA_TYPE_VIDEO,
1501 .config_props = config_output,
1506 const AVFilter ff_vf_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,