summaryrefslogtreecommitdiff
path: root/src/modules/compose/compose.c
diff options
context:
space:
mode:
authorVito Caputo <vcaputo@pengaru.com>2020-09-25 20:50:22 -0700
committerVito Caputo <vcaputo@pengaru.com>2020-09-25 20:57:27 -0700
commit3fc9031e6b10f6accceb7c00f368e34045bbaca7 (patch)
treef3e12be29519e15f33e61eb73bcc529a403dfc52 /src/modules/compose/compose.c
parentd9db2680298c01a82b768dc19e75bf4f2ef1b56e (diff)
modules/compose: add a rudimentary compositing module
--module=compose,layers=first:second:third:... this draws the named modules in the order listed, overdrawing the output of the previous layers in a cumulative fashion.
Diffstat (limited to 'src/modules/compose/compose.c')
-rw-r--r--src/modules/compose/compose.c177
1 files changed, 177 insertions, 0 deletions
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;
+}
© All Rights Reserved