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