FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/vf_ciescope.c
Date: 2022-12-05 03:11:11
Exec Total Coverage
Lines: 0 417 0.0%
Functions: 0 30 0.0%
Branches: 0 162 0.0%

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