LCOV - code coverage report
Current view: top level - src - options.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 270 340 79.4 %
Date: 2025-03-29 09:04:10 Functions: 31 36 86.1 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 153 284 53.9 %

           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                 :            : }

Generated by: LCOV version 1.16