summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/blender/blender.h26
-rw-r--r--include/painter/rasterizer.h41
-rw-r--r--src/blender/blender.c39
-rw-r--r--src/painter/rasterizer.c86
-rw-r--r--test/blender/blender.c29
-rw-r--r--test/painter/rasterizer.c27
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 */
24void 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 */
14typedef struct {
15 Canvas *result, *source, *target;
16 TimeVector frame;
17} RasterizationContext;
18
19/**
20 * Struct: TriangleContext
21 */
22typedef 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 */
39Canvas *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
5static 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
10static 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
17void 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
5static inline IntVector i(double (*f)(double, double), RealVector a, RealVector b) {
6 return (IntVector) floor(f(a, b));
7}
8
9static 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
14static inline RealVector slope(CartesianVector a, CartesianVector b) {
15 return (((RealVector) b.x) - ((RealVector) a.x)) / (((RealVector) b.y) - ((RealVector) a.y));
16}
17
18static inline int positive_y_vertex_comparator(const void *l, const void *r) {
19 return ((CartesianVector *) l)->y - ((CartesianVector *) r)->y;
20}
21
22static 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
27static 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
40static 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
46static 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
56static 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
74Canvas *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
4static 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
26int 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
4static void test_rasterize() {
5 Morphing *morphing;
6 Canvas *origin, *target, *result;
7 CartesianVector sample_point = {13, 17};
8
9 morphing = morphing_create(100, 100);
10 origin = canvas_create(100, 100);
11 target = canvas_create(100, 100);
12 canvas_set_pixel(origin, sample_point, (Color) {{0xFF, 0x00, 0xED, 0xFF}});