diff options
author | Adam NAILI | 2017-12-28 16:23:17 +0100 |
---|---|---|
committer | Adam NAILI | 2017-12-28 16:23:17 +0100 |
commit | 9e4eb30f33867bcb37d5accfb5588cfb3b450f90 (patch) | |
tree | 57cc389a1c302d8278246fb8334ada216be82a01 /src/painter | |
parent | aaf57e5ee1e0cf74afdbdf56293f1afd7e79e6b0 (diff) | |
parent | 426ddbdd27d21383a3870980f9b787a8c58237aa (diff) | |
download | morpher-9e4eb30f33867bcb37d5accfb5588cfb3b450f90.tar.gz |
Merge remote-tracking branch 'origin/master' into gui
Diffstat (limited to 'src/painter')
-rw-r--r-- | src/painter/canvas.c | 33 | ||||
-rw-r--r-- | src/painter/color.c | 20 | ||||
-rw-r--r-- | src/painter/rasterizer.c | 86 |
3 files changed, 139 insertions, 0 deletions
diff --git a/src/painter/canvas.c b/src/painter/canvas.c new file mode 100644 index 0000000..306dc9c --- /dev/null +++ b/src/painter/canvas.c | |||
@@ -0,0 +1,33 @@ | |||
1 | #include "painter/canvas.h" | ||
2 | #include "common/mem.h" | ||
3 | |||
4 | Canvas *canvas_create(IntVector width, IntVector height) { | ||
5 | Canvas *c = malloc_or_die(sizeof(Canvas)); | ||
6 | c->mlv = MLV_create_image(width, height); | ||
7 | return c; | ||
8 | } | ||
9 | |||
10 | Canvas *canvas_create_from_image(const char *fpath) { | ||
11 | Canvas *c = malloc_or_die(sizeof(Canvas)); | ||
12 | c->mlv = MLV_load_image(fpath); | ||
13 | return c; | ||
14 | } | ||
15 | |||
16 | void canvas_destroy(Canvas *c) { | ||
17 | MLV_free_image(c->mlv); | ||
18 | free(c); | ||
19 | } | ||
20 | |||
21 | void canvas_set_pixel(Canvas *c, CartesianVector pos, Color color) { | ||
22 | MLV_set_pixel_on_image(pos.x, pos.y, color.mlv, c->mlv); | ||
23 | } | ||
24 | |||
25 | Color canvas_get_pixel(Canvas *c, CartesianVector pos) { | ||
26 | int r, g, b, a; | ||
27 | MLV_get_pixel_on_image(c->mlv, pos.x, pos.y, &r, &g, &b, &a); | ||
28 | return (Color) {{a, b, g, r}}; | ||
29 | } | ||
30 | |||
31 | CartesianVector canvas_get_dim(Canvas *c) { | ||
32 | return (CartesianVector) {MLV_get_image_width(c->mlv), MLV_get_image_height(c->mlv)}; | ||
33 | } | ||
diff --git a/src/painter/color.c b/src/painter/color.c new file mode 100644 index 0000000..65c4f20 --- /dev/null +++ b/src/painter/color.c | |||
@@ -0,0 +1,20 @@ | |||
1 | #include "painter/color.h" | ||
2 | #include <math.h> | ||
3 | |||
4 | static inline ColorComponent blend_component(ColorComponent origin, ColorComponent target, TimeVector frame) { | ||
5 | return (ColorComponent) round(sqrt((TIME_UNIT - frame) * pow(origin, 2) + frame * pow(target, 2))); | ||
6 | } | ||
7 | |||
8 | bool color_equals(Color c1, Color c2) { | ||
9 | return c1.rgba.r == c2.rgba.r && | ||
10 | c1.rgba.g == c2.rgba.g && | ||
11 | c1.rgba.b == c2.rgba.b && | ||
12 | c1.rgba.a == c2.rgba.a; | ||
13 | } | ||
14 | |||
15 | Color color_blend(Color origin, Color target, TimeVector distance) { | ||
16 | return (Color) {{blend_component(origin.rgba.a, target.rgba.a, distance), | ||
17 | blend_component(origin.rgba.b, target.rgba.b, distance), | ||
18 | blend_component(origin.rgba.g, target.rgba.g, distance), | ||
19 | blend_component(origin.rgba.r, target.rgba.r, distance)}}; | ||
20 | } | ||
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 | } | ||