summaryrefslogtreecommitdiff
path: root/src/painter/rasterizer.c
blob: 1ba726c305d2a34a7f69d9f3a53aa0a1ed515334 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include "painter/rasterizer.h"
#include <math.h>
#include <assert.h>

static inline IntVector i(double (*f)(double, double), RealVector a, RealVector b) {
  return (IntVector) floor(f(a, b));
}

static inline CartesianVector vertex_at_frame(CartesianMapping m, TimeVector t) {
  return (CartesianVector) {(IntVector) round((TIME_UNIT - t) * m.origin.x + t * m.target.x),
                            (IntVector) round((TIME_UNIT - t) * m.origin.y + t * m.target.y)};
}

static inline RealVector slope(CartesianVector a, CartesianVector b) {
  return (((RealVector) b.x) - ((RealVector) a.x)) / (((RealVector) b.y) - ((RealVector) a.y));
}

static inline int positive_y_vertex_comparator(const void *l, const void *r) {
  return ((CartesianVector *) l)->y - ((CartesianVector *) r)->y;
}

static inline Color color_at(Canvas *c, Triangle ref, BarycentricVector b) {
  CartesianVector v = barycentric_to_cartesian(ref, b);
  return canvas_get_pixel(c, v);
}

static inline TriangleContext build_triangle_context(Triangle current, TriangleMap *map) {
  TriangleContext c;
  int cursor;

  for (cursor = 0; cursor < 3; ++cursor) {
    c.current.v[cursor] = current.v[cursor];
    c.source.v[cursor] = map->vertices[cursor].origin;
    c.target.v[cursor] = map->vertices[cursor].target;
  }

  return c;
}

static inline void draw_pixel(CartesianVector pos, TriangleContext *tctx, RasterizationContext *rctx) {
  BarycentricVector b = cartesian_to_barycentric(tctx->current, pos);
  Color c = color_blend(color_at(rctx->source, tctx->source, b), color_at(rctx->target, tctx->target, b), rctx->frame);
  canvas_set_pixel(rctx->result, pos, c);
}

static inline void draw_flat_triangle(IntVector top, IntVector bottom,
                                      RealVector dx1, RealVector dx2, RealVector *x1, RealVector *x2,
                                      TriangleContext *tctx, RasterizationContext *rctx) {

  IntVector y, l, r;
  for (y = top; y <= bottom; ++y, *x1 += dx1, *x2 += dx2)
    for (l = i(fmin, *x1, *x2), r = i(fmax, *x1, *x2); l <= r; ++l)
      draw_pixel(v(l, y), tctx, rctx);
}

static inline void draw_triangle(TriangleMap *t, RasterizationContext *rctx) {
  Triangle triangle = {{vertex_at_frame(t->vertices[0], rctx->frame),
                        vertex_at_frame(t->vertices[1], rctx->frame),
                        vertex_at_frame(t->vertices[2], rctx->frame)}};

  TriangleContext tctx = build_triangle_context(triangle, t);
  CartesianVector *v = triangle.v;
  qsort(v, 3, sizeof(CartesianVector), positive_y_vertex_comparator);

  {
    RealVector dx1 = slope(v[0], v[1]), dx2 = slope(v[0], v[2]), dx3 = slope(v[1], v[2]);
    RealVector x1 = v[0].x, x2 = v[0].x, x3 = v[1].x;

    draw_flat_triangle(v[0].y, v[1].y - 1, dx1, dx2, &x1, &x2, &tctx, rctx);
    draw_flat_triangle(v[1].y, v[2].y, dx2, dx3, &x2, &x3, &tctx, rctx);
  }
}

Canvas *rasterize(Canvas *source, Canvas *target, Morphing *m, TimeVector frame) {
  RasterizationContext rctx;
  TriangleMap *t;

  assert(source != NULL && target != NULL && m != NULL);
  assert(vector_equals(canvas_get_dim(source), m->dim) && vector_equals(canvas_get_dim(target), m->dim));
  assert(frame >= TIME_ORIGIN && frame <= TIME_UNIT);

  rctx = (RasterizationContext) {canvas_create(m->dim.x, m->dim.y), source, target, frame};
  for (t = m->first; t != NULL; t = t->next) draw_triangle(t, &rctx);

  return rctx.result;
}