From 7076aa56d7a2b18d02152696f0e4082223bf86e7 Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Wed, 29 Nov 2023 14:08:03 -0800 Subject: modules/pan: implement a panning overlay module This is primarily intended for overlay use, but when not an overlay uses a little builtin seed-derived tile as a fallback. Currently the only settings are {x,y}=-1..1 for the direction vector. Speed is currently fixed to 1, the vector is always normalized. Nothing terribly exciting. --- configure.ac | 1 + src/Makefile.am | 1 + src/modules/Makefile.am | 1 + src/modules/pan/Makefile.am | 3 + src/modules/pan/pan.c | 247 ++++++++++++++++++++++++++++++++++++++++++++ src/til.c | 2 + 6 files changed, 255 insertions(+) create mode 100644 src/modules/pan/Makefile.am create mode 100644 src/modules/pan/pan.c diff --git a/configure.ac b/configure.ac index 1e68420..41c81b3 100644 --- a/configure.ac +++ b/configure.ac @@ -54,6 +54,7 @@ AC_CONFIG_FILES([ src/modules/mixer/Makefile src/modules/moire/Makefile src/modules/montage/Makefile + src/modules/pan/Makefile src/modules/pixbounce/Makefile src/modules/plasma/Makefile src/modules/plato/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index 759e921..2b629f2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -46,6 +46,7 @@ libtil_la_LIBADD = \ modules/mixer/libmixer.la \ modules/moire/libmoire.la \ modules/montage/libmontage.la \ + modules/pan/libpan.la \ modules/pixbounce/libpixbounce.la \ modules/plasma/libplasma.la \ modules/plato/libplato.la \ diff --git a/src/modules/Makefile.am b/src/modules/Makefile.am index 0a01480..12dd96a 100644 --- a/src/modules/Makefile.am +++ b/src/modules/Makefile.am @@ -11,6 +11,7 @@ SUBDIRS = \ mixer \ moire \ montage \ + pan \ pixbounce \ plasma \ plato \ diff --git a/src/modules/pan/Makefile.am b/src/modules/pan/Makefile.am new file mode 100644 index 0000000..1e5ff5b --- /dev/null +++ b/src/modules/pan/Makefile.am @@ -0,0 +1,3 @@ +noinst_LTLIBRARIES = libpan.la +libpan_la_SOURCES = pan.c +libpan_la_CPPFLAGS = -I@top_srcdir@/src diff --git a/src/modules/pan/pan.c b/src/modules/pan/pan.c new file mode 100644 index 0000000..ede0dff --- /dev/null +++ b/src/modules/pan/pan.c @@ -0,0 +1,247 @@ +#include + +#include "til.h" +#include "til_fb.h" + +/* Copyright (C) 2023 - Vito Caputo */ + +/* This implements a rudimentary panning module primarily for overlay use, + * + * TODO: + * - the minimal default tile could be more clever + * - runtime setting for panning velocity + * - optimization + * (see comments) + */ + +#define PAN_DEFAULT_Y_COMPONENT -.5 +#define PAN_DEFAULT_X_COMPONENT .25 +#define PAN_DEFAULT_TILE_SIZE 32 + +typedef struct pan_context_t { + til_module_context_t til_module_context; + + til_fb_fragment_t *snapshot; + float xoffset, yoffset; + til_fb_fragment_t tile; + uint32_t tile_buf[PAN_DEFAULT_TILE_SIZE * PAN_DEFAULT_TILE_SIZE]; +} pan_context_t; + +typedef struct pan_setup_t { + til_setup_t til_setup; + + float x, y; +} pan_setup_t; + + +static til_module_context_t * pan_create_context(const til_module_t *module, til_stream_t *stream, unsigned seed, unsigned ticks, unsigned n_cpus, til_setup_t *setup) +{ + pan_context_t *ctxt; + + ctxt = til_module_context_new(module, sizeof(pan_context_t), stream, seed, ticks, n_cpus, setup); + if (!ctxt) + return NULL; + + ctxt->tile = (til_fb_fragment_t){ + .buf = ctxt->tile_buf, + .frame_width = PAN_DEFAULT_TILE_SIZE, + .frame_height = PAN_DEFAULT_TILE_SIZE, + .width = PAN_DEFAULT_TILE_SIZE, + .height = PAN_DEFAULT_TILE_SIZE, + .pitch = PAN_DEFAULT_TILE_SIZE, + }; + + { + uint32_t color = (((uint32_t)rand_r(&seed) & 0xff) << 16) | + (((uint32_t)rand_r(&seed) & 0xff) << 8) | + ((uint32_t)rand_r(&seed) & 0xff); + + /* FIXME TODO: make a more interesting default pattern, still influenced by seed tho */ + for (int y = 0; y < PAN_DEFAULT_TILE_SIZE; y++) { + for (int x = 0; x < PAN_DEFAULT_TILE_SIZE; x++) { + uint32_t c = (((x*y & 0xff) << 16) | ((x*y & 0xff) << 8) | (x*y & 0xff)); + + ctxt->tile_buf[y * PAN_DEFAULT_TILE_SIZE + x] = color ^ c; + } + } + } + + return &ctxt->til_module_context; +} + + +static void pan_prepare_frame(til_module_context_t *context, til_stream_t *stream, unsigned ticks, til_fb_fragment_t **fragment_ptr, til_frame_plan_t *res_frame_plan) +{ + pan_context_t *ctxt = (pan_context_t *)context; + pan_setup_t *s = (pan_setup_t *)context->setup; + til_fb_fragment_t *fragment = *fragment_ptr; + float dt = (ticks - context->last_ticks) * .1f; + + ctxt->xoffset += dt * s->x; + ctxt->xoffset = fmodf(ctxt->xoffset, fragment->frame_width); + ctxt->yoffset += dt * s->y; + ctxt->yoffset = fmodf(ctxt->yoffset, fragment->frame_height); + + *res_frame_plan = (til_frame_plan_t){ .fragmenter = til_fragmenter_slice_per_cpu_x16 }; + + if (fragment->cleared) + ctxt->snapshot = til_fb_fragment_snapshot(fragment_ptr, 0); +} + + +/* like til_fb_fragment_get_pixel_clipped, but wraps around (maybe move to til_fb.h?) */ +static inline uint32_t pan_get_pixel_wrapped(til_fb_fragment_t *fragment, int x, int y) +{ + int xcoord, ycoord; + + /* The need for such casts makes me wonder if til_fragment_t.*{width,height} should just be ints, + * which annoys me because those members should never actually be negative. But without the casts, + * the modulos do the entirely wrong thing for negative coordinates. + */ + xcoord = x % (int)fragment->frame_width; + if (xcoord < 0) + xcoord += fragment->frame_width; + + ycoord = y % (int)fragment->frame_height; + if (ycoord < 0) + ycoord += fragment->frame_height; + + return til_fb_fragment_get_pixel_unchecked(fragment, xcoord, ycoord); +} + + +static void pan_render_fragment(til_module_context_t *context, til_stream_t *stream, unsigned ticks, unsigned cpu, til_fb_fragment_t **fragment_ptr) +{ + pan_context_t *ctxt = (pan_context_t *)context; + til_fb_fragment_t *fragment = *fragment_ptr; + int xoff, yoff; + til_fb_fragment_t *snapshot = ctxt->snapshot ? : &ctxt->tile; + + xoff = ctxt->xoffset; + yoff = ctxt->yoffset; + + for (int y = 0; y < fragment->height; y++) { + int ycoord = fragment->y + y + yoff; + + for (int x = 0; x < fragment->width; x++) { + int xcoord = fragment->x + x + xoff; + uint32_t pixel; + + /* TODO: optimize this to at least do contiguous row copies, + * as-is it's doing modulos per-pixel! + */ + + pixel = pan_get_pixel_wrapped(snapshot, xcoord, ycoord); + til_fb_fragment_put_pixel_unchecked(fragment, 0, fragment->x + x, fragment->y + y, pixel); + } + } + + *fragment_ptr = fragment; +} + + +static int pan_finish_frame(til_module_context_t *context, til_stream_t *stream, unsigned int ticks, til_fb_fragment_t **fragment_ptr) +{ + pan_context_t *ctxt = (pan_context_t *)context; + + if (ctxt->snapshot) + ctxt->snapshot = til_fb_fragment_reclaim(ctxt->snapshot); + + return 0; +} + + +static int pan_setup(const til_settings_t *settings, til_setting_t **res_setting, const til_setting_desc_t **res_desc, til_setup_t **res_setup); + + +til_module_t pan_module = { + .create_context = pan_create_context, + .prepare_frame = pan_prepare_frame, + .render_fragment = pan_render_fragment, + .finish_frame = pan_finish_frame, + .setup = pan_setup, + .name = "pan", + .description = "Simple panning effect (threaded)", + .author = "Vito Caputo ", + .flags = TIL_MODULE_OVERLAYABLE, +}; + + +static int pan_setup(const til_settings_t *settings, til_setting_t **res_setting, const til_setting_desc_t **res_desc, til_setup_t **res_setup) +{ + til_setting_t *x, *y; + const char *component_values[] = { + "-1", + "-.8", + "-.7", + "-.5", + "-.25", + "-.2", + "-.1", + "-.05", + "0", + ".05", + ".1", + ".2", + ".25", + ".5", + ".7", + ".8", + "1", + NULL + }; + int r; + + r = til_settings_get_and_describe_setting(settings, + &(til_setting_spec_t){ + .name = "Pan direction vector X component", + .key = "x", + .preferred = TIL_SETTINGS_STR(PAN_DEFAULT_X_COMPONENT), + .values = component_values, + .annotations = NULL + }, + &x, + res_setting, + res_desc); + if (r) + return r; + + r = til_settings_get_and_describe_setting(settings, + &(til_setting_spec_t){ + .name = "Pan direction vector Y component", + .key = "y", + .preferred = TIL_SETTINGS_STR(PAN_DEFAULT_Y_COMPONENT), + .values = component_values, + .annotations = NULL + }, + &y, + res_setting, + res_desc); + if (r) + return r; + + if (res_setup) { + pan_setup_t *setup; + float l; + + setup = til_setup_new(settings, sizeof(*setup), NULL, &pan_module); + if (!setup) + return -ENOMEM; + + if (sscanf(x->value, "%f", &setup->x) != 1) + return til_setup_free_with_failed_setting_ret_err(&setup->til_setup, x, res_setting, -EINVAL); + + if (sscanf(y->value, "%f", &setup->y) != 1) + return til_setup_free_with_failed_setting_ret_err(&setup->til_setup, y, res_setting, -EINVAL); + + l = sqrtf(setup->x * setup->x + setup->y * setup->y); + if (l) { + setup->x /= l; + setup->y /= l; + } + + *res_setup = &setup->til_setup; + } + + return 0; +} diff --git a/src/til.c b/src/til.c index a74ff86..b773e99 100644 --- a/src/til.c +++ b/src/til.c @@ -39,6 +39,7 @@ extern til_module_t meta2d_module; extern til_module_t mixer_module; extern til_module_t moire_module; extern til_module_t montage_module; +extern til_module_t pan_module; extern til_module_t pixbounce_module; extern til_module_t plasma_module; extern til_module_t plato_module; @@ -80,6 +81,7 @@ static const til_module_t *modules[] = { &mixer_module, &moire_module, &montage_module, + &pan_module, &pixbounce_module, &plasma_module, &plato_module, -- cgit v1.2.1