#include #include #include #include "fb.h" #include "rototiller.h" /* Copyright (C) 2016 Vito Caputo */ /* Some defines for the fixed-point stuff in render(). */ #define FIXED_TRIG_LUT_SIZE 4096 /* size of the cos/sin look-up tables */ #define FIXED_BITS 11 /* fractional bits */ #define FIXED_EXP (1 << FIXED_BITS) /* 2^FIXED_BITS */ #define FIXED_MASK (FIXED_EXP - 1) /* fractional part mask */ #define FIXED_COS(_rad) costab[(_rad) % FIXED_TRIG_LUT_SIZE] #define FIXED_SIN(_rad) sintab[(_rad) % FIXED_TRIG_LUT_SIZE] #define FIXED_MULT(_a, _b) (((_a) * (_b)) >> FIXED_BITS) #define FIXED_NEW(_i) ((_i) << FIXED_BITS) #define FIXED_TO_INT(_f) ((_f) >> FIXED_BITS) static void init_roto(uint8_t texture[256][256], int32_t *costab, int32_t *sintab) { int x, y, i; /* Generate simple checker pattern texture, nothing clever, feel free to play! */ /* If you modify texture on every frame instead of only @ initialization you can * produce some neat output. These values are indexed into palette[] below. */ for (y = 0; y < 128; y++) { for (x = 0; x < 128; x++) texture[y][x] = 1; for (; x < 256; x++) texture[y][x] = 0; } for (; y < 256; y++) { for (x = 0; x < 128; x++) texture[y][x] = 0; for (; x < 256; x++) texture[y][x] = 1; } /* Generate fixed-point cos & sin LUTs. */ for (i = 0; i < FIXED_TRIG_LUT_SIZE; i++) { costab[i] = ((cos((double)2*M_PI*i/FIXED_TRIG_LUT_SIZE))*FIXED_EXP); sintab[i] = ((sin((double)2*M_PI*i/FIXED_TRIG_LUT_SIZE))*FIXED_EXP); } } /* Draw a rotating checkered 256x256 texture into fragment. (32-bit version) */ static void roto32(fb_fragment_t *fragment) { static int32_t costab[FIXED_TRIG_LUT_SIZE], sintab[FIXED_TRIG_LUT_SIZE]; static uint8_t texture[256][256]; static int initialized; static uint32_t colors[2]; static unsigned r, rr; int y_cos_r, y_sin_r, x_cos_r, x_sin_r, x_cos_r_init, x_sin_r_init, cos_r, sin_r; int x, y, stride = fragment->stride / 4, width = fragment->width, height = fragment->height; uint8_t tx, ty; /* 256x256 texture; 8 bit texture indices to modulo via overflow. */ uint32_t *buf = fragment->buf; if (!initialized) { initialized = 1; init_roto(texture, costab, sintab); } /* This is all done using fixed-point in the hopes of being faster, and yes assumptions * are being made WRT the overflow of tx/ty as well, only tested on x86_64. */ cos_r = FIXED_COS(r); sin_r = FIXED_SIN(r); /* Vary the colors, this is just a mashup of sinusoidal rgb values. */ colors[0] = ((FIXED_TO_INT(FIXED_MULT(FIXED_COS(rr), FIXED_NEW(127))) + 128) << 16) | ((FIXED_TO_INT(FIXED_MULT(FIXED_SIN(rr / 2), FIXED_NEW(127))) + 128) << 8) | ((FIXED_TO_INT(FIXED_MULT(FIXED_COS(rr / 3), FIXED_NEW(127))) + 128)); colors[1] = ((FIXED_TO_INT(FIXED_MULT(FIXED_SIN(rr / 2), FIXED_NEW(127))) + 128) << 16) | ((FIXED_TO_INT(FIXED_MULT(FIXED_COS(rr / 2), FIXED_NEW(127))) + 128)) << 8 | ((FIXED_TO_INT(FIXED_MULT(FIXED_SIN(rr), FIXED_NEW(127))) + 128) ); /* The dimensions are cut in half and negated to center the rotation. */ /* The [xy]_{sin,cos}_r variables are accumulators to replace multiplication with addition. */ x_cos_r_init = FIXED_MULT(-FIXED_NEW((width / 2)), cos_r); x_sin_r_init = FIXED_MULT(-FIXED_NEW((width / 2)), sin_r); y_cos_r = FIXED_MULT(-FIXED_NEW((height / 2)), cos_r); y_sin_r = FIXED_MULT(-FIXED_NEW((height / 2)), sin_r); for (y = 0; y < height; y++) { x_cos_r = x_cos_r_init; x_sin_r = x_sin_r_init; for (x = 0; x < width; x++, buf++) { tx = FIXED_TO_INT(x_sin_r - y_cos_r); ty = FIXED_TO_INT(y_sin_r + x_cos_r); *buf = colors[texture[ty][tx]]; x_cos_r += cos_r; x_sin_r += sin_r; } buf += stride; y_cos_r += cos_r; y_sin_r += sin_r; } // This governs the rotation and color cycle. r += FIXED_TO_INT(FIXED_MULT(FIXED_SIN(rr), FIXED_NEW(16))); rr += 2; } /* Draw a rotating checkered 256x256 texture into fragment. (64-bit version) */ static void roto64(fb_fragment_t *fragment) { static int32_t costab[FIXED_TRIG_LUT_SIZE], sintab[FIXED_TRIG_LUT_SIZE]; static uint8_t texture[256][256]; static int initialized; static uint32_t colors[2]; static unsigned r, rr; int y_cos_r, y_sin_r, x_cos_r, x_sin_r, x_cos_r_init, x_sin_r_init, cos_r, sin_r; int x, y, stride = fragment->stride / 8, width = fragment->width, height = fragment->height; uint8_t tx, ty; /* 256x256 texture; 8 bit texture indices to modulo via overflow. */ uint64_t *buf = (uint64_t *)fragment->buf; if (!initialized) { initialized = 1; init_roto(texture, costab, sintab); } /* This is all done using fixed-point in the hopes of being faster, and yes assumptions * are being made WRT the overflow of tx/ty as well, only tested on x86_64. */ cos_r = FIXED_COS(r); sin_r = FIXED_SIN(r); /* Vary the colors, this is just a mashup of sinusoidal rgb values. */ colors[0] = ((FIXED_TO_INT(FIXED_MULT(FIXED_COS(rr), FIXED_NEW(127))) + 128) << 16) | ((FIXED_TO_INT(FIXED_MULT(FIXED_SIN(rr / 2), FIXED_NEW(127))) + 128) << 8) | ((FIXED_TO_INT(FIXED_MULT(FIXED_COS(rr / 3), FIXED_NEW(127))) + 128)); colors[1] = ((FIXED_TO_INT(FIXED_MULT(FIXED_SIN(rr / 2), FIXED_NEW(127))) + 128) << 16) | ((FIXED_TO_INT(FIXED_MULT(FIXED_COS(rr / 2), FIXED_NEW(127))) + 128)) << 8 | ((FIXED_TO_INT(FIXED_MULT(FIXED_SIN(rr), FIXED_NEW(127))) + 128) ); /* The dimensions are cut in half and negated to center the rotation. */ /* The [xy]_{sin,cos}_r variables are accumulators to replace multiplication with addition. */ x_cos_r_init = FIXED_MULT(-FIXED_NEW((width / 2)), cos_r); x_sin_r_init = FIXED_MULT(-FIXED_NEW((width / 2)), sin_r); y_cos_r = FIXED_MULT(-FIXED_NEW((height / 2)), cos_r); y_sin_r = FIXED_MULT(-FIXED_NEW((height / 2)), sin_r); width /= 2; /* Since we're processing 64-bit words (2 pixels) at a time */ for (y = 0; y < height; y++) { x_cos_r = x_cos_r_init; x_sin_r = x_sin_r_init; for (x = 0; x < width; x++, buf++) { uint64_t p; tx = FIXED_TO_INT(x_sin_r - y_cos_r); ty = FIXED_TO_INT(y_sin_r + x_cos_r); p = colors[texture[ty][tx]]; x_cos_r += cos_r; x_sin_r += sin_r; tx = FIXED_TO_INT(x_sin_r - y_cos_r); ty = FIXED_TO_INT(y_sin_r + x_cos_r); p |= (uint64_t)colors[texture[ty][tx]] << 32; *buf = p; x_cos_r += cos_r; x_sin_r += sin_r; } buf += stride; y_cos_r += cos_r; y_sin_r += sin_r; } // This governs the rotation and color cycle. r += FIXED_TO_INT(FIXED_MULT(FIXED_SIN(rr), FIXED_NEW(16))); rr += 2; } rototiller_renderer_t roto32_renderer = { .render = roto32, .name = "roto32", .description = "Tiled texture rotation (32-bit)", .author = "Vito Caputo ", .license = "GPLv2", }; rototiller_renderer_t roto64_renderer = { .render = roto64, .name = "roto64", .description = "Tiled texture rotation (64-bit)", .author = "Vito Caputo ", .license = "GPLv2", };