@@ -91,12 +91,16 @@ typedef struct LibplaceboContext {
/* pl_color_map_params */
int intent;
- int tonemapping;
+ int gamut_mode;
+ char *tonemapping;
float tonemapping_param;
+ int tonemapping_mode;
+ int inverse_tonemapping;
+ float crosstalk;
+ int tonemapping_lut_size;
+ /* for backwards compatibility */
float desat_str;
float desat_exp;
- float desat_base;
- float max_boost;
int gamut_warning;
int gamut_clipping;
@@ -184,6 +188,30 @@ static int find_scaler(AVFilterContext *avctx,
return AVERROR(EINVAL);
}
+static int find_tone_mapper(AVFilterContext *avctx,
+ const struct pl_tone_map_function **opt,
+ const char *name)
+{
+ const struct pl_tone_map_function * const *func;
+ if (!strcmp(name, "help")) {
+ av_log(avctx, AV_LOG_INFO, "Available tone mapping functions:\n");
+ for (func = pl_tone_map_functions; *func; func++)
+ av_log(avctx, AV_LOG_INFO, " %s (%s)\n",
+ (*func)->name, (*func)->description);
+ return AVERROR_EXIT;
+ }
+
+ for (func = pl_tone_map_functions; *func; func++) {
+ if (!strcmp(name, (*func)->name)) {
+ *opt = *func;
+ return 0;
+ }
+ }
+
+ av_log(avctx, AV_LOG_ERROR, "No such tone mapping function '%s'.\n", name);
+ return AVERROR(EINVAL);
+}
+
static int libplacebo_init(AVFilterContext *avctx)
{
LibplaceboContext *s = avctx->priv;
@@ -281,6 +309,7 @@ static int process_frames(AVFilterContext *avctx, AVFrame *out, AVFrame *in)
int err = 0, ok;
LibplaceboContext *s = avctx->priv;
struct pl_render_params params;
+ struct pl_color_map_params color_params;
struct pl_frame image, target;
ok = pl_map_avframe_ex(s->gpu, &image, pl_avframe_params(
.frame = in,
@@ -336,18 +365,6 @@ static int process_frames(AVFilterContext *avctx, AVFrame *out, AVFrame *in)
.overshoot_margin = s->overshoot,
),
- .color_map_params = pl_color_map_params(
- .intent = s->intent,
- .tone_mapping_algo = s->tonemapping,
- .tone_mapping_param = s->tonemapping_param,
- .desaturation_strength = s->desat_str,
- .desaturation_exponent = s->desat_exp,
- .desaturation_base = s->desat_base,
- .max_boost = s->max_boost,
- .gamut_warning = s->gamut_warning,
- .gamut_clipping = s->gamut_clipping,
- ),
-
.dither_params = s->dithering < 0 ? NULL : pl_dither_params(
.method = s->dithering,
.lut_size = s->dither_lut_size,
@@ -374,6 +391,39 @@ static int process_frames(AVFilterContext *avctx, AVFrame *out, AVFrame *in)
RET(find_scaler(avctx, ¶ms.upscaler, s->upscaler));
RET(find_scaler(avctx, ¶ms.downscaler, s->downscaler));
+ params.color_map_params = &color_params;
+ color_params = (struct pl_color_map_params) {
+ PL_COLOR_MAP_DEFAULTS
+ .intent = s->intent,
+ .gamut_mode = s->gamut_mode,
+ .tone_mapping_param = s->tonemapping_param,
+ .tone_mapping_mode = s->tonemapping_mode,
+ .inverse_tone_mapping = s->inverse_tonemapping,
+ .tone_mapping_crosstalk = s->crosstalk,
+ .lut_size = s->tonemapping_lut_size,
+ };
+
+ /* backwards compatibility with older API */
+ if (color_params.tone_mapping_mode == PL_TONE_MAP_AUTO &&
+ (s->desat_str >= 0.0f || s->desat_exp >= 0.0f)) {
+ float str = s->desat_str < 0.0f ? 0.9f : s->desat_str;
+ float exp = s->desat_exp < 0.0f ? 0.2f : s->desat_exp;
+ if (str >= 0.9f && exp <= 0.1f) {
+ color_params.tone_mapping_mode = PL_TONE_MAP_RGB;
+ } else if (str > 0.1f) {
+ color_params.tone_mapping_mode = PL_TONE_MAP_HYBRID;
+ } else {
+ color_params.tone_mapping_mode = PL_TONE_MAP_LUMA;
+ }
+ }
+
+ if (s->gamut_warning)
+ color_params.gamut_mode = PL_GAMUT_WARN;
+ if (s->gamut_clipping)
+ color_params.gamut_mode = PL_GAMUT_DESATURATE;
+
+ RET(find_tone_mapper(avctx, &color_params.tone_mapping_function, s->tonemapping));
+
pl_render_image(s->renderer, &image, &target, ¶ms);
pl_unmap_avframe(s->gpu, &image);
pl_unmap_avframe(s->gpu, &target);
@@ -616,21 +666,27 @@ static const AVOption libplacebo_options[] = {
{ "relative", "Relative colorimetric", 0, AV_OPT_TYPE_CONST, {.i64 = PL_INTENT_RELATIVE_COLORIMETRIC}, 0, 0, STATIC, "intent" },
{ "absolute", "Absolute colorimetric", 0, AV_OPT_TYPE_CONST, {.i64 = PL_INTENT_ABSOLUTE_COLORIMETRIC}, 0, 0, STATIC, "intent" },
{ "saturation", "Saturation mapping", 0, AV_OPT_TYPE_CONST, {.i64 = PL_INTENT_SATURATION}, 0, 0, STATIC, "intent" },
- { "tonemapping", "Tone-mapping algorithm", OFFSET(tonemapping), AV_OPT_TYPE_INT, {.i64 = PL_TONE_MAPPING_BT_2390}, 0, PL_TONE_MAPPING_ALGORITHM_COUNT - 1, DYNAMIC, "tonemap" },
- { "clip", "Hard-clipping", 0, AV_OPT_TYPE_CONST, {.i64 = PL_TONE_MAPPING_CLIP}, 0, 0, STATIC, "tonemap" },
- { "mobius", "Mobius tone-mapping", 0, AV_OPT_TYPE_CONST, {.i64 = PL_TONE_MAPPING_MOBIUS}, 0, 0, STATIC, "tonemap" },
- { "reinhard", "Reinhard tone-mapping", 0, AV_OPT_TYPE_CONST, {.i64 = PL_TONE_MAPPING_REINHARD}, 0, 0, STATIC, "tonemap" },
- { "hable", "Hable/Filmic tone-mapping", 0, AV_OPT_TYPE_CONST, {.i64 = PL_TONE_MAPPING_HABLE}, 0, 0, STATIC, "tonemap" },
- { "gamma", "Gamma tone-mapping", 0, AV_OPT_TYPE_CONST, {.i64 = PL_TONE_MAPPING_GAMMA}, 0, 0, STATIC, "tonemap" },
- { "linear", "Linear tone-mapping", 0, AV_OPT_TYPE_CONST, {.i64 = PL_TONE_MAPPING_LINEAR}, 0, 0, STATIC, "tonemap" },
- { "bt.2390", "ITU-R BT.2390 tone-mapping", 0, AV_OPT_TYPE_CONST, {.i64 = PL_TONE_MAPPING_BT_2390}, 0, 0, STATIC, "tonemap" },
+ { "gamut_mode", "Gamut-mapping mode", OFFSET(gamut_mode), AV_OPT_TYPE_INT, {.i64 = PL_GAMUT_CLIP}, 0, PL_GAMUT_MODE_COUNT, DYNAMIC, "gamut_mode" },
+ { "clip", "Hard-clip gamut boundary", 0, AV_OPT_TYPE_CONST, {.i64 = PL_GAMUT_CLIP}, 0, 0, STATIC, "gamut_mode" },
+ { "warn", "Highlight out-of-gamut colors", 0, AV_OPT_TYPE_CONST, {.i64 = PL_GAMUT_WARN}, 0, 0, STATIC, "gamut_mode" },
+ { "darken", "Darken image to fit gamut", 0, AV_OPT_TYPE_CONST, {.i64 = PL_GAMUT_DARKEN}, 0, 0, STATIC, "gamut_mode" },
+ { "desaturate", "Colorimetrically desaturate colors", 0, AV_OPT_TYPE_CONST, {.i64 = PL_GAMUT_DESATURATE}, 0, 0, STATIC, "gamut_mode" },
+ { "tonemapping", "Tone-mapping algorithm", OFFSET(tonemapping), AV_OPT_TYPE_STRING, {.str = "auto"}, .flags = DYNAMIC, "tonemap" },
{ "tonemapping_param", "Tunable parameter for some tone-mapping functions", OFFSET(tonemapping_param), AV_OPT_TYPE_FLOAT, {.dbl = 0.0}, 0.0, 100.0, .flags = DYNAMIC },
- { "desaturation_strength", "Desaturation strength", OFFSET(desat_str), AV_OPT_TYPE_FLOAT, {.dbl = 0.90}, 0.0, 1.0, DYNAMIC },
- { "desaturation_exponent", "Desaturation exponent", OFFSET(desat_exp), AV_OPT_TYPE_FLOAT, {.dbl = 0.2}, 0.0, 10.0, DYNAMIC },
- { "desaturation_base", "Desaturation base", OFFSET(desat_base), AV_OPT_TYPE_FLOAT, {.dbl = 0.18}, 0.0, 10.0, DYNAMIC },
- { "max_boost", "Tone-mapping maximum boost", OFFSET(max_boost), AV_OPT_TYPE_FLOAT, {.dbl = 1.0}, 1.0, 10.0, DYNAMIC },
- { "gamut_warning", "Highlight out-of-gamut colors", OFFSET(gamut_warning), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC },
- { "gamut_clipping", "Enable colorimetric gamut clipping", OFFSET(gamut_clipping), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, DYNAMIC },
+ { "tonemapping_mode", "Tone-mapping mode", OFFSET(tonemapping_mode), AV_OPT_TYPE_INT, {.i64 = PL_TONE_MAP_AUTO}, 0, PL_TONE_MAP_MODE_COUNT, DYNAMIC, "tonemap_mode" },
+ { "auto", "Automatic selection", 0, AV_OPT_TYPE_CONST, {.i64 = PL_TONE_MAP_AUTO}, 0, 0, STATIC, "tonemap_mode" },
+ { "rgb", "Per-channel (RGB)", 0, AV_OPT_TYPE_CONST, {.i64 = PL_TONE_MAP_RGB}, 0, 0, STATIC, "tonemap_mode" },
+ { "max", "Maximum component", 0, AV_OPT_TYPE_CONST, {.i64 = PL_TONE_MAP_MAX}, 0, 0, STATIC, "tonemap_mode" },
+ { "hybrid", "Hybrid of Luma/RGB", 0, AV_OPT_TYPE_CONST, {.i64 = PL_TONE_MAP_HYBRID}, 0, 0, STATIC, "tonemap_mode" },
+ { "luma", "Luminance", 0, AV_OPT_TYPE_CONST, {.i64 = PL_TONE_MAP_LUMA}, 0, 0, STATIC, "tonemap_mode" },
+ { "inverse_tonemapping", "Inverse tone mapping (range expansion)", OFFSET(inverse_tonemapping), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC },
+ { "tonemapping_crosstalk", "Crosstalk factor for tone-mapping", OFFSET(crosstalk), AV_OPT_TYPE_FLOAT, {.dbl = 0.04}, 0.0, 0.30, DYNAMIC },
+ { "tonemapping_lut_size", "Tone-mapping LUT size", OFFSET(tonemapping_lut_size), AV_OPT_TYPE_INT, {.i64 = 256}, 2, 1024, DYNAMIC },
+ /* deprecated options for backwards compatibility, defaulting to -1 to not override the new defaults */
+ { "desaturation_strength", "Desaturation strength", OFFSET(desat_str), AV_OPT_TYPE_FLOAT, {.dbl = -1.0}, -1.0, 1.0, DYNAMIC | AV_OPT_FLAG_DEPRECATED },
+ { "desaturation_exponent", "Desaturation exponent", OFFSET(desat_exp), AV_OPT_TYPE_FLOAT, {.dbl = -1.0}, -1.0, 10.0, DYNAMIC | AV_OPT_FLAG_DEPRECATED },
+ { "gamut_warning", "Highlight out-of-gamut colors", OFFSET(gamut_warning), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC | AV_OPT_FLAG_DEPRECATED },
+ { "gamut_clipping", "Enable colorimetric gamut clipping", OFFSET(gamut_clipping), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC | AV_OPT_FLAG_DEPRECATED },
{ "dithering", "Dither method to use", OFFSET(dithering), AV_OPT_TYPE_INT, {.i64 = PL_DITHER_BLUE_NOISE}, -1, PL_DITHER_METHOD_COUNT - 1, DYNAMIC, "dither" },
{ "none", "Disable dithering", 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, STATIC, "dither" },