/src/mupdf/source/fitz/draw-edge.c
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (C) 2004-2021 Artifex Software, Inc. |
2 | | // |
3 | | // This file is part of MuPDF. |
4 | | // |
5 | | // MuPDF is free software: you can redistribute it and/or modify it under the |
6 | | // terms of the GNU Affero General Public License as published by the Free |
7 | | // Software Foundation, either version 3 of the License, or (at your option) |
8 | | // any later version. |
9 | | // |
10 | | // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY |
11 | | // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
12 | | // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more |
13 | | // details. |
14 | | // |
15 | | // You should have received a copy of the GNU Affero General Public License |
16 | | // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> |
17 | | // |
18 | | // Alternative licensing terms are available from the licensor. |
19 | | // For commercial licensing, see <https://www.artifex.com/> or contact |
20 | | // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
21 | | // CA 94129, USA, for further information. |
22 | | |
23 | | #include "mupdf/fitz.h" |
24 | | #include "draw-imp.h" |
25 | | |
26 | | #include <assert.h> |
27 | | #include <limits.h> |
28 | | #include <math.h> |
29 | | #include <stdlib.h> |
30 | | #include <string.h> |
31 | | |
32 | | /* |
33 | | * Global Edge List -- list of straight path segments for scan conversion |
34 | | * |
35 | | * Stepping along the edges is with Bresenham's line algorithm. |
36 | | * |
37 | | * See Mike Abrash -- Graphics Programming Black Book (notably chapter 40) |
38 | | */ |
39 | | |
40 | | typedef struct fz_edge_s |
41 | | { |
42 | | int x, e, h, y; |
43 | | int adj_up, adj_down; |
44 | | int xmove; |
45 | | int xdir, ydir; /* -1 or +1 */ |
46 | | } fz_edge; |
47 | | |
48 | | typedef struct fz_gel_s |
49 | | { |
50 | | fz_rasterizer super; |
51 | | int cap, len; |
52 | | fz_edge *edges; |
53 | | int acap, alen; |
54 | | fz_edge **active; |
55 | | int bcap; |
56 | | unsigned char *alphas; |
57 | | int *deltas; |
58 | | } fz_gel; |
59 | | |
60 | | static int |
61 | | fz_reset_gel(fz_context *ctx, fz_rasterizer *rast) |
62 | 685k | { |
63 | 685k | fz_gel *gel = (fz_gel *)rast; |
64 | | |
65 | 685k | gel->len = 0; |
66 | 685k | gel->alen = 0; |
67 | | |
68 | 685k | return 0; |
69 | 685k | } |
70 | | |
71 | | static void |
72 | | fz_drop_gel(fz_context *ctx, fz_rasterizer *rast) |
73 | 16.9k | { |
74 | 16.9k | fz_gel *gel = (fz_gel *)rast; |
75 | 16.9k | if (gel == NULL) |
76 | 0 | return; |
77 | 16.9k | fz_free(ctx, gel->active); |
78 | 16.9k | fz_free(ctx, gel->edges); |
79 | 16.9k | fz_free(ctx, gel->alphas); |
80 | 16.9k | fz_free(ctx, gel->deltas); |
81 | 16.9k | fz_free(ctx, gel); |
82 | 16.9k | } |
83 | | |
84 | | enum { INSIDE, OUTSIDE, LEAVE, ENTER }; |
85 | | |
86 | 233M | #define clip_lerp_y(v,m,x0,y0,x1,y1,t) clip_lerp_x(v,m,y0,x0,y1,x1,t) |
87 | | |
88 | | static int |
89 | | clip_lerp_x(int val, int m, int x0, int y0, int x1, int y1, int *out) |
90 | 296M | { |
91 | 296M | int v0out = m ? x0 > val : x0 < val; |
92 | 296M | int v1out = m ? x1 > val : x1 < val; |
93 | | |
94 | 296M | if (v0out + v1out == 0) |
95 | 136M | return INSIDE; |
96 | | |
97 | 160M | if (v0out + v1out == 2) |
98 | 159M | return OUTSIDE; |
99 | | |
100 | 469k | if (v1out) |
101 | 234k | { |
102 | 234k | *out = y0 + (int)(((float)(y1 - y0)) * (val - x0) / (x1 - x0)); |
103 | 234k | return LEAVE; |
104 | 234k | } |
105 | | |
106 | 234k | else |
107 | 234k | { |
108 | 234k | *out = y1 + (int)(((float)(y0 - y1)) * (val - x1) / (x0 - x1)); |
109 | 234k | return ENTER; |
110 | 234k | } |
111 | 469k | } |
112 | | |
113 | | static void |
114 | | fz_insert_gel_raw(fz_context *ctx, fz_rasterizer *ras, int x0, int y0, int x1, int y1) |
115 | 32.8M | { |
116 | 32.8M | fz_gel *gel = (fz_gel *)ras; |
117 | 32.8M | fz_edge *edge; |
118 | 32.8M | int dx, dy; |
119 | 32.8M | int winding; |
120 | 32.8M | int width; |
121 | 32.8M | int tmp; |
122 | | |
123 | 32.8M | if (y0 == y1) |
124 | 18.3M | return; |
125 | | |
126 | 14.4M | if (y0 > y1) { |
127 | 7.23M | winding = -1; |
128 | 7.23M | tmp = x0; x0 = x1; x1 = tmp; |
129 | 7.23M | tmp = y0; y0 = y1; y1 = tmp; |
130 | 7.23M | } |
131 | 7.23M | else |
132 | 7.23M | winding = 1; |
133 | | |
134 | 14.4M | if (x0 < gel->super.bbox.x0) gel->super.bbox.x0 = x0; |
135 | 14.4M | if (x0 > gel->super.bbox.x1) gel->super.bbox.x1 = x0; |
136 | 14.4M | if (x1 < gel->super.bbox.x0) gel->super.bbox.x0 = x1; |
137 | 14.4M | if (x1 > gel->super.bbox.x1) gel->super.bbox.x1 = x1; |
138 | | |
139 | 14.4M | if (y0 < gel->super.bbox.y0) gel->super.bbox.y0 = y0; |
140 | 14.4M | if (y1 > gel->super.bbox.y1) gel->super.bbox.y1 = y1; |
141 | | |
142 | 14.4M | if (gel->len + 1 == gel->cap) { |
143 | 1.43k | int new_cap = gel->cap * 2; |
144 | 1.43k | gel->edges = fz_realloc_array(ctx, gel->edges, new_cap, fz_edge); |
145 | 1.43k | gel->cap = new_cap; |
146 | 1.43k | } |
147 | | |
148 | 14.4M | edge = &gel->edges[gel->len++]; |
149 | | |
150 | 14.4M | dy = y1 - y0; |
151 | 14.4M | dx = x1 - x0; |
152 | 14.4M | width = fz_absi(dx); |
153 | | |
154 | 14.4M | edge->xdir = dx > 0 ? 1 : -1; |
155 | 14.4M | edge->ydir = winding; |
156 | 14.4M | edge->x = x0; |
157 | 14.4M | edge->y = y0; |
158 | 14.4M | edge->h = dy; |
159 | 14.4M | edge->adj_down = dy; |
160 | | |
161 | | /* initial error term going l->r and r->l */ |
162 | 14.4M | if (dx >= 0) |
163 | 10.8M | edge->e = 0; |
164 | 3.66M | else |
165 | 3.66M | edge->e = -dy + 1; |
166 | | |
167 | | /* y-major edge */ |
168 | 14.4M | if (dy >= width) { |
169 | 12.0M | edge->xmove = 0; |
170 | 12.0M | edge->adj_up = width; |
171 | 12.0M | } |
172 | | |
173 | | /* x-major edge */ |
174 | 2.38M | else { |
175 | 2.38M | edge->xmove = (width / dy) * edge->xdir; |
176 | 2.38M | edge->adj_up = width % dy; |
177 | 2.38M | } |
178 | 14.4M | } |
179 | | |
180 | | static void |
181 | | fz_insert_gel(fz_context *ctx, fz_rasterizer *ras, float fx0, float fy0, float fx1, float fy1, int rev) |
182 | 172M | { |
183 | 172M | int x0, y0, x1, y1; |
184 | 172M | int d, v; |
185 | 172M | const int hscale = fz_rasterizer_aa_hscale(ras); |
186 | 172M | const int vscale = fz_rasterizer_aa_vscale(ras); |
187 | | |
188 | 172M | fx0 = floorf(fx0 * hscale); |
189 | 172M | fx1 = floorf(fx1 * hscale); |
190 | 172M | fy0 = floorf(fy0 * vscale); |
191 | 172M | fy1 = floorf(fy1 * vscale); |
192 | | |
193 | | /* Call fz_clamp so that clamping is done in the float domain, THEN |
194 | | * cast down to an int. Calling fz_clampi causes problems due to the |
195 | | * implicit cast down from float to int of the first argument |
196 | | * over/underflowing and flipping sign at extreme values. */ |
197 | 172M | x0 = (int)fz_clamp(fx0, BBOX_MIN * hscale, BBOX_MAX * hscale); |
198 | 172M | y0 = (int)fz_clamp(fy0, BBOX_MIN * vscale, BBOX_MAX * vscale); |
199 | 172M | x1 = (int)fz_clamp(fx1, BBOX_MIN * hscale, BBOX_MAX * hscale); |
200 | 172M | y1 = (int)fz_clamp(fy1, BBOX_MIN * vscale, BBOX_MAX * vscale); |
201 | | |
202 | 172M | d = clip_lerp_y(ras->clip.y0, 0, x0, y0, x1, y1, &v); |
203 | 172M | if (d == OUTSIDE) return; |
204 | 61.4M | if (d == LEAVE) { y1 = ras->clip.y0; x1 = v; } |
205 | 61.4M | if (d == ENTER) { y0 = ras->clip.y0; x0 = v; } |
206 | | |
207 | 61.4M | d = clip_lerp_y(ras->clip.y1, 1, x0, y0, x1, y1, &v); |
208 | 61.4M | if (d == OUTSIDE) return; |
209 | 31.4M | if (d == LEAVE) { y1 = ras->clip.y1; x1 = v; } |
210 | 31.4M | if (d == ENTER) { y0 = ras->clip.y1; x0 = v; } |
211 | | |
212 | 31.4M | d = clip_lerp_x(ras->clip.x0, 0, x0, y0, x1, y1, &v); |
213 | 31.4M | if (d == OUTSIDE) { |
214 | 2.02M | x0 = x1 = ras->clip.x0; |
215 | 2.02M | } |
216 | 31.4M | if (d == LEAVE) { |
217 | 20.9k | fz_insert_gel_raw(ctx, ras, ras->clip.x0, v, ras->clip.x0, y1); |
218 | 20.9k | x1 = ras->clip.x0; |
219 | 20.9k | y1 = v; |
220 | 20.9k | } |
221 | 31.4M | if (d == ENTER) { |
222 | 21.3k | fz_insert_gel_raw(ctx, ras, ras->clip.x0, y0, ras->clip.x0, v); |
223 | 21.3k | x0 = ras->clip.x0; |
224 | 21.3k | y0 = v; |
225 | 21.3k | } |
226 | | |
227 | 31.4M | d = clip_lerp_x(ras->clip.x1, 1, x0, y0, x1, y1, &v); |
228 | 31.4M | if (d == OUTSIDE) { |
229 | 17.0M | x0 = x1 = ras->clip.x1; |
230 | 17.0M | } |
231 | 31.4M | if (d == LEAVE) { |
232 | 10.9k | fz_insert_gel_raw(ctx, ras, ras->clip.x1, v, ras->clip.x1, y1); |
233 | 10.9k | x1 = ras->clip.x1; |
234 | 10.9k | y1 = v; |
235 | 10.9k | } |
236 | 31.4M | if (d == ENTER) { |
237 | 10.4k | fz_insert_gel_raw(ctx, ras, ras->clip.x1, y0, ras->clip.x1, v); |
238 | 10.4k | x0 = ras->clip.x1; |
239 | 10.4k | y0 = v; |
240 | 10.4k | } |
241 | | |
242 | 31.4M | fz_insert_gel_raw(ctx, ras, x0, y0, x1, y1); |
243 | 31.4M | } |
244 | | |
245 | | static void |
246 | | fz_insert_gel_rect(fz_context *ctx, fz_rasterizer *ras, float fx0, float fy0, float fx1, float fy1) |
247 | 637k | { |
248 | 637k | int x0, y0, x1, y1; |
249 | 637k | const int hscale = fz_rasterizer_aa_hscale(ras); |
250 | 637k | const int vscale = fz_rasterizer_aa_vscale(ras); |
251 | | |
252 | 637k | if (fx0 <= fx1) |
253 | 376k | { |
254 | 376k | fx0 = floorf(fx0 * hscale); |
255 | 376k | fx1 = ceilf(fx1 * hscale); |
256 | 376k | } |
257 | 260k | else |
258 | 260k | { |
259 | 260k | fx0 = ceilf(fx0 * hscale); |
260 | 260k | fx1 = floorf(fx1 * hscale); |
261 | 260k | } |
262 | 637k | if (fy0 <= fy1) |
263 | 303k | { |
264 | 303k | fy0 = floorf(fy0 * vscale); |
265 | 303k | fy1 = ceilf(fy1 * vscale); |
266 | 303k | } |
267 | 334k | else |
268 | 334k | { |
269 | 334k | fy0 = ceilf(fy0 * vscale); |
270 | 334k | fy1 = floorf(fy1 * vscale); |
271 | 334k | } |
272 | | |
273 | 637k | fx0 = fz_clamp(fx0, ras->clip.x0, ras->clip.x1); |
274 | 637k | fx1 = fz_clamp(fx1, ras->clip.x0, ras->clip.x1); |
275 | 637k | fy0 = fz_clamp(fy0, ras->clip.y0, ras->clip.y1); |
276 | 637k | fy1 = fz_clamp(fy1, ras->clip.y0, ras->clip.y1); |
277 | | |
278 | | /* Call fz_clamp so that clamping is done in the float domain, THEN |
279 | | * cast down to an int. Calling fz_clampi causes problems due to the |
280 | | * implicit cast down from float to int of the first argument |
281 | | * over/underflowing and flipping sign at extreme values. */ |
282 | 637k | x0 = (int)fz_clamp(fx0, BBOX_MIN * hscale, BBOX_MAX * hscale); |
283 | 637k | y0 = (int)fz_clamp(fy0, BBOX_MIN * vscale, BBOX_MAX * vscale); |
284 | 637k | x1 = (int)fz_clamp(fx1, BBOX_MIN * hscale, BBOX_MAX * hscale); |
285 | 637k | y1 = (int)fz_clamp(fy1, BBOX_MIN * vscale, BBOX_MAX * vscale); |
286 | | |
287 | 637k | fz_insert_gel_raw(ctx, ras, x1, y0, x1, y1); |
288 | 637k | fz_insert_gel_raw(ctx, ras, x0, y1, x0, y0); |
289 | 637k | } |
290 | | |
291 | | static int |
292 | | cmpedge(const void *va, const void *vb) |
293 | 4.40M | { |
294 | 4.40M | const fz_edge *a = va; |
295 | 4.40M | const fz_edge *b = vb; |
296 | 4.40M | return a->y - b->y; |
297 | 4.40M | } |
298 | | |
299 | | static void |
300 | | sort_gel(fz_context *ctx, fz_gel *gel) |
301 | 358k | { |
302 | 358k | fz_edge *a = gel->edges; |
303 | 358k | int n = gel->len; |
304 | 358k | int h, i, k; |
305 | 358k | fz_edge t; |
306 | | |
307 | | /* quick sort for long lists */ |
308 | 358k | if (n > 10000) |
309 | 24 | { |
310 | 24 | qsort(a, n, sizeof *a, cmpedge); |
311 | 24 | return; |
312 | 24 | } |
313 | | |
314 | | /* shell sort for short lists */ |
315 | 358k | h = 1; |
316 | 358k | if (n < 14) { |
317 | 260k | h = 1; |
318 | 260k | } |
319 | 98.2k | else { |
320 | 427k | while (h < n) |
321 | 329k | h = 3 * h + 1; |
322 | 98.2k | h /= 3; |
323 | 98.2k | h /= 3; |
324 | 98.2k | } |
325 | | |
326 | 850k | while (h > 0) |
327 | 491k | { |
328 | 46.4M | for (i = 0; i < n; i++) { |
329 | 45.9M | t = a[i]; |
330 | 45.9M | k = i - h; |
331 | | /* TODO: sort on y major, x minor */ |
332 | 82.3M | while (k >= 0 && a[k].y > t.y) { |
333 | 36.3M | a[k + h] = a[k]; |
334 | 36.3M | k -= h; |
335 | 36.3M | } |
336 | 45.9M | a[k + h] = t; |
337 | 45.9M | } |
338 | 491k | h /= 3; |
339 | 491k | } |
340 | 358k | } |
341 | | |
342 | | static int |
343 | | fz_is_rect_gel(fz_context *ctx, fz_rasterizer *ras) |
344 | 116k | { |
345 | 116k | fz_gel *gel = (fz_gel *)ras; |
346 | | /* a rectangular path is converted into two vertical edges of identical height */ |
347 | 116k | if (gel->len == 2) |
348 | 74.9k | { |
349 | 74.9k | fz_edge *a = gel->edges + 0; |
350 | 74.9k | fz_edge *b = gel->edges + 1; |
351 | 74.9k | return a->y == b->y && a->h == b->h && |
352 | 74.9k | a->xmove == 0 && a->adj_up == 0 && |
353 | 74.9k | b->xmove == 0 && b->adj_up == 0; |
354 | 74.9k | } |
355 | 41.6k | return 0; |
356 | 116k | } |
357 | | |
358 | | /* |
359 | | * Active Edge List -- keep track of active edges while sweeping |
360 | | */ |
361 | | |
362 | | static void |
363 | | sort_active(fz_edge **a, int n) |
364 | 83.2M | { |
365 | 83.2M | int h, i, k; |
366 | 83.2M | fz_edge *t; |
367 | | |
368 | 83.2M | h = 1; |
369 | 83.2M | if (n < 14) { |
370 | 77.1M | h = 1; |
371 | 77.1M | } |
372 | 6.10M | else { |
373 | 24.8M | while (h < n) |
374 | 18.7M | h = 3 * h + 1; |
375 | 6.10M | h /= 3; |
376 | 6.10M | h /= 3; |
377 | 6.10M | } |
378 | | |
379 | 172M | while (h > 0) |
380 | 89.7M | { |
381 | 919M | for (i = 0; i < n; i++) { |
382 | 829M | t = a[i]; |
383 | 829M | k = i - h; |
384 | 863M | while (k >= 0 && a[k]->x > t->x) { |
385 | 33.3M | a[k + h] = a[k]; |
386 | 33.3M | k -= h; |
387 | 33.3M | } |
388 | 829M | a[k + h] = t; |
389 | 829M | } |
390 | | |
391 | 89.7M | h /= 3; |
392 | 89.7M | } |
393 | 83.2M | } |
394 | | |
395 | | static int |
396 | | insert_active(fz_context *ctx, fz_gel *gel, int y, int *e_) |
397 | 83.2M | { |
398 | 83.2M | int h_min = INT_MAX; |
399 | 83.2M | int e = *e_; |
400 | | |
401 | | /* insert edges that start here */ |
402 | 83.2M | if (e < gel->len && gel->edges[e].y == y) |
403 | 6.31M | { |
404 | 10.5M | do { |
405 | 10.5M | if (gel->alen + 1 == gel->acap) { |
406 | 3.28k | int newcap = gel->acap + 64; |
407 | 3.28k | fz_edge **newactive = fz_realloc_array(ctx, gel->active, newcap, fz_edge*); |
408 | 3.28k | gel->active = newactive; |
409 | 3.28k | gel->acap = newcap; |
410 | 3.28k | } |
411 | 10.5M | gel->active[gel->alen++] = &gel->edges[e++]; |
412 | 10.5M | } while (e < gel->len && gel->edges[e].y == y); |
413 | 6.31M | *e_ = e; |
414 | 6.31M | } |
415 | | |
416 | 83.2M | if (e < gel->len) |
417 | 64.3M | h_min = gel->edges[e].y - y; |
418 | | |
419 | 180M | for (e=0; e < gel->alen; e++) |
420 | 178M | { |
421 | 178M | if (gel->active[e]->xmove != 0 || gel->active[e]->adj_up != 0) |
422 | 81.3M | { |
423 | 81.3M | h_min = 1; |
424 | 81.3M | break; |
425 | 81.3M | } |
426 | 97.4M | if (gel->active[e]->h < h_min) |
427 | 3.78M | { |
428 | 3.78M | h_min = gel->active[e]->h; |
429 | 3.78M | if (h_min == 1) |
430 | 234k | break; |
431 | 3.78M | } |
432 | 97.4M | } |
433 | | |
434 | | /* shell-sort the edges by increasing x */ |
435 | 83.2M | sort_active(gel->active, gel->alen); |
436 | | |
437 | 83.2M | return h_min; |
438 | 83.2M | } |
439 | | |
440 | | static void |
441 | | advance_active(fz_context *ctx, fz_gel *gel, int inc) |
442 | 83.2M | { |
443 | 83.2M | fz_edge *edge; |
444 | 83.2M | int i = 0; |
445 | | |
446 | 537M | while (i < gel->alen) |
447 | 454M | { |
448 | 454M | edge = gel->active[i]; |
449 | | |
450 | 454M | edge->h -= inc; |
451 | | |
452 | | /* terminator! */ |
453 | 454M | if (edge->h == 0) { |
454 | 10.5M | gel->active[i] = gel->active[--gel->alen]; |
455 | 10.5M | } |
456 | | |
457 | 444M | else { |
458 | 444M | edge->x += edge->xmove; |
459 | 444M | edge->e += edge->adj_up; |
460 | 444M | if (edge->e > 0) { |
461 | 49.6M | edge->x += edge->xdir; |
462 | 49.6M | edge->e -= edge->adj_down; |
463 | 49.6M | } |
464 | 444M | i ++; |
465 | 444M | } |
466 | 454M | } |
467 | 83.2M | } |
468 | | |
469 | | /* |
470 | | * Anti-aliased scan conversion. |
471 | | */ |
472 | | |
473 | | static inline void |
474 | | add_span_aa(fz_context *ctx, fz_gel *gel, int *list, int x0, int x1, int xofs, int h) |
475 | 156M | { |
476 | 156M | int x0pix, x0sub; |
477 | 156M | int x1pix, x1sub; |
478 | 156M | const int hscale = fz_rasterizer_aa_hscale(&gel->super); |
479 | | |
480 | 156M | if (x0 == x1) |
481 | 64.3M | return; |
482 | | |
483 | | /* x between 0 and width of bbox */ |
484 | 92.2M | x0 -= xofs; |
485 | 92.2M | x1 -= xofs; |
486 | | |
487 | | /* The cast to unsigned below helps the compiler produce faster |
488 | | * code on ARMs as the multiply by reciprocal trick it uses does not |
489 | | * need to correct for signedness. */ |
490 | 92.2M | x0pix = ((unsigned int)x0) / hscale; |
491 | 92.2M | x0sub = ((unsigned int)x0) % hscale; |
492 | 92.2M | x1pix = ((unsigned int)x1) / hscale; |
493 | 92.2M | x1sub = ((unsigned int)x1) % hscale; |
494 | | |
495 | 92.2M | if (x0pix == x1pix) |
496 | 18.8M | { |
497 | 18.8M | list[x0pix] += h*(x1sub - x0sub); |
498 | 18.8M | list[x0pix+1] += h*(x0sub - x1sub); |
499 | 18.8M | } |
500 | | |
501 | 73.3M | else |
502 | 73.3M | { |
503 | 73.3M | list[x0pix] += h*(hscale - x0sub); |
504 | 73.3M | list[x0pix+1] += h*x0sub; |
505 | 73.3M | list[x1pix] += h*(x1sub - hscale); |
506 | 73.3M | list[x1pix+1] += h*-x1sub; |
507 | 73.3M | } |
508 | 92.2M | } |
509 | | |
510 | | static inline void |
511 | | non_zero_winding_aa(fz_context *ctx, fz_gel *gel, int *list, int xofs, int h) |
512 | 82.4M | { |
513 | 82.4M | int winding = 0; |
514 | 82.4M | int x = 0; |
515 | 82.4M | int i; |
516 | | |
517 | 534M | for (i = 0; i < gel->alen; i++) |
518 | 452M | { |
519 | 452M | if (!winding && (winding + gel->active[i]->ydir)) |
520 | 154M | x = gel->active[i]->x; |
521 | 452M | if (winding && !(winding + gel->active[i]->ydir)) |
522 | 154M | add_span_aa(ctx, gel, list, x, gel->active[i]->x, xofs, h); |
523 | 452M | winding += gel->active[i]->ydir; |
524 | 452M | } |
525 | 82.4M | } |
526 | | |
527 | | static inline void |
528 | | even_odd_aa(fz_context *ctx, fz_gel *gel, int *list, int xofs, int h) |
529 | 1.50M | { |
530 | 1.50M | int even = 0; |
531 | 1.50M | int x = 0; |
532 | 1.50M | int i; |
533 | | |
534 | 5.95M | for (i = 0; i < gel->alen; i++) |
535 | 4.44M | { |
536 | 4.44M | if (!even) |
537 | 2.22M | x = gel->active[i]->x; |
538 | 2.22M | else |
539 | 2.22M | add_span_aa(ctx, gel, list, x, gel->active[i]->x, xofs, h); |
540 | 4.44M | even = !even; |
541 | 4.44M | } |
542 | 1.50M | } |
543 | | |
544 | | static inline void |
545 | | undelta_aa(fz_context *ctx, unsigned char * FZ_RESTRICT out, int * FZ_RESTRICT in, int n, int scale) |
546 | 6.62M | { |
547 | 6.62M | int d = 0; |
548 | 6.62M | (void)scale; /* Avoid warnings in some builds */ |
549 | | |
550 | 2.39G | while (n--) |
551 | 2.39G | { |
552 | 2.39G | d += *in++; |
553 | 2.39G | *out++ = AA_SCALE(scale, d); |
554 | 2.39G | } |
555 | 6.62M | } |
556 | | |
557 | | static inline void |
558 | | blit_aa(fz_pixmap *dst, int x, int y, unsigned char *mp, int w, unsigned char *color, void *fn, fz_overprint *eop) |
559 | 22.9M | { |
560 | 22.9M | unsigned char *dp; |
561 | 22.9M | dp = dst->samples + (y - dst->y) * (size_t)dst->stride + (x - dst->x) * (size_t)dst->n; |
562 | 22.9M | if (color) |
563 | 21.8M | (*(fz_span_color_painter_t *)fn)(dp, mp, dst->n, w, color, dst->alpha, eop); |
564 | 1.17M | else |
565 | 1.17M | (*(fz_span_painter_t *)fn)(dp, dst->alpha, mp, 1, 0, w, 255, eop); |
566 | 22.9M | } |
567 | | |
568 | | static void |
569 | | fz_scan_convert_aa(fz_context *ctx, fz_gel *gel, int eofill, const fz_irect *clip, fz_pixmap *dst, unsigned char *color, void *painter, fz_overprint *eop) |
570 | 358k | { |
571 | 358k | unsigned char *alphas; |
572 | 358k | int *deltas; |
573 | 358k | int y, e; |
574 | 358k | int yd, yc; |
575 | 358k | int height, h0, rh; |
576 | 358k | int bcap; |
577 | 358k | const int hscale = fz_rasterizer_aa_hscale(&gel->super); |
578 | 358k | const int vscale = fz_rasterizer_aa_vscale(&gel->super); |
579 | 358k | const int scale = fz_rasterizer_aa_scale(&gel->super); |
580 | | |
581 | 358k | int xmin = fz_idiv(gel->super.bbox.x0, hscale); |
582 | 358k | int xmax = fz_idiv_up(gel->super.bbox.x1, hscale); |
583 | | |
584 | 358k | int xofs = xmin * hscale; |
585 | | |
586 | 358k | int skipx = clip->x0 - xmin; |
587 | 358k | int clipn = clip->x1 - clip->x0; |
588 | | |
589 | 358k | if (gel->len == 0) |
590 | 0 | return; |
591 | | |
592 | 358k | assert(xmin < xmax); |
593 | 358k | assert(clip->x0 >= xmin); |
594 | 358k | assert(clip->x1 <= xmax); |
595 | | |
596 | 358k | bcap = xmax - xmin + 2; /* big enough for both alphas and deltas */ |
597 | 358k | if (bcap > gel->bcap) |
598 | 6.02k | { |
599 | 6.02k | gel->bcap = bcap; |
600 | 6.02k | fz_free(ctx, gel->alphas); |
601 | 6.02k | fz_free(ctx, gel->deltas); |
602 | 6.02k | gel->alphas = NULL; |
603 | 6.02k | gel->deltas = NULL; |
604 | 6.02k | gel->alphas = Memento_label(fz_malloc_array(ctx, bcap, unsigned char), "gel_alphas"); |
605 | 6.02k | gel->deltas = Memento_label(fz_malloc_array(ctx, bcap, int), "gel_deltas"); |
606 | 6.02k | } |
607 | 358k | alphas = gel->alphas; |
608 | 358k | deltas = gel->deltas; |
609 | | |
610 | 358k | memset(deltas, 0, (xmax - xmin + 1) * sizeof(int)); |
611 | 358k | gel->alen = 0; |
612 | | |
613 | | /* The theory here is that we have a list of the edges (gel) of length |
614 | | * gel->len. We have an initially empty list of 'active' edges (of |
615 | | * length gel->alen). As we increase y, we move any edge that is |
616 | | * active at this point into the active list. We know that any edge |
617 | | * before index 'e' is either active, or has been retired. |
618 | | * Once the length of the active list is 0, and e has reached gel->len |
619 | | * we know we are finished. |
620 | | * |
621 | | * As we move through the list, we group fz_aa_vscale 'sub scanlines' |
622 | | * into single scanlines, and we blit them. |
623 | | */ |
624 | | |
625 | 358k | e = 0; |
626 | 358k | y = gel->edges[0].y; |
627 | 358k | yd = fz_idiv(y, vscale); |
628 | | |
629 | | /* Quickly skip to the start of the clip region */ |
630 | 358k | while (yd < clip->y0 && (gel->alen > 0 || e < gel->len)) |
631 | 0 | { |
632 | | /* rh = remaining height = number of subscanlines left to be |
633 | | * inserted into the current scanline, which will be plotted |
634 | | * at yd. */ |
635 | 0 | rh = (yd+1)*vscale - y; |
636 | | |
637 | | /* height = The number of subscanlines with identical edge |
638 | | * positions (i.e. 1 if we have any non vertical edges). */ |
639 | 0 | height = insert_active(ctx, gel, y, &e); |
640 | 0 | h0 = height; |
641 | 0 | if (h0 >= rh) |
642 | 0 | { |
643 | | /* We have enough subscanlines to skip to the next |
644 | | * scanline. */ |
645 | 0 | h0 -= rh; |
646 | 0 | yd++; |
647 | 0 | } |
648 | | /* Skip any whole scanlines we can */ |
649 | 0 | while (yd < clip->y0 && h0 >= vscale) |
650 | 0 | { |
651 | 0 | h0 -= vscale; |
652 | 0 | yd++; |
653 | 0 | } |
654 | | /* If we haven't hit the start of the clip region, then we |
655 | | * have less than a scanline left. */ |
656 | 0 | if (yd < clip->y0) |
657 | 0 | { |
658 | 0 | h0 = 0; |
659 | 0 | } |
660 | 0 | height -= h0; |
661 | 0 | advance_active(ctx, gel, height); |
662 | |
|
663 | 0 | y += height; |
664 | 0 | } |
665 | | |
666 | | /* Now do the active lines */ |
667 | 83.5M | while (gel->alen > 0 || e < gel->len) |
668 | 83.2M | { |
669 | 83.2M | yc = fz_idiv(y, vscale); /* yc = current scanline */ |
670 | | /* rh = remaining height = number of subscanlines left to be |
671 | | * inserted into the current scanline, which will be plotted |
672 | | * at yd. */ |
673 | 83.2M | rh = (yc+1)*vscale - y; |
674 | 83.2M | if (yc != yd) |
675 | 5.54M | { |
676 | 5.54M | undelta_aa(ctx, alphas, deltas, skipx + clipn, scale); |
677 | 5.54M | blit_aa(dst, xmin + skipx, yd, alphas + skipx, clipn, color, painter, eop); |
678 | 5.54M | memset(deltas, 0, (skipx + clipn) * sizeof(int)); |
679 | 5.54M | } |
680 | 83.2M | yd = yc; |
681 | 83.2M | if (yd >= clip->y1) |
682 | 0 | break; |
683 | | |
684 | | /* height = The number of subscanlines with identical edge |
685 | | * positions (i.e. 1 if we have any non vertical edges). */ |
686 | 83.2M | height = insert_active(ctx, gel, y, &e); |
687 | 83.2M | h0 = height; |
688 | 83.2M | if (h0 > rh) |
689 | 557k | { |
690 | 557k | if (rh < vscale) |
691 | 500k | { |
692 | | /* We have to finish a scanline off, and we |
693 | | * have more sub scanlines than will fit into |
694 | | * it. */ |
695 | 500k | if (eofill) |
696 | 5.93k | even_odd_aa(ctx, gel, deltas, xofs, rh); |
697 | 494k | else |
698 | 494k | non_zero_winding_aa(ctx, gel, deltas, xofs, rh); |
699 | 500k | undelta_aa(ctx, alphas, deltas, skipx + clipn, scale); |
700 | 500k | blit_aa(dst, xmin + skipx, yd, alphas + skipx, clipn, color, painter, eop); |
701 | 500k | memset(deltas, 0, (skipx + clipn) * sizeof(int)); |
702 | 500k | yd++; |
703 | 500k | if (yd >= clip->y1) |
704 | 0 | break; |
705 | 500k | h0 -= rh; |
706 | 500k | } |
707 | 557k | if (h0 > vscale) |
708 | 223k | { |
709 | | /* Calculate the deltas for any completely full |
710 | | * scanlines. */ |
711 | 223k | h0 -= vscale; |
712 | 223k | if (eofill) |
713 | 6.55k | even_odd_aa(ctx, gel, deltas, xofs, vscale); |
714 | 217k | else |
715 | 217k | non_zero_winding_aa(ctx, gel, deltas, xofs, vscale); |
716 | 223k | undelta_aa(ctx, alphas, deltas, skipx + clipn, scale); |
717 | 223k | do |
718 | 16.5M | { |
719 | | /* Do any successive whole scanlines - no need |
720 | | * to recalculate deltas here. */ |
721 | 16.5M | blit_aa(dst, xmin + skipx, yd, alphas + skipx, clipn, color, painter, eop); |
722 | 16.5M | yd++; |
723 | 16.5M | if (yd >= clip->y1) |
724 | 0 | goto clip_ended; |
725 | 16.5M | h0 -= vscale; |
726 | 16.5M | } |
727 | 16.5M | while (h0 > 0); |
728 | | /* If we have exactly one full scanline left |
729 | | * to go, then the deltas/alphas are set up |
730 | | * already. */ |
731 | 223k | if (h0 == 0) |
732 | 46.3k | goto advance; |
733 | 177k | memset(deltas, 0, (skipx + clipn) * sizeof(int)); |
734 | 177k | h0 += vscale; |
735 | 177k | } |
736 | 557k | } |
737 | 83.1M | if (eofill) |
738 | 1.49M | even_odd_aa(ctx, gel, deltas, xofs, h0); |
739 | 81.6M | else |
740 | 81.6M | non_zero_winding_aa(ctx, gel, deltas, xofs, h0); |
741 | 83.2M | advance: |
742 | 83.2M | advance_active(ctx, gel, height); |
743 | | |
744 | 83.2M | y += height; |
745 | 83.2M | } |
746 | | |
747 | 358k | if (yd < clip->y1) |
748 | 358k | { |
749 | 358k | undelta_aa(ctx, alphas, deltas, skipx + clipn, scale); |
750 | 358k | blit_aa(dst, xmin + skipx, yd, alphas + skipx, clipn, color, painter, eop); |
751 | 358k | } |
752 | 358k | clip_ended: |
753 | 358k | ; |
754 | 358k | } |
755 | | |
756 | | /* |
757 | | * Sharp (not anti-aliased) scan conversion |
758 | | */ |
759 | | |
760 | | static inline void |
761 | | blit_sharp(int x0, int x1, int y, const fz_irect *clip, fz_pixmap *dst, unsigned char *color, fz_solid_color_painter_t *fn, fz_overprint *eop) |
762 | 0 | { |
763 | 0 | unsigned char *dp; |
764 | 0 | int da = dst->alpha; |
765 | 0 | x0 = fz_clampi(x0, dst->x, dst->x + dst->w); |
766 | 0 | x1 = fz_clampi(x1, dst->x, dst->x + dst->w); |
767 | 0 | if (x0 < x1) |
768 | 0 | { |
769 | 0 | dp = dst->samples + (y - dst->y) * (size_t)dst->stride + (x0 - dst->x) * (size_t)dst->n; |
770 | 0 | if (color) |
771 | 0 | (*fn)(dp, dst->n, x1 - x0, color, da, eop); |
772 | 0 | else |
773 | 0 | memset(dp, 255, x1-x0); |
774 | 0 | } |
775 | 0 | } |
776 | | |
777 | | static inline void |
778 | | non_zero_winding_sharp(fz_context *ctx, fz_gel *gel, int y, const fz_irect *clip, fz_pixmap *dst, unsigned char *color, fz_solid_color_painter_t *fn, fz_overprint *eop) |
779 | 0 | { |
780 | 0 | int winding = 0; |
781 | 0 | int x = 0; |
782 | 0 | int i; |
783 | 0 | for (i = 0; i < gel->alen; i++) |
784 | 0 | { |
785 | 0 | if (!winding && (winding + gel->active[i]->ydir)) |
786 | 0 | x = gel->active[i]->x; |
787 | 0 | if (winding && !(winding + gel->active[i]->ydir)) |
788 | 0 | blit_sharp(x, gel->active[i]->x, y, clip, dst, color, fn, eop); |
789 | 0 | winding += gel->active[i]->ydir; |
790 | 0 | } |
791 | 0 | } |
792 | | |
793 | | static inline void |
794 | | even_odd_sharp(fz_context *ctx, fz_gel *gel, int y, const fz_irect *clip, fz_pixmap *dst, unsigned char *color, fz_solid_color_painter_t *fn, fz_overprint *eop) |
795 | 0 | { |
796 | 0 | int even = 0; |
797 | 0 | int x = 0; |
798 | 0 | int i; |
799 | 0 | for (i = 0; i < gel->alen; i++) |
800 | 0 | { |
801 | 0 | if (!even) |
802 | 0 | x = gel->active[i]->x; |
803 | 0 | else |
804 | 0 | blit_sharp(x, gel->active[i]->x, y, clip, dst, color, fn, eop); |
805 | 0 | even = !even; |
806 | 0 | } |
807 | 0 | } |
808 | | |
809 | | static void |
810 | | fz_scan_convert_sharp(fz_context *ctx, |
811 | | fz_gel *gel, int eofill, const fz_irect *clip, |
812 | | fz_pixmap *dst, unsigned char *color, fz_solid_color_painter_t *fn, fz_overprint *eop) |
813 | 0 | { |
814 | 0 | int e = 0; |
815 | 0 | int y = gel->edges[0].y; |
816 | 0 | int height; |
817 | |
|
818 | 0 | gel->alen = 0; |
819 | | |
820 | | /* Skip any lines before the clip region */ |
821 | 0 | if (y < clip->y0) |
822 | 0 | { |
823 | 0 | while (gel->alen > 0 || e < gel->len) |
824 | 0 | { |
825 | 0 | height = insert_active(ctx, gel, y, &e); |
826 | 0 | y += height; |
827 | 0 | if (y >= clip->y0) |
828 | 0 | { |
829 | 0 | y = clip->y0; |
830 | 0 | break; |
831 | 0 | } |
832 | 0 | } |
833 | 0 | } |
834 | | |
835 | | /* Now process as lines within the clip region */ |
836 | 0 | while (gel->alen > 0 || e < gel->len) |
837 | 0 | { |
838 | 0 | height = insert_active(ctx, gel, y, &e); |
839 | |
|
840 | 0 | if (gel->alen == 0) |
841 | 0 | y += height; |
842 | 0 | else |
843 | 0 | { |
844 | 0 | int h; |
845 | 0 | if (height >= clip->y1 - y) |
846 | 0 | height = clip->y1 - y; |
847 | |
|
848 | 0 | h = height; |
849 | 0 | while (h--) |
850 | 0 | { |
851 | 0 | if (eofill) |
852 | 0 | even_odd_sharp(ctx, gel, y, clip, dst, color, fn, eop); |
853 | 0 | else |
854 | 0 | non_zero_winding_sharp(ctx, gel, y, clip, dst, color, fn, eop); |
855 | 0 | y++; |
856 | 0 | } |
857 | 0 | } |
858 | 0 | if (y >= clip->y1) |
859 | 0 | break; |
860 | | |
861 | 0 | advance_active(ctx, gel, height); |
862 | 0 | } |
863 | 0 | } |
864 | | |
865 | | static void |
866 | | fz_convert_gel(fz_context *ctx, fz_rasterizer *rast, int eofill, const fz_irect *clip, fz_pixmap *dst, unsigned char *color, fz_overprint *eop) |
867 | 358k | { |
868 | 358k | fz_gel *gel = (fz_gel *)rast; |
869 | | |
870 | 358k | sort_gel(ctx, gel); |
871 | | |
872 | 358k | if (fz_aa_bits > 0) |
873 | 358k | { |
874 | 358k | void *fn; |
875 | 358k | if (color) |
876 | 316k | fn = (void *)fz_get_span_color_painter(dst->n, dst->alpha, color, eop); |
877 | 42.2k | else |
878 | 42.2k | fn = (void *)fz_get_span_painter(dst->alpha, 1, 0, 255, eop); |
879 | 358k | if (fn == NULL) |
880 | 0 | return; |
881 | 358k | fz_scan_convert_aa(ctx, gel, eofill, clip, dst, color, fn, eop); |
882 | 358k | } |
883 | 0 | else |
884 | 0 | { |
885 | 0 | fz_solid_color_painter_t *fn = fz_get_solid_color_painter(dst->n, color, dst->alpha, eop); |
886 | 0 | assert(fn); |
887 | 0 | if (fn == NULL) |
888 | 0 | return; |
889 | 0 | fz_scan_convert_sharp(ctx, gel, eofill, clip, dst, color, (fz_solid_color_painter_t *)fn, eop); |
890 | 0 | } |
891 | 358k | } |
892 | | |
893 | | static const fz_rasterizer_fns gel_rasterizer = |
894 | | { |
895 | | fz_drop_gel, |
896 | | fz_reset_gel, |
897 | | NULL, /* postindex */ |
898 | | fz_insert_gel, |
899 | | fz_insert_gel_rect, |
900 | | NULL, /* gap */ |
901 | | fz_convert_gel, |
902 | | fz_is_rect_gel, |
903 | | 0 /* Not reusable */ |
904 | | }; |
905 | | |
906 | | fz_rasterizer * |
907 | | fz_new_gel(fz_context *ctx) |
908 | 16.9k | { |
909 | 16.9k | fz_gel *gel; |
910 | | |
911 | 16.9k | gel = fz_new_derived_rasterizer(ctx, fz_gel, &gel_rasterizer); |
912 | 33.8k | fz_try(ctx) |
913 | 33.8k | { |
914 | 16.9k | gel->edges = NULL; |
915 | 16.9k | gel->cap = 512; |
916 | 16.9k | gel->len = 0; |
917 | 16.9k | gel->edges = Memento_label(fz_malloc_array(ctx, gel->cap, fz_edge), "gel_edges"); |
918 | | |
919 | 16.9k | gel->acap = 64; |
920 | 16.9k | gel->alen = 0; |
921 | 16.9k | gel->active = Memento_label(fz_malloc_array(ctx, gel->acap, fz_edge*), "gel_active"); |
922 | 16.9k | } |
923 | 33.8k | fz_catch(ctx) |
924 | 0 | { |
925 | 0 | fz_free(ctx, gel->edges); |
926 | 0 | fz_free(ctx, gel); |
927 | 0 | fz_rethrow(ctx); |
928 | 0 | } |
929 | | |
930 | 16.9k | return &gel->super; |
931 | 16.9k | } |