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"
52 typedef struct CiescopeContext {
64 double log2lin[65536];
69 void (*filter)(AVFilterContext *ctx, AVFrame *in, double *cx, double *cy, int x, int y);
72 #define OFFSET(x) offsetof(CiescopeContext, x)
73 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
75 static const AVOption ciescope_options[] = {
76 { "system", "set color system", OFFSET(color_system), AV_OPT_TYPE_INT, {.i64=Rec709system}, 0, NB_CS-1, FLAGS, "system" },
77 { "ntsc", "NTSC 1953 Y'I'O' (ITU-R BT.470 System M)", 0, AV_OPT_TYPE_CONST, {.i64=NTSCsystem}, 0, 0, FLAGS, "system" },
78 { "470m", "NTSC 1953 Y'I'O' (ITU-R BT.470 System M)", 0, AV_OPT_TYPE_CONST, {.i64=NTSCsystem}, 0, 0, FLAGS, "system" },
79 { "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" },
80 { "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" },
81 { "smpte", "SMPTE-C RGB", 0, AV_OPT_TYPE_CONST, {.i64=SMPTEsystem}, 0, 0, FLAGS, "system" },
82 { "240m", "SMPTE-240M Y'PbPr", 0, AV_OPT_TYPE_CONST, {.i64=SMPTE240Msystem},0, 0, FLAGS, "system" },
83 { "apple", "Apple RGB", 0, AV_OPT_TYPE_CONST, {.i64=APPLEsystem}, 0, 0, FLAGS, "system" },
84 { "widergb", "Adobe Wide Gamut RGB", 0, AV_OPT_TYPE_CONST, {.i64=wRGBsystem}, 0, 0, FLAGS, "system" },
85 { "cie1931", "CIE 1931 RGB", 0, AV_OPT_TYPE_CONST, {.i64=CIE1931system}, 0, 0, FLAGS, "system" },
86 { "hdtv", "ITU.BT-709 Y'CbCr", 0, AV_OPT_TYPE_CONST, {.i64=Rec709system}, 0, 0, FLAGS, "system" },
87 { "rec709", "ITU.BT-709 Y'CbCr", 0, AV_OPT_TYPE_CONST, {.i64=Rec709system}, 0, 0, FLAGS, "system" },
88 { "uhdtv", "ITU-R.BT-2020", 0, AV_OPT_TYPE_CONST, {.i64=Rec2020system}, 0, 0, FLAGS, "system" },
89 { "rec2020", "ITU-R.BT-2020", 0, AV_OPT_TYPE_CONST, {.i64=Rec2020system}, 0, 0, FLAGS, "system" },
90 { "cie", "set cie system", OFFSET(cie), AV_OPT_TYPE_INT, {.i64=XYY}, 0, NB_CIE-1, FLAGS, "cie" },
91 { "xyy", "CIE 1931 xyY", 0, AV_OPT_TYPE_CONST, {.i64=XYY}, 0, 0, FLAGS, "cie" },
92 { "ucs", "CIE 1960 UCS", 0, AV_OPT_TYPE_CONST, {.i64=UCS}, 0, 0, FLAGS, "cie" },
93 { "luv", "CIE 1976 Luv", 0, AV_OPT_TYPE_CONST, {.i64=LUV}, 0, 0, FLAGS, "cie" },
94 { "gamuts", "set what gamuts to draw", OFFSET(gamuts), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, 0xFFF, FLAGS, "gamuts" },
95 { "ntsc", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1<<NTSCsystem}, 0, 0, FLAGS, "gamuts" },
96 { "470m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1<<NTSCsystem}, 0, 0, FLAGS, "gamuts" },
97 { "ebu", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1<<EBUsystem}, 0, 0, FLAGS, "gamuts" },
98 { "470bg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1<<EBUsystem}, 0, 0, FLAGS, "gamuts" },
99 { "smpte", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1<<SMPTEsystem}, 0, 0, FLAGS, "gamuts" },
100 { "240m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1<<SMPTE240Msystem}, 0, 0, FLAGS, "gamuts" },
101 { "apple", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1<<APPLEsystem}, 0, 0, FLAGS, "gamuts" },
102 { "widergb", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1<<wRGBsystem}, 0, 0, FLAGS, "gamuts" },
103 { "cie1931", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1<<CIE1931system}, 0, 0, FLAGS, "gamuts" },
104 { "hdtv", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1<<Rec709system}, 0, 0, FLAGS, "gamuts" },
105 { "rec709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1<<Rec709system}, 0, 0, FLAGS, "gamuts" },
106 { "uhdtv", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1<<Rec2020system}, 0, 0, FLAGS, "gamuts" },
107 { "rec2020", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1<<Rec2020system}, 0, 0, FLAGS, "gamuts" },
108 { "size", "set ciescope size", OFFSET(size), AV_OPT_TYPE_INT, {.i64=512}, 256, 8192, FLAGS },
109 { "s", "set ciescope size", OFFSET(size), AV_OPT_TYPE_INT, {.i64=512}, 256, 8192, FLAGS },
110 { "intensity", "set ciescope intensity", OFFSET(intensity), AV_OPT_TYPE_FLOAT, {.dbl=0.001}, 0, 1, FLAGS },
111 { "i", "set ciescope intensity", OFFSET(intensity), AV_OPT_TYPE_FLOAT, {.dbl=0.001}, 0, 1, FLAGS },
112 { "contrast", NULL, OFFSET(contrast), AV_OPT_TYPE_FLOAT, {.dbl=0.75}, 0, 1, FLAGS },
113 { "corrgamma", NULL, OFFSET(correct_gamma), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, FLAGS },
114 { "showwhite", NULL, OFFSET(show_white), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
115 { "gamma", NULL, OFFSET(igamma), AV_OPT_TYPE_DOUBLE, {.dbl=2.6}, 0.1, 6, FLAGS },
119 AVFILTER_DEFINE_CLASS(ciescope);
121 static const enum AVPixelFormat in_pix_fmts[] = {
130 static const enum AVPixelFormat out_pix_fmts[] = {
135 static int query_formats(AVFilterContext *ctx)
139 if ((ret = ff_formats_ref(ff_make_format_list(in_pix_fmts), &ctx->inputs[0]->out_formats)) < 0)
142 if ((ret = ff_formats_ref(ff_make_format_list(out_pix_fmts), &ctx->outputs[0]->in_formats)) < 0)
148 static int config_output(AVFilterLink *outlink)
150 CiescopeContext *s = outlink->src->priv;
152 outlink->h = outlink->w = s->size;
153 outlink->sample_aspect_ratio = (AVRational){1,1};
158 /* A color system is defined by the CIE x and y coordinates of its
159 three primary illuminants and the x and y coordinates of the white
163 double xRed, yRed, /* Red primary illuminant */
164 xGreen, yGreen, /* Green primary illuminant */
165 xBlue, yBlue, /* Blue primary illuminant */
166 xWhite, yWhite, /* White point */
167 gamma; /* gamma of nonlinear correction */
170 static float const spectral_chromaticity[][3] = {
171 { 0.175560, 0.005294, 0.819146 },
172 { 0.175483, 0.005286, 0.819231 },
173 { 0.175400, 0.005279, 0.819321 },
174 { 0.175317, 0.005271, 0.819412 },
175 { 0.175237, 0.005263, 0.819500 },
176 { 0.175161, 0.005256, 0.819582 },
177 { 0.175088, 0.005247, 0.819665 },
178 { 0.175015, 0.005236, 0.819749 },
179 { 0.174945, 0.005226, 0.819829 },
180 { 0.174880, 0.005221, 0.819899 },
181 { 0.174821, 0.005221, 0.819959 },
182 { 0.174770, 0.005229, 0.820001 },
183 { 0.174722, 0.005238, 0.820040 },
184 { 0.174665, 0.005236, 0.820098 },
185 { 0.174595, 0.005218, 0.820187 },
186 { 0.174510, 0.005182, 0.820309 },
187 { 0.174409, 0.005127, 0.820464 },
188 { 0.174308, 0.005068, 0.820624 },
189 { 0.174222, 0.005017, 0.820761 },
190 { 0.174156, 0.004981, 0.820863 },
191 { 0.174112, 0.004964, 0.820924 },
192 { 0.174088, 0.004964, 0.820948 },
193 { 0.174073, 0.004973, 0.820955 },
194 { 0.174057, 0.004982, 0.820961 },
195 { 0.174036, 0.004986, 0.820978 },
196 { 0.174008, 0.004981, 0.821012 },
197 { 0.173972, 0.004964, 0.821064 },
198 { 0.173932, 0.004943, 0.821125 },
199 { 0.173889, 0.004926, 0.821185 },
200 { 0.173845, 0.004916, 0.821239 },
201 { 0.173801, 0.004915, 0.821284 },
202 { 0.173754, 0.004925, 0.821321 },
203 { 0.173705, 0.004937, 0.821358 },
204 { 0.173655, 0.004944, 0.821401 },
205 { 0.173606, 0.004940, 0.821454 },
206 { 0.173560, 0.004923, 0.821517 },
207 { 0.173514, 0.004895, 0.821590 },
208 { 0.173468, 0.004865, 0.821667 },
209 { 0.173424, 0.004836, 0.821740 },
210 { 0.173380, 0.004813, 0.821807 },
211 { 0.173337, 0.004797, 0.821866 },
212 { 0.173291, 0.004786, 0.821923 },
213 { 0.173238, 0.004779, 0.821983 },
214 { 0.173174, 0.004775, 0.822051 },
215 { 0.173101, 0.004774, 0.822125 },
216 { 0.173021, 0.004775, 0.822204 },
217 { 0.172934, 0.004781, 0.822285 },
218 { 0.172843, 0.004791, 0.822366 },
219 { 0.172751, 0.004799, 0.822450 },
220 { 0.172662, 0.004802, 0.822536 },
221 { 0.172577, 0.004799, 0.822624 },
222 { 0.172489, 0.004795, 0.822715 },
223 { 0.172396, 0.004796, 0.822808 },
224 { 0.172296, 0.004803, 0.822901 },
225 { 0.172192, 0.004815, 0.822993 },
226 { 0.172087, 0.004833, 0.823081 },
227 { 0.171982, 0.004855, 0.823163 },
228 { 0.171871, 0.004889, 0.823240 },
229 { 0.171741, 0.004939, 0.823319 },
230 { 0.171587, 0.005010, 0.823402 },
231 { 0.171407, 0.005102, 0.823490 },
232 { 0.171206, 0.005211, 0.823583 },
233 { 0.170993, 0.005334, 0.823674 },
234 { 0.170771, 0.005470, 0.823759 },
235 { 0.170541, 0.005621, 0.823838 },
236 { 0.170301, 0.005789, 0.823911 },
237 { 0.170050, 0.005974, 0.823976 },
238 { 0.169786, 0.006177, 0.824037 },
239 { 0.169505, 0.006398, 0.824097 },
240 { 0.169203, 0.006639, 0.824158 },
241 { 0.168878, 0.006900, 0.824222 },
242 { 0.168525, 0.007184, 0.824291 },
243 { 0.168146, 0.007491, 0.824363 },
244 { 0.167746, 0.007821, 0.824433 },
245 { 0.167328, 0.008175, 0.824496 },
246 { 0.166895, 0.008556, 0.824549 },
247 { 0.166446, 0.008964, 0.824589 },
248 { 0.165977, 0.009402, 0.824622 },
249 { 0.165483, 0.009865, 0.824652 },
250 { 0.164963, 0.010351, 0.824687 },
251 { 0.164412, 0.010858, 0.824731 },
252 { 0.163828, 0.011385, 0.824787 },
253 { 0.163210, 0.011937, 0.824853 },
254 { 0.162552, 0.012520, 0.824928 },
255 { 0.161851, 0.013137, 0.825011 },
256 { 0.161105, 0.013793, 0.825102 },
257 { 0.160310, 0.014491, 0.825199 },
258 { 0.159466, 0.015232, 0.825302 },
259 { 0.158573, 0.016015, 0.825412 },
260 { 0.157631, 0.016840, 0.825529 },
261 { 0.156641, 0.017705, 0.825654 },
262 { 0.155605, 0.018609, 0.825786 },
263 { 0.154525, 0.019556, 0.825920 },
264 { 0.153397, 0.020554, 0.826049 },
265 { 0.152219, 0.021612, 0.826169 },
266 { 0.150985, 0.022740, 0.826274 },
267 { 0.149691, 0.023950, 0.826359 },
268 { 0.148337, 0.025247, 0.826416 },
269 { 0.146928, 0.026635, 0.826437 },
270 { 0.145468, 0.028118, 0.826413 },
271 { 0.143960, 0.029703, 0.826337 },
272 { 0.142405, 0.031394, 0.826201 },
273 { 0.140796, 0.033213, 0.825991 },
274 { 0.139121, 0.035201, 0.825679 },
275 { 0.137364, 0.037403, 0.825233 },
276 { 0.135503, 0.039879, 0.824618 },
277 { 0.133509, 0.042692, 0.823798 },
278 { 0.131371, 0.045876, 0.822753 },
279 { 0.129086, 0.049450, 0.821464 },
280 { 0.126662, 0.053426, 0.819912 },
281 { 0.124118, 0.057803, 0.818079 },
282 { 0.121469, 0.062588, 0.815944 },
283 { 0.118701, 0.067830, 0.813468 },
284 { 0.115807, 0.073581, 0.810612 },
285 { 0.112776, 0.079896, 0.807328 },
286 { 0.109594, 0.086843, 0.803563 },
287 { 0.106261, 0.094486, 0.799253 },
288 { 0.102776, 0.102864, 0.794360 },
289 { 0.099128, 0.112007, 0.788865 },
290 { 0.095304, 0.121945, 0.782751 },
291 { 0.091294, 0.132702, 0.776004 },
292 { 0.087082, 0.144317, 0.768601 },
293 { 0.082680, 0.156866, 0.760455 },
294 { 0.078116, 0.170420, 0.751464 },
295 { 0.073437, 0.185032, 0.741531 },
296 { 0.068706, 0.200723, 0.730571 },
297 { 0.063993, 0.217468, 0.718539 },
298 { 0.059316, 0.235254, 0.705430 },
299 { 0.054667, 0.254096, 0.691238 },
300 { 0.050031, 0.274002, 0.675967 },
301 { 0.045391, 0.294976, 0.659633 },
302 { 0.040757, 0.316981, 0.642262 },
303 { 0.036195, 0.339900, 0.623905 },
304 { 0.031756, 0.363598, 0.604646 },
305 { 0.027494, 0.387921, 0.584584 },
306 { 0.023460, 0.412703, 0.563837 },
307 { 0.019705, 0.437756, 0.542539 },
308 { 0.016268, 0.462955, 0.520777 },
309 { 0.013183, 0.488207, 0.498610 },
310 { 0.010476, 0.513404, 0.476120 },
311 { 0.008168, 0.538423, 0.453409 },
312 { 0.006285, 0.563068, 0.430647 },
313 { 0.004875, 0.587116, 0.408008 },
314 { 0.003982, 0.610447, 0.385570 },
315 { 0.003636, 0.633011, 0.363352 },
316 { 0.003859, 0.654823, 0.341318 },
317 { 0.004646, 0.675898, 0.319456 },
318 { 0.006011, 0.696120, 0.297869 },
319 { 0.007988, 0.715342, 0.276670 },
320 { 0.010603, 0.733413, 0.255984 },
321 { 0.013870, 0.750186, 0.235943 },
322 { 0.017766, 0.765612, 0.216622 },
323 { 0.022244, 0.779630, 0.198126 },
324 { 0.027273, 0.792104, 0.180623 },
325 { 0.032820, 0.802926, 0.164254 },
326 { 0.038852, 0.812016, 0.149132 },
327 { 0.045328, 0.819391, 0.135281 },
328 { 0.052177, 0.825164, 0.122660 },
329 { 0.059326, 0.829426, 0.111249 },
330 { 0.066716, 0.832274, 0.101010 },
331 { 0.074302, 0.833803, 0.091894 },
332 { 0.082053, 0.834090, 0.083856 },
333 { 0.089942, 0.833289, 0.076769 },
334 { 0.097940, 0.831593, 0.070468 },
335 { 0.106021, 0.829178, 0.064801 },
336 { 0.114161, 0.826207, 0.059632 },
337 { 0.122347, 0.822770, 0.054882 },
338 { 0.130546, 0.818928, 0.050526 },
339 { 0.138702, 0.814774, 0.046523 },
340 { 0.146773, 0.810395, 0.042832 },
341 { 0.154722, 0.805864, 0.039414 },
342 { 0.162535, 0.801238, 0.036226 },
343 { 0.170237, 0.796519, 0.033244 },
344 { 0.177850, 0.791687, 0.030464 },
345 { 0.185391, 0.786728, 0.027881 },
346 { 0.192876, 0.781629, 0.025495 },
347 { 0.200309, 0.776399, 0.023292 },
348 { 0.207690, 0.771055, 0.021255 },
349 { 0.215030, 0.765595, 0.019375 },
350 { 0.222337, 0.760020, 0.017643 },
351 { 0.229620, 0.754329, 0.016051 },
352 { 0.236885, 0.748524, 0.014591 },
353 { 0.244133, 0.742614, 0.013253 },
354 { 0.251363, 0.736606, 0.012031 },
355 { 0.258578, 0.730507, 0.010916 },
356 { 0.265775, 0.724324, 0.009901 },
357 { 0.272958, 0.718062, 0.008980 },
358 { 0.280129, 0.711725, 0.008146 },
359 { 0.287292, 0.705316, 0.007391 },
360 { 0.294450, 0.698842, 0.006708 },
361 { 0.301604, 0.692308, 0.006088 },
362 { 0.308760, 0.685712, 0.005528 },
363 { 0.315914, 0.679063, 0.005022 },
364 { 0.323066, 0.672367, 0.004566 },
365 { 0.330216, 0.665628, 0.004156 },
366 { 0.337363, 0.658848, 0.003788 },
367 { 0.344513, 0.652028, 0.003459 },
368 { 0.351664, 0.645172, 0.003163 },
369 { 0.358814, 0.638287, 0.002899 },
370 { 0.365959, 0.631379, 0.002662 },
371 { 0.373102, 0.624451, 0.002448 },
372 { 0.380244, 0.617502, 0.002254 },
373 { 0.387379, 0.610542, 0.002079 },
374 { 0.394507, 0.603571, 0.001922 },
375 { 0.401626, 0.596592, 0.001782 },
376 { 0.408736, 0.589607, 0.001657 },
377 { 0.415836, 0.582618, 0.001546 },
378 { 0.422921, 0.575631, 0.001448 },
379 { 0.429989, 0.568649, 0.001362 },
380 { 0.437036, 0.561676, 0.001288 },
381 { 0.444062, 0.554714, 0.001224 },
382 { 0.451065, 0.547766, 0.001169 },
383 { 0.458041, 0.540837, 0.001123 },
384 { 0.464986, 0.533930, 0.001084 },
385 { 0.471899, 0.527051, 0.001051 },
386 { 0.478775, 0.520202, 0.001023 },
387 { 0.485612, 0.513389, 0.001000 },
388 { 0.492405, 0.506615, 0.000980 },
389 { 0.499151, 0.499887, 0.000962 },
390 { 0.505845, 0.493211, 0.000944 },
391 { 0.512486, 0.486591, 0.000923 },
392 { 0.519073, 0.480029, 0.000899 },
393 { 0.525600, 0.473527, 0.000872 },
394 { 0.532066, 0.467091, 0.000843 },
395 { 0.538463, 0.460725, 0.000812 },
396 { 0.544787, 0.454434, 0.000779 },
397 { 0.551031, 0.448225, 0.000744 },
398 { 0.557193, 0.442099, 0.000708 },
399 { 0.563269, 0.436058, 0.000673 },
400 { 0.569257, 0.430102, 0.000641 },
401 { 0.575151, 0.424232, 0.000616 },
402 { 0.580953, 0.418447, 0.000601 },
403 { 0.586650, 0.412758, 0.000591 },
404 { 0.592225, 0.407190, 0.000586 },
405 { 0.597658, 0.401762, 0.000580 },
406 { 0.602933, 0.396497, 0.000571 },
407 { 0.608035, 0.391409, 0.000556 },
408 { 0.612977, 0.386486, 0.000537 },
409 { 0.617779, 0.381706, 0.000516 },
410 { 0.622459, 0.377047, 0.000493 },
411 { 0.627037, 0.372491, 0.000472 },
412 { 0.631521, 0.368026, 0.000453 },
413 { 0.635900, 0.363665, 0.000435 },
414 { 0.640156, 0.359428, 0.000416 },
415 { 0.644273, 0.355331, 0.000396 },
416 { 0.648233, 0.351395, 0.000372 },
417 { 0.652028, 0.347628, 0.000344 },
418 { 0.655669, 0.344018, 0.000313 },
419 { 0.659166, 0.340553, 0.000281 },
420 { 0.662528, 0.337221, 0.000251 },
421 { 0.665764, 0.334011, 0.000226 },
422 { 0.668874, 0.330919, 0.000207 },
423 { 0.671859, 0.327947, 0.000194 },
424 { 0.674720, 0.325095, 0.000185 },
425 { 0.677459, 0.322362, 0.000179 },
426 { 0.680079, 0.319747, 0.000174 },
427 { 0.682582, 0.317249, 0.000170 },
428 { 0.684971, 0.314863, 0.000167 },
429 { 0.687250, 0.312586, 0.000164 },
430 { 0.689426, 0.310414, 0.000160 },
431 { 0.691504, 0.308342, 0.000154 },
432 { 0.693490, 0.306366, 0.000145 },
433 { 0.695389, 0.304479, 0.000133 },
434 { 0.697206, 0.302675, 0.000119 },
435 { 0.698944, 0.300950, 0.000106 },
436 { 0.700606, 0.299301, 0.000093 },
437 { 0.702193, 0.297725, 0.000083 },
438 { 0.703709, 0.296217, 0.000074 },
439 { 0.705163, 0.294770, 0.000067 },
440 { 0.706563, 0.293376, 0.000061 },
441 { 0.707918, 0.292027, 0.000055 },
442 { 0.709231, 0.290719, 0.000050 },
443 { 0.710500, 0.289453, 0.000047 },
444 { 0.711724, 0.288232, 0.000044 },
445 { 0.712901, 0.287057, 0.000041 },
446 { 0.714032, 0.285929, 0.000040 },
447 { 0.715117, 0.284845, 0.000038 },
448 { 0.716159, 0.283804, 0.000036 },
449 { 0.717159, 0.282806, 0.000035 },
450 { 0.718116, 0.281850, 0.000034 },
451 { 0.719033, 0.280935, 0.000032 },
452 { 0.719912, 0.280058, 0.000030 },
453 { 0.720753, 0.279219, 0.000028 },
454 { 0.721555, 0.278420, 0.000026 },
455 { 0.722315, 0.277662, 0.000023 },
456 { 0.723032, 0.276948, 0.000020 },
457 { 0.723702, 0.276282, 0.000016 },
458 { 0.724328, 0.275660, 0.000012 },
459 { 0.724914, 0.275078, 0.000007 },
460 { 0.725467, 0.274530, 0.000003 },
461 { 0.725992, 0.274008, 0.000000 },
462 { 0.726495, 0.273505, 0.000000 },
463 { 0.726975, 0.273025, 0.000000 },
464 { 0.727432, 0.272568, 0.000000 },
465 { 0.727864, 0.272136, 0.000000 },
466 { 0.728272, 0.271728, 0.000000 },
467 { 0.728656, 0.271344, 0.000000 },
468 { 0.729020, 0.270980, 0.000000 },
469 { 0.729361, 0.270639, 0.000000 },
470 { 0.729678, 0.270322, 0.000000 },
471 { 0.729969, 0.270031, 0.000000 },
472 { 0.730234, 0.269766, 0.000000 },
473 { 0.730474, 0.269526, 0.000000 },
474 { 0.730693, 0.269307, 0.000000 },
475 { 0.730896, 0.269104, 0.000000 },
476 { 0.731089, 0.268911, 0.000000 },
477 { 0.731280, 0.268720, 0.000000 },
478 { 0.731467, 0.268533, 0.000000 },
479 { 0.731650, 0.268350, 0.000000 },
480 { 0.731826, 0.268174, 0.000000 },
481 { 0.731993, 0.268007, 0.000000 },
482 { 0.732150, 0.267850, 0.000000 },
483 { 0.732300, 0.267700, 0.000000 },
484 { 0.732443, 0.267557, 0.000000 },
485 { 0.732581, 0.267419, 0.000000 },
486 { 0.732719, 0.267281, 0.000000 },
487 { 0.732859, 0.267141, 0.000000 },
488 { 0.733000, 0.267000, 0.000000 },
489 { 0.733142, 0.266858, 0.000000 },
490 { 0.733281, 0.266719, 0.000000 },
491 { 0.733417, 0.266583, 0.000000 },
492 { 0.733551, 0.266449, 0.000000 },
493 { 0.733683, 0.266317, 0.000000 },
494 { 0.733813, 0.266187, 0.000000 },
495 { 0.733936, 0.266064, 0.000000 },
496 { 0.734047, 0.265953, 0.000000 },
497 { 0.734143, 0.265857, 0.000000 },
498 { 0.734221, 0.265779, 0.000000 },
499 { 0.734286, 0.265714, 0.000000 },
500 { 0.734341, 0.265659, 0.000000 },
501 { 0.734390, 0.265610, 0.000000 },
502 { 0.734438, 0.265562, 0.000000 },
503 { 0.734482, 0.265518, 0.000000 },
504 { 0.734523, 0.265477, 0.000000 },
505 { 0.734560, 0.265440, 0.000000 },
506 { 0.734592, 0.265408, 0.000000 },
507 { 0.734621, 0.265379, 0.000000 },
508 { 0.734649, 0.265351, 0.000000 },
509 { 0.734673, 0.265327, 0.000000 },
510 { 0.734690, 0.265310, 0.000000 },
511 { 0.734690, 0.265310, 0.000000 },
512 { 0.734690, 0.265310, 0.000000 },
513 { 0.734690, 0.265310, 0.000000 },
514 { 0.734690, 0.265310, 0.000000 },
515 { 0.734690, 0.265310, 0.000000 },
516 { 0.734690, 0.265310, 0.000000 },
517 { 0.734690, 0.265310, 0.000000 },
518 { 0.734690, 0.265310, 0.000000 },
519 { 0.734690, 0.265310, 0.000000 },
520 { 0.734690, 0.265310, 0.000000 },
521 { 0.734690, 0.265310, 0.000000 },
522 { 0.734690, 0.265310, 0.000000 },
523 { 0.734690, 0.265310, 0.000000 },
524 { 0.734690, 0.265310, 0.000000 },
525 { 0.734690, 0.265310, 0.000000 },
526 { 0.734690, 0.265310, 0.000000 },
527 { 0.734690, 0.265310, 0.000000 },
528 { 0.734690, 0.265310, 0.000000 },
529 { 0.734690, 0.265310, 0.000000 },
530 { 0.734690, 0.265310, 0.000000 },
531 { 0.734690, 0.265310, 0.000000 },
532 { 0.734690, 0.265310, 0.000000 },
533 { 0.734690, 0.265310, 0.000000 },
534 { 0.734690, 0.265310, 0.000000 },
535 { 0.734690, 0.265310, 0.000000 },
536 { 0.734690, 0.265310, 0.000000 },
537 { 0.734690, 0.265310, 0.000000 },
538 { 0.734690, 0.265310, 0.000000 },
539 { 0.734690, 0.265310, 0.000000 },
540 { 0.734690, 0.265310, 0.000000 },
541 { 0.734690, 0.265310, 0.000000 },
542 { 0.734690, 0.265310, 0.000000 },
543 { 0.734690, 0.265310, 0.000000 },
544 { 0.734690, 0.265310, 0.000000 },
545 { 0.734690, 0.265310, 0.000000 },
546 { 0.734690, 0.265310, 0.000000 },
547 { 0.734690, 0.265310, 0.000000 },
548 { 0.734690, 0.265310, 0.000000 },
549 { 0.734690, 0.265310, 0.000000 },
550 { 0.734690, 0.265310, 0.000000 },
551 { 0.734690, 0.265310, 0.000000 },
552 { 0.734690, 0.265310, 0.000000 },
553 { 0.734690, 0.265310, 0.000000 },
554 { 0.734690, 0.265310, 0.000000 },
555 { 0.734690, 0.265310, 0.000000 },
556 { 0.734690, 0.265310, 0.000000 },
557 { 0.734690, 0.265310, 0.000000 },
558 { 0.734690, 0.265310, 0.000000 },
559 { 0.734690, 0.265310, 0.000000 },
560 { 0.734690, 0.265310, 0.000000 },
561 { 0.734690, 0.265310, 0.000000 },
562 { 0.734690, 0.265310, 0.000000 },
563 { 0.734690, 0.265310, 0.000000 },
564 { 0.734690, 0.265310, 0.000000 },
565 { 0.734690, 0.265310, 0.000000 },
566 { 0.734690, 0.265310, 0.000000 },
567 { 0.734690, 0.265310, 0.000000 },
568 { 0.734690, 0.265310, 0.000000 },
569 { 0.734690, 0.265310, 0.000000 },
570 { 0.734690, 0.265310, 0.000000 },
571 { 0.734690, 0.265310, 0.000000 },
572 { 0.734690, 0.265310, 0.000000 },
573 { 0.734690, 0.265310, 0.000000 },
574 { 0.734690, 0.265310, 0.000000 },
575 { 0.734690, 0.265310, 0.000000 },
576 { 0.734690, 0.265310, 0.000000 },
577 { 0.734690, 0.265310, 0.000000 },
578 { 0.734690, 0.265310, 0.000000 },
579 { 0.734690, 0.265310, 0.000000 },
580 { 0.734690, 0.265310, 0.000000 },
581 { 0.734690, 0.265310, 0.000000 },
582 { 0.734690, 0.265310, 0.000000 },
583 { 0.734690, 0.265310, 0.000000 },
584 { 0.734690, 0.265310, 0.000000 },
585 { 0.734690, 0.265310, 0.000000 },
586 { 0.734690, 0.265310, 0.000000 },
587 { 0.734690, 0.265310, 0.000000 },
588 { 0.734690, 0.265310, 0.000000 },
589 { 0.734690, 0.265310, 0.000000 },
590 { 0.734690, 0.265310, 0.000000 },
591 { 0.734690, 0.265310, 0.000000 },
592 { 0.734690, 0.265310, 0.000000 },
593 { 0.734690, 0.265310, 0.000000 },
594 { 0.734690, 0.265310, 0.000000 },
595 { 0.734690, 0.265310, 0.000000 },
596 { 0.734690, 0.265310, 0.000000 },
597 { 0.734690, 0.265310, 0.000000 },
598 { 0.734690, 0.265310, 0.000000 },
599 { 0.734690, 0.265310, 0.000000 },
600 { 0.734690, 0.265310, 0.000000 },
601 { 0.734690, 0.265310, 0.000000 },
602 { 0.734690, 0.265310, 0.000000 },
603 { 0.734690, 0.265310, 0.000000 },
604 { 0.734690, 0.265310, 0.000000 },
605 { 0.734690, 0.265310, 0.000000 },
606 { 0.734690, 0.265310, 0.000000 },
607 { 0.734690, 0.265310, 0.000000 },
608 { 0.734690, 0.265310, 0.000000 },
609 { 0.734690, 0.265310, 0.000000 },
610 { 0.734690, 0.265310, 0.000000 },
611 { 0.734690, 0.265310, 0.000000 },
612 { 0.734690, 0.265310, 0.000000 },
613 { 0.734690, 0.265310, 0.000000 },
614 { 0.734690, 0.265310, 0.000000 },
615 { 0.734690, 0.265310, 0.000000 },
616 { 0.734690, 0.265310, 0.000000 },
617 { 0.734690, 0.265310, 0.000000 },
618 { 0.734690, 0.265310, 0.000000 },
619 { 0.734690, 0.265310, 0.000000 },
620 { 0.734690, 0.265310, 0.000000 },
621 { 0.734690, 0.265310, 0.000000 },
622 { 0.734690, 0.265310, 0.000000 },
623 { 0.734690, 0.265310, 0.000000 },
624 { 0.734690, 0.265310, 0.000000 },
625 { 0.734690, 0.265310, 0.000000 },
626 { 0.734690, 0.265310, 0.000000 },
627 { 0.734690, 0.265310, 0.000000 },
628 { 0.734690, 0.265310, 0.000000 },
629 { 0.734690, 0.265310, 0.000000 },
630 { 0.734690, 0.265310, 0.000000 },
631 { 0.734690, 0.265310, 0.000000 },
632 { 0.734690, 0.265310, 0.000000 },
633 { 0.734690, 0.265310, 0.000000 },
634 { 0.734690, 0.265310, 0.000000 },
635 { 0.734690, 0.265310, 0.000000 },
636 { 0.734690, 0.265310, 0.000000 },
637 { 0.734690, 0.265310, 0.000000 },
638 { 0.734690, 0.265310, 0.000000 },
639 { 0.734690, 0.265310, 0.000000 },
640 { 0.734690, 0.265310, 0.000000 },
641 { 0.734690, 0.265310, 0.000000 },
645 /* Standard white point chromaticities. */
647 #define C 0.310063, 0.316158
648 #define E 1.0/3.0, 1.0/3.0
649 #define D50 0.34570, 0.3585
650 #define D65 0.312713, 0.329016
652 /* Gamma of nonlinear correction.
653 See Charles Poynton's ColorFAQ Item 45 and GammaFAQ Item 6 at
654 http://www.inforamp.net/~poynton/ColorFAQ.html
655 http://www.inforamp.net/~poynton/GammaFAQ.html
658 #define GAMMA_REC709 0. /* Rec. 709 */
660 static const struct ColorSystem color_systems[] = {
662 0.67, 0.33, 0.21, 0.71, 0.14, 0.08,
666 0.64, 0.33, 0.29, 0.60, 0.15, 0.06,
670 0.630, 0.340, 0.310, 0.595, 0.155, 0.070,
673 [SMPTE240Msystem] = {
674 0.670, 0.330, 0.210, 0.710, 0.150, 0.060,
678 0.625, 0.340, 0.280, 0.595, 0.115, 0.070,
682 0.7347, 0.2653, 0.1152, 0.8264, 0.1566, 0.0177,
686 0.7347, 0.2653, 0.2738, 0.7174, 0.1666, 0.0089,
690 0.64, 0.33, 0.30, 0.60, 0.15, 0.06,
694 0.708, 0.292, 0.170, 0.797, 0.131, 0.046,
700 static struct ColorSystem CustomSystem = {
702 0.64, 0.33, 0.30, 0.60, 0.15, 0.06,
708 uv_to_xy(double const u,
714 Given 1970 coordinates u, v, determine 1931 chromaticities x, y
716 *xc = 3*u / (2*u - 8*v + 4);
717 *yc = 2*v / (2*u - 8*v + 4);
721 upvp_to_xy(double const up,
727 Given 1976 coordinates u', v', determine 1931 chromaticities x, y
729 *xc = 9*up / (6*up - 16*vp + 12);
730 *yc = 4*vp / (6*up - 16*vp + 12);
734 xy_to_upvp(double xc,
740 Given 1931 chromaticities x, y, determine 1976 coordinates u', v'
742 *up = 4*xc / (- 2*xc + 12*yc + 3);
743 *vp = 9*yc / (- 2*xc + 12*yc + 3);
753 Given 1931 chromaticities x, y, determine 1960 coordinates u, v
755 *u = 4*xc / (- 2*xc + 12*yc + 3);
756 *v = 6*yc / (- 2*xc + 12*yc + 3);
760 xyz_to_rgb(const double m[3][3],
761 double xc, double yc, double zc,
762 double * const r, double * const g, double * const b)
764 *r = m[0][0]*xc + m[0][1]*yc + m[0][2]*zc;
765 *g = m[1][0]*xc + m[1][1]*yc + m[1][2]*zc;
766 *b = m[2][0]*xc + m[2][1]*yc + m[2][2]*zc;
769 static void invert_matrix3x3(double in[3][3], double out[3][3])
771 double m00 = in[0][0], m01 = in[0][1], m02 = in[0][2],
772 m10 = in[1][0], m11 = in[1][1], m12 = in[1][2],
773 m20 = in[2][0], m21 = in[2][1], m22 = in[2][2];
777 out[0][0] = (m11 * m22 - m21 * m12);
778 out[0][1] = -(m01 * m22 - m21 * m02);
779 out[0][2] = (m01 * m12 - m11 * m02);
780 out[1][0] = -(m10 * m22 - m20 * m12);
781 out[1][1] = (m00 * m22 - m20 * m02);
782 out[1][2] = -(m00 * m12 - m10 * m02);
783 out[2][0] = (m10 * m21 - m20 * m11);
784 out[2][1] = -(m00 * m21 - m20 * m01);
785 out[2][2] = (m00 * m11 - m10 * m01);
787 det = m00 * out[0][0] + m10 * out[0][1] + m20 * out[0][2];
790 for (i = 0; i < 3; i++) {
791 for (j = 0; j < 3; j++)
796 static void get_rgb2xyz_matrix(struct ColorSystem system, double m[3][3])
798 double S[3], X[4], Z[4];
801 X[0] = system.xRed / system.yRed;
802 X[1] = system.xGreen / system.yGreen;
803 X[2] = system.xBlue / system.yBlue;
804 X[3] = system.xWhite / system.yWhite;
806 Z[0] = (1 - system.xRed - system.yRed) / system.yRed;
807 Z[1] = (1 - system.xGreen - system.yGreen) / system.yGreen;
808 Z[2] = (1 - system.xBlue - system.yBlue) / system.yBlue;
809 Z[3] = (1 - system.xWhite - system.yWhite) / system.yWhite;
811 for (i = 0; i < 3; i++) {
817 invert_matrix3x3(m, m);
819 for (i = 0; i < 3; i++)
820 S[i] = m[i][0] * X[3] + m[i][1] * 1 + m[i][2] * Z[3];
822 for (i = 0; i < 3; i++) {
823 m[0][i] = S[i] * X[i];
825 m[2][i] = S[i] * Z[i];
836 const double m[3][3])
840 *x = m[0][0] * rc + m[0][1] * gc + m[0][2] * bc;
841 *y = m[1][0] * rc + m[1][1] * gc + m[1][2] * bc;
842 *z = m[2][0] * rc + m[2][1] * gc + m[2][2] * bc;
851 constrain_rgb(double * const r,
855 /*----------------------------------------------------------------------------
856 If the requested RGB shade contains a negative weight for one of
857 the primaries, it lies outside the color gamut accessible from
858 the given triple of primaries. Desaturate it by adding white,
859 equal quantities of R, G, and B, enough to make RGB all positive.
860 -----------------------------------------------------------------------------*/
863 /* Amount of white needed is w = - min(0, *r, *g, *b) */
864 w = (0 < *r) ? 0 : *r;
865 w = (w < *g) ? w : *g;
866 w = (w < *b) ? w : *b;
869 /* Add just enough white to make r, g, b all positive. */
871 *r += w; *g += w; *b += w;
873 return 1; /* Color modified to fit RGB gamut */
876 return 0; /* Color within RGB gamut */
880 gamma_correct(const struct ColorSystem * const cs,
883 /*----------------------------------------------------------------------------
884 Transform linear RGB values to nonlinear RGB values.
886 Rec. 709 is ITU-R Recommendation BT. 709 (1990)
887 ``Basic Parameter Values for the HDTV Standard for the Studio and for
888 International Programme Exchange'', formerly CCIR Rec. 709.
891 http://www.inforamp.net/~poynton/ColorFAQ.html
892 http://www.inforamp.net/~poynton/GammaFAQ.html
893 -----------------------------------------------------------------------------*/
900 /* Rec. 709 gamma correction. */
903 *c *= (1.099 * pow(cc, 0.45) - 0.099) / cc;
905 *c = 1.099 * pow(*c, 0.45) - 0.099;
908 /* Nonlinear color = (Linear color)^(1/gamma) */
909 *c = pow(*c, 1./gamma);
916 gamma_correct_rgb(const struct ColorSystem * const cs,
921 gamma_correct(cs, r);
922 gamma_correct(cs, g);
923 gamma_correct(cs, b);
926 /* Sz(X) is the displacement in pixels of a displacement of X normalized
927 distance units. (A normalized distance unit is 1/512 of the smaller
928 dimension of the canvas)
930 #define Sz(x) (((x) * (int)FFMIN(w, h)) / 512)
933 monochrome_color_location(double waveLength, int w, int h,
934 int cie, int *xP, int *yP)
936 const int ix = waveLength - 360;
937 const double pX = spectral_chromaticity[ix][0];
938 const double pY = spectral_chromaticity[ix][1];
939 const double pZ = spectral_chromaticity[ix][2];
940 const double px = pX / (pX + pY + pZ);
941 const double py = pY / (pX + pY + pZ);
946 xy_to_upvp(px, py, &up, &vp);
948 *yP = (h - 1) - vp * (h - 1);
949 } else if (cie == UCS) {
952 xy_to_uv(px, py, &u, &v);
954 *yP = (h - 1) - v * (h - 1);
955 } else if (cie == XYY) {
957 *yP = (h - 1) - py * (h - 1);
964 find_tongue(uint16_t* const pixels,
968 int * const presentP,
969 int * const leftEdgeP,
970 int * const rightEdgeP)
974 for (i = 0; i < w && pixels[row * linesize + i * 4 + 0] == 0; i++)
981 int const leftEdge = i;
985 for (j = w - 1; j >= leftEdge && pixels[row * linesize + j * 4 + 0] == 0; j--)
989 *leftEdgeP = leftEdge;
993 static void draw_line(uint16_t *const pixels, int linesize,
994 int x0, int y0, int x1, int y1,
996 const uint16_t *const rgbcolor)
998 int dx = FFABS(x1 - x0), sx = x0 < x1 ? 1 : -1;
999 int dy = FFABS(y1 - y0), sy = y0 < y1 ? 1 : -1;
1000 int err = (dx > dy ? dx : -dy) / 2, e2;
1003 pixels[y0 * linesize + x0 * 4 + 0] = rgbcolor[0];
1004 pixels[y0 * linesize + x0 * 4 + 1] = rgbcolor[1];
1005 pixels[y0 * linesize + x0 * 4 + 2] = rgbcolor[2];
1006 pixels[y0 * linesize + x0 * 4 + 3] = rgbcolor[3];
1008 if (x0 == x1 && y0 == y1)
1025 static void draw_rline(uint16_t *const pixels, int linesize,
1026 int x0, int y0, int x1, int y1,
1029 int dx = FFABS(x1 - x0), sx = x0 < x1 ? 1 : -1;
1030 int dy = FFABS(y1 - y0), sy = y0 < y1 ? 1 : -1;
1031 int err = (dx > dy ? dx : -dy) / 2, e2;
1034 pixels[y0 * linesize + x0 * 4 + 0] = 65535 - pixels[y0 * linesize + x0 * 4 + 0];
1035 pixels[y0 * linesize + x0 * 4 + 1] = 65535 - pixels[y0 * linesize + x0 * 4 + 1];
1036 pixels[y0 * linesize + x0 * 4 + 2] = 65535 - pixels[y0 * linesize + x0 * 4 + 2];
1037 pixels[y0 * linesize + x0 * 4 + 3] = 65535;
1039 if (x0 == x1 && y0 == y1)
1057 tongue_outline(uint16_t* const pixels,
1061 uint16_t const maxval,
1064 const uint16_t rgbcolor[4] = { maxval, maxval, maxval, maxval };
1069 for (wavelength = 360; wavelength <= 830; wavelength++) {
1072 monochrome_color_location(wavelength, w, h, cie,
1075 if (wavelength > 360)
1076 draw_line(pixels, linesize, lx, ly, icx, icy, w, h, rgbcolor);
1084 draw_line(pixels, linesize, lx, ly, fx, fy, w, h, rgbcolor);
1088 fill_in_tongue(uint16_t* const pixels,
1092 uint16_t const maxval,
1093 const struct ColorSystem * const cs,
1094 double const m[3][3],
1096 int const correct_gamma,
1097 float const contrast)
1101 /* Scan the image line by line and fill the tongue outline
1102 with the RGB values determined by the color system for the x-y
1103 co-ordinates within the tongue.
1106 for (y = 0; y < h; ++y) {
1107 int present; /* There is some tongue on this line */
1108 int leftEdge; /* x position of leftmost pixel in tongue on this line */
1109 int rightEdge; /* same, but rightmost */
1111 find_tongue(pixels, w, linesize, y, &present, &leftEdge, &rightEdge);
1116 for (x = leftEdge; x <= rightEdge; ++x) {
1117 double cx, cy, cz, jr, jg, jb, jmax;
1118 int r, g, b, mx = maxval;
1122 up = ((double) x) / (w - 1);
1123 vp = 1.0 - ((double) y) / (h - 1);
1124 upvp_to_xy(up, vp, &cx, &cy);
1125 cz = 1.0 - (cx + cy);
1126 } else if (cie == UCS) {
1128 u = ((double) x) / (w - 1);
1129 v = 1.0 - ((double) y) / (h - 1);
1130 uv_to_xy(u, v, &cx, &cy);
1131 cz = 1.0 - (cx + cy);
1132 } else if (cie == XYY) {
1133 cx = ((double) x) / (w - 1);
1134 cy = 1.0 - ((double) y) / (h - 1);
1135 cz = 1.0 - (cx + cy);
1140 xyz_to_rgb(m, cx, cy, cz, &jr, &jg, &jb);
1142 /* Check whether the requested color is within the
1143 gamut achievable with the given color system. If
1144 not, draw it in a reduced intensity, interpolated
1145 by desaturation to the closest within-gamut color. */
1147 if (constrain_rgb(&jr, &jg, &jb))
1150 jmax = FFMAX3(jr, jg, jb);
1156 /* gamma correct from linear rgb to nonlinear rgb. */
1158 gamma_correct_rgb(cs, &jr, &jg, &jb);
1162 pixels[y * linesize + x * 4 + 0] = r;
1163 pixels[y * linesize + x * 4 + 1] = g;
1164 pixels[y * linesize + x * 4 + 2] = b;
1165 pixels[y * linesize + x * 4 + 3] = 65535;
1172 plot_white_point(uint16_t* pixels,
1177 int const color_system,
1180 const struct ColorSystem *cs = &color_systems[color_system];
1185 xy_to_upvp(cs->xWhite, cs->yWhite, &wup, &wvp);
1189 wy = (h - 1) - ((int) ((h - 1) * wvp));
1190 } else if (cie == UCS) {
1192 xy_to_uv(cs->xWhite, cs->yWhite, &wu, &wv);
1196 wy = (h - 1) - ((int) ((h - 1) * wv));
1197 } else if (cie == XYY) {
1198 wx = (w - 1) * cs->xWhite;
1199 wy = (h - 1) - ((int) ((h - 1) * cs->yWhite));
1204 draw_rline(pixels, linesize,
1205 wx + Sz(3), wy, wx + Sz(10), wy,
1207 draw_rline(pixels, linesize,
1208 wx - Sz(3), wy, wx - Sz(10), wy,
1210 draw_rline(pixels, linesize,
1211 wx, wy + Sz(3), wx, wy + Sz(10),
1213 draw_rline(pixels, linesize,
1214 wx, wy - Sz(3), wx, wy - Sz(10),
1218 static int draw_background(AVFilterContext *ctx)
1220 CiescopeContext *s = ctx->priv;
1221 const struct ColorSystem *cs = &color_systems[s->color_system];
1222 AVFilterLink *outlink = ctx->outputs[0];
1227 if ((s->f = ff_get_video_buffer(outlink, outlink->w, outlink->h)) == NULL)
1228 return AVERROR(ENOMEM);
1229 pixels = (uint16_t *)s->f->data[0];
1231 tongue_outline(pixels, s->f->linesize[0] / 2, w, h, 65535, s->cie);
1233 fill_in_tongue(pixels, s->f->linesize[0] / 2, w, h, 65535, cs, (const double (*)[3])s->i, s->cie,
1234 s->correct_gamma, s->contrast);
1239 static void filter_rgb48(AVFilterContext *ctx, AVFrame *in, double *cx, double *cy, int x, int y)
1241 CiescopeContext *s = ctx->priv;
1242 const uint16_t* src = (const uint16_t*)(in->data[0] + in->linesize[0] * y + x * 6);
1243 double r = src[0] / 65535.;
1244 double g = src[1] / 65535.;
1245 double b = src[2] / 65535.;
1248 rgb_to_xy(r, g, b, cx, cy, &cz, (const double (*)[3])s->m);
1251 static void filter_rgba64(AVFilterContext *ctx, AVFrame *in, double *cx, double *cy, int x, int y)
1253 CiescopeContext *s = ctx->priv;
1254 const uint16_t* src = (const uint16_t*)(in->data[0] + in->linesize[0] * y + x * 8);
1255 double r = src[0] / 65535.;
1256 double g = src[1] / 65535.;
1257 double b = src[2] / 65535.;
1260 rgb_to_xy(r, g, b, cx, cy, &cz, (const double (*)[3])s->m);
1263 static void filter_rgb24(AVFilterContext *ctx, AVFrame *in, double *cx, double *cy, int x, int y)
1265 CiescopeContext *s = ctx->priv;
1266 const uint8_t* src = in->data[0] + in->linesize[0] * y + x * 3;
1267 double r = src[0] / 255.;
1268 double g = src[1] / 255.;
1269 double b = src[2] / 255.;
1272 rgb_to_xy(r, g, b, cx, cy, &cz, (const double (*)[3])s->m);
1275 static void filter_rgba(AVFilterContext *ctx, AVFrame *in, double *cx, double *cy, int x, int y)
1277 CiescopeContext *s = ctx->priv;
1278 const uint8_t* src = in->data[0] + in->linesize[0] * y + x * 4;
1279 double r = src[0] / 255.;
1280 double g = src[1] / 255.;
1281 double b = src[2] / 255.;
1284 rgb_to_xy(r, g, b, cx, cy, &cz, (const double (*)[3])s->m);
1287 static void filter_xyz(AVFilterContext *ctx, AVFrame *in, double *cx, double *cy, int x, int y)
1289 CiescopeContext *s = ctx->priv;
1290 const uint16_t* src = (uint16_t *)(in->data[0] + in->linesize[0] * y + x * 6);
1291 double lx = s->log2lin[src[0]];
1292 double ly = s->log2lin[src[1]];
1293 double lz = s->log2lin[src[2]];
1294 double sum = lx + ly + lz;
1302 static void plot_gamuts(uint16_t *pixels, int linesize, int w, int h,
1303 int cie, int gamuts)
1307 for (i = 0; i < NB_CS; i++) {
1308 const struct ColorSystem *cs = &color_systems[i];
1309 int rx, ry, gx, gy, bx, by;
1311 if (!((1 << i) & gamuts))
1315 xy_to_upvp(cs->xRed, cs->yRed, &wup, &wvp);
1317 ry = (h - 1) - ((int) ((h - 1) * wvp));
1318 xy_to_upvp(cs->xGreen, cs->yGreen, &wup, &wvp);
1320 gy = (h - 1) - ((int) ((h - 1) * wvp));
1321 xy_to_upvp(cs->xBlue, cs->yBlue, &wup, &wvp);
1323 by = (h - 1) - ((int) ((h - 1) * wvp));
1324 } else if (cie == UCS) {
1326 xy_to_uv(cs->xRed, cs->yRed, &wu, &wv);
1328 ry = (h - 1) - ((int) ((h - 1) * wv));
1329 xy_to_uv(cs->xGreen, cs->yGreen, &wu, &wv);
1331 gy = (h - 1) - ((int) ((h - 1) * wv));
1332 xy_to_uv(cs->xBlue, cs->yBlue, &wu, &wv);
1334 by = (h - 1) - ((int) ((h - 1) * wv));
1335 } else if (cie == XYY) {
1336 rx = (w - 1) * cs->xRed;
1337 ry = (h - 1) - ((int) ((h - 1) * cs->yRed));
1338 gx = (w - 1) * cs->xGreen;
1339 gy = (h - 1) - ((int) ((h - 1) * cs->yGreen));
1340 bx = (w - 1) * cs->xBlue;
1341 by = (h - 1) - ((int) ((h - 1) * cs->yBlue));
1346 draw_rline(pixels, linesize, rx, ry, gx, gy, w, h);
1347 draw_rline(pixels, linesize, gx, gy, bx, by, w, h);
1348 draw_rline(pixels, linesize, bx, by, rx, ry, w, h);
1352 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
1354 AVFilterContext *ctx = inlink->dst;
1355 CiescopeContext *s = ctx->priv;
1356 AVFilterLink *outlink = ctx->outputs[0];
1357 int i = s->intensity * 65535;
1363 out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
1366 return AVERROR(ENOMEM);
1370 if (!s->background) {
1371 ret = draw_background(ctx);
1373 av_frame_free(&out);
1378 for (y = 0; y < outlink->h; y++) {
1379 memset(out->data[0] + y * out->linesize[0], 0, outlink->w * 8);
1382 for (y = 0; y < in->height; y++) {
1383 for (x = 0; x < in->width; x++) {
1388 s->filter(ctx, in, &cx, &cy, x, y);
1390 if (s->cie == LUV) {
1392 xy_to_upvp(cx, cy, &up, &vp);
1395 } else if (s->cie == UCS) {
1397 xy_to_uv(cx, cy, &u, &v);
1403 wy = (h - 1) - ((h - 1) * cy);
1405 if (wx < 0 || wx >= w ||
1409 dst = (uint16_t *)(out->data[0] + wy * out->linesize[0] + wx * 8 + 0);
1410 dst[0] = FFMIN(dst[0] + i, 65535);
1411 dst[1] = FFMIN(dst[1] + i, 65535);
1412 dst[2] = FFMIN(dst[2] + i, 65535);
1417 for (y = 0; y < outlink->h; y++) {
1418 uint16_t *dst = (uint16_t *)(out->data[0] + y * out->linesize[0]);
1419 const uint16_t *src = (const uint16_t *)(s->f->data[0] + y * s->f->linesize[0]);
1420 for (x = 0; x < outlink->w; x++) {
1421 const int xx = x * 4;
1422 if (dst[xx + 3] == 0) {
1423 dst[xx + 0] = src[xx + 0];
1424 dst[xx + 1] = src[xx + 1];
1425 dst[xx + 2] = src[xx + 2];
1426 dst[xx + 3] = src[xx + 3];
1432 plot_white_point((uint16_t *)out->data[0], out->linesize[0] / 2,
1433 outlink->w, outlink->h, 65535,
1434 s->color_system, s->cie);
1436 plot_gamuts((uint16_t *)out->data[0], out->linesize[0] / 2,
1437 outlink->w, outlink->h,
1441 return ff_filter_frame(outlink, out);
1444 static void av_cold uninit(AVFilterContext *ctx)
1446 CiescopeContext *s = ctx->priv;
1448 av_frame_free(&s->f);
1451 static int config_input(AVFilterLink *inlink)
1453 CiescopeContext *s = inlink->dst->priv;
1456 get_rgb2xyz_matrix(color_systems[s->color_system], s->m);
1457 invert_matrix3x3(s->m, s->i);
1459 switch (inlink->format) {
1460 case AV_PIX_FMT_RGB24:
1461 s->filter = filter_rgb24;
1463 case AV_PIX_FMT_RGBA:
1464 s->filter = filter_rgba;
1466 case AV_PIX_FMT_RGB48:
1467 s->filter = filter_rgb48;
1469 case AV_PIX_FMT_RGBA64:
1470 s->filter = filter_rgba64;
1472 case AV_PIX_FMT_XYZ12:
1473 s->filter = filter_xyz;
1474 for (i = 0; i < 65536; i++)
1475 s->log2lin[i] = pow(i / 65535., s->igamma) * 65535.;
1484 static const AVFilterPad inputs[] = {
1487 .type = AVMEDIA_TYPE_VIDEO,
1488 .filter_frame = filter_frame,
1489 .config_props = config_input,
1494 static const AVFilterPad outputs[] = {
1497 .type = AVMEDIA_TYPE_VIDEO,
1498 .config_props = config_output,
1503 AVFilter ff_vf_ciescope = {
1505 .description = NULL_IF_CONFIG_SMALL("Video CIE scope."),
1506 .priv_size = sizeof(CiescopeContext),
1507 .priv_class = &ciescope_class,
1508 .query_formats = query_formats,