/* * Copyright (C) 2022 - Vito Caputo - * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "til.h" #include "til_fb.h" #define CHECKERS_DEFAULT_SIZE 32 #define CHECKERS_DEFAULT_PATTERN CHECKERS_PATTERN_CHECKERED #define CHECKERS_DEFAULT_DYNAMICS CHECKERS_DYNAMICS_ODD #define CHECKERS_DEFAULT_DYNAMICS_RATE 1.0 #define CHECKERS_DEFAULT_SAMPLED 0 #define CHECKERS_DEFAULT_COLOR 0xffffffff typedef enum checkers_pattern_t { CHECKERS_PATTERN_CHECKERED, CHECKERS_PATTERN_RANDOM, } checkers_pattern_t; typedef enum checkers_dynamics_t { CHECKERS_DYNAMICS_ODD, CHECKERS_DYNAMICS_EVEN, CHECKERS_DYNAMICS_ALTERNATING, CHECKERS_DYNAMICS_RANDOM, CHECKERS_DYNAMICS_SONAR, } checkers_dynamics_t; typedef struct checkers_setup_t { til_setup_t til_setup; unsigned size; checkers_pattern_t pattern; checkers_dynamics_t dynamics; float rate; uint32_t color; unsigned sampled:1; } checkers_setup_t; typedef struct checkers_context_t { unsigned tickoff; checkers_setup_t setup; } checkers_context_t; static checkers_setup_t checkers_default_setup = { .size = CHECKERS_DEFAULT_SIZE, .pattern = CHECKERS_DEFAULT_PATTERN, .dynamics = CHECKERS_DEFAULT_DYNAMICS, .rate = CHECKERS_DEFAULT_DYNAMICS_RATE, .sampled = CHECKERS_DEFAULT_SAMPLED, .color = CHECKERS_DEFAULT_COLOR, }; static void * checkers_create_context(unsigned ticks, unsigned n_cpus, til_setup_t *setup) { checkers_context_t *ctxt; if (!setup) setup = &checkers_default_setup.til_setup; ctxt = calloc(1, sizeof(checkers_context_t)); if (!ctxt) return NULL; ctxt->setup = *(checkers_setup_t *)setup; ctxt->tickoff = ticks; return ctxt; } static void checkers_destroy_context(void *context) { free(context); } static int checkers_fragmenter(void *context, unsigned n_cpus, const til_fb_fragment_t *fragment, unsigned number, til_fb_fragment_t *res_fragment) { checkers_context_t *ctxt = context; return til_fb_fragment_tile_single(fragment, ctxt->setup.size, number, res_fragment); } static void checkers_prepare_frame(void *context, unsigned ticks, unsigned n_cpus, til_fb_fragment_t *fragment, til_fragmenter_t *res_fragmenter) { *res_fragmenter = checkers_fragmenter; } static inline unsigned hash(unsigned x) { x = ((x >> 16) ^ x) * 0x61C88647; x = ((x >> 16) ^ x) * 0x61C88647; x = ((x >> 16) ^ x) * 0x61C88647; x = (x >> 16) ^ x; return x; } static void checkers_render_fragment(void *context, unsigned ticks, unsigned cpu, til_fb_fragment_t *fragment) { checkers_context_t *ctxt = context; int state; switch (ctxt->setup.pattern) { case CHECKERS_PATTERN_CHECKERED: { unsigned tiles_per_row; tiles_per_row = fragment->frame_width / ctxt->setup.size; if (tiles_per_row * ctxt->setup.size < fragment->frame_width) tiles_per_row++; state = (fragment->number + (fragment->y / ctxt->setup.size) * !(tiles_per_row & 0x1)) & 0x1; break; } case CHECKERS_PATTERN_RANDOM: state = hash(fragment->number * 0x61C88647) & 0x1; break; } switch (ctxt->setup.dynamics) { case CHECKERS_DYNAMICS_ODD: break; case CHECKERS_DYNAMICS_EVEN: state = ~state & 0x1; break; case CHECKERS_DYNAMICS_ALTERNATING: state ^= ((unsigned)((float)ticks * ctxt->setup.rate) & 0x1); break; case CHECKERS_DYNAMICS_RANDOM: /* note: the big multiply here is just to get up out of the low bits */ state &= hash(fragment->number * 0x61C88647 + (unsigned)((float)ticks * ctxt->setup.rate)) & 0x1; break; case CHECKERS_DYNAMICS_SONAR: { float t = (float)((ticks - ctxt->tickoff) % 1000) * .001f * 2; float dist_sq, x, y; x = fragment->x + (fragment->width >> 1); y = fragment->y + (fragment->height >> 1); x = x / (float)fragment->frame_width * 2.f - 1.f; y = y / (float)fragment->frame_height * 2.f - 1.f; dist_sq = x * x + y * y; t *= t; state &= (dist_sq > (t - .1f) && dist_sq < (t + .1f)); break; } } if (!state) til_fb_fragment_clear(fragment); else { uint32_t color = ctxt->setup.color; if (ctxt->setup.sampled && fragment->cleared) color = til_fb_fragment_get_pixel_unchecked(fragment, fragment->x + (fragment->width >> 1), fragment->y + (fragment->height >> 1)); til_fb_fragment_fill(fragment, TIL_FB_DRAW_FLAG_TEXTURABLE, color); } } static int checkers_setup(const til_settings_t *settings, til_setting_t **res_setting, const til_setting_desc_t **res_desc, til_setup_t **res_setup) { const char *size; const char *pattern; const char *dynamics; const char *dynamics_rate; const char *sampled; const char *size_values[] = { "4", "8", "16", "32", "64", "128", NULL }; const char *pattern_values[] = { "checkered", "random", NULL }; const char *dynamics_values[] = { "odd", "even", "alternating", "random", "sonar", NULL }; const char *dynamics_rate_values[] = { "1.0", ".75", ".5", ".25", ".1", ".01", ".001", ".0001", NULL }; const char *bool_values[] = { "off", "on", NULL }; int r; r = til_settings_get_and_describe_value(settings, &(til_setting_desc_t){ .name = "Checker size", .key = "size", .regex = "\\.[0-9]+", .preferred = TIL_SETTINGS_STR(CHECKERS_DEFAULT_SIZE), .values = size_values, .annotations = NULL }, &size, res_setting, res_desc); if (r) return r; r = til_settings_get_and_describe_value(settings, &(til_setting_desc_t){ .name = "Checkers pattern", .key = "pattern", .preferred = pattern_values[0], .values = pattern_values, .annotations = NULL }, &pattern, res_setting, res_desc); if (r) return r; r = til_settings_get_and_describe_value(settings, &(til_setting_desc_t){ .name = "Checkers dynamics", .key = "dynamics", .preferred = dynamics_values[0], .values = dynamics_values, .annotations = NULL }, &dynamics, res_setting, res_desc); if (r) return r; if (strcasecmp(dynamics, "odd") && strcasecmp(dynamics, "even")) { r = til_settings_get_and_describe_value(settings, &(til_setting_desc_t){ .name = "Checkers dynamics rate", .key = "dynamics_rate", .preferred = dynamics_rate_values[0], .values = dynamics_rate_values, .annotations = NULL }, &dynamics_rate, res_setting, res_desc); if (r) return r; } r = til_settings_get_and_describe_value(settings, &(til_setting_desc_t){ .name = "Sample checker colors when overlayed", .key = "sampled", .preferred = bool_values[CHECKERS_DEFAULT_SAMPLED], .values = bool_values, .annotations = NULL }, &sampled, res_setting, res_desc); if (r) return r; if (res_setup) { checkers_setup_t *setup; setup = til_setup_new(sizeof(*setup), (void(*)(til_setup_t *))free); if (!setup) return -ENOMEM; sscanf(size, "%u", &setup->size); if (!strcmp(pattern, "checkered")) setup->pattern = CHECKERS_PATTERN_CHECKERED; else if (!strcmp(pattern, "random")) setup->pattern = CHECKERS_PATTERN_RANDOM; else { free(setup); return -EINVAL; } if (!strcmp(dynamics, "odd")) setup->dynamics = CHECKERS_DYNAMICS_ODD; else if (!strcmp(dynamics, "even")) setup->dynamics = CHECKERS_DYNAMICS_EVEN; else if (!strcmp(dynamics, "alternating")) setup->dynamics = CHECKERS_DYNAMICS_ALTERNATING; else if (!strcmp(dynamics, "random")) setup->dynamics = CHECKERS_DYNAMICS_RANDOM; else if (!strcmp(dynamics, "sonar")) setup->dynamics = CHECKERS_DYNAMICS_SONAR; else { free(setup); return -EINVAL; } if (setup->dynamics != CHECKERS_DYNAMICS_ODD && setup->dynamics != CHECKERS_DYNAMICS_EVEN) sscanf(dynamics_rate, "%f", &setup->rate); /* FIXME TODO: I wonder if sampled should be more like a color mode {color, randomized, sampled, textured} * with the til_fb_fragment_* putters adding a flag for allowing texturing. * the problem here is that if a texture isn't made available on the fb, we wouldn't still be doing things * like sampling or randomizing the colors. They should both be occurring, maybe there needs to be a separate * field for controlling wether texturing is permitted, not part of color mode. like texturing=on,off */ if (!strcasecmp(sampled, "on")) setup->sampled = 1; /* XXX FIXME TODO: color setting, parse web-style #rrggbb? */ /* XXX FIXME TODO: color setting, parse web-style #rrggbb? */ /* XXX FIXME TODO: color setting, parse web-style #rrggbb? */ /* XXX FIXME TODO: color setting, parse web-style #rrggbb? */ setup->color = CHECKERS_DEFAULT_COLOR; *res_setup = &setup->til_setup; } return 0; } til_module_t checkers_module = { .create_context = checkers_create_context, .destroy_context = checkers_destroy_context, .prepare_frame = checkers_prepare_frame, .render_fragment = checkers_render_fragment, .setup = checkers_setup, .name = "checkers", .description = "Checker-patterned overlay (threaded)", .author = "Vito Caputo ", .flags = TIL_MODULE_OVERLAYABLE, };