diff options
Diffstat (limited to 'src/painter/rasterizer.c')
-rw-r--r-- | src/painter/rasterizer.c | 86 |
1 files changed, 86 insertions, 0 deletions
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 | } | ||