FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/vf_ciescope.c
Date: 2026-09-26 20:14:34
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_pixel_format_list(in_pix_fmts), &cfg_in[0]->formats)) < 0)
149 ✗ return ret;
150
151 ✗ if ((ret = ff_formats_ref(ff_make_pixel_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 coordinates 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 FFFilter ff_vf_ciescope = {
1557 .p.name = "ciescope",
1558 .p.description = NULL_IF_CONFIG_SMALL("Video CIE scope."),
1559 .p.priv_class = &ciescope_class,
1560 .priv_size = sizeof(CiescopeContext),
1561 .uninit = uninit,
1562 FILTER_INPUTS(inputs),
1563 FILTER_OUTPUTS(outputs),
1564 FILTER_QUERY_FUNC2(query_formats),
1565 };
1566