FFmpeg coverage


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