summaryrefslogtreecommitdiff
path: root/fitzdraw/pathfill.c
blob: 8703e066551a1fcc298bc6aecfc4360447b8ae86 (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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#include "fitz.h"

static fz_error
line(fz_gel *gel, fz_matrix *ctm, float x0, float y0, float x1, float y1)
{
	float tx0 = ctm->a * x0 + ctm->c * y0 + ctm->e;
	float ty0 = ctm->b * x0 + ctm->d * y0 + ctm->f;
	float tx1 = ctm->a * x1 + ctm->c * y1 + ctm->e;
	float ty1 = ctm->b * x1 + ctm->d * y1 + ctm->f;
	return fz_insertgel(gel, tx0, ty0, tx1, ty1);
}

static fz_error
bezier(fz_gel *gel, fz_matrix *ctm, float flatness,
	float xa, float ya,
	float xb, float yb,
	float xc, float yc,
	float xd, float yd)
{
	fz_error error;
	float dmax;
	float xab, yab;
	float xbc, ybc;
	float xcd, ycd;
	float xabc, yabc;
	float xbcd, ybcd;
	float xabcd, yabcd;

	/* termination check */
	dmax = ABS(xa - xb);
	dmax = MAX(dmax, ABS(ya - yb));
	dmax = MAX(dmax, ABS(xd - xc));
	dmax = MAX(dmax, ABS(yd - yc));
	if (dmax < flatness)
		return line(gel, ctm, xa, ya, xd, yd);

	xab = xa + xb;
	yab = ya + yb;
	xbc = xb + xc;
	ybc = yb + yc;
	xcd = xc + xd;
	ycd = yc + yd;

	xabc = xab + xbc;
	yabc = yab + ybc;
	xbcd = xbc + xcd;
	ybcd = ybc + ycd;

	xabcd = xabc + xbcd;
	yabcd = yabc + ybcd;

	xab *= 0.5f; yab *= 0.5f;
	xbc *= 0.5f; ybc *= 0.5f;
	xcd *= 0.5f; ycd *= 0.5f;

	xabc *= 0.25f; yabc *= 0.25f;
	xbcd *= 0.25f; ybcd *= 0.25f;

	xabcd *= 0.125f; yabcd *= 0.125f;

	error = bezier(gel, ctm, flatness, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd);
	if (error)
		return error;
	return bezier(gel, ctm, flatness, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd);
}

fz_error
fz_fillpath(fz_gel *gel, fz_pathnode *path, fz_matrix ctm, float flatness)
{
	fz_error error;
	float x1, y1, x2, y2, x3, y3;
	float cx = 0;
	float cy = 0;
	float bx = 0;
	float by = 0;
	int i = 0;

	while (i < path->len)
	{
		switch (path->els[i++].k)
		{
		case FZ_MOVETO:
			/* implicit closepath before moveto */
			if (i && (cx != bx || cy != by))
			{
				error = line(gel, &ctm, cx, cy, bx, by);
				if (error)
					return error;
			}

			x1 = path->els[i++].v;
			y1 = path->els[i++].v;
			cx = bx = x1;
			cy = by = y1;
			break;

		case FZ_LINETO:
			x1 = path->els[i++].v;
			y1 = path->els[i++].v;
			error = line(gel, &ctm, cx, cy, x1, y1);
			if (error)
				return error;
			cx = x1;
			cy = y1;
			break;

		case FZ_CURVETO:
			x1 = path->els[i++].v;
			y1 = path->els[i++].v;
			x2 = path->els[i++].v;
			y2 = path->els[i++].v;
			x3 = path->els[i++].v;
			y3 = path->els[i++].v;
			error = bezier(gel, &ctm, flatness, cx, cy, x1, y1, x2, y2, x3, y3);
			if (error)
				return error;
			cx = x3;
			cy = y3;
			break;

		case FZ_CLOSEPATH:
			error = line(gel, &ctm, cx, cy, bx, by);
			if (error)
				return error;
			cx = bx;
			cy = by;
			break;
		}
	}

	if (i && (cx != bx || cy != by))
	{
		error = line(gel, &ctm, cx, cy, bx, by);
		if (error)
			return error;
	}

	return fz_okay;
}