From 330fd85db8c89c178621d978929d911bbe93fec7 Mon Sep 17 00:00:00 2001 From: pacien Date: Thu, 28 Dec 2017 01:23:08 +0100 Subject: Refactor canvas blender into rasterizer Signed-off-by: pacien --- include/blender/blender.h | 26 -------------- include/painter/rasterizer.h | 41 +++++++++++++++++++++ src/blender/blender.c | 39 -------------------- src/painter/rasterizer.c | 86 ++++++++++++++++++++++++++++++++++++++++++++ test/blender/blender.c | 29 --------------- test/painter/rasterizer.c | 27 ++++++++++++++ 6 files changed, 154 insertions(+), 94 deletions(-) delete mode 100644 include/blender/blender.h create mode 100644 include/painter/rasterizer.h delete mode 100644 src/blender/blender.c create mode 100644 src/painter/rasterizer.c delete mode 100644 test/blender/blender.c create mode 100644 test/painter/rasterizer.c diff --git a/include/blender/blender.h b/include/blender/blender.h deleted file mode 100644 index 26ff802..0000000 --- a/include/blender/blender.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef UPEM_MORPHING_BLENDER -#define UPEM_MORPHING_BLENDER - -/** - * File: blender.h - * Will it blend? That is the question. - */ - -#include "common/time.h" -#include "blender/canvas.h" -#include "morpher/morphing.h" - -/** - * Function: blender_blend_canvas - * Blends two canvas by applying the given morphing at the requested time frame. - * - * Parameters: - * *canvas - pointer to the canvas to paint - * *source - source image - * *target - target image - * *morphing - morphing transform to apply - * frame - the interpolation distance from the origin canvas [0;1] - */ -void blender_blend_canvas(Canvas *canvas, Canvas *source, Canvas *target, Morphing *morphing, TimeVector frame); - -#endif diff --git a/include/painter/rasterizer.h b/include/painter/rasterizer.h new file mode 100644 index 0000000..204d616 --- /dev/null +++ b/include/painter/rasterizer.h @@ -0,0 +1,41 @@ +#ifndef UPEM_MORPHING_RASTERIZER +#define UPEM_MORPHING_RASTERIZER + +/** + * File: rasterizer.h + */ + +#include "painter/canvas.h" +#include "morpher/morphing.h" + +/** + * Struct: RasterizationContext + */ +typedef struct { + Canvas *result, *source, *target; + TimeVector frame; +} RasterizationContext; + +/** + * Struct: TriangleContext + */ +typedef struct { + Triangle current, source, target; +} TriangleContext; + +/** + * Function: rasterize + * Rasterises a morphing from a source and a target image at the given time frame. + * + * Parameters: + * *source - source image canvas + * *target - target image canvas + * *m - reference morphing + * frame - time frame + * + * Returns: + * The drawn canvas, dynamically allocated + */ +Canvas *rasterize(Canvas *source, Canvas *target, Morphing *m, TimeVector frame); + +#endif diff --git a/src/blender/blender.c b/src/blender/blender.c deleted file mode 100644 index 08cafa4..0000000 --- a/src/blender/blender.c +++ /dev/null @@ -1,39 +0,0 @@ -#include "blender/blender.h" -#include -#include - -static inline ColorComponent blend_components(ColorComponent origin, ColorComponent target, TimeVector frame) { - // https://www.youtube.com/watch?v=LKnqECcg6Gw - return (ColorComponent) sqrt((TIME_UNIT - frame) * pow(origin, 2) + frame * pow(target, 2)); -} - -static inline Color blend_colors(Color origin, Color target, TimeVector frame) { - return (Color) {{blend_components(origin.rgba.r, target.rgba.r, frame), - blend_components(origin.rgba.g, target.rgba.g, frame), - blend_components(origin.rgba.b, target.rgba.b, frame), - blend_components(origin.rgba.a, target.rgba.a, frame)}}; -} - -void blender_blend_canvas(Canvas *canvas, Canvas *source, Canvas *target, Morphing *morphing, TimeVector frame) { - IntVector flat_dim; - CartesianVector dim, point; - CartesianMapping mapping; - Color pixel; - - dim = morphing->dim; - - assert(dim.x > 0 && dim.y > 0); - assert(vector_equals(dim, canvas_get_dim(canvas))); - assert(vector_equals(dim, canvas_get_dim(source))); - assert(vector_equals(dim, canvas_get_dim(target))); - assert(frame >= TIME_ORIGIN && frame <= TIME_UNIT); - - for (flat_dim = (dim.x - 1) * (dim.y - 1); flat_dim >= 0; --flat_dim) { - point.x = flat_dim % dim.y; - point.y = flat_dim / dim.y; - - mapping = (CartesianMapping) {point, point}; - pixel = blend_colors(canvas_get_pixel(source, mapping.origin), canvas_get_pixel(target, mapping.target), frame); - canvas_set_pixel(canvas, point, pixel); - } -} diff --git a/src/painter/rasterizer.c b/src/painter/rasterizer.c new file mode 100644 index 0000000..881920a --- /dev/null +++ b/src/painter/rasterizer.c @@ -0,0 +1,86 @@ +#include "painter/rasterizer.h" +#include +#include + +static inline IntVector i(double (*f)(double, double), RealVector a, RealVector b) { + return (IntVector) floor(f(a, b)); +} + +static inline CartesianVector vertex_at_frame(CartesianMapping m, TimeVector t) { + return (CartesianVector) {(IntVector) round((TIME_UNIT - t) * m.origin.x + t * m.target.x), + (IntVector) round((TIME_UNIT - t) * m.origin.y + t * m.target.y)}; +} + +static inline RealVector slope(CartesianVector a, CartesianVector b) { + return (((RealVector) b.x) - ((RealVector) a.x)) / (((RealVector) b.y) - ((RealVector) a.y)); +} + +static inline int positive_y_vertex_comparator(const void *l, const void *r) { + return ((CartesianVector *) l)->y - ((CartesianVector *) r)->y; +} + +static inline Color color_at(Canvas *c, Triangle ref, BarycentricVector b) { + CartesianVector v = barycentric_to_cartesian(ref, b); + return canvas_get_pixel(c, v); +} + +static inline TriangleContext build_triangle_context(Triangle current, TriangleMap *map) { + TriangleContext c; + int cursor; + + for (cursor = 0; cursor < 3; ++cursor) { + c.current.v[cursor] = current.v[cursor]; + c.source.v[cursor] = map->vertices[cursor].origin; + c.target.v[cursor] = map->vertices[cursor].target; + } + + return c; +} + +static inline void draw_pixel(CartesianVector pos, TriangleContext *tctx, RasterizationContext *rctx) { + BarycentricVector b = cartesian_to_barycentric(tctx->current, pos); + Color c = color_blend(color_at(rctx->source, tctx->source, b), color_at(rctx->target, tctx->target, b), rctx->frame); + canvas_set_pixel(rctx->result, pos, c); +} + +static inline void draw_flat_triangle(IntVector top, IntVector bottom, + RealVector dx1, RealVector dx2, RealVector *x1, RealVector *x2, + TriangleContext *tctx, RasterizationContext *rctx) { + + IntVector y, l, r; + for (y = top; y <= bottom; ++y, *x1 += dx1, *x2 += dx2) + for (l = i(fmin, *x1, *x2), r = i(fmax, *x1, *x2); l <= r; ++l) + draw_pixel(v(l, y), tctx, rctx); +} + +static inline void draw_triangle(TriangleMap *t, RasterizationContext *rctx) { + Triangle triangle = {{vertex_at_frame(t->vertices[0], rctx->frame), + vertex_at_frame(t->vertices[1], rctx->frame), + vertex_at_frame(t->vertices[2], rctx->frame)}}; + + TriangleContext tctx = build_triangle_context(triangle, t); + CartesianVector *v = triangle.v; + qsort(v, 3, sizeof(CartesianVector), positive_y_vertex_comparator); + + { + RealVector dx1 = slope(v[0], v[1]), dx2 = slope(v[0], v[2]), dx3 = slope(v[1], v[2]); + RealVector x1 = v[0].x, x2 = v[0].x, x3 = v[1].x; + + draw_flat_triangle(v[0].y, v[1].y - 1, dx1, dx2, &x1, &x2, &tctx, rctx); + draw_flat_triangle(v[1].y, v[2].y, dx2, dx3, &x2, &x3, &tctx, rctx); + } +} + +Canvas *rasterize(Canvas *source, Canvas *target, Morphing *m, TimeVector frame) { + RasterizationContext rctx; + TriangleMap *t; + + assert(source != NULL && target != NULL && m != NULL); + assert(vector_equals(canvas_get_dim(source), m->dim) && vector_equals(canvas_get_dim(target), m->dim)); + assert(frame >= TIME_ORIGIN && frame <= TIME_UNIT); + + rctx = (RasterizationContext) {canvas_create(m->dim.y, m->dim.x), source, target, frame}; + for (t = m->first; t != NULL; t = t->next) draw_triangle(t, &rctx); + + return rctx.result; +} diff --git a/test/blender/blender.c b/test/blender/blender.c deleted file mode 100644 index f42322f..0000000 --- a/test/blender/blender.c +++ /dev/null @@ -1,29 +0,0 @@ -#include "blender/blender.h" -#include - -static void test_canvas_blending() { - Morphing *morphing; - Canvas origin, target, result; - CartesianVector sample_point = {13, 17}; - - morphing = morphing_create(64, 64); - canvas_init(&origin, 64, 64); - canvas_init(&target, 64, 64); - canvas_init(&result, 64, 64); - - canvas_set_pixel(&origin, sample_point, (Color) {{0xFF, 0xED, 0x00, 0x00}}); - canvas_set_pixel(&target, sample_point, (Color) {{0x00, 0x47, 0xAB, 0x00}}); - - blender_blend_canvas(&result, &origin, &target, morphing, 0.125); - assert(color_equals(canvas_get_pixel(&result, sample_point), (Color) {{0xEE, 0xDF, 0x3C, 0x00}})); - - canvas_free(&result); - canvas_free(&target); - canvas_free(&origin); - morphing_destroy(morphing); -} - -int main(int argc, char **argv) { - test_canvas_blending(); - return 0; -} diff --git a/test/painter/rasterizer.c b/test/painter/rasterizer.c new file mode 100644 index 0000000..99a70b4 --- /dev/null +++ b/test/painter/rasterizer.c @@ -0,0 +1,27 @@ +#include "painter/rasterizer.h" +#include + +static void test_rasterize() { + Morphing *morphing; + Canvas *origin, *target, *result; + CartesianVector sample_point = {13, 17}; + + morphing = morphing_create(100, 100); + origin = canvas_create(100, 100); + target = canvas_create(100, 100); + canvas_set_pixel(origin, sample_point, (Color) {{0xFF, 0x00, 0xED, 0xFF}}); + canvas_set_pixel(target, sample_point, (Color) {{0xFF, 0xAB, 0x47, 0x00}}); + + result = rasterize(origin, target, morphing, 0.125); + assert(color_equals(canvas_get_pixel(result, sample_point), (Color) {{0xFF, 0x3C, 0xDF, 0xEF}})); + + canvas_destroy(result); + canvas_destroy(target); + canvas_destroy(origin); + morphing_destroy(morphing); +} + +int main(int argc, char **argv) { + test_rasterize(); + return 0; +} -- cgit v1.2.3