Commit 80ab65fd authored by Niklas Haas's avatar Niklas Haas
Browse files

filters: refactor and expand filter presets

Rename this to pl_filter_preset to make the intent clearer, and add an
extra field `description` to provide a friendlier name.

In addition to this change, re-order the list to move the popular
filters up to the top of the list, as well as adding a "none" entry
corresponding to the built-in sampling.

Finally, add pl_frame_mixers to provide frame mixer presets.

Update plplay to use these new fields/structs.
parent 8a1c10f0
Pipeline #82927 passed with stages
in 8 minutes and 1 second
......@@ -54,7 +54,7 @@ struct plplay {
bool eof;
// settings / ui state
const struct pl_named_filter_config *upscaler, *downscaler, *frame_mixer;
const struct pl_filter_preset *upscaler, *downscaler, *frame_mixer;
struct pl_render_params params;
struct pl_deband_params deband_params;
struct pl_sigmoid_params sigmoid_params;
......@@ -294,13 +294,6 @@ static enum pl_queue_status get_frame(struct pl_source_frame *out_frame,
return QUEUE_OK;
}
static const struct pl_named_filter_config frame_mixers[] = {
{ .name = "none", .filter = NULL },
{ .name = "oversample", .filter = &pl_oversample_frame_mixer, },
{ .name = "mitchell (clamped)", .filter = &pl_filter_mitchell_clamp, },
{0}
};
static void update_settings(struct plplay *p);
static bool render_frame(struct plplay *p, const struct pl_swapchain_frame *frame,
......@@ -470,15 +463,15 @@ int main(int argc, char **argv)
goto error;
// Find the right named filter entries for the defaults
const struct pl_named_filter_config *f;
for (f = pl_named_filters; f->name; f++) {
const struct pl_filter_preset *f;
for (f = pl_filter_presets; f->name; f++) {
if (p->params.upscaler == f->filter)
p->upscaler = f;
if (p->params.downscaler == f->filter)
p->downscaler = f;
}
for (f = frame_mixers; f->name; f++) {
for (f = pl_frame_mixers; f->name; f++) {
if (p->params.frame_mixer == f->filter)
p->frame_mixer = f;
}
......@@ -521,17 +514,19 @@ static void update_settings(struct plplay *p)
ui_update_input(p->ui, p->win);
const struct pl_named_filter_config *f;
const struct pl_filter_preset *f;
struct pl_render_params *par = &p->params;
if (nk_begin(nk, "Settings", nk_rect(100, 100, 600, 600), win_flags)) {
nk_layout_row_dynamic(nk, 24, 2);
nk_layout_row(nk, NK_DYNAMIC, 24, 2, (float[2]){ 0.3, 0.7 });
nk_label(nk, "Upscaler:", NK_TEXT_LEFT);
if (nk_combo_begin_label(nk, p->upscaler->name, nk_vec2(nk_widget_width(nk), 500))) {
if (nk_combo_begin_label(nk, p->upscaler->description, nk_vec2(nk_widget_width(nk), 500))) {
nk_layout_row_dynamic(nk, 16, 1);
for (f = pl_named_filters; f->name; f++) {
if (nk_combo_item_label(nk, f->name, NK_TEXT_LEFT))
for (f = pl_filter_presets; f->name; f++) {
if (!f->description)
continue;
if (nk_combo_item_label(nk, f->description, NK_TEXT_LEFT))
p->upscaler = f;
}
par->upscaler = p->upscaler->filter;
......@@ -539,16 +534,19 @@ static void update_settings(struct plplay *p)
}
nk_label(nk, "Downscaler:", NK_TEXT_LEFT);
if (nk_combo_begin_label(nk, p->downscaler->name, nk_vec2(nk_widget_width(nk), 300))) {
if (nk_combo_begin_label(nk, p->downscaler->description, nk_vec2(nk_widget_width(nk), 500))) {
nk_layout_row_dynamic(nk, 16, 1);
for (f = pl_named_filters; f->name; f++) {
if (nk_combo_item_label(nk, f->name, NK_TEXT_LEFT))
for (f = pl_filter_presets; f->name; f++) {
if (!f->description)
continue;
if (nk_combo_item_label(nk, f->description, NK_TEXT_LEFT))
p->downscaler = f;
}
par->downscaler = p->downscaler->filter;
nk_combo_end(nk);
}
nk_layout_row_dynamic(nk, 24, 2);
if (par->lut_entries) {
nk_labelf(nk, NK_TEXT_LEFT, "LUT precision: (%d)", par->lut_entries);
} else {
......@@ -560,11 +558,14 @@ static void update_settings(struct plplay *p)
nk_label(nk, "Antiringing:", NK_TEXT_LEFT);
nk_slider_float(nk, 0.0, &par->antiringing_strength, 1.0, 0.01f);
nk_layout_row(nk, NK_DYNAMIC, 24, 2, (float[2]){ 0.3, 0.7 });
nk_label(nk, "Frame mixer:", NK_TEXT_LEFT);
if (nk_combo_begin_label(nk, p->frame_mixer->name, nk_vec2(nk_widget_width(nk), 100))) {
if (nk_combo_begin_label(nk, p->frame_mixer->description, nk_vec2(nk_widget_width(nk), 300))) {
nk_layout_row_dynamic(nk, 16, 1);
for (f = frame_mixers; f->name; f++) {
if (nk_combo_item_label(nk, f->name, NK_TEXT_LEFT))
for (f = pl_frame_mixers; f->name; f++) {
if (!f->description)
continue;
if (nk_combo_item_label(nk, f->description, NK_TEXT_LEFT))
p->frame_mixer = f;
}
par->frame_mixer = p->frame_mixer->filter;
......
......@@ -2,7 +2,7 @@ project('libplacebo', ['c', 'cpp'],
license: 'LGPL2.1+',
default_options: ['c_std=c99', 'cpp_std=c++11', 'warning_level=2'],
meson_version: '>=0.51',
version: '3.119.0',
version: '3.120.0',
)
# Version number
......
......@@ -195,27 +195,27 @@ void pl_filter_free(const struct pl_filter **filter)
pl_free_ptr((void **) filter);
}
const struct pl_named_filter_function *pl_find_named_filter_function(const char *name)
const struct pl_filter_function_preset *pl_find_filter_function_preset(const char *name)
{
if (!name)
return NULL;
for (int i = 0; pl_named_filter_functions[i].function; i++) {
if (strcmp(pl_named_filter_functions[i].name, name) == 0)
return &pl_named_filter_functions[i];
for (int i = 0; pl_filter_function_presets[i].name; i++) {
if (strcmp(pl_filter_function_presets[i].name, name) == 0)
return &pl_filter_function_presets[i];
}
return NULL;
}
const struct pl_named_filter_config *pl_find_named_filter(const char *name)
const struct pl_filter_preset *pl_find_filter_preset(const char *name)
{
if (!name)
return NULL;
for (int i = 0; pl_named_filters[i].filter; i++) {
if (strcmp(pl_named_filters[i].name, name) == 0)
return &pl_named_filters[i];
for (int i = 0; pl_filter_presets[i].name; i++) {
if (strcmp(pl_filter_presets[i].name, name) == 0)
return &pl_filter_presets[i];
}
return NULL;
......@@ -492,7 +492,8 @@ const struct pl_filter_function pl_filter_function_spline64 = {
};
// Named filter functions
const struct pl_named_filter_function pl_named_filter_functions[] = {
const struct pl_filter_function_preset pl_filter_function_presets[] = {
{"none", NULL},
{"box", &pl_filter_function_box},
{"dirichlet", &pl_filter_function_box}, // alias
{"triangle", &pl_filter_function_triangle},
......@@ -519,6 +520,8 @@ const struct pl_named_filter_function pl_named_filter_functions[] = {
{0},
};
const int pl_num_filter_function_presets = PL_ARRAY_SIZE(pl_filter_function_presets) - 1;
// Built-in filter function presets
const struct pl_filter_config pl_filter_spline16 = {
.kernel = &pl_filter_function_spline16,
......@@ -595,16 +598,6 @@ const struct pl_filter_config pl_filter_ewa_hann = {
.polar = true,
};
const struct pl_filter_config pl_filter_haasnsoft = {
.kernel = &jinc3,
.window = &pl_filter_function_hann,
// The blur is tuned to equal out orthogonal and diagonal contributions
// on a regular grid. This has the effect of almost completely killing
// aliasing.
.blur = 1.11,
.polar = true,
};
// Spline family
const struct pl_filter_config pl_filter_bicubic = {
.kernel = &pl_filter_function_bicubic,
......@@ -642,30 +635,37 @@ const struct pl_filter_config pl_filter_ewa_robidouxsharp = {
};
// Named filter configs
const struct pl_named_filter_config pl_named_filters[] = {
{"spline16", &pl_filter_spline16},
{"spline36", &pl_filter_spline36},
{"spline64", &pl_filter_spline64},
{"nearest", &pl_filter_nearest},
const struct pl_filter_preset pl_filter_presets[] = {
// Highest priority / recommended filters
{"none", NULL, "Built-in sampling"},
{"bilinear", &pl_filter_bilinear, "Bilinear"},
{"nearest", &pl_filter_nearest, "Nearest neighbour"},
{"bicubic", &pl_filter_bicubic, "Bicubic"},
{"lanczos", &pl_filter_lanczos, "Lanczos"},
{"ewa_lanczos", &pl_filter_ewa_lanczos, "Jinc (EWA Lanczos)"},
{"gaussian", &pl_filter_gaussian, "Gaussian"},
{"spline16", &pl_filter_spline16, "Spline (2 taps)"},
{"spline36", &pl_filter_spline36, "Spline (3 taps)"},
{"spline64", &pl_filter_spline64, "Spline (4 taps)"},
{"mitchell", &pl_filter_mitchell, "Mitchell-Netravali"},
// Remaining filters
{"sinc", &pl_filter_sinc, "Sinc (unwindowed)"},
{"ginseng", &pl_filter_ginseng, "Ginseng (Jinc-Sinc)"},
{"ewa_jinc", &pl_filter_ewa_jinc, "EWA Jinc (unwindowed)"},
{"ewa_ginseng", &pl_filter_ewa_ginseng, "EWA Ginseng"},
{"ewa_hann", &pl_filter_ewa_hann, "EWA Hann"},
{"catmull_rom", &pl_filter_catmull_rom, "Catmull-Rom"},
{"robidoux", &pl_filter_robidoux, "Robidoux"},
{"robidouxsharp", &pl_filter_robidouxsharp, "RobidouxSharp"},
{"ewa_robidoux", &pl_filter_ewa_robidoux, "EWA Robidoux"},
{"ewa_robidouxsharp", &pl_filter_ewa_robidouxsharp, "EWA RobidouxSharp"},
// Aliases
{"box", &pl_filter_nearest}, // alias
{"bilinear", &pl_filter_bilinear},
{"triangle", &pl_filter_bilinear}, // alias
{"gaussian", &pl_filter_gaussian},
{"sinc", &pl_filter_sinc},
{"lanczos", &pl_filter_lanczos},
{"ginseng", &pl_filter_ginseng},
{"ewa_jinc", &pl_filter_ewa_jinc},
{"ewa_lanczos", &pl_filter_ewa_lanczos},
{"ewa_ginseng", &pl_filter_ewa_ginseng},
{"ewa_hann", &pl_filter_ewa_hann},
{"ewa_hanning", &pl_filter_ewa_hann}, // alias
{"haasnsoft", &pl_filter_haasnsoft},
{"bicubic", &pl_filter_bicubic},
{"catmull_rom", &pl_filter_catmull_rom},
{"mitchell", &pl_filter_mitchell},
{"robidoux", &pl_filter_robidoux},
{"robidouxsharp", &pl_filter_robidouxsharp},
{"ewa_robidoux", &pl_filter_ewa_robidoux},
{"ewa_robidouxsharp", &pl_filter_ewa_robidouxsharp},
{0},
};
const int pl_num_filter_presets = PL_ARRAY_SIZE(pl_filter_presets) - 1;
......@@ -130,18 +130,22 @@ extern const struct pl_filter_function pl_filter_function_spline16;
extern const struct pl_filter_function pl_filter_function_spline36;
extern const struct pl_filter_function pl_filter_function_spline64;
struct pl_named_filter_function {
struct pl_filter_function_preset {
const char *name;
const struct pl_filter_function *function;
};
// As a convenience, this contains a list of all supported filter function,
// terminated by a single {0} entry.
extern const struct pl_named_filter_function pl_named_filter_functions[];
// A list of built-in filter function presets, terminated by {0}
extern const struct pl_filter_function_preset pl_filter_function_presets[];
extern const int pl_num_filter_function_presets; // excluding trailing {0}
// Returns a filter function with a given name, or NULL on failure. Safe to
// call on name = NULL.
const struct pl_named_filter_function *pl_find_named_filter_function(const char *name);
// Find the filter function preset with the given name, or NULL on failure.
const struct pl_filter_function_preset *pl_find_filter_function_preset(const char *name);
// Backwards compatibility
#define pl_named_filter_function pl_filter_function_preset
#define pl_named_filter_functions pl_filter_function_presets
#define pl_find_named_filter_function pl_find_filter_function_preset
// Represents a particular configuration/combination of filter functions to
// form a filter.
......@@ -198,7 +202,6 @@ extern const struct pl_filter_config pl_filter_ewa_jinc; // unwindowed
extern const struct pl_filter_config pl_filter_ewa_lanczos; // jinc-jinc
extern const struct pl_filter_config pl_filter_ewa_ginseng; // jinc-sinc
extern const struct pl_filter_config pl_filter_ewa_hann; // jinc-hann
extern const struct pl_filter_config pl_filter_haasnsoft; // blurred ewa_hann
// Spline family
extern const struct pl_filter_config pl_filter_bicubic;
extern const struct pl_filter_config pl_filter_catmull_rom;
......@@ -213,18 +216,25 @@ extern const struct pl_filter_config pl_filter_ewa_robidouxsharp;
#define pl_filter_box pl_filter_nearest
#define pl_filter_triangle pl_filter_bilinear
struct pl_named_filter_config {
struct pl_filter_preset {
const char *name;
const struct pl_filter_config *filter;
// Longer / friendly name, or NULL for aliases
const char *description;
};
// As a convenience, this contains a list of built-in filter configurations,
// terminated by a single {0} entry.
extern const struct pl_named_filter_config pl_named_filters[];
// A list of built-in filter presets, terminated by {0}
extern const struct pl_filter_preset pl_filter_presets[];
extern const int pl_num_filter_presets; // excluding trailing {0}
// Returns a filter config with a given name, or NULL on failure. Safe to call
// on name = NULL.
const struct pl_named_filter_config *pl_find_named_filter(const char *name);
// Find the filter preset with the given name, or NULL on failure.
const struct pl_filter_preset *pl_find_filter_preset(const char *name);
// Backwards compatibility
#define pl_named_filter_config pl_filter_preset
#define pl_named_filters pl_filter_presets
#define pl_find_named_filter pl_find_filter_preset
// Parameters for filter generation.
struct pl_filter_params {
......
......@@ -258,6 +258,10 @@ extern const struct pl_render_params pl_render_high_quality_params;
// This is equivalent to (struct pl_filter_config) {0}.
extern const struct pl_filter_config pl_oversample_frame_mixer;
// A list of recommended frame mixer presets, terminated by {0}
extern const struct pl_filter_preset pl_frame_mixers[];
extern const int pl_num_frame_mixers; // excluding trailing {0}
#define PL_MAX_PLANES 4
// High level description of a single slice of an image. This basically
......
......@@ -245,6 +245,15 @@ const struct pl_render_params pl_render_high_quality_params = {
const struct pl_filter_config pl_oversample_frame_mixer = {0};
const struct pl_filter_preset pl_frame_mixers[] = {
{ "none", NULL, "No frame mixing" },
{ "oversample", &pl_oversample_frame_mixer, "Oversample (AKA SmoothMotion)" },
{ "mitchell_clamp", &pl_filter_mitchell_clamp, "Cubic spline (clamped)" },
{0}
};
const int pl_num_frame_mixers = PL_ARRAY_SIZE(pl_frame_mixers) - 1;
#define FBOFMT(n) (params->disable_fbos ? NULL : rr->fbofmt[n])
// Represents a "in-flight" image, which is either a shader that's in the
......
......@@ -3,9 +3,10 @@
int main()
{
struct pl_context *ctx = pl_test_context();
for (const struct pl_named_filter_config *conf = pl_named_filters;
conf->filter; conf++)
{
for (const struct pl_filter_preset *conf = pl_filter_presets; conf->name; conf++) {
if (!conf->filter)
continue;
struct pl_filter_params params = {
.config = *conf->filter,
.lut_entries = 128,
......
......@@ -949,9 +949,9 @@ static void pl_render_tests(const struct pl_gpu *gpu)
#define TEST_PARAMS(NAME, FIELD, LIMIT) \
TEST(NAME##_params, pl_##NAME##_params, pl_##NAME##_default_params, FIELD, LIMIT)
for (const struct pl_named_filter_config *f = pl_named_filters; f->name; f++) {
for (int i = 0; i < pl_num_filter_presets; i++) {
struct pl_render_params params = pl_render_default_params;
params.upscaler = f->filter;
params.upscaler = pl_filter_presets[i].filter;
REQUIRE(pl_render_image(rr, &image, &target, &params));
pl_gpu_flush(gpu);
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment