Branch data Line data Source code
1 : : /*
2 : : * This file is part of libplacebo.
3 : : *
4 : : * libplacebo is free software; you can redistribute it and/or
5 : : * modify it under the terms of the GNU Lesser General Public
6 : : * License as published by the Free Software Foundation; either
7 : : * version 2.1 of the License, or (at your option) any later version.
8 : : *
9 : : * libplacebo is distributed in the hope that it will be useful,
10 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : : * GNU Lesser General Public License for more details.
13 : : *
14 : : * You should have received a copy of the GNU Lesser General Public
15 : : * License along with libplacebo. If not, see <http://www.gnu.org/licenses/>.
16 : : */
17 : :
18 : : #include <math.h>
19 : :
20 : : #include "common.h"
21 : : #include "log.h"
22 : :
23 : : #include <libplacebo/options.h>
24 : :
25 : : struct priv {
26 : : pl_log log;
27 : :
28 : : // for pl_options_get
29 : : struct pl_opt_data_t data;
30 : : pl_str data_text;
31 : :
32 : : // for pl_options_save
33 : : pl_str saved;
34 : :
35 : : // internally managed hooks array
36 : : PL_ARRAY(const struct pl_hook *) hooks;
37 : : };
38 : :
39 : : static const struct pl_options_t defaults = {
40 : : .params = { PL_RENDER_DEFAULTS },
41 : : .deband_params = { PL_DEBAND_DEFAULTS },
42 : : .sigmoid_params = { PL_SIGMOID_DEFAULTS },
43 : : .color_adjustment = { PL_COLOR_ADJUSTMENT_NEUTRAL },
44 : : .peak_detect_params = { PL_PEAK_DETECT_DEFAULTS },
45 : : .color_map_params = { PL_COLOR_MAP_DEFAULTS },
46 : : .dither_params = { PL_DITHER_DEFAULTS },
47 : : .icc_params = { PL_ICC_DEFAULTS },
48 : : .cone_params = { PL_CONE_NONE, 1.0 },
49 : : .deinterlace_params = { PL_DEINTERLACE_DEFAULTS },
50 : : .distort_params = { PL_DISTORT_DEFAULTS },
51 : : .upscaler = {
52 : : .name = "custom",
53 : : .description = "Custom upscaler",
54 : : .allowed = PL_FILTER_UPSCALING,
55 : : },
56 : : .downscaler = {
57 : : .name = "custom",
58 : : .description = "Custom downscaler",
59 : : .allowed = PL_FILTER_DOWNSCALING,
60 : : },
61 : : .plane_upscaler = {
62 : : .name = "custom",
63 : : .description = "Custom plane upscaler",
64 : : .allowed = PL_FILTER_UPSCALING,
65 : : },
66 : : .plane_downscaler = {
67 : : .name = "custom",
68 : : .description = "Custom plane downscaler",
69 : : .allowed = PL_FILTER_DOWNSCALING,
70 : : },
71 : : .frame_mixer = {
72 : : .name = "custom",
73 : : .description = "Custom frame mixer",
74 : : .allowed = PL_FILTER_FRAME_MIXING,
75 : : },
76 : : };
77 : :
78 : : // Copies only whitelisted fields
79 : : static inline void copy_filter(struct pl_filter_config *dst,
80 : : const struct pl_filter_config *src)
81 : : {
82 : 2 : dst->kernel = src->kernel;
83 : 2 : dst->window = src->window;
84 : 2 : dst->radius = src->radius;
85 : 2 : dst->clamp = src->clamp;
86 : 2 : dst->blur = src->blur;
87 : 2 : dst->taper = src->taper;
88 : 2 : dst->polar = src->polar;
89 [ + + + + : 6 : for (int i = 0; i < PL_FILTER_MAX_PARAMS; i++) {
- - - - -
- - - ]
90 : 4 : dst->params[i] = src->params[i];
91 : 4 : dst->wparams[i] = src->wparams[i];
92 : : }
93 : : }
94 : :
95 : 15 : static inline void redirect_params(pl_options opts)
96 : : {
97 : : // Copy all non-NULL params structs into pl_options and redirect them
98 : : #define REDIRECT_PARAMS(field) do \
99 : : { \
100 : : if (opts->params.field) { \
101 : : opts->field = *opts->params.field; \
102 : : opts->params.field = &opts->field; \
103 : : } \
104 : : } while (0)
105 : :
106 [ + + ]: 15 : REDIRECT_PARAMS(deband_params);
107 [ + + ]: 15 : REDIRECT_PARAMS(sigmoid_params);
108 [ + - ]: 15 : REDIRECT_PARAMS(color_adjustment);
109 [ + + ]: 15 : REDIRECT_PARAMS(peak_detect_params);
110 [ + - ]: 15 : REDIRECT_PARAMS(color_map_params);
111 [ + + ]: 15 : REDIRECT_PARAMS(dither_params);
112 [ - + ]: 15 : REDIRECT_PARAMS(icc_params);
113 [ - + ]: 15 : REDIRECT_PARAMS(cone_params);
114 [ - + ]: 15 : REDIRECT_PARAMS(deinterlace_params);
115 [ - + ]: 15 : REDIRECT_PARAMS(distort_params);
116 : 15 : }
117 : :
118 : 12 : void pl_options_reset(pl_options opts, const struct pl_render_params *preset)
119 : : {
120 : 12 : *opts = defaults;
121 [ + + ]: 12 : if (preset)
122 : 4 : opts->params = *preset;
123 : 12 : redirect_params(opts);
124 : :
125 : : // Make a copy of all scaler configurations that aren't built-in filters
126 : : struct {
127 : : bool upscaler;
128 : : bool downscaler;
129 : : bool plane_upscaler;
130 : : bool plane_downscaler;
131 : : bool frame_mixer;
132 : : } fixed = {0};
133 : :
134 [ + + ]: 360 : for (int i = 0; i < pl_num_filter_configs; i++) {
135 : 348 : const struct pl_filter_config *f = pl_filter_configs[i];
136 : 348 : fixed.upscaler |= f == opts->params.upscaler;
137 : 348 : fixed.downscaler |= f == opts->params.downscaler;
138 : 348 : fixed.plane_upscaler |= f == opts->params.plane_upscaler;
139 : 348 : fixed.plane_downscaler |= f == opts->params.plane_downscaler;
140 : 348 : fixed.frame_mixer |= f == opts->params.frame_mixer;
141 : : }
142 : :
143 : : #define REDIRECT_SCALER(scaler) do \
144 : : { \
145 : : if (opts->params.scaler && !fixed.scaler) { \
146 : : copy_filter(&opts->scaler, opts->params.scaler); \
147 : : opts->params.scaler = &opts->scaler; \
148 : : } \
149 : : } while (0)
150 : :
151 [ + + + + ]: 13 : REDIRECT_SCALER(upscaler);
152 [ + + - + ]: 12 : REDIRECT_SCALER(downscaler);
153 [ - + - - ]: 12 : REDIRECT_SCALER(plane_upscaler);
154 [ - + - - ]: 12 : REDIRECT_SCALER(plane_downscaler);
155 [ + + - + ]: 12 : REDIRECT_SCALER(frame_mixer);
156 : 12 : }
157 : :
158 : 3 : pl_options pl_options_alloc(pl_log log)
159 : : {
160 : 3 : struct pl_options_t *opts = pl_zalloc_obj(NULL, opts, struct priv);
161 : 3 : struct priv *p = PL_PRIV(opts);
162 : 3 : pl_options_reset(opts, NULL);
163 : 3 : p->log = log;
164 : 3 : return opts;
165 : : }
166 : :
167 : 3 : void pl_options_free(pl_options *popts)
168 : : {
169 : 3 : pl_free_ptr((void **) popts);
170 : 3 : }
171 : :
172 : 0 : static void make_hooks_internal(pl_options opts)
173 : : {
174 : 0 : struct priv *p = PL_PRIV(opts);
175 : : struct pl_render_params *params = &opts->params;
176 [ # # # # ]: 0 : if (params->num_hooks && params->hooks != p->hooks.elem) {
177 [ # # ]: 0 : PL_ARRAY_MEMDUP(opts, p->hooks, params->hooks, params->num_hooks);
178 : 0 : params->hooks = p->hooks.elem;
179 : : }
180 : 0 : }
181 : :
182 : 0 : void pl_options_add_hook(pl_options opts, const struct pl_hook *hook)
183 : : {
184 : 0 : struct priv *p = PL_PRIV(opts);
185 : 0 : make_hooks_internal(opts);
186 [ # # # # : 0 : PL_ARRAY_APPEND(opts, p->hooks, hook);
# # ]
187 : 0 : opts->params.hooks = p->hooks.elem;
188 : 0 : }
189 : :
190 : 0 : void pl_options_insert_hook(pl_options opts, const struct pl_hook *hook, int idx)
191 : : {
192 : 0 : struct priv *p = PL_PRIV(opts);
193 : 0 : make_hooks_internal(opts);
194 [ # # # # : 0 : PL_ARRAY_INSERT_AT(opts, p->hooks, idx, hook);
# # # # #
# # # ]
195 : 0 : opts->params.hooks = p->hooks.elem;
196 : 0 : }
197 : :
198 : 0 : void pl_options_remove_hook_at(pl_options opts, int idx)
199 : : {
200 : 0 : struct priv *p = PL_PRIV(opts);
201 : 0 : make_hooks_internal(opts);
202 [ # # # # : 0 : PL_ARRAY_REMOVE_AT(p->hooks, idx);
# # ]
203 : 0 : opts->params.hooks = p->hooks.elem;
204 : 0 : }
205 : :
206 : : // Options printing/parsing context
207 : : typedef const struct opt_ctx_t {
208 : : pl_log log; // as a convenience, only needed when parsing
209 : : pl_opt opt;
210 : : void *alloc; // for printing only
211 : : pl_options opts; // current base ptr
212 : : } *opt_ctx;
213 : :
214 : : struct enum_val {
215 : : const char *name;
216 : : unsigned val;
217 : : };
218 : :
219 : : struct preset {
220 : : const char *name;
221 : : const void *val;
222 : : };
223 : :
224 : : struct named {
225 : : const char *name;
226 : : };
227 : :
228 : : typedef const struct opt_priv_t {
229 : : int (*compare)(opt_ctx p, const void *a, const void *b); // optional
230 : : void (*print)(opt_ctx p, pl_str *out, const void *val); // apends to `out`
231 : : bool (*parse)(opt_ctx p, pl_str str, void *out_val);
232 : : const struct enum_val *values; // for enums, terminated by {0}
233 : : const struct preset *presets; // for preset lists, terminated by {0}
234 : : const struct named * const *names; // for array-backed options, terminated by NULL
235 : :
236 : : // Offset and size of option in `struct pl_options_t`
237 : : size_t offset;
238 : : size_t size;
239 : : size_t offset_params; // offset of actual struct (for params toggles)
240 : : } *opt_priv;
241 : :
242 : 92 : static pl_opt_data get_opt_data(opt_ctx ctx)
243 : : {
244 : 92 : pl_options opts = ctx->opts;
245 : 92 : struct priv *p = PL_PRIV(opts);
246 : 92 : opt_priv priv = ctx->opt->priv;
247 : 92 : const void *val = (void *) ((uintptr_t) opts + priv->offset);
248 : :
249 : 92 : p->data_text.len = 0;
250 : 92 : priv->print(ctx, &p->data_text, val);
251 : 92 : p->data = (struct pl_opt_data_t) {
252 : : .opts = opts,
253 : 92 : .opt = ctx->opt,
254 : : .value = val,
255 : 92 : .text = (char *) p->data_text.buf,
256 : : };
257 : :
258 : 92 : return &p->data;
259 : : }
260 : :
261 : 2 : pl_opt_data pl_options_get(pl_options opts, const char *key)
262 : : {
263 : 2 : struct priv *p = PL_PRIV(opts);
264 : :
265 : 2 : pl_opt opt = pl_find_option(key);
266 [ + + - + ]: 2 : if (!opt || opt->preset) {
267 : 1 : PL_ERR(p, "Unrecognized or invalid option '%s'", key);
268 : 1 : return NULL;
269 : : }
270 : :
271 : 1 : return get_opt_data(&(struct opt_ctx_t) {
272 : : .alloc = opts,
273 : : .opts = opts,
274 : : .opt = opt,
275 : : });
276 : : }
277 : :
278 : 20 : void pl_options_iterate(pl_options opts,
279 : : void (*cb)(void *priv, pl_opt_data data),
280 : : void *cb_priv)
281 : : {
282 [ + + ]: 4300 : for (pl_opt opt = pl_option_list; opt->key; opt++) {
283 [ + + ]: 4280 : if (opt->preset)
284 : 340 : continue;
285 : :
286 : 3940 : struct opt_ctx_t ctx = {
287 : : .alloc = opts,
288 : : .opts = opts,
289 : : .opt = opt,
290 : : };
291 : :
292 : 3940 : opt_priv priv = opt->priv;
293 : 3940 : const void *val = (void *) ((uintptr_t) opts + priv->offset);
294 : 3940 : const void *ref = (void *) ((uintptr_t) &defaults + priv->offset);
295 : 220 : int cmp = priv->compare ? priv->compare(&ctx, val, ref)
296 [ + + ]: 3940 : : memcmp(val, ref, priv->size);
297 [ + + ]: 3940 : if (cmp != 0)
298 : 91 : cb(cb_priv, get_opt_data(&ctx));
299 : : }
300 : 20 : }
301 : :
302 : 76 : static void save_cb(void *priv, pl_opt_data data)
303 : : {
304 : 76 : pl_opt opt = data->opt;
305 : 76 : void *alloc = data->opts;
306 : : pl_str *out = priv;
307 : :
308 [ + + ]: 76 : if (out->len)
309 : 65 : pl_str_append_raw(alloc, out, ",", 1);
310 : 76 : pl_str_append_raw(alloc, out, opt->key, strlen(opt->key));
311 : 76 : pl_str_append_raw(alloc, out, "=", 1);
312 [ + - ]: 152 : pl_str_append(alloc, out, pl_str0(data->text));
313 : 76 : }
314 : :
315 : 18 : const char *pl_options_save(pl_options opts)
316 : : {
317 : 18 : struct priv *p = PL_PRIV(opts);
318 : :
319 : 18 : p->saved.len = 0;
320 : 18 : pl_options_iterate(opts, save_cb, &p->saved);
321 [ + + ]: 18 : return p->saved.len ? (char *) p->saved.buf : "";
322 : : }
323 : :
324 : 45 : static bool option_set_raw(pl_options opts, pl_str k, pl_str v)
325 : : {
326 : 45 : struct priv *p = PL_PRIV(opts);
327 : 45 : k = pl_str_strip(k);
328 : 45 : v = pl_str_strip(v);
329 : :
330 : : pl_opt opt;
331 [ + + ]: 3746 : for (opt = pl_option_list; opt->key; opt++) {
332 [ + + ]: 3743 : if (pl_str_equals0(k, opt->key))
333 : 42 : goto found;
334 : : }
335 : :
336 [ + + + - ]: 7 : PL_ERR(p, "Unrecognized option '%.*s', in '%.*s=%.*s'",
337 : : PL_STR_FMT(k), PL_STR_FMT(k), PL_STR_FMT(v));
338 : 3 : return false;
339 : :
340 : : found:
341 [ + - ]: 84 : PL_TRACE(p, "Parsing option '%s' = '%.*s'", opt->key, PL_STR_FMT(v));
342 [ + + ]: 42 : if (opt->deprecated)
343 : 1 : PL_WARN(p, "Option '%s' is deprecated", opt->key);
344 : :
345 : 42 : struct opt_ctx_t ctx = {
346 : 42 : .log = p->log,
347 : : .opts = opts,
348 : : .opt = opt,
349 : : };
350 : :
351 : 42 : opt_priv priv = opt->priv;
352 : 42 : void *val = (void *) ((uintptr_t) opts + priv->offset);
353 : 42 : return priv->parse(&ctx, v, val);
354 : : }
355 : :
356 [ + - ]: 9 : bool pl_options_set_str(pl_options opts, const char *key, const char *value)
357 : : {
358 : 9 : return option_set_raw(opts, pl_str0(key), pl_str0(value));
359 : : }
360 : :
361 [ + - ]: 20 : bool pl_options_load(pl_options opts, const char *str)
362 : : {
363 : : bool ret = true;
364 : :
365 : 20 : pl_str rest = pl_str0(str);
366 [ + + ]: 56 : while (rest.len) {
367 : 36 : pl_str kv = pl_str_strip(pl_str_split_chars(rest, " ,;:\n", &rest));
368 [ - + ]: 36 : if (!kv.len)
369 : 0 : continue;
370 : 36 : pl_str v, k = pl_str_split_char(kv, '=', &v);
371 : 36 : ret &= option_set_raw(opts, k, v);
372 : : }
373 : :
374 : 20 : return ret;
375 : : }
376 : :
377 : : // Individual option types
378 : :
379 : 39 : static void print_bool(opt_ctx p, pl_str *out, const void *ptr)
380 : : {
381 : : const bool *val = ptr;
382 [ + - ]: 39 : if (*val) {
383 : 39 : pl_str_append(p->alloc, out, pl_str0("yes"));
384 : : } else {
385 : 0 : pl_str_append(p->alloc, out, pl_str0("no"));
386 : : }
387 : 39 : }
388 : :
389 : 13 : static bool parse_bool(opt_ctx p, pl_str str, void *out)
390 : : {
391 : : bool *res = out;
392 [ + + ]: 13 : if (pl_str_equals0(str, "yes") ||
393 [ + - ]: 1 : pl_str_equals0(str, "y") ||
394 [ + - ]: 1 : pl_str_equals0(str, "on") ||
395 [ + - ]: 1 : pl_str_equals0(str, "true") ||
396 [ + - - + ]: 1 : pl_str_equals0(str, "enabled") ||
397 : : !str.len) // accept naked option name as well
398 : : {
399 : 12 : *res = true;
400 : 12 : return true;
401 [ + - ]: 1 : } else if (pl_str_equals0(str, "no") ||
402 [ + - ]: 1 : pl_str_equals0(str, "n") ||
403 [ + - ]: 1 : pl_str_equals0(str, "off") ||
404 [ + - ]: 1 : pl_str_equals0(str, "false") ||
405 [ - + ]: 1 : pl_str_equals0(str, "disabled"))
406 : : {
407 : 0 : *res = false;
408 : 0 : return true;
409 : : }
410 : :
411 [ + - ]: 2 : PL_ERR(p, "Invalid value '%.*s' for option '%s', expected boolean",
412 : : PL_STR_FMT(str), p->opt->key);
413 : 1 : return false;
414 : : }
415 : :
416 : 1 : static void print_int(opt_ctx p, pl_str *out, const void *ptr)
417 : : {
418 : 1 : pl_opt opt = p->opt;
419 : : const int *val = ptr;
420 [ + - + - : 1 : pl_assert(opt->min == opt->max || (*val >= opt->min && *val <= opt->max));
- + ]
421 : 1 : pl_str_append_asprintf_c(p->alloc, out, "%d", *val);
422 : 1 : }
423 : :
424 : 3 : static bool parse_int(opt_ctx p, pl_str str, void *out)
425 : : {
426 : 3 : pl_opt opt = p->opt;
427 : : int val;
428 [ + + ]: 3 : if (!pl_str_parse_int(str, &val)) {
429 [ + - ]: 2 : PL_ERR(p, "Invalid value '%.*s' for option '%s', expected integer",
430 : : PL_STR_FMT(str), opt->key);
431 : 1 : return false;
432 : : }
433 : :
434 [ + - ]: 2 : if (opt->min != opt->max) {
435 [ + + + - ]: 2 : if (val < opt->min || val > opt->max) {
436 : 2 : PL_ERR(p, "Value of %d out of range for option '%s': [%d, %d]",
437 : : val, opt->key, (int) opt->min, (int) opt->max);
438 : 2 : return false;
439 : : }
440 : : }
441 : :
442 : 0 : *(int *) out = val;
443 : 0 : return true;
444 : : }
445 : :
446 : 15 : static void print_float(opt_ctx p, pl_str *out, const void *ptr)
447 : : {
448 : 15 : pl_opt opt = p->opt;
449 : : const float *val = ptr;
450 [ + + + - : 15 : pl_assert(opt->min == opt->max || (*val >= opt->min && *val <= opt->max));
- + ]
451 : 15 : pl_str_append_asprintf_c(p->alloc, out, "%f", *val);
452 : 15 : }
453 : :
454 : 9 : static bool parse_fraction(pl_str str, float *val)
455 : : {
456 : 9 : pl_str denom, num = pl_str_split_char(str, '/', &denom);
457 : : float n, d;
458 [ - + - - : 9 : bool ok = denom.buf && denom.len && pl_str_parse_float(num, &n) &&
- - - - ]
459 : 0 : pl_str_parse_float(denom, &d);
460 : : if (ok)
461 : 0 : *val = n / d;
462 : 9 : return ok;
463 : : }
464 : :
465 : 9 : static bool parse_float(opt_ctx p, pl_str str, void *out)
466 : : {
467 : 9 : pl_opt opt = p->opt;
468 : : float val;
469 [ + - + + ]: 9 : if (!parse_fraction(str, &val) && !pl_str_parse_float(str, &val)) {
470 [ + - ]: 4 : PL_ERR(p, "Invalid value '%.*s' for option '%s', expected floating point "
471 : : "or fraction", PL_STR_FMT(str), opt->key);
472 : 2 : return false;
473 : : }
474 : :
475 [ + - + - : 7 : switch (fpclassify(val)) {
+ + - + ]
476 : 0 : case FP_NAN:
477 : : case FP_INFINITE:
478 : : case FP_SUBNORMAL:
479 : 0 : PL_ERR(p, "Invalid value '%f' for option '%s', non-normal float",
480 : : val, opt->key);
481 : 0 : return false;
482 : :
483 : : case FP_ZERO:
484 : : case FP_NORMAL:
485 : : break;
486 : : }
487 : :
488 [ + - ]: 7 : if (opt->min != opt->max) {
489 [ + - + + ]: 7 : if (val < opt->min || val > opt->max) {
490 : 1 : PL_ERR(p, "Value of %.3f out of range for option '%s': [%.2f, %.2f]",
491 : : val, opt->key, opt->min, opt->max);
492 : 1 : return false;
493 : : }
494 : : }
495 : :
496 : 6 : *(float *) out = val;
497 : 6 : return true;
498 : : }
499 : :
500 : 220 : static int compare_params(opt_ctx p, const void *pa, const void *pb)
501 : : {
502 : 220 : const bool a = *(const void * const *) pa;
503 : 220 : const bool b = *(const void * const *) pb;
504 : 220 : return PL_CMP(a, b);
505 : : }
506 : :
507 : 37 : static void print_params(opt_ctx p, pl_str *out, const void *ptr)
508 : : {
509 : 37 : const bool value = *(const void * const *) ptr;
510 : 37 : print_bool(p, out, &value);
511 : 37 : }
512 : :
513 : 12 : static bool parse_params(opt_ctx p, pl_str str, void *out)
514 : : {
515 : 12 : pl_opt opt = p->opt;
516 : 12 : opt_priv priv = opt->priv;
517 : : const void **res = out;
518 : : bool set;
519 [ + - ]: 12 : if (!parse_bool(p, str, &set))
520 : : return false;
521 [ + - ]: 12 : if (set) {
522 : 12 : *res = (const void *) ((uintptr_t) p->opts + priv->offset_params);
523 : : } else {
524 : 0 : *res = NULL;
525 : : }
526 : : return true;
527 : : }
528 : :
529 : 1 : static void print_enum(opt_ctx p, pl_str *out, const void *ptr)
530 : : {
531 : 1 : pl_opt opt = p->opt;
532 : 1 : opt_priv priv = opt->priv;
533 : 1 : const unsigned value = *(const unsigned *) ptr;
534 [ + - ]: 3 : for (int i = 0; priv->values[i].name; i++) {
535 [ + + ]: 3 : if (priv->values[i].val == value) {
536 : 1 : pl_str_append(p->alloc, out, pl_str0(priv->values[i].name));
537 : 1 : return;
538 : : }
539 : : }
540 : :
541 : 0 : pl_unreachable();
542 : : }
543 : :
544 : 1 : static bool parse_enum(opt_ctx p, pl_str str, void *out)
545 : : {
546 : 1 : pl_opt opt = p->opt;
547 : 1 : opt_priv priv = opt->priv;
548 [ + + ]: 5 : for (int i = 0; priv->values[i].name; i++) {
549 [ - + ]: 4 : if (pl_str_equals0(str, priv->values[i].name)) {
550 : 0 : *(unsigned *) out = priv->values[i].val;
551 : 0 : return true;
552 : : }
553 : : }
554 : :
555 [ + - ]: 2 : PL_ERR(p, "Value of '%.*s' unrecognized for option '%s', valid values:",
556 : : PL_STR_FMT(str), opt->key);
557 [ + + ]: 5 : for (int i = 0; priv->values[i].name; i++)
558 : 4 : PL_ERR(p, " %s", priv->values[i].name);
559 : : return false;
560 : : }
561 : :
562 : 5 : static bool parse_preset(opt_ctx p, pl_str str, void *out)
563 : : {
564 : 5 : pl_opt opt = p->opt;
565 : 5 : opt_priv priv = opt->priv;
566 [ + + ]: 14 : for (int i = 0; priv->presets[i].name; i++) {
567 [ + + ]: 13 : if (pl_str_equals0(str, priv->presets[i].name)) {
568 [ + + ]: 4 : if (priv->offset == offsetof(struct pl_options_t, params)) {
569 : 3 : const struct pl_render_params *preset = priv->presets[i].val;
570 [ - + ]: 3 : pl_assert(priv->size == sizeof(*preset));
571 : :
572 : : // Redirect params structs into internal system after loading
573 : 3 : struct pl_render_params *params = out, prev = *params;
574 : 3 : *params = *preset;
575 : 3 : redirect_params(p->opts);
576 : :
577 : : // Re-apply excluded options
578 : 3 : params->lut = prev.lut;
579 : 3 : params->hooks = prev.hooks;
580 : 3 : params->num_hooks = prev.num_hooks;
581 : 3 : params->info_callback = prev.info_callback;
582 : 3 : params->info_priv = prev.info_priv;
583 : : } else {
584 : 1 : memcpy(out, priv->presets[i].val, priv->size);
585 : : }
586 : 4 : return true;
587 : : }
588 : : }
589 : :
590 [ + - ]: 2 : PL_ERR(p, "Value of '%.*s' unrecognized for option '%s', valid values:",
591 : : PL_STR_FMT(str), opt->key);
592 [ + + ]: 4 : for (int i = 0; priv->presets[i].name; i++)
593 : 3 : PL_ERR(p, " %s", priv->presets[i].name);
594 : : return false;
595 : : }
596 : :
597 : 4 : static void print_named(opt_ctx p, pl_str *out, const void *ptr)
598 : : {
599 : 4 : const struct named *value = *(const struct named **) ptr;
600 [ + - ]: 4 : if (value) {
601 [ + - ]: 8 : pl_str_append(p->alloc, out, pl_str0(value->name));
602 : : } else {
603 : 0 : pl_str_append(p->alloc, out, pl_str0("none"));
604 : : }
605 : 4 : }
606 : :
607 : 0 : static bool parse_named(opt_ctx p, pl_str str, void *out)
608 : : {
609 : 0 : pl_opt opt = p->opt;
610 : 0 : opt_priv priv = opt->priv;
611 : : const struct named **res = out;
612 [ # # ]: 0 : if (pl_str_equals0(str, "none")) {
613 : 0 : *res = NULL;
614 : 0 : return true;
615 : : }
616 : :
617 [ # # ]: 0 : for (int i = 0; priv->names[i]; i++) {
618 [ # # ]: 0 : if (pl_str_equals0(str, priv->names[i]->name)) {
619 : 0 : *res = priv->names[i];
620 : 0 : return true;
621 : : }
622 : : }
623 : :
624 [ # # ]: 0 : PL_ERR(p, "Value of '%.*s' unrecognized for option '%s', valid values:",
625 : : PL_STR_FMT(str), opt->key);
626 : 0 : PL_ERR(p, " none");
627 [ # # ]: 0 : for (int i = 0; priv->names[i]; i++)
628 : 0 : PL_ERR(p, " %s", priv->names[i]->name);
629 : : return false;
630 : : }
631 : :
632 : 32 : static void print_scaler(opt_ctx p, pl_str *out, const void *ptr)
633 : : {
634 : 32 : const struct pl_filter_config *f = *(const struct pl_filter_config **) ptr;
635 [ + - ]: 32 : if (f) {
636 [ - + ]: 32 : pl_assert(f->name); // this is either a built-in scaler or ptr to custom
637 : 32 : pl_str_append(p->alloc, out, pl_str0(f->name));
638 : : } else {
639 : 0 : pl_str_append(p->alloc, out, pl_str0("none"));
640 : : }
641 : 32 : }
642 : :
643 : 10 : static enum pl_filter_usage scaler_usage(pl_opt opt)
644 : : {
645 : 10 : opt_priv priv = opt->priv;
646 [ + + - + ]: 10 : switch (priv->offset) {
647 : : case offsetof(struct pl_options_t, params.upscaler):
648 : : case offsetof(struct pl_options_t, params.plane_upscaler):
649 : : case offsetof(struct pl_options_t, upscaler):
650 : : case offsetof(struct pl_options_t, plane_upscaler):
651 : : return PL_FILTER_UPSCALING;
652 : :
653 : 3 : case offsetof(struct pl_options_t, params.downscaler):
654 : : case offsetof(struct pl_options_t, params.plane_downscaler):
655 : : case offsetof(struct pl_options_t, downscaler):
656 : : case offsetof(struct pl_options_t, plane_downscaler):
657 : 3 : return PL_FILTER_DOWNSCALING;
658 : :
659 : 3 : case offsetof(struct pl_options_t, params.frame_mixer):
660 : : case offsetof(struct pl_options_t, frame_mixer):
661 : 3 : return PL_FILTER_FRAME_MIXING;
662 : : }
663 : :
664 : 0 : pl_unreachable();
665 : : }
666 : :
667 : 10 : static bool parse_scaler(opt_ctx p, pl_str str, void *out)
668 : : {
669 : 10 : pl_opt opt = p->opt;
670 : 10 : opt_priv priv = opt->priv;
671 : : const struct pl_filter_config **res = out;
672 [ - + ]: 10 : if (pl_str_equals0(str, "none")) {
673 : 0 : *res = NULL;
674 : 0 : return true;
675 [ + + ]: 10 : } else if (pl_str_equals0(str, "custom")) {
676 : 1 : *res = (void *) ((uintptr_t) p->opts + priv->offset_params);
677 : 1 : return true;
678 : : }
679 : :
680 : 9 : const enum pl_filter_usage usage = scaler_usage(opt);
681 [ + - ]: 118 : for (int i = 0; i < pl_num_filter_configs; i++) {
682 [ + + ]: 118 : if (!(pl_filter_configs[i]->allowed & usage))
683 : 30 : continue;
684 [ + + ]: 88 : if (pl_str_equals0(str, pl_filter_configs[i]->name)) {
685 : 9 : *res = pl_filter_configs[i];
686 : 9 : return true;
687 : : }
688 : : }
689 : :
690 [ # # ]: 0 : PL_ERR(p, "Value of '%.*s' unrecognized for option '%s', valid values:",
691 : : PL_STR_FMT(str), opt->key);
692 : 0 : PL_ERR(p, " none");
693 : 0 : PL_ERR(p, " custom");
694 [ # # ]: 0 : for (int i = 0; i < pl_num_filter_configs; i++) {
695 [ # # ]: 0 : if (pl_filter_configs[i]->allowed & usage)
696 : 0 : PL_ERR(p, " %s", pl_filter_configs[i]->name);
697 : : }
698 : : return false;
699 : : }
700 : :
701 : 1 : static bool parse_scaler_preset(opt_ctx p, pl_str str, void *out)
702 : : {
703 : 1 : pl_opt opt = p->opt;
704 : : struct pl_filter_config *res = out;
705 [ - + ]: 1 : if (pl_str_equals0(str, "none")) {
706 : 0 : *res = (struct pl_filter_config) { .name = "custom" };
707 : : return true;
708 : : }
709 : :
710 : 1 : const enum pl_filter_usage usage = scaler_usage(opt);
711 [ + - ]: 9 : for (int i = 0; i < pl_num_filter_configs; i++) {
712 [ + + ]: 9 : if (!(pl_filter_configs[i]->allowed & usage))
713 : 1 : continue;
714 [ + + ]: 8 : if (pl_str_equals0(str, pl_filter_configs[i]->name)) {
715 : : copy_filter(res, pl_filter_configs[i]);
716 : : return true;
717 : : }
718 : : }
719 : :
720 [ # # ]: 0 : PL_ERR(p, "Value of '%.*s' unrecognized for option '%s', valid values:",
721 : : PL_STR_FMT(str), opt->key);
722 : 0 : PL_ERR(p, " none");
723 [ # # ]: 0 : for (int i = 0; i < pl_num_filter_configs; i++) {
724 [ # # ]: 0 : if (pl_filter_configs[i]->allowed & usage)
725 : 0 : PL_ERR(p, " %s", pl_filter_configs[i]->name);
726 : : }
727 : : return false;
728 : : }
729 : :
730 : : #define OPT_BOOL(KEY, NAME, FIELD, ...) \
731 : : { \
732 : : .key = KEY, \
733 : : .name = NAME, \
734 : : .type = PL_OPT_BOOL, \
735 : : .priv = &(struct opt_priv_t) { \
736 : : .print = print_bool, \
737 : : .parse = parse_bool, \
738 : : .offset = offsetof(struct pl_options_t, FIELD), \
739 : : .size = sizeof(struct { \
740 : : bool dummy; \
741 : : pl_static_assert(sizeof(defaults.FIELD) == sizeof(bool)); \
742 : : }), \
743 : : }, \
744 : : __VA_ARGS__ \
745 : : }
746 : :
747 : : #define OPT_INT(KEY, NAME, FIELD, ...) \
748 : : { \
749 : : .key = KEY, \
750 : : .name = NAME, \
751 : : .type = PL_OPT_INT, \
752 : : .priv = &(struct opt_priv_t) { \
753 : : .print = print_int, \
754 : : .parse = parse_int, \
755 : : .offset = offsetof(struct pl_options_t, FIELD), \
756 : : .size = sizeof(struct { \
757 : : int dummy; \
758 : : pl_static_assert(sizeof(defaults.FIELD) == sizeof(int)); \
759 : : }), \
760 : : }, \
761 : : __VA_ARGS__ \
762 : : }
763 : :
764 : : #define OPT_FLOAT(KEY, NAME, FIELD, ...) \
765 : : { \
766 : : .key = KEY, \
767 : : .name = NAME, \
768 : : .type = PL_OPT_FLOAT, \
769 : : .priv = &(struct opt_priv_t) { \
770 : : .print = print_float, \
771 : : .parse = parse_float, \
772 : : .offset = offsetof(struct pl_options_t, FIELD), \
773 : : .size = sizeof(struct { \
774 : : float dummy; \
775 : : pl_static_assert(sizeof(defaults.FIELD) == sizeof(float)); \
776 : : }), \
777 : : }, \
778 : : __VA_ARGS__ \
779 : : }
780 : :
781 : : #define OPT_ENABLE_PARAMS(KEY, NAME, PARAMS, ...) \
782 : : { \
783 : : .key = KEY, \
784 : : .name = NAME, \
785 : : .type = PL_OPT_BOOL, \
786 : : .priv = &(struct opt_priv_t) { \
787 : : .compare = compare_params, \
788 : : .print = print_params, \
789 : : .parse = parse_params, \
790 : : .offset = offsetof(struct pl_options_t, params.PARAMS), \
791 : : .offset_params = offsetof(struct pl_options_t, PARAMS), \
792 : : .size = sizeof(struct { \
793 : : void *dummy; \
794 : : pl_static_assert(sizeof(defaults.params.PARAMS) == sizeof(void*));\
795 : : }), \
796 : : }, \
797 : : __VA_ARGS__ \
798 : : }
799 : :
800 : : #define OPT_ENUM(KEY, NAME, FIELD, VALUES, ...) \
801 : : { \
802 : : .key = KEY, \
803 : : .name = NAME, \
804 : : .type = PL_OPT_STRING, \
805 : : .priv = &(struct opt_priv_t) { \
806 : : .print = print_enum, \
807 : : .parse = parse_enum, \
808 : : .offset = offsetof(struct pl_options_t, FIELD), \
809 : : .size = sizeof(struct { \
810 : : unsigned dummy; \
811 : : pl_static_assert(sizeof(defaults.FIELD) == sizeof(unsigned)); \
812 : : }), \
813 : : .values = (struct enum_val[]) { VALUES } \
814 : : }, \
815 : : __VA_ARGS__ \
816 : : }
817 : :
818 : : #define OPT_PRESET(KEY, NAME, PARAMS, PRESETS, ...) \
819 : : { \
820 : : .key = KEY, \
821 : : .name = NAME, \
822 : : .type = PL_OPT_STRING, \
823 : : .preset = true, \
824 : : .priv = &(struct opt_priv_t) { \
825 : : .parse = parse_preset, \
826 : : .offset = offsetof(struct pl_options_t, PARAMS), \
827 : : .size = sizeof(defaults.PARAMS), \
828 : : .presets = (struct preset[]) { PRESETS }, \
829 : : }, \
830 : : __VA_ARGS__ \
831 : : }
832 : :
833 : : #define OPT_NAMED(KEY, NAME, FIELD, NAMES, ...) \
834 : : { \
835 : : .key = KEY, \
836 : : .name = NAME, \
837 : : .type = PL_OPT_STRING, \
838 : : .priv = &(struct opt_priv_t) { \
839 : : .print = print_named, \
840 : : .parse = parse_named, \
841 : : .offset = offsetof(struct pl_options_t, FIELD), \
842 : : .names = (const struct named * const * ) NAMES, \
843 : : .size = sizeof(struct { \
844 : : const struct named *dummy; \
845 : : pl_static_assert(offsetof(__typeof__(*NAMES[0]), name) == 0); \
846 : : pl_static_assert(sizeof(defaults.FIELD) == \
847 : : sizeof(const struct named *)); \
848 : : }), \
849 : : }, \
850 : : __VA_ARGS__ \
851 : : }
852 : :
853 : : #define OPT_SCALER(KEY, NAME, SCALER, ...) \
854 : : { \
855 : : .key = KEY, \
856 : : .name = NAME, \
857 : : .type = PL_OPT_STRING, \
858 : : .priv = &(struct opt_priv_t) { \
859 : : .print = print_scaler, \
860 : : .parse = parse_scaler, \
861 : : .offset = offsetof(struct pl_options_t, params.SCALER), \
862 : : .offset_params = offsetof(struct pl_options_t, SCALER), \
863 : : .size = sizeof(struct { \
864 : : const struct pl_filter_config *dummy; \
865 : : pl_static_assert(sizeof(defaults.SCALER) == \
866 : : sizeof(struct pl_filter_config)); \
867 : : }), \
868 : : }, \
869 : : __VA_ARGS__ \
870 : : }
871 : :
872 : : #define OPT_SCALER_PRESET(KEY, NAME, SCALER, ...) \
873 : : { \
874 : : .key = KEY, \
875 : : .name = NAME, \
876 : : .type = PL_OPT_STRING, \
877 : : .preset = true, \
878 : : .priv = &(struct opt_priv_t) { \
879 : : .parse = parse_scaler_preset, \
880 : : .offset = offsetof(struct pl_options_t, SCALER), \
881 : : .size = sizeof(struct { \
882 : : struct pl_filter_config dummy; \
883 : : pl_static_assert(sizeof(defaults.SCALER) == \
884 : : sizeof(struct pl_filter_config)); \
885 : : }), \
886 : : }, \
887 : : __VA_ARGS__ \
888 : : }
889 : :
890 : : #define LIST(...) __VA_ARGS__, {0}
891 : :
892 : : #define SCALE_OPTS(PREFIX, NAME, FIELD) \
893 : : OPT_SCALER(PREFIX, NAME, FIELD), \
894 : : OPT_SCALER_PRESET(PREFIX"_preset", NAME "preset", FIELD), \
895 : : OPT_NAMED(PREFIX"_kernel", NAME" kernel", FIELD.kernel, pl_filter_functions), \
896 : : OPT_NAMED(PREFIX"_window", NAME" window", FIELD.window, pl_filter_functions), \
897 : : OPT_FLOAT(PREFIX"_radius", NAME" radius", FIELD.radius, .min = 0.0, .max = 16.0), \
898 : : OPT_FLOAT(PREFIX"_clamp", NAME" clamping", FIELD.clamp, .max = 1.0), \
899 : : OPT_FLOAT(PREFIX"_blur", NAME" blur factor", FIELD.blur, .max = 100.0), \
900 : : OPT_FLOAT(PREFIX"_taper", NAME" taper factor", FIELD.taper, .max = 1.0), \
901 : : OPT_FLOAT(PREFIX"_antiring", NAME" antiringing", FIELD.antiring, .max = 1.0), \
902 : : OPT_FLOAT(PREFIX"_param1", NAME" parameter 1", FIELD.params[0]), \
903 : : OPT_FLOAT(PREFIX"_param2", NAME" parameter 2", FIELD.params[1]), \
904 : : OPT_FLOAT(PREFIX"_wparam1", NAME" window parameter 1", FIELD.wparams[0]), \
905 : : OPT_FLOAT(PREFIX"_wparam2", NAME" window parameter 2", FIELD.wparams[1]), \
906 : : OPT_BOOL(PREFIX"_polar", NAME" polar", FIELD.polar)
907 : :
908 : : const struct pl_opt_t pl_option_list[] = {
909 : : OPT_PRESET("preset", "Global preset", params, LIST(
910 : : {"default", &pl_render_default_params},
911 : : {"fast", &pl_render_fast_params},
912 : : {"high_quality", &pl_render_high_quality_params})),
913 : :
914 : : // Scalers
915 : : SCALE_OPTS("upscaler", "Upscaler", upscaler),
916 : : SCALE_OPTS("downscaler", "Downscaler", downscaler),
917 : : SCALE_OPTS("plane_upscaler", "Plane upscaler", plane_upscaler),
918 : : SCALE_OPTS("plane_downscaler", "Plane downscaler", plane_downscaler),
919 : : SCALE_OPTS("frame_mixer", "Frame mixer", frame_mixer),
920 : : OPT_FLOAT("antiringing_strength", "Anti-ringing strength", params.antiringing_strength, .max = 1.0),
921 : :
922 : : // Debanding
923 : : OPT_ENABLE_PARAMS("deband", "Enable debanding", deband_params),
924 : : OPT_PRESET("deband_preset", "Debanding preset", deband_params, LIST(
925 : : {"default", &pl_deband_default_params})),
926 : : OPT_INT("deband_iterations", "Debanding iterations", deband_params.iterations, .max = 16),
927 : : OPT_FLOAT("deband_threshold", "Debanding threshold", deband_params.threshold, .max = 1000.0),
928 : : OPT_FLOAT("deband_radius", "Debanding radius", deband_params.radius, .max = 1000.0),
929 : : OPT_FLOAT("deband_grain", "Debanding grain", deband_params.grain, .max = 1000.0),
930 : : OPT_FLOAT("deband_grain_neutral_r", "Debanding grain neutral R", deband_params.grain_neutral[0]),
931 : : OPT_FLOAT("deband_grain_neutral_g", "Debanding grain neutral G", deband_params.grain_neutral[1]),
932 : : OPT_FLOAT("deband_grain_neutral_b", "Debanding grain neutral B", deband_params.grain_neutral[2]),
933 : :
934 : : // Sigmodization
935 : : OPT_ENABLE_PARAMS("sigmoid", "Enable sigmoidization", sigmoid_params),
936 : : OPT_PRESET("sigmoid_preset", "Sigmoidization preset", sigmoid_params, LIST(
937 : : {"default", &pl_sigmoid_default_params})),
938 : : OPT_FLOAT("sigmoid_center", "Sigmoidization center", sigmoid_params.center, .max = 1.0),
939 : : OPT_FLOAT("sigmoid_slope", "Sigmoidization slope", sigmoid_params.slope, .min = 1.0, .max = 20.0),
940 : :
941 : : // Color adjustment
942 : : OPT_ENABLE_PARAMS("color_adjustment", "Enable color adjustment", color_adjustment),
943 : : OPT_PRESET("color_adjustment_preset", "Color adjustment preset", color_adjustment, LIST(
944 : : {"neutral", &pl_color_adjustment_neutral})),
945 : : OPT_FLOAT("brightness", "Brightness boost", color_adjustment.brightness, .min = -1.0, .max = 1.0),
946 : : OPT_FLOAT("contrast", "Contrast boost", color_adjustment.contrast, .max = 100.0),
947 : : OPT_FLOAT("saturation", "Saturation gain", color_adjustment.saturation, .max = 100.0),
948 : : OPT_FLOAT("hue", "Hue shift", color_adjustment.hue),
949 : : OPT_FLOAT("gamma", "Gamma adjustment", color_adjustment.gamma, .max = 100.0),
950 : : OPT_FLOAT("temperature", "Color temperature shift", color_adjustment.temperature,
951 : : .min = (2500 - 6500) / 3500.0, // see `pl_white_from_temp`
952 : : .max = (25000 - 6500) / 3500.0),
953 : :
954 : : // Peak detection
955 : : OPT_ENABLE_PARAMS("peak_detect", "Enable peak detection", peak_detect_params),
956 : : OPT_PRESET("peak_detect_preset", "Peak detection preset", peak_detect_params, LIST(
957 : : {"default", &pl_peak_detect_default_params},
958 : : {"high_quality", &pl_peak_detect_high_quality_params})),
959 : : OPT_FLOAT("peak_smoothing_period", "Peak detection smoothing coefficient", peak_detect_params.smoothing_period, .max = 1000.0),
960 : : OPT_FLOAT("scene_threshold_low", "Scene change threshold low", peak_detect_params.scene_threshold_low, .max = 100.0),
961 : : OPT_FLOAT("scene_threshold_high", "Scene change threshold high", peak_detect_params.scene_threshold_high, .max = 100.0),
962 : : OPT_FLOAT("minimum_peak", "Minimum detected peak", peak_detect_params.minimum_peak, .max = 100.0, .deprecated = true),
963 : : OPT_FLOAT("peak_percentile", "Peak detection percentile", peak_detect_params.percentile, .max = 100.0),
964 : : OPT_FLOAT("black_cutoff", "Peak detection black cutoff", peak_detect_params.black_cutoff, .max = 100.0),
965 : : OPT_BOOL("allow_delayed_peak", "Allow delayed peak detection", peak_detect_params.allow_delayed),
966 : :
967 : : // Color mapping
968 : : OPT_ENABLE_PARAMS("color_map", "Enable color mapping", color_map_params),
969 : : OPT_PRESET("color_map_preset", "Color mapping preset", color_map_params, LIST(
970 : : {"default", &pl_color_map_default_params},
971 : : {"high_quality", &pl_color_map_high_quality_params})),
972 : : OPT_NAMED("gamut_mapping", "Gamut mapping function", color_map_params.gamut_mapping,
973 : : pl_gamut_map_functions),
974 : : OPT_FLOAT("perceptual_deadzone", "Gamut mapping perceptual deadzone", color_map_params.gamut_constants.perceptual_deadzone, .max = 1.0f),
975 : : OPT_FLOAT("perceptual_strength", "Gamut mapping perceptual strength", color_map_params.gamut_constants.perceptual_strength, .max = 1.0f),
976 : : OPT_FLOAT("colorimetric_gamma", "Gamut mapping colorimetric gamma", color_map_params.gamut_constants.colorimetric_gamma, .max = 10.0f),
977 : : OPT_FLOAT("softclip_knee", "Gamut mapping softclip knee point", color_map_params.gamut_constants.softclip_knee, .max = 1.0f),
978 : : OPT_FLOAT("softclip_desat", "Gamut mapping softclip desaturation strength", color_map_params.gamut_constants.softclip_desat, .max = 1.0f),
979 : : OPT_INT("lut3d_size_I", "Gamut 3DLUT size I", color_map_params.lut3d_size[0], .max = 1024),
980 : : OPT_INT("lut3d_size_C", "Gamut 3DLUT size C", color_map_params.lut3d_size[1], .max = 1024),
981 : : OPT_INT("lut3d_size_h", "Gamut 3DLUT size h", color_map_params.lut3d_size[2], .max = 1024),
982 : : OPT_BOOL("lut3d_tricubic", "Gamut 3DLUT tricubic interpolation", color_map_params.lut3d_tricubic),
983 : : OPT_BOOL("gamut_expansion", "Gamut expansion", color_map_params.gamut_expansion),
984 : : OPT_NAMED("tone_mapping", "Tone mapping function", color_map_params.tone_mapping_function,
985 : : pl_tone_map_functions),
986 : : OPT_FLOAT("knee_adaptation", "Tone mapping knee point adaptation", color_map_params.tone_constants.knee_adaptation, .max = 1.0f),
987 : : OPT_FLOAT("knee_minimum", "Tone mapping knee point minimum", color_map_params.tone_constants.knee_minimum, .max = 0.5f),
988 : : OPT_FLOAT("knee_maximum", "Tone mapping knee point maximum", color_map_params.tone_constants.knee_maximum, .min = 0.5f, .max = 1.0f),
989 : : OPT_FLOAT("knee_default", "Tone mapping knee point default", color_map_params.tone_constants.knee_default, .max = 1.0f),
990 : : OPT_FLOAT("knee_offset", "BT.2390 knee point offset", color_map_params.tone_constants.knee_offset, .min = 0.5f, .max = 2.0f),
991 : : OPT_FLOAT("slope_tuning", "Spline slope tuning strength", color_map_params.tone_constants.slope_tuning, .max = 10.0f),
992 : : OPT_FLOAT("slope_offset", "Spline slope tuning offset", color_map_params.tone_constants.slope_offset, .max = 1.0f),
993 : : OPT_FLOAT("spline_contrast", "Spline slope contrast", color_map_params.tone_constants.spline_contrast, .max = 1.5f),
994 : : OPT_FLOAT("reinhard_contrast", "Reinhard contrast", color_map_params.tone_constants.reinhard_contrast, .max = 1.0f),
995 : : OPT_FLOAT("linear_knee", "Tone mapping linear knee point", color_map_params.tone_constants.linear_knee, .max = 1.0f),
996 : : OPT_FLOAT("exposure", "Tone mapping linear exposure", color_map_params.tone_constants.exposure, .max = 10.0f),
997 : : OPT_BOOL("inverse_tone_mapping", "Inverse tone mapping", color_map_params.inverse_tone_mapping),
998 : : OPT_ENUM("tone_map_metadata", "Source of HDR metadata to use", color_map_params.metadata, LIST(
999 : : {"any", PL_HDR_METADATA_ANY},
1000 : : {"none", PL_HDR_METADATA_NONE},
1001 : : {"hdr10", PL_HDR_METADATA_HDR10},
1002 : : {"hdr10plus", PL_HDR_METADATA_HDR10PLUS},
1003 : : {"cie_y", PL_HDR_METADATA_CIE_Y})),
1004 : : OPT_INT("tone_lut_size", "Tone mapping LUT size", color_map_params.lut_size, .max = 4096),
1005 : : OPT_FLOAT("contrast_recovery", "HDR contrast recovery strength", color_map_params.contrast_recovery, .max = 2.0),
1006 : : OPT_FLOAT("contrast_smoothness", "HDR contrast recovery smoothness", color_map_params.contrast_smoothness, .min = 1.0, .max = 32.0),
1007 : : OPT_BOOL("force_tone_mapping_lut", "Force tone mapping LUT", color_map_params.force_tone_mapping_lut),
1008 : : OPT_BOOL("visualize_lut", "Visualize tone mapping LUTs", color_map_params.visualize_lut),
1009 : : OPT_FLOAT("visualize_lut_x0", "Visualization rect x0", color_map_params.visualize_rect.x0),
1010 : : OPT_FLOAT("visualize_lut_y0", "Visualization rect y0", color_map_params.visualize_rect.y0),
1011 : : OPT_FLOAT("visualize_lut_x1", "Visualization rect x0", color_map_params.visualize_rect.x1),
1012 : : OPT_FLOAT("visualize_lut_y1", "Visualization rect y0", color_map_params.visualize_rect.y1),
1013 : : OPT_FLOAT("visualize_hue", "Visualization hue slice", color_map_params.visualize_hue),
1014 : : OPT_FLOAT("visualize_theta", "Visualization rotation", color_map_params.visualize_theta),
1015 : : OPT_BOOL("show_clipping", "Highlight clipped pixels", color_map_params.show_clipping),
1016 : : OPT_FLOAT("tone_mapping_param", "Tone mapping function parameter", color_map_params.tone_mapping_param, .deprecated = true),
1017 : :
1018 : : // Dithering
1019 : : OPT_ENABLE_PARAMS("dither", "Enable dithering", dither_params),
1020 : : OPT_PRESET("dither_preset", "Dithering preset", dither_params, LIST(
1021 : : {"default", &pl_dither_default_params})),
1022 : : OPT_ENUM("dither_method", "Dither method", dither_params.method, LIST(
1023 : : {"blue", PL_DITHER_BLUE_NOISE},
1024 : : {"ordered_lut", PL_DITHER_ORDERED_LUT},
1025 : : {"ordered", PL_DITHER_ORDERED_FIXED},
1026 : : {"white", PL_DITHER_WHITE_NOISE})),
1027 : : OPT_INT("dither_lut_size", "Dither LUT size", dither_params.lut_size, .min = 1, .max = 8),
1028 : : OPT_BOOL("dither_temporal", "Temporal dithering", dither_params.temporal),
1029 : :
1030 : : // ICC
1031 : : OPT_ENABLE_PARAMS("icc", "Enable ICC settings", icc_params, .deprecated = true),
1032 : : OPT_PRESET("icc_preset", "ICC preset", icc_params, LIST(
1033 : : {"default", &pl_icc_default_params}), .deprecated = true),
1034 : : OPT_ENUM("icc_intent", "ICC rendering intent", icc_params.intent, LIST(
1035 : : {"auto", PL_INTENT_AUTO},
1036 : : {"perceptual", PL_INTENT_PERCEPTUAL},
1037 : : {"relative", PL_INTENT_RELATIVE_COLORIMETRIC},
1038 : : {"saturation", PL_INTENT_SATURATION},
1039 : : {"absolute", PL_INTENT_ABSOLUTE_COLORIMETRIC}), .deprecated = true),
1040 : : OPT_INT("icc_size_r", "ICC 3DLUT size R", icc_params.size_r, .max = 256, .deprecated = true),
1041 : : OPT_INT("icc_size_g", "ICC 3DLUT size G", icc_params.size_g, .max = 256, .deprecated = true),
1042 : : OPT_INT("icc_size_b", "ICC 3DLUT size G", icc_params.size_b, .max = 256, .deprecated = true),
1043 : : OPT_FLOAT("icc_max_luma", "ICC profile luma override", icc_params.max_luma, .max = 10000, .deprecated = true),
1044 : : OPT_BOOL("icc_force_bpc", "Force ICC black point compensation", icc_params.force_bpc, .deprecated = true),
1045 : :
1046 : : // Cone distortion
1047 : : OPT_ENABLE_PARAMS("cone", "Enable cone distortion", cone_params),
1048 : : OPT_PRESET("cone_preset", "Cone distortion preset", cone_params, LIST(
1049 : : {"normal", &pl_vision_normal},
1050 : : {"protanomaly", &pl_vision_protanomaly},
1051 : : {"protanopia", &pl_vision_protanopia},
1052 : : {"deuteranomaly", &pl_vision_deuteranomaly},
1053 : : {"deuteranopia", &pl_vision_deuteranopia},
1054 : : {"tritanomaly", &pl_vision_tritanomaly},
1055 : : {"tritanopia", &pl_vision_tritanopia},
1056 : : {"monochromacy", &pl_vision_monochromacy},
1057 : : {"achromatopsia", &pl_vision_achromatopsia})),
1058 : : OPT_ENUM("cones", "Cone selection", cone_params.cones, LIST(
1059 : : {"none", PL_CONE_NONE},
1060 : : {"l", PL_CONE_L},
1061 : : {"m", PL_CONE_M},
1062 : : {"s", PL_CONE_S},
1063 : : {"lm", PL_CONE_LM},
1064 : : {"ms", PL_CONE_MS},
1065 : : {"ls", PL_CONE_LS},
1066 : : {"lms", PL_CONE_LMS})),
1067 : : OPT_FLOAT("cone_strength", "Cone distortion gain", cone_params.strength),
1068 : :
1069 : : // Blending
1070 : : #define BLEND_VALUES LIST( \
1071 : : {"zero", PL_BLEND_ZERO}, \
1072 : : {"one", PL_BLEND_ONE}, \
1073 : : {"alpha", PL_BLEND_SRC_ALPHA}, \
1074 : : {"one_minus_alpha", PL_BLEND_ONE_MINUS_SRC_ALPHA})
1075 : :
1076 : : OPT_ENABLE_PARAMS("blend", "Enable output blending", blend_params),
1077 : : OPT_PRESET("blend_preset", "Output blending preset", blend_params, LIST(
1078 : : {"alpha_overlay", &pl_alpha_overlay})),
1079 : : OPT_ENUM("blend_src_rgb", "Source RGB blend mode", blend_params.src_rgb, BLEND_VALUES),
1080 : : OPT_ENUM("blend_src_alpha", "Source alpha blend mode", blend_params.src_alpha, BLEND_VALUES),
1081 : : OPT_ENUM("blend_dst_rgb", "Target RGB blend mode", blend_params.dst_rgb, BLEND_VALUES),
1082 : : OPT_ENUM("blend_dst_alpha", "Target alpha blend mode", blend_params.dst_alpha, BLEND_VALUES),
1083 : :
1084 : : // Deinterlacing
1085 : : OPT_ENABLE_PARAMS("deinterlace", "Enable deinterlacing", deinterlace_params),
1086 : : OPT_PRESET("deinterlace_preset", "Deinterlacing preset", deinterlace_params, LIST(
1087 : : {"default", &pl_deinterlace_default_params})),
1088 : : OPT_ENUM("deinterlace_algo", "Deinterlacing algorithm", deinterlace_params.algo, LIST(
1089 : : {"weave", PL_DEINTERLACE_WEAVE},
1090 : : {"bob", PL_DEINTERLACE_BOB},
1091 : : {"yadif", PL_DEINTERLACE_YADIF})),
1092 : : OPT_BOOL("deinterlace_skip_spatial", "Skip spatial interlacing check", deinterlace_params.skip_spatial_check),
1093 : :
1094 : : // Distortion
1095 : : OPT_ENABLE_PARAMS("distort", "Enable distortion", distort_params),
1096 : : OPT_PRESET("distort_preset", "Distortion preset", distort_params, LIST(
1097 : : {"default", &pl_distort_default_params})),
1098 : : OPT_FLOAT("distort_scale_x", "Distortion X scale", distort_params.transform.mat.m[0][0]),
1099 : : OPT_FLOAT("distort_scale_y", "Distortion Y scale", distort_params.transform.mat.m[1][1]),
1100 : : OPT_FLOAT("distort_shear_x", "Distortion X shear", distort_params.transform.mat.m[0][1]),
1101 : : OPT_FLOAT("distort_shear_y", "Distortion Y shear", distort_params.transform.mat.m[1][0]),
1102 : : OPT_FLOAT("distort_offset_x", "Distortion X offset", distort_params.transform.c[0]),
1103 : : OPT_FLOAT("distort_offset_y", "Distortion Y offset", distort_params.transform.c[1]),
1104 : : OPT_BOOL("distort_unscaled", "Distortion unscaled", distort_params.unscaled),
1105 : : OPT_BOOL("distort_constrain", "Constrain distortion", distort_params.constrain),
1106 : : OPT_BOOL("distort_bicubic", "Distortion bicubic interpolation", distort_params.bicubic),
1107 : : OPT_ENUM("distort_address_mode", "Distortion texture address mode", distort_params.address_mode, LIST(
1108 : : {"clamp", PL_TEX_ADDRESS_CLAMP},
1109 : : {"repeat", PL_TEX_ADDRESS_REPEAT},
1110 : : {"mirror", PL_TEX_ADDRESS_MIRROR})),
1111 : : OPT_ENUM("distort_alpha_mode", "Distortion alpha blending mode", distort_params.alpha_mode, LIST(
1112 : : {"unknown", PL_ALPHA_UNKNOWN},
1113 : : {"independent", PL_ALPHA_INDEPENDENT},
1114 : : {"premultiplied", PL_ALPHA_PREMULTIPLIED},
1115 : : {"none", PL_ALPHA_NONE})),
1116 : :
1117 : : // Misc renderer settings
1118 : : OPT_NAMED("error_diffusion", "Error diffusion kernel", params.error_diffusion,
1119 : : pl_error_diffusion_kernels),
1120 : : OPT_ENUM("lut_type", "Color mapping LUT type", params.lut_type, LIST(
1121 : : {"unknown", PL_LUT_UNKNOWN},
1122 : : {"native", PL_LUT_NATIVE},
1123 : : {"normalized", PL_LUT_NORMALIZED},
1124 : : {"conversion", PL_LUT_CONVERSION})),
1125 : : OPT_ENUM("background", "Background clearing mode", params.background, LIST(
1126 : : {"color", PL_CLEAR_COLOR},
1127 : : {"tiles", PL_CLEAR_TILES},
1128 : : {"skip", PL_CLEAR_SKIP})),
1129 : : OPT_ENUM("border", "Border clearing mode", params.border, LIST(
1130 : : {"color", PL_CLEAR_COLOR},
1131 : : {"tiles", PL_CLEAR_TILES},
1132 : : {"skip", PL_CLEAR_SKIP})),
1133 : : OPT_FLOAT("background_r", "Background color R", params.background_color[0], .max = 1.0),
1134 : : OPT_FLOAT("background_g", "Background color G", params.background_color[1], .max = 1.0),
1135 : : OPT_FLOAT("background_b", "Background color B", params.background_color[2], .max = 1.0),
1136 : : OPT_FLOAT("background_transparency", "Background color transparency", params.background_transparency, .max = 1),
1137 : : OPT_BOOL("skip_target_clearing", "Skip target clearing", params.skip_target_clearing, .deprecated = true),
1138 : : OPT_FLOAT("corner_rounding", "Corner rounding", params.corner_rounding, .max = 1.0),
1139 : : OPT_BOOL("blend_against_tiles", "Blend against tiles", params.blend_against_tiles, .deprecated = true),
1140 : : OPT_FLOAT("tile_color_hi_r", "Bright tile R", params.tile_colors[0][0], .max = 1.0),
1141 : : OPT_FLOAT("tile_color_hi_g", "Bright tile G", params.tile_colors[0][1], .max = 1.0),
1142 : : OPT_FLOAT("tile_color_hi_b", "Bright tile B", params.tile_colors[0][2], .max = 1.0),
1143 : : OPT_FLOAT("tile_color_lo_r", "Dark tile R", params.tile_colors[1][0], .max = 1.0),
1144 : : OPT_FLOAT("tile_color_lo_g", "Dark tile G", params.tile_colors[1][1], .max = 1.0),
1145 : : OPT_FLOAT("tile_color_lo_b", "Dark tile B", params.tile_colors[1][2], .max = 1.0),
1146 : : OPT_INT("tile_size", "Tile size", params.tile_size, .min = 2, .max = 256),
1147 : :
1148 : : // Performance / quality trade-offs and debugging options
1149 : : OPT_BOOL("skip_anti_aliasing", "Skip anti-aliasing", params.skip_anti_aliasing),
1150 : : OPT_INT("lut_entries", "Scaler LUT entries", params.lut_entries, .max = 256, .deprecated = true),
1151 : : OPT_FLOAT("polar_cutoff", "Polar LUT cutoff", params.polar_cutoff, .max = 1.0, .deprecated = true),
1152 : : OPT_BOOL("preserve_mixing_cache", "Preserve mixing cache", params.preserve_mixing_cache),
1153 : : OPT_BOOL("skip_caching_single_frame", "Skip caching single frame", params.skip_caching_single_frame),
1154 : : OPT_BOOL("disable_linear_scaling", "Disable linear scaling", params.disable_linear_scaling),
1155 : : OPT_BOOL("disable_builtin_scalers", "Disable built-in scalers", params.disable_builtin_scalers),
1156 : : OPT_BOOL("correct_subpixel_offset", "Correct subpixel offsets", params.correct_subpixel_offsets),
1157 : : OPT_BOOL("ignore_icc_profiles", "Ignore ICC profiles", params.ignore_icc_profiles, .deprecated = true),
1158 : : OPT_BOOL("force_dither", "Force-enable dithering", params.force_dither),
1159 : : OPT_BOOL("disable_dither_gamma_correction", "Disable gamma-correct dithering", params.disable_dither_gamma_correction),
1160 : : OPT_BOOL("disable_fbos", "Disable FBOs", params.disable_fbos),
1161 : : OPT_BOOL("force_low_bit_depth_fbos", "Force 8-bit FBOs", params.force_low_bit_depth_fbos),
1162 : : OPT_BOOL("dynamic_constants", "Dynamic constants", params.dynamic_constants),
1163 : : {0},
1164 : : };
1165 : :
1166 : : const int pl_option_count = PL_ARRAY_SIZE(pl_option_list) - 1;
1167 : :
1168 : 2 : pl_opt pl_find_option(const char *key)
1169 : : {
1170 [ + + ]: 415 : for (int i = 0; i < pl_option_count; i++) {
1171 [ + + ]: 414 : if (!strcmp(key, pl_option_list[i].key))
1172 : 1 : return &pl_option_list[i];
1173 : : }
1174 : :
1175 : : return NULL;
1176 : : }
|