diff options
Diffstat (limited to 'src/modules/compose')
-rw-r--r-- | src/modules/compose/Makefile.am | 3 | ||||
-rw-r--r-- | src/modules/compose/compose.c | 177 |
2 files changed, 180 insertions, 0 deletions
diff --git a/src/modules/compose/Makefile.am b/src/modules/compose/Makefile.am new file mode 100644 index 0000000..926db7e --- /dev/null +++ b/src/modules/compose/Makefile.am @@ -0,0 +1,3 @@ +noinst_LIBRARIES = libcompose.a +libcompose_a_SOURCES = compose.c +libcompose_a_CPPFLAGS = -I@top_srcdir@/src -I@top_srcdir@/src/libs diff --git a/src/modules/compose/compose.c b/src/modules/compose/compose.c new file mode 100644 index 0000000..dd85c93 --- /dev/null +++ b/src/modules/compose/compose.c @@ -0,0 +1,177 @@ +#include <stdlib.h> +#include <time.h> + +#include "fb.h" +#include "rototiller.h" +#include "settings.h" +#include "txt/txt.h" +#include "util.h" + +/* Copyright (C) 2020 - Vito Caputo <vcaputo@pengaru.com> */ + +/* This implements a rudimentary compositing module for layering + * the output from other modules into a single frame. + */ + +/* some TODOs: + * - support randomizing settings and context resets, configurable + * - maybe add a way for the user to supply the settings on the cli + * for the composed layers. That might actually need to be a more + * general solution in the top-level rototiller code, since the + * other meta modules like montage and rtv could probably benefit + * from the ability to feed in settings to the underlying modules. + */ + +typedef struct compose_layer_t { + const rototiller_module_t *module; + void *module_ctxt; + char *settings; +} compose_layer_t; + +typedef struct compose_context_t { + unsigned n_cpus; + + size_t n_layers; + compose_layer_t layers[]; +} compose_context_t; + +static void * compose_create_context(unsigned ticks, unsigned num_cpus); +static void compose_destroy_context(void *context); +static void compose_prepare_frame(void *context, unsigned ticks, unsigned n_cpus, fb_fragment_t *fragment, rototiller_fragmenter_t *res_fragmenter); +static int compose_setup(const settings_t *settings, setting_desc_t **next_setting); + +static char **compose_layers; + + +rototiller_module_t compose_module = { + .create_context = compose_create_context, + .destroy_context = compose_destroy_context, + .prepare_frame = compose_prepare_frame, + .name = "compose", + .description = "Layered Modules Compositor", + .author = "Vito Caputo <vcaputo@pengaru.com>", + .license = "GPLv2", + .setup = compose_setup, +}; + + +static void * compose_create_context(unsigned ticks, unsigned num_cpus) +{ + compose_context_t *ctxt; + int n; + + for (n = 0; compose_layers[n]; n++); + + ctxt = calloc(1, sizeof(compose_context_t) + n * sizeof(compose_layer_t)); + if (!ctxt) + return NULL; + + ctxt->n_cpus = num_cpus; + + for (int i = 0; i < n; i++) { + const rototiller_module_t *module; + + module = rototiller_lookup_module(compose_layers[i]); + + ctxt->layers[i].module = module; + if (module->create_context) + ctxt->layers[i].module_ctxt = module->create_context(ticks, num_cpus); + + ctxt->n_layers++; + } + + return ctxt; +} + + +static void compose_destroy_context(void *context) +{ + compose_context_t *ctxt = context; + + for (int i = 0; i < ctxt->n_layers; i++) { + if (ctxt->layers[i].module_ctxt) + ctxt->layers[i].module->destroy_context(ctxt->layers[i].module_ctxt); + } + free(context); +} + + +static void compose_prepare_frame(void *context, unsigned ticks, unsigned n_cpus, fb_fragment_t *fragment, rototiller_fragmenter_t *res_fragmenter) +{ + compose_context_t *ctxt = context; + + fb_fragment_zero(fragment); + + for (int i = 0; i < ctxt->n_layers; i++) + rototiller_module_render(ctxt->layers[i].module, ctxt->layers[i].module_ctxt, ticks, fragment); +} + + +static int compose_setup(const settings_t *settings, setting_desc_t **next_setting) +{ + const char *layers; + + layers = settings_get_value(settings, "layers"); + if (!layers) { + int r; + + r = setting_desc_clone(&(setting_desc_t){ + .name = "Colon-Separated List Of Module Layers, In Draw Order", + .key = "layers", + .preferred = "drizzle:stars:spiro", + .annotations = NULL + }, next_setting); + if (r < 0) + return r; + + return 1; + } + + /* turn layers colon-separated list into a null-terminated array of strings */ + { + const rototiller_module_t **modules; + size_t n_modules; + char *toklayers, *layer; + int n = 2; + + rototiller_get_modules(&modules, &n_modules); + + toklayers = strdup(layers); + if (!toklayers) + return -ENOMEM; + + layer = strtok(toklayers, ":"); + do { + char **new; + size_t i; + + /* other meta-modules like montage and rtv may need to + * have some consideration here, but for now I'm just + * going to let the user potentially compose with montage + * or rtv as one of the layers. + */ + if (!strcmp(layer, "compose")) /* XXX: prevent infinite recursion */ + return -EINVAL; + + for (i = 0; i < n_modules; i++) { + if (!strcmp(layer, modules[i]->name)) + break; + } + + if (i >= n_modules) + return -EINVAL; + + new = realloc(compose_layers, n * sizeof(*compose_layers)); + if (!new) + return -ENOMEM; + + new[n - 2] = layer; + new[n - 1] = NULL; + n++; + + compose_layers = new; + } while (layer = strtok(NULL, ":")); + } + + return 0; +} |