-
Notifications
You must be signed in to change notification settings - Fork 20
/
Copy pathpoly.c
324 lines (281 loc) · 8.61 KB
/
poly.c
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
/*
* Twin - A Tiny Window System
* Copyright (c) 2004 Keith Packard <[email protected]>
* Copyright (c) 2024 National Cheng Kung University, Taiwan
* All rights reserved.
*/
#include <stdlib.h>
#include "twin_private.h"
typedef struct _twin_edge {
struct _twin_edge *next;
twin_sfixed_t top, bot;
twin_sfixed_t x;
twin_sfixed_t e;
twin_sfixed_t dx, dy;
twin_sfixed_t inc_x;
twin_sfixed_t step_x;
int winding;
} twin_edge_t;
#define TWIN_POLY_SHIFT 2
#define TWIN_POLY_FIXED_SHIFT (4 - TWIN_POLY_SHIFT)
#define TWIN_POLY_SAMPLE (1 << TWIN_POLY_SHIFT)
#define TWIN_POLY_MASK (TWIN_POLY_SAMPLE - 1)
#define TWIN_POLY_STEP (TWIN_SFIXED_ONE >> TWIN_POLY_SHIFT)
#define TWIN_POLY_START (TWIN_POLY_STEP >> 1)
#define TWIN_POLY_CEIL(c) (((c) + (TWIN_POLY_STEP - 1)) & ~(TWIN_POLY_STEP - 1))
#define TWIN_POLY_COL(x) (((x) >> TWIN_POLY_FIXED_SHIFT) & TWIN_POLY_MASK)
static int _edge_compare_y(const void *a, const void *b)
{
const twin_edge_t *ae = a;
const twin_edge_t *be = b;
return (int) (ae->top - be->top);
}
static void _edge_step_by(twin_edge_t *edge, twin_sfixed_t dy)
{
twin_dfixed_t e;
e = edge->e + (twin_dfixed_t) dy * edge->dx;
edge->x += edge->step_x * dy + edge->inc_x * (e / edge->dy);
edge->e = e % edge->dy;
}
/*
* Returns the nearest grid coordinate no less than f
*
* Grid coordinates are at TWIN_POLY_STEP/2 + n*TWIN_POLY_STEP
*/
static twin_sfixed_t _twin_sfixed_grid_ceil(twin_sfixed_t f)
{
return ((f + (TWIN_POLY_START - 1)) & ~(TWIN_POLY_STEP - 1)) +
TWIN_POLY_START;
}
static int _twin_edge_build(twin_spoint_t *vertices,
int nvertices,
twin_edge_t *edges,
twin_sfixed_t dx,
twin_sfixed_t dy,
twin_sfixed_t top_y)
{
int tv, bv;
int e = 0;
for (int v = 0; v < nvertices; v++) {
int nv = v + 1;
if (nv == nvertices)
nv = 0;
/* skip horizontal edges */
if (vertices[v].y == vertices[nv].y)
continue;
/* figure winding */
if (vertices[v].y < vertices[nv].y) {
edges[e].winding = 1;
tv = v;
bv = nv;
} else {
edges[e].winding = -1;
tv = nv;
bv = v;
}
/* snap top to first grid point in pixmap */
twin_sfixed_t y = _twin_sfixed_grid_ceil(vertices[tv].y + dy);
if (y < TWIN_POLY_START + top_y)
y = TWIN_POLY_START + top_y;
/* skip vertices which don't span a sample row */
if (y >= vertices[bv].y + dy)
continue;
/* Compute bresenham terms */
edges[e].dx = vertices[bv].x - vertices[tv].x;
edges[e].dy = vertices[bv].y - vertices[tv].y;
if (edges[e].dx >= 0)
edges[e].inc_x = 1;
else {
edges[e].inc_x = -1;
edges[e].dx = -edges[e].dx;
}
edges[e].step_x = edges[e].inc_x * (edges[e].dx / edges[e].dy);
edges[e].dx = edges[e].dx % edges[e].dy;
edges[e].top = vertices[tv].y + dy;
edges[e].bot = vertices[bv].y + dy;
edges[e].x = vertices[tv].x + dx;
edges[e].e = 0;
/* step to first grid point */
_edge_step_by(&edges[e], y - edges[e].top);
edges[e].top = y;
e++;
}
return e;
}
static void _span_fill(twin_pixmap_t *pixmap,
twin_sfixed_t y,
twin_sfixed_t left,
twin_sfixed_t right)
{
#if TWIN_POLY_SHIFT == 0
/* 1x1 */
static const twin_a8_t coverage[1][1] = {
{0xff},
};
#endif
#if TWIN_POLY_SHIFT == 1
/* 2x2 */
static const twin_a8_t coverage[2][2] = {
{0x40, 0x40},
{0x3f, 0x40},
};
#endif
#if TWIN_POLY_SHIFT == 2
/* 4x4 */
static const twin_a8_t coverage[4][4] = {
{0x10, 0x10, 0x10, 0x10},
{0x10, 0x10, 0x10, 0x10},
{0x0f, 0x10, 0x10, 0x10},
{0x10, 0x10, 0x10, 0x10},
};
#endif
#if TWIN_POLY_SHIFT == 3
/* 8x8 */
static const twin_a8_t coverage[8][8] = {
{4, 4, 4, 4, 4, 4, 4, 4}, {4, 4, 4, 4, 4, 4, 4, 4},
{4, 4, 4, 4, 4, 4, 4, 4}, {4, 4, 4, 4, 4, 4, 4, 4},
{3, 4, 4, 4, 4, 4, 4, 4}, {4, 4, 4, 4, 4, 4, 4, 4},
{4, 4, 4, 4, 4, 4, 4, 4}, {4, 4, 4, 4, 4, 4, 4, 4},
};
#endif
const twin_a8_t *cover =
coverage[(y >> TWIN_POLY_FIXED_SHIFT) & TWIN_POLY_MASK];
int row = twin_sfixed_trunc(y);
twin_a8_t *span = pixmap->p.a8 + row * pixmap->stride;
twin_a8_t *s;
twin_sfixed_t x;
twin_a16_t a;
twin_a16_t w;
int col;
/* clip to pixmap */
if (left < twin_int_to_sfixed(pixmap->clip.left))
left = twin_int_to_sfixed(pixmap->clip.left);
if (right > twin_int_to_sfixed(pixmap->clip.right))
right = twin_int_to_sfixed(pixmap->clip.right);
/* convert to sample grid */
left = _twin_sfixed_grid_ceil(left) >> TWIN_POLY_FIXED_SHIFT;
right = _twin_sfixed_grid_ceil(right) >> TWIN_POLY_FIXED_SHIFT;
/* check for empty */
if (right <= left)
return;
x = left;
/* starting address */
s = span + (x >> TWIN_POLY_SHIFT);
/* first pixel */
if (x & TWIN_POLY_MASK) {
w = 0;
col = 0;
while (x < right && (x & TWIN_POLY_MASK)) {
w += cover[col++];
x++;
}
a = *s + w;
*s++ = twin_sat(a);
}
w = 0;
for (col = 0; col < TWIN_POLY_SAMPLE; col++)
w += cover[col];
/* middle pixels */
while (x + TWIN_POLY_MASK < right) {
a = *s + w;
*s++ = twin_sat(a);
x += TWIN_POLY_SAMPLE;
}
/* last pixel */
if (right & TWIN_POLY_MASK && x != right) {
w = 0;
col = 0;
while (x < right) {
w += cover[col++];
x++;
}
a = *s + w;
*s = twin_sat(a);
}
}
static void _twin_edge_fill(twin_pixmap_t *pixmap,
twin_edge_t *edges,
int nedges)
{
twin_edge_t *active, *a, *n, **prev;
twin_sfixed_t x0 = 0;
qsort(edges, nedges, sizeof(twin_edge_t), _edge_compare_y);
int e = 0;
twin_sfixed_t y = edges[0].top;
active = 0;
for (;;) {
/* add in new edges */
for (; e < nedges && edges[e].top <= y; e++) {
for (prev = &active; (a = *prev); prev = &(a->next))
if (a->x > edges[e].x)
break;
edges[e].next = *prev;
*prev = &edges[e];
}
/* walk this y value marking coverage */
int w = 0;
for (a = active; a; a = a->next) {
if (w == 0)
x0 = a->x;
w += a->winding;
if (w == 0)
_span_fill(pixmap, y, x0, a->x);
}
/* step down, clipping to pixmap */
y += TWIN_POLY_STEP;
if (twin_sfixed_trunc(y) >= pixmap->clip.bottom)
break;
/* strip out dead edges */
for (prev = &active; (a = *prev);) {
if (a->bot <= y)
*prev = a->next;
else
prev = &a->next;
}
/* check for all done */
if (!active && e == nedges)
break;
/* step all edges */
for (a = active; a; a = a->next)
_edge_step_by(a, TWIN_POLY_STEP);
/* fix x sorting */
for (prev = &active; (a = *prev) && (n = a->next);) {
if (a->x > n->x) {
a->next = n->next;
n->next = a;
*prev = n;
prev = &active;
} else
prev = &a->next;
}
}
}
void twin_fill_path(twin_pixmap_t *pixmap,
twin_path_t *path,
twin_coord_t dx,
twin_coord_t dy)
{
twin_sfixed_t sdx = twin_int_to_sfixed(dx + pixmap->origin_x);
twin_sfixed_t sdy = twin_int_to_sfixed(dy + pixmap->origin_y);
int nalloc = path->npoints + path->nsublen + 1;
twin_edge_t *edges = malloc(sizeof(twin_edge_t) * nalloc);
int p = 0;
int nedges = 0;
for (int s = 0; s <= path->nsublen; s++) {
int sublen;
if (s == path->nsublen)
sublen = path->npoints;
else
sublen = path->sublen[s];
int npoints = sublen - p;
if (npoints > 1) {
int n =
_twin_edge_build(path->points + p, npoints, edges + nedges, sdx,
sdy, twin_int_to_sfixed(pixmap->clip.top));
p = sublen;
nedges += n;
}
}
_twin_edge_fill(pixmap, edges, nedges);
free(edges);
}