diff options
-rw-r--r-- | include/blender/blender.h | 26 | ||||
-rw-r--r-- | include/painter/rasterizer.h | 41 | ||||
-rw-r--r-- | src/blender/blender.c | 39 | ||||
-rw-r--r-- | src/painter/rasterizer.c | 86 | ||||
-rw-r--r-- | test/blender/blender.c | 29 | ||||
-rw-r--r-- | test/painter/rasterizer.c | 27 |
6 files changed, 154 insertions, 94 deletions
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 @@ | |||
1 | #ifndef UPEM_MORPHING_BLENDER | ||
2 | #define UPEM_MORPHING_BLENDER | ||
3 | |||
4 | /** | ||
5 | * File: blender.h | ||
6 | * Will it blend? That is the question. | ||
7 | */ | ||
8 | |||
9 | #include "common/time.h" | ||
10 | #include "blender/canvas.h" | ||
11 | #include "morpher/morphing.h" | ||
12 | |||
13 | /** | ||
14 | * Function: blender_blend_canvas | ||
15 | * Blends two canvas by applying the given morphing at the requested time frame. | ||
16 | * | ||
17 | * Parameters: | ||
18 | * *canvas - pointer to the canvas to paint | ||
19 | * *source - source image | ||
20 | * *target - target image | ||
21 | * *morphing - morphing transform to apply | ||
22 | * frame - the interpolation distance from the origin canvas [0;1] | ||
23 | */ | ||
24 | void blender_blend_canvas(Canvas *canvas, Canvas *source, Canvas *target, Morphing *morphing, TimeVector frame); | ||
25 | |||
26 | #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 @@ | |||
1 | #ifndef UPEM_MORPHING_RASTERIZER | ||
2 | #define UPEM_MORPHING_RASTERIZER | ||
3 | |||
4 | /** | ||
5 | * File: rasterizer.h | ||
6 | */ | ||
7 | |||
8 | #include "painter/canvas.h" | ||
9 | #include "morpher/morphing.h" | ||
10 | |||
11 | /** | ||
12 | * Struct: RasterizationContext | ||
13 | */ | ||
14 | typedef struct { | ||
15 | Canvas *result, *source, *target; | ||
16 | TimeVector frame; | ||
17 | } RasterizationContext; | ||
18 | |||
19 | /** | ||
20 | * Struct: TriangleContext | ||
21 | */ | ||
22 | typedef struct { | ||
23 | Triangle current, source, target; | ||
24 | } TriangleContext; | ||
25 | |||
26 | /** | ||
27 | * Function: rasterize | ||
28 | * Rasterises a morphing from a source and a target image at the given time frame. | ||
29 | * | ||
30 | * Parameters: | ||
31 | * *source - source image canvas | ||
32 | * *target - target image canvas | ||
33 | * *m - reference morphing | ||
34 | * frame - time frame | ||
35 | * | ||
36 | * Returns: | ||
37 | * The drawn canvas, dynamically allocated | ||
38 | */ | ||
39 | Canvas *rasterize(Canvas *source, Canvas *target, Morphing *m, TimeVector frame); | ||
40 | |||
41 | #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 @@ | |||
1 | #include "blender/blender.h" | ||
2 | #include <assert.h> | ||
3 | #include <math.h> | ||
4 | |||
5 | static inline ColorComponent blend_components(ColorComponent origin, ColorComponent target, TimeVector frame) { | ||
6 | // https://www.youtube.com/watch?v=LKnqECcg6Gw | ||
7 | return (ColorComponent) sqrt((TIME_UNIT - frame) * pow(origin, 2) + frame * pow(target, 2)); | ||
8 | } | ||
9 | |||
10 | static inline Color blend_colors(Color origin, Color target, TimeVector frame) { | ||
11 | return (Color) {{blend_components(origin.rgba.r, target.rgba.r, frame), | ||
12 | blend_components(origin.rgba.g, target.rgba.g, frame), | ||
13 | blend_components(origin.rgba.b, target.rgba.b, frame), | ||
14 | blend_components(origin.rgba.a, target.rgba.a, frame)}}; | ||
15 | } | ||
16 | |||
17 | void blender_blend_canvas(Canvas *canvas, Canvas *source, Canvas *target, Morphing *morphing, TimeVector frame) { | ||
18 | IntVector flat_dim; | ||
19 | CartesianVector dim, point; | ||
20 | CartesianMapping mapping; | ||
21 | Color pixel; | ||
22 | |||
23 | dim = morphing->dim; | ||
24 | |||
25 | assert(dim.x > 0 && dim.y > 0); | ||
26 | assert(vector_equals(dim, canvas_get_dim(canvas))); | ||
27 | assert(vector_equals(dim, canvas_get_dim(source))); | ||
28 | assert(vector_equals(dim, canvas_get_dim(target))); | ||
29 | assert(frame >= TIME_ORIGIN && frame <= TIME_UNIT); | ||
30 | |||
31 | for (flat_dim = (dim.x - 1) * (dim.y - 1); flat_dim >= 0; --flat_dim) { | ||
32 | point.x = flat_dim % dim.y; | ||
33 | point.y = flat_dim / dim.y; | ||
34 | |||
35 | mapping = (CartesianMapping) {point, point}; | ||
36 | pixel = blend_colors(canvas_get_pixel(source, mapping.origin), canvas_get_pixel(target, mapping.target), frame); | ||
37 | canvas_set_pixel(canvas, point, pixel); | ||
38 | } | ||
39 | } | ||
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 @@ | |||
1 | #include "painter/rasterizer.h" | ||
2 | #include <math.h> | ||
3 | #include <assert.h> | ||
4 | |||
5 | static inline IntVector i(double (*f)(double, double), RealVector a, RealVector b) { | ||
6 | return (IntVector) floor(f(a, b)); | ||
7 | } | ||
8 | |||
9 | static inline CartesianVector vertex_at_frame(CartesianMapping m, TimeVector t) { | ||
10 | return (CartesianVector) {(IntVector) round((TIME_UNIT - t) * m.origin.x + t * m.target.x), | ||
11 | (IntVector) round((TIME_UNIT - t) * m.origin.y + t * m.target.y)}; | ||
12 | } | ||
13 | |||
14 | static inline RealVector slope(CartesianVector a, CartesianVector b) { | ||
15 | return (((RealVector) b.x) - ((RealVector) a.x)) / (((RealVector) b.y) - ((RealVector) a.y)); | ||
16 | } | ||
17 | |||
18 | static inline int positive_y_vertex_comparator(const void *l, const void *r) { | ||
19 | return ((CartesianVector *) l)->y - ((CartesianVector *) r)->y; | ||
20 | } | ||
21 | |||
22 | static inline Color color_at(Canvas *c, Triangle ref, BarycentricVector b) { | ||
23 | CartesianVector v = barycentric_to_cartesian(ref, b); | ||
24 | return canvas_get_pixel(c, v); | ||
25 | } | ||
26 | |||
27 | static inline TriangleContext build_triangle_context(Triangle current, TriangleMap *map) { | ||
28 | TriangleContext c; | ||
29 | int cursor; | ||
30 | |||
31 | for (cursor = 0; cursor < 3; ++cursor) { | ||
32 | c.current.v[cursor] = current.v[cursor]; | ||
33 | c.source.v[cursor] = map->vertices[cursor].origin; | ||
34 | c.target.v[cursor] = map->vertices[cursor].target; | ||
35 | } | ||
36 | |||
37 | return c; | ||
38 | } | ||
39 | |||
40 | static inline void draw_pixel(CartesianVector pos, TriangleContext *tctx, RasterizationContext *rctx) { | ||
41 | BarycentricVector b = cartesian_to_barycentric(tctx->current, pos); | ||
42 | Color c = color_blend(color_at(rctx->source, tctx->source, b), color_at(rctx->target, tctx->target, b), rctx->frame); | ||
43 | canvas_set_pixel(rctx->result, pos, c); | ||
44 | } | ||
45 | |||
46 | static inline void draw_flat_triangle(IntVector top, IntVector bottom, | ||
47 | RealVector dx1, RealVector dx2, RealVector *x1, RealVector *x2, | ||
48 | TriangleContext *tctx, RasterizationContext *rctx) { | ||
49 | |||
50 | IntVector y, l, r; | ||
51 | for (y = top; y <= bottom; ++y, *x1 += dx1, *x2 += dx2) | ||
52 | for (l = i(fmin, *x1, *x2), r = i(fmax, *x1, *x2); l <= r; ++l) | ||
53 | draw_pixel(v(l, y), tctx, rctx); | ||
54 | } | ||
55 | |||
56 | static inline void draw_triangle(TriangleMap *t, RasterizationContext *rctx) { | ||
57 | Triangle triangle = {{vertex_at_frame(t->vertices[0], rctx->frame), | ||
58 | vertex_at_frame(t->vertices[1], rctx->frame), | ||
59 | vertex_at_frame(t->vertices[2], rctx->frame)}}; | ||
60 | |||
61 | TriangleContext tctx = build_triangle_context(triangle, t); | ||
62 | CartesianVector *v = triangle.v; | ||
63 | qsort(v, 3, sizeof(CartesianVector), positive_y_vertex_comparator); | ||
64 | |||
65 | { | ||
66 | RealVector dx1 = slope(v[0], v[1]), dx2 = slope(v[0], v[2]), dx3 = slope(v[1], v[2]); | ||
67 | RealVector x1 = v[0].x, x2 = v[0].x, x3 = v[1].x; | ||
68 | |||
69 | draw_flat_triangle(v[0].y, v[1].y - 1, dx1, dx2, &x1, &x2, &tctx, rctx); | ||
70 | draw_flat_triangle(v[1].y, v[2].y, dx2, dx3, &x2, &x3, &tctx, rctx); | ||
71 | } | ||
72 | } | ||
73 | |||
74 | Canvas *rasterize(Canvas *source, Canvas *target, Morphing *m, TimeVector frame) { | ||
75 | RasterizationContext rctx; | ||
76 | TriangleMap *t; | ||
77 | |||
78 | assert(source != NULL && target != NULL && m != NULL); | ||
79 | assert(vector_equals(canvas_get_dim(source), m->dim) && vector_equals(canvas_get_dim(target), m->dim)); | ||
80 | assert(frame >= TIME_ORIGIN && frame <= TIME_UNIT); | ||
81 | |||
82 | rctx = (RasterizationContext) {canvas_create(m->dim.y, m->dim.x), source, target, frame}; | ||
83 | for (t = m->first; t != NULL; t = t->next) draw_triangle(t, &rctx); | ||
84 | |||
85 | return rctx.result; | ||
86 | } | ||
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 @@ | |||
1 | #include "blender/blender.h" | ||
2 | #include <assert.h> | ||
3 | |||
4 | static void test_canvas_blending() { | ||
5 | Morphing *morphing; | ||
6 | Canvas origin, target, result; | ||
7 | CartesianVector sample_point = {13, 17}; | ||
8 | |||
9 | morphing = morphing_create(64, 64); | ||
10 | canvas_init(&origin, 64, 64); | ||
11 | canvas_init(&target, 64, 64); | ||
12 | canvas_init(&result, 64, 64); | ||
13 | |||
14 | canvas_set_pixel(&origin, sample_point, (Color) {{0xFF, 0xED, 0x00, 0x00}}); | ||
15 | canvas_set_pixel(&target, sample_point, (Color) {{0x00, 0x47, 0xAB, 0x00}}); | ||
16 | |||
17 | blender_blend_canvas(&result, &origin, &target, morphing, 0.125); | ||
18 | assert(color_equals(canvas_get_pixel(&result, sample_point), (Color) {{0xEE, 0xDF, 0x3C, 0x00}})); | ||
19 | |||
20 | canvas_free(&result); | ||
21 | canvas_free(&target); | ||
22 | canvas_free(&origin); | ||
23 | morphing_destroy(morphing); | ||
24 | } | ||
25 | |||
26 | int main(int argc, char **argv) { | ||
27 | test_canvas_blending(); | ||
28 | return 0; | ||
29 | } | ||
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 @@ | |||
1 | #include "painter/rasterizer.h" | ||
2 | #include <assert.h> | ||
3 | |||
4 | static void test_rasterize() { | ||
5 | Morphing *morphing; | ||
6 | Canvas *origin, *target, *result; | ||
7 | CartesianVector sample_point = {13, 17}; | ||
8 | |||
9 | morphing = morphing_create(100, 100); | ||