summaryrefslogtreecommitdiff
path: root/modules/roto/roto.c
diff options
context:
space:
mode:
authorVito Caputo <vcaputo@gnugeneration.com>2016-12-30 20:39:40 -0800
committerVito Caputo <vcaputo@gnugeneration.com>2016-12-30 20:49:57 -0800
commit7f8b26c6c317b1a4d020b2bb2569f8b1b552d8e8 (patch)
treed2657ca8ba2c28f630fe97195073658008645db2 /modules/roto/roto.c
parent819ca7efa92843a741d3dbc3d77fc39b6f3e5dae (diff)
roto: bilinear interpolation of texture colors
This implements anti-aliasing, no more jaggies! Still 100% software rendering, fixed point arithmetic. Maybe add zooming with mipmaps next?
Diffstat (limited to 'modules/roto/roto.c')
-rw-r--r--modules/roto/roto.c131
1 files changed, 100 insertions, 31 deletions
diff --git a/modules/roto/roto.c b/modules/roto/roto.c
index 1e06be0..bf92df4 100644
--- a/modules/roto/roto.c
+++ b/modules/roto/roto.c
@@ -18,6 +18,87 @@
#define FIXED_NEW(_i) ((_i) << FIXED_BITS)
#define FIXED_TO_INT(_f) ((_f) >> FIXED_BITS)
+typedef struct color_t {
+ int r, g, b;
+} color_t;
+
+
+/* linearly interpolate between two colors, alpha is fixed-point value 0-FIXED_EXP. */
+static inline color_t lerp_color(color_t *a, color_t *b, int alpha)
+{
+ color_t c = {
+ .r = a->r + FIXED_MULT(alpha, b->r - a->r),
+ .g = a->g + FIXED_MULT(alpha, b->g - a->g),
+ .b = a->b + FIXED_MULT(alpha, b->b - a->b),
+ };
+
+ return c;
+}
+
+
+/* Return the bilinearly interpolated color palette[texture[ty][tx]] (Anti-Aliasing) */
+/* tx, ty are fixed-point for fractions, palette colors are also in fixed-point format. */
+static uint32_t bilerp_color(uint8_t texture[256][256], color_t *palette, int tx, int ty)
+{
+ uint8_t itx = FIXED_TO_INT(tx), ity = FIXED_TO_INT(ty);
+ color_t n_color, s_color, color;
+ int x_alpha, y_alpha;
+ uint8_t nw, ne, sw, se;
+
+ /* We need the 4 texels constituting a 2x2 square pattern to interpolate.
+ * A point tx,ty can only intersect one texel; one corner of the 2x2 square.
+ * Where relative to the corner's center the intersection occurs determines which corner has been intersected,
+ * and the other corner texels may then be addressed relative to that corner.
+ * Alpha values must also be determined for both axis, these values describe the position between
+ * the 2x2 texel centers the intersection occurred, aka the weight or bias.
+ * Once the two alpha values are known, linear interpolation between the texel colors is trivial.
+ */
+
+ if ((ty & FIXED_MASK) > (FIXED_EXP >> 1)) {
+ y_alpha = ty & (FIXED_MASK >> 1);
+
+ if ((tx & (FIXED_MASK)) > (FIXED_EXP >> 1)) {
+ nw = texture[ity][itx];
+ ne = texture[ity][(uint8_t)(itx + 1)];
+ sw = texture[(uint8_t)(ity + 1)][itx];
+ se = texture[(uint8_t)(ity + 1)][(uint8_t)(itx + 1)];
+
+ x_alpha = tx & (FIXED_MASK >> 1);
+ } else {
+ ne = texture[ity][itx];
+ nw = texture[ity][(uint8_t)(itx - 1)];
+ se = texture[(uint8_t)(ity + 1)][itx];
+ sw = texture[(uint8_t)(ity + 1)][(uint8_t)(itx - 1)];
+
+ x_alpha = (FIXED_EXP >> 1) + (tx & (FIXED_MASK >> 1));
+ }
+ } else {
+ y_alpha = (FIXED_EXP >> 1) + (ty & (FIXED_MASK >> 1));
+
+ if ((tx & (FIXED_MASK)) > (FIXED_EXP >> 1)) {
+ sw = texture[ity][itx];
+ se = texture[ity][(uint8_t)(itx + 1)];
+ nw = texture[(uint8_t)(ity - 1)][itx];
+ ne = texture[(uint8_t)(ity - 1)][(uint8_t)(itx + 1)];
+
+ x_alpha = tx & (FIXED_MASK >> 1);
+ } else {
+ se = texture[ity][itx];
+ sw = texture[ity][(uint8_t)(itx - 1)];
+ ne = texture[(uint8_t)(ity - 1)][itx];
+ nw = texture[(uint8_t)(ity - 1)][(uint8_t)(itx - 1)];
+
+ x_alpha = (FIXED_EXP >> 1) + (tx & (FIXED_MASK >> 1));
+ }
+ }
+
+ n_color = lerp_color(&palette[nw], &palette[ne], x_alpha);
+ s_color = lerp_color(&palette[sw], &palette[se], x_alpha);
+ color = lerp_color(&n_color, &s_color, y_alpha);
+
+ return (FIXED_TO_INT(color.r) << 16) | (FIXED_TO_INT(color.g) << 8) | FIXED_TO_INT(color.b);
+}
+
static void init_roto(uint8_t texture[256][256], int32_t *costab, int32_t *sintab)
{
@@ -53,12 +134,11 @@ 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 color_t palette[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) {
@@ -73,13 +153,13 @@ static void roto32(fb_fragment_t *fragment)
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));
+ palette[0].r = (FIXED_MULT(FIXED_COS(rr), FIXED_NEW(127)) + FIXED_NEW(128));
+ palette[0].g = (FIXED_MULT(FIXED_SIN(rr / 2), FIXED_NEW(127)) + FIXED_NEW(128));
+ palette[0].b = (FIXED_MULT(FIXED_COS(rr / 3), FIXED_NEW(127)) + FIXED_NEW(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) );
+ palette[1].r = (FIXED_MULT(FIXED_SIN(rr / 2), FIXED_NEW(127)) + FIXED_NEW(128));
+ palette[1].g = (FIXED_MULT(FIXED_COS(rr / 2), FIXED_NEW(127)) + FIXED_NEW(128));
+ palette[1].b = (FIXED_MULT(FIXED_SIN(rr), FIXED_NEW(127)) + FIXED_NEW(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. */
@@ -95,11 +175,7 @@ static void roto32(fb_fragment_t *fragment)
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]];
+ *buf = bilerp_color(texture, palette, x_sin_r - y_cos_r, y_sin_r + x_cos_r);
x_cos_r += cos_r;
x_sin_r += sin_r;
@@ -122,12 +198,11 @@ 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 color_t palette[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) {
@@ -142,13 +217,13 @@ static void roto64(fb_fragment_t *fragment)
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));
+ palette[0].r = (FIXED_MULT(FIXED_COS(rr), FIXED_NEW(127)) + FIXED_NEW(128));
+ palette[0].g = (FIXED_MULT(FIXED_SIN(rr / 2), FIXED_NEW(127)) + FIXED_NEW(128));
+ palette[0].b = (FIXED_MULT(FIXED_COS(rr / 3), FIXED_NEW(127)) + FIXED_NEW(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) );
+ palette[1].r = (FIXED_MULT(FIXED_SIN(rr / 2), FIXED_NEW(127)) + FIXED_NEW(128));
+ palette[1].g = (FIXED_MULT(FIXED_COS(rr / 2), FIXED_NEW(127)) + FIXED_NEW(128));
+ palette[1].b = (FIXED_MULT(FIXED_SIN(rr), FIXED_NEW(127)) + FIXED_NEW(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. */
@@ -168,19 +243,13 @@ static void roto64(fb_fragment_t *fragment)
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]];
+ p = bilerp_color(texture, palette, x_sin_r - y_cos_r, y_sin_r + x_cos_r);
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)(bilerp_color(texture, palette, x_sin_r - y_cos_r, y_sin_r + x_cos_r)) << 32;
- p |= (uint64_t)colors[texture[ty][tx]] << 32;
-
*buf = p;
x_cos_r += cos_r;
@@ -201,7 +270,7 @@ static void roto64(fb_fragment_t *fragment)
rototiller_renderer_t roto32_renderer = {
.render = roto32,
.name = "roto32",
- .description = "Tiled texture rotation (32-bit)",
+ .description = "Anti-aliased tiled texture rotation (32-bit)",
.author = "Vito Caputo <vcaputo@pengaru.com>",
.license = "GPLv2",
};
@@ -210,7 +279,7 @@ rototiller_renderer_t roto32_renderer = {
rototiller_renderer_t roto64_renderer = {
.render = roto64,
.name = "roto64",
- .description = "Tiled texture rotation (64-bit)",
+ .description = "Anti-aliased tiled texture rotation (64-bit)",
.author = "Vito Caputo <vcaputo@pengaru.com>",
.license = "GPLv2",
};
© All Rights Reserved