1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 /*
24 * This code is based on Libart_LGPL - library of basic graphic primitives
25 *
26 * Copyright (c) 1998 Raph Levien
27 *
28 * Licensed under GNU LGPL v2
29 *
30 */
31
32 /* Various utility functions RLL finds useful. */
33
34 #include "common/math.h"
35 #include "common/textconsole.h"
36
37 #include "sword25/gfx/image/art.h"
38
39 namespace Sword25 {
40
41 /**
42 * art_svp_free: Free an #ArtSVP structure.
43 * @svp: #ArtSVP to free.
44 *
45 * Frees an #ArtSVP structure and all the segments in it.
46 **/
art_svp_free(ArtSVP * svp)47 void art_svp_free(ArtSVP *svp) {
48 int n_segs = svp->n_segs;
49 int i;
50
51 for (i = 0; i < n_segs; i++)
52 free(svp->segs[i].points);
53 free(svp);
54 }
55
56 #define EPSILON 0
57
58 /**
59 * art_svp_seg_compare: Compare two segments of an svp.
60 * @seg1: First segment to compare.
61 * @seg2: Second segment to compare.
62 *
63 * Compares two segments of an svp. Return 1 if @seg2 is below or to the
64 * right of @seg1, -1 otherwise.
65 **/
art_svp_seg_compare(const void * s1,const void * s2)66 int art_svp_seg_compare(const void *s1, const void *s2) {
67 const ArtSVPSeg *seg1 = (const ArtSVPSeg *)s1;
68 const ArtSVPSeg *seg2 = (const ArtSVPSeg *)s2;
69
70 if (seg1->points[0].y - EPSILON > seg2->points[0].y) return 1;
71 else if (seg1->points[0].y + EPSILON < seg2->points[0].y) return -1;
72 else if (seg1->points[0].x - EPSILON > seg2->points[0].x) return 1;
73 else if (seg1->points[0].x + EPSILON < seg2->points[0].x) return -1;
74 else if ((seg1->points[1].x - seg1->points[0].x) *
75 (seg2->points[1].y - seg2->points[0].y) -
76 (seg1->points[1].y - seg1->points[0].y) *
77 (seg2->points[1].x - seg2->points[0].x) > 0) return 1;
78 else return -1;
79 }
80
81 /**
82 * art_vpath_add_point: Add point to vpath.
83 * @p_vpath: Where the pointer to the #ArtVpath structure is stored.
84 * @pn_points: Pointer to the number of points in *@p_vpath.
85 * @pn_points_max: Pointer to the number of points allocated.
86 * @code: The pathcode for the new point.
87 * @x: The X coordinate of the new point.
88 * @y: The Y coordinate of the new point.
89 *
90 * Adds a new point to *@p_vpath, reallocating and updating *@p_vpath
91 * and *@pn_points_max as necessary. *@pn_points is incremented.
92 *
93 * This routine always adds the point after all points already in the
94 * vpath. Thus, it should be called in the order the points are
95 * desired.
96 **/
art_vpath_add_point(ArtVpath ** p_vpath,int * pn_points,int * pn_points_max,ArtPathcode code,double x,double y)97 void art_vpath_add_point(ArtVpath **p_vpath, int *pn_points, int *pn_points_max,
98 ArtPathcode code, double x, double y) {
99 int i;
100
101 i = (*pn_points)++;
102 if (i == *pn_points_max)
103 art_expand(*p_vpath, ArtVpath, *pn_points_max);
104 (*p_vpath)[i].code = code;
105 (*p_vpath)[i].x = x;
106 (*p_vpath)[i].y = y;
107 }
108
109 /* Sort vector paths into sorted vector paths */
110
111 /* reverse a list of points in place */
reverse_points(ArtPoint * points,int n_points)112 static void reverse_points(ArtPoint *points, int n_points) {
113 int i;
114 ArtPoint tmp_p;
115
116 for (i = 0; i < (n_points >> 1); i++) {
117 tmp_p = points[i];
118 points[i] = points[n_points - (i + 1)];
119 points[n_points - (i + 1)] = tmp_p;
120 }
121 }
122
123 /**
124 * art_svp_from_vpath: Convert a vpath to a sorted vector path.
125 * @vpath: #ArtVPath to convert.
126 *
127 * Converts a vector path into sorted vector path form. The svp form is
128 * more efficient for rendering and other vector operations.
129 *
130 * Basically, the implementation is to traverse the vector path,
131 * generating a new segment for each "run" of points in the vector
132 * path with monotonically increasing Y values. All the resulting
133 * values are then sorted.
134 *
135 * Note: I'm not sure that the sorting rule is correct with respect
136 * to numerical stability issues.
137 *
138 * Return value: Resulting sorted vector path.
139 **/
art_svp_from_vpath(ArtVpath * vpath)140 ArtSVP *art_svp_from_vpath(ArtVpath *vpath) {
141 int n_segs, n_segs_max;
142 ArtSVP *svp;
143 int dir;
144 int new_dir;
145 int i;
146 ArtPoint *points;
147 int n_points, n_points_max;
148 double x, y;
149 double x_min, x_max;
150
151 n_segs = 0;
152 n_segs_max = 16;
153 svp = (ArtSVP *)malloc(sizeof(ArtSVP) +
154 (n_segs_max - 1) * sizeof(ArtSVPSeg));
155 if (!svp)
156 error("[art_svp_from_vpath] Cannot allocate memory");
157
158 dir = 0;
159 n_points = 0;
160 n_points_max = 0;
161 points = NULL;
162 i = 0;
163
164 x = y = 0; /* unnecessary, given "first code must not be LINETO" invariant,
165 but it makes gcc -Wall -ansi -pedantic happier */
166 x_min = x_max = 0; /* same */
167
168 while (vpath[i].code != ART_END) {
169 if (vpath[i].code == ART_MOVETO || vpath[i].code == ART_MOVETO_OPEN) {
170 if (points != NULL && n_points >= 2) {
171 if (n_segs == n_segs_max) {
172 n_segs_max <<= 1;
173 ArtSVP *tmp = (ArtSVP *)realloc(svp, sizeof(ArtSVP) +
174 (n_segs_max - 1) *
175 sizeof(ArtSVPSeg));
176
177 if (!tmp)
178 error("Cannot reallocate memory in art_svp_from_vpath()");
179
180 svp = tmp;
181 }
182 svp->segs[n_segs].n_points = n_points;
183 svp->segs[n_segs].dir = (dir > 0);
184 if (dir < 0)
185 reverse_points(points, n_points);
186 svp->segs[n_segs].points = points;
187 svp->segs[n_segs].bbox.x0 = x_min;
188 svp->segs[n_segs].bbox.x1 = x_max;
189 svp->segs[n_segs].bbox.y0 = points[0].y;
190 svp->segs[n_segs].bbox.y1 = points[n_points - 1].y;
191 n_segs++;
192 points = NULL;
193 }
194
195 if (points == NULL) {
196 n_points_max = 4;
197 points = art_new(ArtPoint, n_points_max);
198 }
199
200 n_points = 1;
201 points[0].x = x = vpath[i].x;
202 points[0].y = y = vpath[i].y;
203 x_min = x;
204 x_max = x;
205 dir = 0;
206 } else { /* must be LINETO */
207 new_dir = (vpath[i].y > y ||
208 (vpath[i].y == y && vpath[i].x > x)) ? 1 : -1;
209 if (dir && dir != new_dir) {
210 /* new segment */
211 x = points[n_points - 1].x;
212 y = points[n_points - 1].y;
213 if (n_segs == n_segs_max) {
214 n_segs_max <<= 1;
215 ArtSVP *tmp = (ArtSVP *)realloc(svp, sizeof(ArtSVP) +
216 (n_segs_max - 1) *
217 sizeof(ArtSVPSeg));
218
219 if (!tmp)
220 error("Cannot reallocate memory in art_svp_from_vpath()");
221
222 svp = tmp;
223 }
224 svp->segs[n_segs].n_points = n_points;
225 svp->segs[n_segs].dir = (dir > 0);
226 if (dir < 0)
227 reverse_points(points, n_points);
228 svp->segs[n_segs].points = points;
229 svp->segs[n_segs].bbox.x0 = x_min;
230 svp->segs[n_segs].bbox.x1 = x_max;
231 svp->segs[n_segs].bbox.y0 = points[0].y;
232 svp->segs[n_segs].bbox.y1 = points[n_points - 1].y;
233 n_segs++;
234
235 n_points = 1;
236 n_points_max = 4;
237 points = art_new(ArtPoint, n_points_max);
238 points[0].x = x;
239 points[0].y = y;
240 x_min = x;
241 x_max = x;
242 }
243
244 if (points != NULL) {
245 if (n_points == n_points_max)
246 art_expand(points, ArtPoint, n_points_max);
247 points[n_points].x = x = vpath[i].x;
248 points[n_points].y = y = vpath[i].y;
249 if (x < x_min) x_min = x;
250 else if (x > x_max) x_max = x;
251 n_points++;
252 }
253 dir = new_dir;
254 }
255 i++;
256 }
257
258 if (points != NULL) {
259 if (n_points >= 2) {
260 if (n_segs == n_segs_max) {
261 n_segs_max <<= 1;
262 ArtSVP *tmp = (ArtSVP *)realloc(svp, sizeof(ArtSVP) +
263 (n_segs_max - 1) *
264 sizeof(ArtSVPSeg));
265
266 if (!tmp)
267 error("Cannot reallocate memory in art_svp_from_vpath()");
268
269 svp = tmp;
270 }
271 svp->segs[n_segs].n_points = n_points;
272 svp->segs[n_segs].dir = (dir > 0);
273 if (dir < 0)
274 reverse_points(points, n_points);
275 svp->segs[n_segs].points = points;
276 svp->segs[n_segs].bbox.x0 = x_min;
277 svp->segs[n_segs].bbox.x1 = x_max;
278 svp->segs[n_segs].bbox.y0 = points[0].y;
279 svp->segs[n_segs].bbox.y1 = points[n_points - 1].y;
280 n_segs++;
281 } else
282 free(points);
283 }
284
285 svp->n_segs = n_segs;
286
287 qsort(&svp->segs, n_segs, sizeof(ArtSVPSeg), art_svp_seg_compare);
288
289 return svp;
290 }
291
292
293 /* Basic constructors and operations for bezier paths */
294
295 #define RENDER_LEVEL 4
296 #define RENDER_SIZE (1 << (RENDER_LEVEL))
297
298 /**
299 * art_vpath_render_bez: Render a bezier segment into the vpath.
300 * @p_vpath: Where the pointer to the #ArtVpath structure is stored.
301 * @pn_points: Pointer to the number of points in *@p_vpath.
302 * @pn_points_max: Pointer to the number of points allocated.
303 * @x0: X coordinate of starting bezier point.
304 * @y0: Y coordinate of starting bezier point.
305 * @x1: X coordinate of first bezier control point.
306 * @y1: Y coordinate of first bezier control point.
307 * @x2: X coordinate of second bezier control point.
308 * @y2: Y coordinate of second bezier control point.
309 * @x3: X coordinate of ending bezier point.
310 * @y3: Y coordinate of ending bezier point.
311 * @flatness: Flatness control.
312 *
313 * Renders a bezier segment into the vector path, reallocating and
314 * updating *@p_vpath and *@pn_vpath_max as necessary. *@pn_vpath is
315 * incremented by the number of vector points added.
316 *
317 * This step includes (@x0, @y0) but not (@x3, @y3).
318 *
319 * The @flatness argument guides the amount of subdivision. The Adobe
320 * PostScript reference manual defines flatness as the maximum
321 * deviation between the any point on the vpath approximation and the
322 * corresponding point on the "true" curve, and we follow this
323 * definition here. A value of 0.25 should ensure high quality for aa
324 * rendering.
325 **/
art_vpath_render_bez(ArtVpath ** p_vpath,int * pn,int * pn_max,double x0,double y0,double x1,double y1,double x2,double y2,double x3,double y3,double flatness)326 static void art_vpath_render_bez(ArtVpath **p_vpath, int *pn, int *pn_max,
327 double x0, double y0,
328 double x1, double y1,
329 double x2, double y2,
330 double x3, double y3,
331 double flatness) {
332 /* It's possible to optimize this routine a fair amount.
333
334 First, once the _dot conditions are met, they will also be met in
335 all further subdivisions. So we might recurse to a different
336 routine that only checks the _perp conditions.
337
338 Second, the distance _should_ decrease according to fairly
339 predictable rules (a factor of 4 with each subdivision). So it might
340 be possible to note that the distance is within a factor of 4 of
341 acceptable, and subdivide once. But proving this might be hard.
342
343 Third, at the last subdivision, x_m and y_m can be computed more
344 expeditiously (as in the routine above).
345
346 Finally, if we were able to subdivide by, say 2 or 3, this would
347 allow considerably finer-grain control, i.e. fewer points for the
348 same flatness tolerance. This would speed things up downstream.
349
350 In any case, this routine is unlikely to be the bottleneck. It's
351 just that I have this undying quest for more speed...
352
353 */
354
355 bool subDivide = false;
356
357 double x3_0 = x3 - x0;
358 double y3_0 = y3 - y0;
359
360 // z3_0_dot is dist z0-z3 squared
361 double z3_0_dot = x3_0 * x3_0 + y3_0 * y3_0;
362
363 if (z3_0_dot < 0.001) {
364 /* if start and end point are almost identical, the flatness tests
365 * don't work properly, so fall back on testing whether both of
366 * the other two control points are the same as the start point,
367 * too.
368 */
369 if (!(Common::hypotenuse(x1 - x0, y1 - y0) < 0.001
370 && Common::hypotenuse(x2 - x0, y2 - y0) < 0.001))
371 subDivide = true;
372 } else {
373 /* we can avoid subdivision if:
374
375 z1 has distance no more than flatness from the z0-z3 line
376
377 z1 is no more z0'ward than flatness past z0-z3
378
379 z1 is more z0'ward than z3'ward on the line traversing z0-z3
380
381 and correspondingly for z2 */
382
383 // perp is distance from line, multiplied by dist z0-z3
384 double max_perp_sq = flatness * flatness * z3_0_dot;
385
386 double z1_perp = (y1 - y0) * x3_0 - (x1 - x0) * y3_0;
387 if (z1_perp * z1_perp > max_perp_sq) {
388 subDivide = true;
389 } else {
390 double z2_perp = (y3 - y2) * x3_0 - (x3 - x2) * y3_0;
391 if (z2_perp * z2_perp > max_perp_sq) {
392 subDivide = true;
393 } else {
394 double z1_dot = (x1 - x0) * x3_0 + (y1 - y0) * y3_0;
395 if (z1_dot < 0 && z1_dot * z1_dot > max_perp_sq) {
396 subDivide = true;
397 } else {
398 double z2_dot = (x3 - x2) * x3_0 + (y3 - y2) * y3_0;
399 if (z2_dot < 0 && z2_dot * z2_dot > max_perp_sq)
400 subDivide = true;
401 else if (z1_dot + z1_dot > z3_0_dot)
402 subDivide = true;
403 else if (z2_dot + z2_dot > z3_0_dot)
404 subDivide = true;
405 }
406 }
407 }
408 }
409
410 if (subDivide) {
411 double xa1 = (x0 + x1) * 0.5;
412 double ya1 = (y0 + y1) * 0.5;
413 double xa2 = (x0 + 2 * x1 + x2) * 0.25;
414 double ya2 = (y0 + 2 * y1 + y2) * 0.25;
415 double xb1 = (x1 + 2 * x2 + x3) * 0.25;
416 double yb1 = (y1 + 2 * y2 + y3) * 0.25;
417 double xb2 = (x2 + x3) * 0.5;
418 double yb2 = (y2 + y3) * 0.5;
419 double x_m = (xa2 + xb1) * 0.5;
420 double y_m = (ya2 + yb1) * 0.5;
421
422 art_vpath_render_bez(p_vpath, pn, pn_max,
423 x0, y0, xa1, ya1, xa2, ya2, x_m, y_m, flatness);
424 art_vpath_render_bez(p_vpath, pn, pn_max,
425 x_m, y_m, xb1, yb1, xb2, yb2, x3, y3, flatness);
426 } else {
427 // don't subdivide
428 art_vpath_add_point(p_vpath, pn, pn_max, ART_LINETO, x3, y3);
429 }
430 }
431
432 /**
433 * art_bez_path_to_vec: Create vpath from bezier path.
434 * @bez: Bezier path.
435 * @flatness: Flatness control.
436 *
437 * Creates a vector path closely approximating the bezier path defined by
438 * @bez. The @flatness argument controls the amount of subdivision. In
439 * general, the resulting vpath deviates by at most @flatness pixels
440 * from the "ideal" path described by @bez.
441 *
442 * Return value: Newly allocated vpath.
443 **/
art_bez_path_to_vec(const ArtBpath * bez,double flatness)444 ArtVpath *art_bez_path_to_vec(const ArtBpath *bez, double flatness) {
445 ArtVpath *vec;
446 int vec_n, vec_n_max;
447 int bez_index;
448 double x, y;
449
450 vec_n = 0;
451 vec_n_max = RENDER_SIZE;
452 vec = art_new(ArtVpath, vec_n_max);
453
454 /* Initialization is unnecessary because of the precondition that the
455 bezier path does not begin with LINETO or CURVETO, but is here
456 to make the code warning-free. */
457 x = 0;
458 y = 0;
459
460 bez_index = 0;
461 do {
462 /* make sure space for at least one more code */
463 if (vec_n >= vec_n_max)
464 art_expand(vec, ArtVpath, vec_n_max);
465 switch (bez[bez_index].code) {
466 case ART_MOVETO_OPEN:
467 case ART_MOVETO:
468 case ART_LINETO:
469 x = bez[bez_index].x3;
470 y = bez[bez_index].y3;
471 vec[vec_n].code = bez[bez_index].code;
472 vec[vec_n].x = x;
473 vec[vec_n].y = y;
474 vec_n++;
475 break;
476 case ART_END:
477 vec[vec_n].code = bez[bez_index].code;
478 vec[vec_n].x = 0;
479 vec[vec_n].y = 0;
480 vec_n++;
481 break;
482 case ART_CURVETO:
483 art_vpath_render_bez(&vec, &vec_n, &vec_n_max,
484 x, y,
485 bez[bez_index].x1, bez[bez_index].y1,
486 bez[bez_index].x2, bez[bez_index].y2,
487 bez[bez_index].x3, bez[bez_index].y3,
488 flatness);
489 x = bez[bez_index].x3;
490 y = bez[bez_index].y3;
491 break;
492 default:
493 break;
494 }
495 } while (bez[bez_index++].code != ART_END);
496 return vec;
497 }
498
499
500 #define EPSILON_6 1e-6
501 #define EPSILON_2 1e-12
502
503 /* Render an arc segment starting at (xc + x0, yc + y0) to (xc + x1,
504 yc + y1), centered at (xc, yc), and with given radius. Both x0^2 +
505 y0^2 and x1^2 + y1^2 should be equal to radius^2.
506
507 A positive value of radius means curve to the left, negative means
508 curve to the right.
509 */
art_svp_vpath_stroke_arc(ArtVpath ** p_vpath,int * pn,int * pn_max,double xc,double yc,double x0,double y0,double x1,double y1,double radius,double flatness)510 static void art_svp_vpath_stroke_arc(ArtVpath **p_vpath, int *pn, int *pn_max,
511 double xc, double yc,
512 double x0, double y0,
513 double x1, double y1,
514 double radius,
515 double flatness) {
516 double theta;
517 double th_0, th_1;
518 int n_pts;
519 int i;
520 double aradius;
521
522 aradius = fabs(radius);
523 theta = 2 * M_SQRT2 * sqrt(flatness / aradius);
524 th_0 = atan2(y0, x0);
525 th_1 = atan2(y1, x1);
526 if (radius > 0) {
527 /* curve to the left */
528 if (th_0 < th_1) th_0 += M_PI * 2;
529 n_pts = (int)ceil((th_0 - th_1) / theta);
530 } else {
531 /* curve to the right */
532 if (th_1 < th_0) th_1 += M_PI * 2;
533 n_pts = (int)ceil((th_1 - th_0) / theta);
534 }
535 art_vpath_add_point(p_vpath, pn, pn_max,
536 ART_LINETO, xc + x0, yc + y0);
537 for (i = 1; i < n_pts; i++) {
538 theta = th_0 + (th_1 - th_0) * i / n_pts;
539 art_vpath_add_point(p_vpath, pn, pn_max,
540 ART_LINETO, xc + cos(theta) * aradius,
541 yc + sin(theta) * aradius);
542 }
543 art_vpath_add_point(p_vpath, pn, pn_max,
544 ART_LINETO, xc + x1, yc + y1);
545 }
546
547 /* Assume that forw and rev are at point i0. Bring them to i1,
548 joining with the vector i1 - i2.
549
550 This used to be true, but isn't now that the stroke_raw code is
551 filtering out (near)zero length vectors: {It so happens that all
552 invocations of this function maintain the precondition i1 = i0 + 1,
553 so we could decrease the number of arguments by one. We haven't
554 done that here, though.}
555
556 forw is to the line's right and rev is to its left.
557
558 Precondition: no zero-length vectors, otherwise a divide by
559 zero will happen. */
render_seg(ArtVpath ** p_forw,int * pn_forw,int * pn_forw_max,ArtVpath ** p_rev,int * pn_rev,int * pn_rev_max,ArtVpath * vpath,int i0,int i1,int i2,ArtPathStrokeJoinType join,double line_width,double miter_limit,double flatness)560 static void render_seg(ArtVpath **p_forw, int *pn_forw, int *pn_forw_max,
561 ArtVpath **p_rev, int *pn_rev, int *pn_rev_max,
562 ArtVpath *vpath, int i0, int i1, int i2,
563 ArtPathStrokeJoinType join,
564 double line_width, double miter_limit, double flatness) {
565 double dx0, dy0;
566 double dx1, dy1;
567 double dlx0, dly0;
568 double dlx1, dly1;
569 double dmx, dmy;
570 double dmr2;
571 double scale;
572 double cross;
573
574 /* The vectors of the lines from i0 to i1 and i1 to i2. */
575 dx0 = vpath[i1].x - vpath[i0].x;
576 dy0 = vpath[i1].y - vpath[i0].y;
577
578 dx1 = vpath[i2].x - vpath[i1].x;
579 dy1 = vpath[i2].y - vpath[i1].y;
580
581 /* Set dl[xy]0 to the vector from i0 to i1, rotated counterclockwise
582 90 degrees, and scaled to the length of line_width. */
583 scale = line_width / sqrt(dx0 * dx0 + dy0 * dy0);
584 dlx0 = dy0 * scale;
585 dly0 = -dx0 * scale;
586
587 /* Set dl[xy]1 to the vector from i1 to i2, rotated counterclockwise
588 90 degrees, and scaled to the length of line_width. */
589 scale = line_width / sqrt(dx1 * dx1 + dy1 * dy1);
590 dlx1 = dy1 * scale;
591 dly1 = -dx1 * scale;
592
593 /* now, forw's last point is expected to be colinear along d[xy]0
594 to point i0 - dl[xy]0, and rev with i0 + dl[xy]0. */
595
596 /* positive for positive area (i.e. left turn) */
597 cross = dx1 * dy0 - dx0 * dy1;
598
599 dmx = (dlx0 + dlx1) * 0.5;
600 dmy = (dly0 + dly1) * 0.5;
601 dmr2 = dmx * dmx + dmy * dmy;
602
603 if (join == ART_PATH_STROKE_JOIN_MITER &&
604 dmr2 * miter_limit * miter_limit < line_width * line_width)
605 join = ART_PATH_STROKE_JOIN_BEVEL;
606
607 /* the case when dmr2 is zero or very small bothers me
608 (i.e. near a 180 degree angle)
609 ALEX: So, we avoid the optimization when dmr2 is very small. This should
610 be safe since dmx/y is only used in optimization and in MITER case, and MITER
611 should be converted to BEVEL when dmr2 is very small. */
612 if (dmr2 > EPSILON_2) {
613 scale = line_width * line_width / dmr2;
614 dmx *= scale;
615 dmy *= scale;
616 }
617
618 if (cross *cross < EPSILON_2 && dx0 *dx1 + dy0 *dy1 >= 0) {
619 /* going straight */
620 art_vpath_add_point(p_forw, pn_forw, pn_forw_max,
621 ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0);
622 art_vpath_add_point(p_rev, pn_rev, pn_rev_max,
623 ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0);
624 } else if (cross > 0) {
625 /* left turn, forw is outside and rev is inside */
626
627 if (
628 (dmr2 > EPSILON_2) &&
629 /* check that i1 + dm[xy] is inside i0-i1 rectangle */
630 (dx0 + dmx) * dx0 + (dy0 + dmy) * dy0 > 0 &&
631 /* and that i1 + dm[xy] is inside i1-i2 rectangle */
632 ((dx1 - dmx) * dx1 + (dy1 - dmy) * dy1 > 0)
633 #ifdef PEDANTIC_INNER
634 &&
635 /* check that i1 + dl[xy]1 is inside i0-i1 rectangle */
636 (dx0 + dlx1) * dx0 + (dy0 + dly1) * dy0 > 0 &&
637 /* and that i1 + dl[xy]0 is inside i1-i2 rectangle */
638 ((dx1 - dlx0) * dx1 + (dy1 - dly0) * dy1 > 0)
639 #endif
640 ) {
641 /* can safely add single intersection point */
642 art_vpath_add_point(p_rev, pn_rev, pn_rev_max,
643 ART_LINETO, vpath[i1].x + dmx, vpath[i1].y + dmy);
644 } else {
645 /* need to loop-de-loop the inside */
646 art_vpath_add_point(p_rev, pn_rev, pn_rev_max,
647 ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0);
648 art_vpath_add_point(p_rev, pn_rev, pn_rev_max,
649 ART_LINETO, vpath[i1].x, vpath[i1].y);
650 art_vpath_add_point(p_rev, pn_rev, pn_rev_max,
651 ART_LINETO, vpath[i1].x + dlx1, vpath[i1].y + dly1);
652 }
653
654 if (join == ART_PATH_STROKE_JOIN_BEVEL) {
655 /* bevel */
656 art_vpath_add_point(p_forw, pn_forw, pn_forw_max,
657 ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0);
658 art_vpath_add_point(p_forw, pn_forw, pn_forw_max,
659 ART_LINETO, vpath[i1].x - dlx1, vpath[i1].y - dly1);
660 } else if (join == ART_PATH_STROKE_JOIN_MITER) {
661 art_vpath_add_point(p_forw, pn_forw, pn_forw_max,
662 ART_LINETO, vpath[i1].x - dmx, vpath[i1].y - dmy);
663 } else if (join == ART_PATH_STROKE_JOIN_ROUND)
664 art_svp_vpath_stroke_arc(p_forw, pn_forw, pn_forw_max,
665 vpath[i1].x, vpath[i1].y,
666 -dlx0, -dly0,
667 -dlx1, -dly1,
668 line_width,
669 flatness);
670 } else {
671 /* right turn, rev is outside and forw is inside */
672
673 if (
674 (dmr2 > EPSILON_2) &&
675 /* check that i1 - dm[xy] is inside i0-i1 rectangle */
676 (dx0 - dmx) * dx0 + (dy0 - dmy) * dy0 > 0 &&
677 /* and that i1 - dm[xy] is inside i1-i2 rectangle */
678 ((dx1 + dmx) * dx1 + (dy1 + dmy) * dy1 > 0)
679 #ifdef PEDANTIC_INNER
680 &&
681 /* check that i1 - dl[xy]1 is inside i0-i1 rectangle */
682 (dx0 - dlx1) * dx0 + (dy0 - dly1) * dy0 > 0 &&
683 /* and that i1 - dl[xy]0 is inside i1-i2 rectangle */
684 ((dx1 + dlx0) * dx1 + (dy1 + dly0) * dy1 > 0)
685 #endif
686 ) {
687 /* can safely add single intersection point */
688 art_vpath_add_point(p_forw, pn_forw, pn_forw_max,
689 ART_LINETO, vpath[i1].x - dmx, vpath[i1].y - dmy);
690 } else {
691 /* need to loop-de-loop the inside */
692 art_vpath_add_point(p_forw, pn_forw, pn_forw_max,
693 ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0);
694 art_vpath_add_point(p_forw, pn_forw, pn_forw_max,
695 ART_LINETO, vpath[i1].x, vpath[i1].y);
696 art_vpath_add_point(p_forw, pn_forw, pn_forw_max,
697 ART_LINETO, vpath[i1].x - dlx1, vpath[i1].y - dly1);
698 }
699
700 if (join == ART_PATH_STROKE_JOIN_BEVEL) {
701 /* bevel */
702 art_vpath_add_point(p_rev, pn_rev, pn_rev_max,
703 ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0);
704 art_vpath_add_point(p_rev, pn_rev, pn_rev_max,
705 ART_LINETO, vpath[i1].x + dlx1, vpath[i1].y + dly1);
706 } else if (join == ART_PATH_STROKE_JOIN_MITER) {
707 art_vpath_add_point(p_rev, pn_rev, pn_rev_max,
708 ART_LINETO, vpath[i1].x + dmx, vpath[i1].y + dmy);
709 } else if (join == ART_PATH_STROKE_JOIN_ROUND)
710 art_svp_vpath_stroke_arc(p_rev, pn_rev, pn_rev_max,
711 vpath[i1].x, vpath[i1].y,
712 dlx0, dly0,
713 dlx1, dly1,
714 -line_width,
715 flatness);
716
717 }
718 }
719
720 /* caps i1, under the assumption of a vector from i0 */
render_cap(ArtVpath ** p_result,int * pn_result,int * pn_result_max,ArtVpath * vpath,int i0,int i1,ArtPathStrokeCapType cap,double line_width,double flatness)721 static void render_cap(ArtVpath **p_result, int *pn_result, int *pn_result_max,
722 ArtVpath *vpath, int i0, int i1,
723 ArtPathStrokeCapType cap, double line_width, double flatness) {
724 double dx0, dy0;
725 double dlx0, dly0;
726 double scale;
727 int n_pts;
728 int i;
729
730 dx0 = vpath[i1].x - vpath[i0].x;
731 dy0 = vpath[i1].y - vpath[i0].y;
732
733 /* Set dl[xy]0 to the vector from i0 to i1, rotated counterclockwise
734 90 degrees, and scaled to the length of line_width. */
735 scale = line_width / sqrt(dx0 * dx0 + dy0 * dy0);
736 dlx0 = dy0 * scale;
737 dly0 = -dx0 * scale;
738
739 switch (cap) {
740 case ART_PATH_STROKE_CAP_BUTT:
741 art_vpath_add_point(p_result, pn_result, pn_result_max,
742 ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0);
743 art_vpath_add_point(p_result, pn_result, pn_result_max,
744 ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0);
745 break;
746 case ART_PATH_STROKE_CAP_ROUND:
747 n_pts = (int)ceil(M_PI / (2.0 * M_SQRT2 * sqrt(flatness / line_width)));
748 art_vpath_add_point(p_result, pn_result, pn_result_max,
749 ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0);
750 for (i = 1; i < n_pts; i++) {
751 double theta, c_th, s_th;
752
753 theta = M_PI * i / n_pts;
754 c_th = cos(theta);
755 s_th = sin(theta);
756 art_vpath_add_point(p_result, pn_result, pn_result_max,
757 ART_LINETO,
758 vpath[i1].x - dlx0 * c_th - dly0 * s_th,
759 vpath[i1].y - dly0 * c_th + dlx0 * s_th);
760 }
761 art_vpath_add_point(p_result, pn_result, pn_result_max,
762 ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0);
763 break;
764 case ART_PATH_STROKE_CAP_SQUARE:
765 art_vpath_add_point(p_result, pn_result, pn_result_max,
766 ART_LINETO,
767 vpath[i1].x - dlx0 - dly0,
768 vpath[i1].y - dly0 + dlx0);
769 art_vpath_add_point(p_result, pn_result, pn_result_max,
770 ART_LINETO,
771 vpath[i1].x + dlx0 - dly0,
772 vpath[i1].y + dly0 + dlx0);
773 break;
774 default:
775 break;
776 }
777 }
778
779 /**
780 * art_svp_from_vpath_raw: Stroke a vector path, raw version
781 * @vpath: #ArtVPath to stroke.
782 * @join: Join style.
783 * @cap: Cap style.
784 * @line_width: Width of stroke.
785 * @miter_limit: Miter limit.
786 * @flatness: Flatness.
787 *
788 * Exactly the same as art_svp_vpath_stroke(), except that the resulting
789 * stroke outline may self-intersect and have regions of winding number
790 * greater than 1.
791 *
792 * Return value: Resulting raw stroked outline in svp format.
793 **/
art_svp_vpath_stroke_raw(ArtVpath * vpath,ArtPathStrokeJoinType join,ArtPathStrokeCapType cap,double line_width,double miter_limit,double flatness)794 ArtVpath *art_svp_vpath_stroke_raw(ArtVpath *vpath,
795 ArtPathStrokeJoinType join,
796 ArtPathStrokeCapType cap,
797 double line_width,
798 double miter_limit,
799 double flatness) {
800 int begin_idx, end_idx;
801 int i;
802 ArtVpath *forw, *rev;
803 int n_forw, n_rev;
804 int n_forw_max, n_rev_max;
805 ArtVpath *result;
806 int n_result, n_result_max;
807 double half_lw = 0.5 * line_width;
808 int closed;
809 int last, this_, next, second;
810 double dx, dy;
811
812 n_forw_max = 16;
813 forw = art_new(ArtVpath, n_forw_max);
814
815 n_rev_max = 16;
816 rev = art_new(ArtVpath, n_rev_max);
817
818 n_result = 0;
819 n_result_max = 16;
820 result = art_new(ArtVpath, n_result_max);
821
822 for (begin_idx = 0; vpath[begin_idx].code != ART_END; begin_idx = end_idx) {
823 n_forw = 0;
824 n_rev = 0;
825
826 closed = (vpath[begin_idx].code == ART_MOVETO);
827
828 /* we don't know what the first point joins with until we get to the
829 last point and see if it's closed. So we start with the second
830 line in the path.
831
832 Note: this is not strictly true (we now know it's closed from
833 the opening pathcode), but why fix code that isn't broken?
834 */
835
836 this_ = begin_idx;
837 /* skip over identical points at the beginning of the subpath */
838 for (i = this_ + 1; vpath[i].code == ART_LINETO; i++) {
839 dx = vpath[i].x - vpath[this_].x;
840 dy = vpath[i].y - vpath[this_].y;
841 if (dx * dx + dy * dy > EPSILON_2)
842 break;
843 }
844 next = i;
845 second = next;
846
847 /* invariant: this doesn't coincide with next */
848 while (vpath[next].code == ART_LINETO) {
849 last = this_;
850 this_ = next;
851 /* skip over identical points after the beginning of the subpath */
852 for (i = this_ + 1; vpath[i].code == ART_LINETO; i++) {
853 dx = vpath[i].x - vpath[this_].x;
854 dy = vpath[i].y - vpath[this_].y;
855 if (dx * dx + dy * dy > EPSILON_2)
856 break;
857 }
858 next = i;
859 if (vpath[next].code != ART_LINETO) {
860 /* reached end of path */
861 /* make "closed" detection conform to PostScript
862 semantics (i.e. explicit closepath code rather than
863 just the fact that end of the path is the beginning) */
864 if (closed &&
865 vpath[this_].x == vpath[begin_idx].x &&
866 vpath[this_].y == vpath[begin_idx].y) {
867 int j;
868
869 /* path is closed, render join to beginning */
870 render_seg(&forw, &n_forw, &n_forw_max,
871 &rev, &n_rev, &n_rev_max,
872 vpath, last, this_, second,
873 join, half_lw, miter_limit, flatness);
874
875 /* do forward path */
876 art_vpath_add_point(&result, &n_result, &n_result_max,
877 ART_MOVETO, forw[n_forw - 1].x,
878 forw[n_forw - 1].y);
879 for (j = 0; j < n_forw; j++)
880 art_vpath_add_point(&result, &n_result, &n_result_max,
881 ART_LINETO, forw[j].x,
882 forw[j].y);
883
884 /* do reverse path, reversed */
885 art_vpath_add_point(&result, &n_result, &n_result_max,
886 ART_MOVETO, rev[0].x,
887 rev[0].y);
888 for (j = n_rev - 1; j >= 0; j--)
889 art_vpath_add_point(&result, &n_result, &n_result_max,
890 ART_LINETO, rev[j].x,
891 rev[j].y);
892 } else {
893 /* path is open */
894 int j;
895
896 /* add to forw rather than result to ensure that
897 forw has at least one point. */
898 render_cap(&forw, &n_forw, &n_forw_max,
899 vpath, last, this_,
900 cap, half_lw, flatness);
901 art_vpath_add_point(&result, &n_result, &n_result_max,
902 ART_MOVETO, forw[0].x,
903 forw[0].y);
904 for (j = 1; j < n_forw; j++)
905 art_vpath_add_point(&result, &n_result, &n_result_max,
906 ART_LINETO, forw[j].x,
907 forw[j].y);
908 for (j = n_rev - 1; j >= 0; j--)
909 art_vpath_add_point(&result, &n_result, &n_result_max,
910 ART_LINETO, rev[j].x,
911 rev[j].y);
912 render_cap(&result, &n_result, &n_result_max,
913 vpath, second, begin_idx,
914 cap, half_lw, flatness);
915 art_vpath_add_point(&result, &n_result, &n_result_max,
916 ART_LINETO, forw[0].x,
917 forw[0].y);
918 }
919 } else
920 render_seg(&forw, &n_forw, &n_forw_max,
921 &rev, &n_rev, &n_rev_max,
922 vpath, last, this_, next,
923 join, half_lw, miter_limit, flatness);
924 }
925 end_idx = next;
926 }
927
928 free(forw);
929 free(rev);
930 art_vpath_add_point(&result, &n_result, &n_result_max, ART_END, 0, 0);
931 return result;
932 }
933
934
935 /* Render a vector path into a stroked outline.
936
937 Status of this routine:
938
939 Basic correctness: Only miter and bevel line joins are implemented,
940 and only butt line caps. Otherwise, seems to be fine.
941
942 Numerical stability: We cheat (adding random perturbation). Thus,
943 it seems very likely that no numerical stability problems will be
944 seen in practice.
945
946 Speed: Should be pretty good.
947
948 Precision: The perturbation fuzzes the coordinates slightly,
949 but not enough to be visible. */
950
951 /**
952 * art_svp_vpath_stroke: Stroke a vector path.
953 * @vpath: #ArtVPath to stroke.
954 * @join: Join style.
955 * @cap: Cap style.
956 * @line_width: Width of stroke.
957 * @miter_limit: Miter limit.
958 * @flatness: Flatness.
959 *
960 * Computes an svp representing the stroked outline of @vpath. The
961 * width of the stroked line is @line_width.
962 *
963 * Lines are joined according to the @join rule. Possible values are
964 * ART_PATH_STROKE_JOIN_MITER (for mitered joins),
965 * ART_PATH_STROKE_JOIN_ROUND (for round joins), and
966 * ART_PATH_STROKE_JOIN_BEVEL (for bevelled joins). The mitered join
967 * is converted to a bevelled join if the miter would extend to a
968 * distance of more than @miter_limit * @line_width from the actual
969 * join point.
970 *
971 * If there are open subpaths, the ends of these subpaths are capped
972 * according to the @cap rule. Possible values are
973 * ART_PATH_STROKE_CAP_BUTT (squared cap, extends exactly to end
974 * point), ART_PATH_STROKE_CAP_ROUND (rounded half-circle centered at
975 * the end point), and ART_PATH_STROKE_CAP_SQUARE (squared cap,
976 * extending half @line_width past the end point).
977 *
978 * The @flatness parameter controls the accuracy of the rendering. It
979 * is most important for determining the number of points to use to
980 * approximate circular arcs for round lines and joins. In general, the
981 * resulting vector path will be within @flatness pixels of the "ideal"
982 * path containing actual circular arcs. I reserve the right to use
983 * the @flatness parameter to convert bevelled joins to miters for very
984 * small turn angles, as this would reduce the number of points in the
985 * resulting outline path.
986 *
987 * The resulting path is "clean" with respect to self-intersections, i.e.
988 * the winding number is 0 or 1 at each point.
989 *
990 * Return value: Resulting stroked outline in svp format.
991 **/
art_svp_vpath_stroke(ArtVpath * vpath,ArtPathStrokeJoinType join,ArtPathStrokeCapType cap,double line_width,double miter_limit,double flatness)992 ArtSVP *art_svp_vpath_stroke(ArtVpath *vpath,
993 ArtPathStrokeJoinType join,
994 ArtPathStrokeCapType cap,
995 double line_width,
996 double miter_limit,
997 double flatness) {
998 ArtVpath *vpath_stroke;
999 ArtSVP *svp, *svp2;
1000 ArtSvpWriter *swr;
1001
1002 vpath_stroke = art_svp_vpath_stroke_raw(vpath, join, cap,
1003 line_width, miter_limit, flatness);
1004 svp = art_svp_from_vpath(vpath_stroke);
1005 free(vpath_stroke);
1006
1007 swr = art_svp_writer_rewind_new(ART_WIND_RULE_NONZERO);
1008 art_svp_intersector(svp, swr);
1009
1010 svp2 = art_svp_writer_rewind_reap(swr);
1011 art_svp_free(svp);
1012 return svp2;
1013 }
1014
1015
1016 /* Testbed implementation of the new intersection code.
1017 */
1018
1019 typedef struct _ArtPriQ ArtPriQ;
1020 typedef struct _ArtPriPoint ArtPriPoint;
1021
1022 struct _ArtPriQ {
1023 int n_items;
1024 int n_items_max;
1025 ArtPriPoint **items;
1026 };
1027
1028 struct _ArtPriPoint {
1029 double x;
1030 double y;
1031 void *user_data;
1032 };
1033
art_pri_new(void)1034 static ArtPriQ *art_pri_new(void) {
1035 ArtPriQ *result = art_new(ArtPriQ, 1);
1036 if (!result)
1037 error("[art_pri_new] Cannot allocate memory");
1038
1039 result->n_items = 0;
1040 result->n_items_max = 16;
1041 result->items = art_new(ArtPriPoint *, result->n_items_max);
1042 return result;
1043 }
1044
art_pri_free(ArtPriQ * pq)1045 static void art_pri_free(ArtPriQ *pq) {
1046 free(pq->items);
1047 free(pq);
1048 }
1049
art_pri_empty(ArtPriQ * pq)1050 static bool art_pri_empty(ArtPriQ *pq) {
1051 return pq->n_items == 0;
1052 }
1053
1054 /* This heap implementation is based on Vasek Chvatal's course notes:
1055 http://www.cs.rutgers.edu/~chvatal/notes/pq.html#heap */
1056
art_pri_bubble_up(ArtPriQ * pq,int vacant,ArtPriPoint * missing)1057 static void art_pri_bubble_up(ArtPriQ *pq, int vacant, ArtPriPoint *missing) {
1058 ArtPriPoint **items = pq->items;
1059 int parent;
1060
1061 parent = (vacant - 1) >> 1;
1062 while (vacant > 0 && (missing->y < items[parent]->y ||
1063 (missing->y == items[parent]->y &&
1064 missing->x < items[parent]->x))) {
1065 items[vacant] = items[parent];
1066 vacant = parent;
1067 parent = (vacant - 1) >> 1;
1068 }
1069
1070 items[vacant] = missing;
1071 }
1072
art_pri_insert(ArtPriQ * pq,ArtPriPoint * point)1073 static void art_pri_insert(ArtPriQ *pq, ArtPriPoint *point) {
1074 if (pq->n_items == pq->n_items_max)
1075 art_expand(pq->items, ArtPriPoint *, pq->n_items_max);
1076
1077 art_pri_bubble_up(pq, pq->n_items++, point);
1078 }
1079
art_pri_sift_down_from_root(ArtPriQ * pq,ArtPriPoint * missing)1080 static void art_pri_sift_down_from_root(ArtPriQ *pq, ArtPriPoint *missing) {
1081 ArtPriPoint **items = pq->items;
1082 int vacant = 0, child = 2;
1083 int n = pq->n_items;
1084
1085 while (child < n) {
1086 if (items[child - 1]->y < items[child]->y ||
1087 (items[child - 1]->y == items[child]->y &&
1088 items[child - 1]->x < items[child]->x))
1089 child--;
1090 items[vacant] = items[child];
1091 vacant = child;
1092 child = (vacant + 1) << 1;
1093 }
1094 if (child == n) {
1095 items[vacant] = items[n - 1];
1096 vacant = n - 1;
1097 }
1098
1099 art_pri_bubble_up(pq, vacant, missing);
1100 }
1101
art_pri_choose(ArtPriQ * pq)1102 static ArtPriPoint *art_pri_choose(ArtPriQ *pq) {
1103 ArtPriPoint *result = pq->items[0];
1104
1105 art_pri_sift_down_from_root(pq, pq->items[--pq->n_items]);
1106 return result;
1107 }
1108
1109 /* A virtual class for an "svp writer". A client of this object creates an
1110 SVP by repeatedly calling "add segment" and "add point" methods on it.
1111 */
1112
1113 typedef struct _ArtSvpWriterRewind ArtSvpWriterRewind;
1114
1115 /* An implementation of the svp writer virtual class that applies the
1116 winding rule. */
1117
1118 struct _ArtSvpWriterRewind {
1119 ArtSvpWriter super;
1120 ArtWindRule rule;
1121 ArtSVP *svp;
1122 int n_segs_max;
1123 int *n_points_max;
1124 };
1125
art_svp_writer_rewind_add_segment(ArtSvpWriter * self,int wind_left,int delta_wind,double x,double y)1126 static int art_svp_writer_rewind_add_segment(ArtSvpWriter *self, int wind_left,
1127 int delta_wind, double x, double y) {
1128 ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self;
1129 ArtSVP *svp;
1130 ArtSVPSeg *seg;
1131 bool left_filled = 0, right_filled = 0;
1132 int wind_right = wind_left + delta_wind;
1133 int seg_num;
1134 const int init_n_points_max = 4;
1135
1136 switch (swr->rule) {
1137 case ART_WIND_RULE_NONZERO:
1138 left_filled = (wind_left != 0);
1139 right_filled = (wind_right != 0);
1140 break;
1141 case ART_WIND_RULE_INTERSECT:
1142 left_filled = (wind_left > 1);
1143 right_filled = (wind_right > 1);
1144 break;
1145 case ART_WIND_RULE_ODDEVEN:
1146 left_filled = (wind_left & 1);
1147 right_filled = (wind_right & 1);
1148 break;
1149 case ART_WIND_RULE_POSITIVE:
1150 left_filled = (wind_left > 0);
1151 right_filled = (wind_right > 0);
1152 break;
1153 default:
1154 error("Unknown wind rule %d", swr->rule);
1155 }
1156 if (left_filled == right_filled) {
1157 /* discard segment now */
1158 return -1;
1159 }
1160
1161 svp = swr->svp;
1162 seg_num = svp->n_segs++;
1163 if (swr->n_segs_max == seg_num) {
1164 swr->n_segs_max <<= 1;
1165 svp = (ArtSVP *)realloc(svp, sizeof(ArtSVP) +
1166 (swr->n_segs_max - 1) *
1167 sizeof(ArtSVPSeg));
1168 swr->svp = svp;
1169 int *tmp = art_renew(swr->n_points_max, int,
1170 swr->n_segs_max);
1171
1172 if (!tmp)
1173 error("Cannot reallocate memory in art_svp_writer_rewind_add_segment()");
1174
1175 swr->n_points_max = tmp;
1176 }
1177 seg = &svp->segs[seg_num];
1178 seg->n_points = 1;
1179 seg->dir = right_filled;
1180 swr->n_points_max[seg_num] = init_n_points_max;
1181 seg->bbox.x0 = x;
1182 seg->bbox.y0 = y;
1183 seg->bbox.x1 = x;
1184 seg->bbox.y1 = y;
1185 seg->points = art_new(ArtPoint, init_n_points_max);
1186 if (!seg->points)
1187 error("[art_svp_writer_rewind_add_segment] Cannot allocate memory");
1188
1189 seg->points[0].x = x;
1190 seg->points[0].y = y;
1191 return seg_num;
1192 }
1193
art_svp_writer_rewind_add_point(ArtSvpWriter * self,int seg_id,double x,double y)1194 static void art_svp_writer_rewind_add_point(ArtSvpWriter *self, int seg_id,
1195 double x, double y) {
1196 ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self;
1197 ArtSVPSeg *seg;
1198 int n_points;
1199
1200 if (seg_id < 0)
1201 /* omitted segment */
1202 return;
1203
1204 seg = &swr->svp->segs[seg_id];
1205 n_points = seg->n_points++;
1206 if (swr->n_points_max[seg_id] == n_points)
1207 art_expand(seg->points, ArtPoint, swr->n_points_max[seg_id]);
1208 seg->points[n_points].x = x;
1209 seg->points[n_points].y = y;
1210 if (x < seg->bbox.x0)
1211 seg->bbox.x0 = x;
1212 if (x > seg->bbox.x1)
1213 seg->bbox.x1 = x;
1214 seg->bbox.y1 = y;
1215 }
1216
art_svp_writer_rewind_close_segment(ArtSvpWriter * self,int seg_id)1217 static void art_svp_writer_rewind_close_segment(ArtSvpWriter *self, int seg_id) {
1218 /* Not needed for this simple implementation. A potential future
1219 optimization is to merge segments that can be merged safely. */
1220 }
1221
art_svp_writer_rewind_reap(ArtSvpWriter * self)1222 ArtSVP *art_svp_writer_rewind_reap(ArtSvpWriter *self) {
1223 ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self;
1224 ArtSVP *result = swr->svp;
1225
1226 free(swr->n_points_max);
1227 free(swr);
1228 return result;
1229 }
1230
art_svp_writer_rewind_new(ArtWindRule rule)1231 ArtSvpWriter *art_svp_writer_rewind_new(ArtWindRule rule) {
1232 ArtSvpWriterRewind *result = art_new(ArtSvpWriterRewind, 1);
1233 if (!result)
1234 error("[art_svp_writer_rewind_new] Cannot allocate memory");
1235
1236 result->super.add_segment = art_svp_writer_rewind_add_segment;
1237 result->super.add_point = art_svp_writer_rewind_add_point;
1238 result->super.close_segment = art_svp_writer_rewind_close_segment;
1239
1240 result->rule = rule;
1241 result->n_segs_max = 16;
1242 result->svp = (ArtSVP *)malloc(sizeof(ArtSVP) +
1243 (result->n_segs_max - 1) * sizeof(ArtSVPSeg));
1244 if (!result->svp)
1245 error("[art_svp_writer_rewind_new] Cannot allocate memory");
1246
1247 result->svp->n_segs = 0;
1248 result->n_points_max = art_new(int, result->n_segs_max);
1249
1250 return &result->super;
1251 }
1252
1253 /* Now, data structures for the active list */
1254
1255 typedef struct _ArtActiveSeg ArtActiveSeg;
1256
1257 /* Note: BNEG is 1 for \ lines, and 0 for /. Thus,
1258 x[(flags & BNEG) ^ 1] <= x[flags & BNEG] */
1259 #define ART_ACTIVE_FLAGS_BNEG 1
1260
1261 /* This flag is set if the segment has been inserted into the active
1262 list. */
1263 #define ART_ACTIVE_FLAGS_IN_ACTIVE 2
1264
1265 /* This flag is set when the segment is to be deleted in the
1266 horiz commit process. */
1267 #define ART_ACTIVE_FLAGS_DEL 4
1268
1269 /* This flag is set if the seg_id is a valid output segment. */
1270 #define ART_ACTIVE_FLAGS_OUT 8
1271
1272 /* This flag is set if the segment is in the horiz list. */
1273 #define ART_ACTIVE_FLAGS_IN_HORIZ 16
1274
1275 struct _ArtActiveSeg {
1276 int flags;
1277 int wind_left, delta_wind;
1278 ArtActiveSeg *left, *right; /* doubly linked list structure */
1279
1280 const ArtSVPSeg *in_seg;
1281 int in_curs;
1282
1283 double x[2];
1284 double y0, y1;
1285 double a, b, c; /* line equation; ax+by+c = 0 for the line, a^2 + b^2 = 1,
1286 and a>0 */
1287
1288 /* bottom point and intersection point stack */
1289 int n_stack;
1290 int n_stack_max;
1291 ArtPoint *stack;
1292
1293 /* horiz commit list */
1294 ArtActiveSeg *horiz_left, *horiz_right;
1295 double horiz_x;
1296 int horiz_delta_wind;
1297 int seg_id;
1298 };
1299
1300 typedef struct _ArtIntersectCtx ArtIntersectCtx;
1301
1302 struct _ArtIntersectCtx {
1303 const ArtSVP *in;
1304 ArtSvpWriter *out;
1305
1306 ArtPriQ *pq;
1307
1308 ArtActiveSeg *active_head;
1309
1310 double y;
1311 ArtActiveSeg *horiz_first;
1312 ArtActiveSeg *horiz_last;
1313
1314 /* segment index of next input segment to be added to pri q */
1315 int in_curs;
1316 };
1317
1318 #define EPSILON_A 1e-5 /* Threshold for breaking lines at point insertions */
1319
1320 /**
1321 * art_svp_intersect_setup_seg: Set up an active segment from input segment.
1322 * @seg: Active segment.
1323 * @pri_pt: Priority queue point to initialize.
1324 *
1325 * Sets the x[], a, b, c, flags, and stack fields according to the
1326 * line from the current cursor value. Sets the priority queue point
1327 * to the bottom point of this line. Also advances the input segment
1328 * cursor.
1329 **/
art_svp_intersect_setup_seg(ArtActiveSeg * seg,ArtPriPoint * pri_pt)1330 static void art_svp_intersect_setup_seg(ArtActiveSeg *seg, ArtPriPoint *pri_pt) {
1331 const ArtSVPSeg *in_seg = seg->in_seg;
1332 int in_curs = seg->in_curs++;
1333 double x0, y0, x1, y1;
1334 double dx, dy, s;
1335 double a, b, r2;
1336
1337 x0 = in_seg->points[in_curs].x;
1338 y0 = in_seg->points[in_curs].y;
1339 x1 = in_seg->points[in_curs + 1].x;
1340 y1 = in_seg->points[in_curs + 1].y;
1341 pri_pt->x = x1;
1342 pri_pt->y = y1;
1343 dx = x1 - x0;
1344 dy = y1 - y0;
1345 r2 = dx * dx + dy * dy;
1346 s = r2 == 0 ? 1 : 1 / sqrt(r2);
1347 seg->a = a = dy * s;
1348 seg->b = b = -dx * s;
1349 seg->c = -(a * x0 + b * y0);
1350 seg->flags = (seg->flags & ~ART_ACTIVE_FLAGS_BNEG) | (dx > 0);
1351 seg->x[0] = x0;
1352 seg->x[1] = x1;
1353 seg->y0 = y0;
1354 seg->y1 = y1;
1355 seg->n_stack = 1;
1356 seg->stack[0].x = x1;
1357 seg->stack[0].y = y1;
1358 }
1359
1360 /**
1361 * art_svp_intersect_add_horiz: Add point to horizontal list.
1362 * @ctx: Intersector context.
1363 * @seg: Segment with point to insert into horizontal list.
1364 *
1365 * Inserts @seg into horizontal list, keeping it in ascending horiz_x
1366 * order.
1367 *
1368 * Note: the horiz_commit routine processes "clusters" of segs in the
1369 * horiz list, all sharing the same horiz_x value. The cluster is
1370 * processed in active list order, rather than horiz list order. Thus,
1371 * the order of segs in the horiz list sharing the same horiz_x
1372 * _should_ be irrelevant. Even so, we use b as a secondary sorting key,
1373 * as a "belt and suspenders" defensive coding tactic.
1374 **/
art_svp_intersect_add_horiz(ArtIntersectCtx * ctx,ArtActiveSeg * seg)1375 static void art_svp_intersect_add_horiz(ArtIntersectCtx *ctx, ArtActiveSeg *seg) {
1376 ArtActiveSeg **pp = &ctx->horiz_last;
1377 ArtActiveSeg *place;
1378 ArtActiveSeg *place_right = NULL;
1379
1380 if (seg->flags & ART_ACTIVE_FLAGS_IN_HORIZ) {
1381 warning("attempt to put segment in horiz list twice");
1382 return;
1383 }
1384 seg->flags |= ART_ACTIVE_FLAGS_IN_HORIZ;
1385
1386 for (place = *pp; place != NULL && (place->horiz_x > seg->horiz_x ||
1387 (place->horiz_x == seg->horiz_x &&
1388 place->b < seg->b));
1389 place = *pp) {
1390 place_right = place;
1391 pp = &place->horiz_left;
1392 }
1393 *pp = seg;
1394 seg->horiz_left = place;
1395 seg->horiz_right = place_right;
1396 if (place == NULL)
1397 ctx->horiz_first = seg;
1398 else
1399 place->horiz_right = seg;
1400 }
1401
art_svp_intersect_push_pt(ArtIntersectCtx * ctx,ArtActiveSeg * seg,double x,double y)1402 static void art_svp_intersect_push_pt(ArtIntersectCtx *ctx, ArtActiveSeg *seg,
1403 double x, double y) {
1404 ArtPriPoint *pri_pt;
1405 int n_stack = seg->n_stack;
1406
1407 if (n_stack == seg->n_stack_max)
1408 art_expand(seg->stack, ArtPoint, seg->n_stack_max);
1409 seg->stack[n_stack].x = x;
1410 seg->stack[n_stack].y = y;
1411 seg->n_stack++;
1412
1413 seg->x[1] = x;
1414 seg->y1 = y;
1415
1416 pri_pt = art_new(ArtPriPoint, 1);
1417 if (!pri_pt)
1418 error("[art_svp_intersect_push_pt] Cannot allocate memory");
1419
1420 pri_pt->x = x;
1421 pri_pt->y = y;
1422 pri_pt->user_data = seg;
1423 art_pri_insert(ctx->pq, pri_pt);
1424 }
1425
1426 typedef enum {
1427 ART_BREAK_LEFT = 1,
1428 ART_BREAK_RIGHT = 2
1429 } ArtBreakFlags;
1430
1431 /**
1432 * art_svp_intersect_break: Break an active segment.
1433 *
1434 * Note: y must be greater than the top point's y, and less than
1435 * the bottom's.
1436 *
1437 * Return value: x coordinate of break point.
1438 */
art_svp_intersect_break(ArtIntersectCtx * ctx,ArtActiveSeg * seg,double x_ref,double y,ArtBreakFlags break_flags)1439 static double art_svp_intersect_break(ArtIntersectCtx *ctx, ArtActiveSeg *seg,
1440 double x_ref, double y, ArtBreakFlags break_flags) {
1441 double x0, y0, x1, y1;
1442 const ArtSVPSeg *in_seg = seg->in_seg;
1443 int in_curs = seg->in_curs;
1444 double x;
1445
1446 x0 = in_seg->points[in_curs - 1].x;
1447 y0 = in_seg->points[in_curs - 1].y;
1448 x1 = in_seg->points[in_curs].x;
1449 y1 = in_seg->points[in_curs].y;
1450 x = x0 + (x1 - x0) * ((y - y0) / (y1 - y0));
1451 if ((break_flags == ART_BREAK_LEFT && x > x_ref) ||
1452 (break_flags == ART_BREAK_RIGHT && x < x_ref)) {
1453 }
1454
1455 /* I think we can count on min(x0, x1) <= x <= max(x0, x1) with sane
1456 arithmetic, but it might be worthwhile to check just in case. */
1457
1458 if (y > ctx->y)
1459 art_svp_intersect_push_pt(ctx, seg, x, y);
1460 else {
1461 seg->x[0] = x;
1462 seg->y0 = y;
1463 seg->horiz_x = x;
1464 art_svp_intersect_add_horiz(ctx, seg);
1465 }
1466
1467 return x;
1468 }
1469
1470 /**
1471 * art_svp_intersect_add_point: Add a point, breaking nearby neighbors.
1472 * @ctx: Intersector context.
1473 * @x: X coordinate of point to add.
1474 * @y: Y coordinate of point to add.
1475 * @seg: "nearby" segment, or NULL if leftmost.
1476 *
1477 * Return value: Segment immediately to the left of the new point, or
1478 * NULL if the new point is leftmost.
1479 **/
art_svp_intersect_add_point(ArtIntersectCtx * ctx,double x,double y,ArtActiveSeg * seg,ArtBreakFlags break_flags)1480 static ArtActiveSeg *art_svp_intersect_add_point(ArtIntersectCtx *ctx, double x, double y,
1481 ArtActiveSeg *seg, ArtBreakFlags break_flags) {
1482 ArtActiveSeg *left, *right;
1483 double x_min = x, x_max = x;
1484 bool left_live, right_live;
1485 double d;
1486 double new_x;
1487 ArtActiveSeg *test, *result = NULL;
1488 double x_test;
1489
1490 left = seg;
1491 if (left == NULL)
1492 right = ctx->active_head;
1493 else
1494 right = left->right;
1495 left_live = (break_flags & ART_BREAK_LEFT) && (left != NULL);
1496 right_live = (break_flags & ART_BREAK_RIGHT) && (right != NULL);
1497 while (left_live || right_live) {
1498 if (left_live) {
1499 if (x <= left->x[left->flags & ART_ACTIVE_FLAGS_BNEG] &&
1500 /* It may be that one of these conjuncts turns out to be always
1501 true. We test both anyway, to be defensive. */
1502 y != left->y0 && y < left->y1) {
1503 d = x_min * left->a + y * left->b + left->c;
1504 if (d < EPSILON_A) {
1505 new_x = art_svp_intersect_break(ctx, left, x_min, y,
1506 ART_BREAK_LEFT);
1507 if (new_x > x_max) {
1508 x_max = new_x;
1509 right_live = (right != NULL);
1510 } else if (new_x < x_min)
1511 x_min = new_x;
1512 left = left->left;
1513 left_live = (left != NULL);
1514 } else
1515 left_live = false;
1516 } else
1517 left_live = false;
1518 } else if (right_live) {
1519 if (x >= right->x[(right->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1] &&
1520 /* It may be that one of these conjuncts turns out to be always
1521 true. We test both anyway, to be defensive. */
1522 y != right->y0 && y < right->y1) {
1523 d = x_max * right->a + y * right->b + right->c;
1524 if (d > -EPSILON_A) {
1525 new_x = art_svp_intersect_break(ctx, right, x_max, y,
1526 ART_BREAK_RIGHT);
1527 if (new_x < x_min) {
1528 x_min = new_x;
1529 left_live = (left != NULL);
1530 } else if (new_x >= x_max)
1531 x_max = new_x;
1532 right = right->right;
1533 right_live = (right != NULL);
1534 } else
1535 right_live = false;
1536 } else
1537 right_live = false;
1538 }
1539 }
1540
1541 /* Ascending order is guaranteed by break_flags. Thus, we don't need
1542 to actually fix up non-ascending pairs. */
1543
1544 /* Now, (left, right) defines an interval of segments broken. Sort
1545 into ascending x order. */
1546 test = left == NULL ? ctx->active_head : left->right;
1547 result = left;
1548 if (test != NULL && test != right) {
1549 if (y == test->y0)
1550 x_test = test->x[0];
1551 else /* assert y == test->y1, I think */
1552 x_test = test->x[1];
1553 for (;;) {
1554 if (x_test <= x)
1555 result = test;
1556 test = test->right;
1557 if (test == right)
1558 break;
1559 new_x = x_test;
1560 if (new_x < x_test) {
1561 warning("art_svp_intersect_add_point: non-ascending x");
1562 }
1563 x_test = new_x;
1564 }
1565 }
1566 return result;
1567 }
1568
art_svp_intersect_swap_active(ArtIntersectCtx * ctx,ArtActiveSeg * left_seg,ArtActiveSeg * right_seg)1569 static void art_svp_intersect_swap_active(ArtIntersectCtx *ctx,
1570 ArtActiveSeg *left_seg, ArtActiveSeg *right_seg) {
1571 right_seg->left = left_seg->left;
1572 if (right_seg->left != NULL)
1573 right_seg->left->right = right_seg;
1574 else
1575 ctx->active_head = right_seg;
1576 left_seg->right = right_seg->right;
1577 if (left_seg->right != NULL)
1578 left_seg->right->left = left_seg;
1579 left_seg->left = right_seg;
1580 right_seg->right = left_seg;
1581 }
1582
1583 /**
1584 * art_svp_intersect_test_cross: Test crossing of a pair of active segments.
1585 * @ctx: Intersector context.
1586 * @left_seg: Left segment of the pair.
1587 * @right_seg: Right segment of the pair.
1588 * @break_flags: Flags indicating whether to break neighbors.
1589 *
1590 * Tests crossing of @left_seg and @right_seg. If there is a crossing,
1591 * inserts the intersection point into both segments.
1592 *
1593 * Return value: True if the intersection took place at the current
1594 * scan line, indicating further iteration is needed.
1595 **/
art_svp_intersect_test_cross(ArtIntersectCtx * ctx,ArtActiveSeg * left_seg,ArtActiveSeg * right_seg,ArtBreakFlags break_flags)1596 static bool art_svp_intersect_test_cross(ArtIntersectCtx *ctx,
1597 ArtActiveSeg *left_seg, ArtActiveSeg *right_seg,
1598 ArtBreakFlags break_flags) {
1599 double left_x0, left_y0, left_x1;
1600 double left_y1 = left_seg->y1;
1601 double right_y1 = right_seg->y1;
1602 double d;
1603
1604 const ArtSVPSeg *in_seg;
1605 int in_curs;
1606 double d0, d1, t;
1607 double x, y; /* intersection point */
1608
1609 if (left_seg->y0 == right_seg->y0 && left_seg->x[0] == right_seg->x[0]) {
1610 /* Top points of left and right segments coincide. This case
1611 feels like a bit of duplication - we may want to merge it
1612 with the cases below. However, this way, we're sure that this
1613 logic makes only localized changes. */
1614
1615 if (left_y1 < right_y1) {
1616 /* Test left (x1, y1) against right segment */
1617 left_x1 = left_seg->x[1];
1618
1619 if (left_x1 <
1620 right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1] ||
1621 left_y1 == right_seg->y0)
1622 return false;
1623 d = left_x1 * right_seg->a + left_y1 * right_seg->b + right_seg->c;
1624 if (d < -EPSILON_A)
1625 return false;
1626 else if (d < EPSILON_A) {
1627 /* I'm unsure about the break flags here. */
1628 double right_x1 = art_svp_intersect_break(ctx, right_seg,
1629 left_x1, left_y1,
1630 ART_BREAK_RIGHT);
1631 if (left_x1 <= right_x1)
1632 return false;
1633 }
1634 } else if (left_y1 > right_y1) {
1635 /* Test right (x1, y1) against left segment */
1636 double right_x1 = right_seg->x[1];
1637
1638 if (right_x1 > left_seg->x[left_seg->flags & ART_ACTIVE_FLAGS_BNEG] ||
1639 right_y1 == left_seg->y0)
1640 return false;
1641 d = right_x1 * left_seg->a + right_y1 * left_seg->b + left_seg->c;
1642 if (d > EPSILON_A)
1643 return false;
1644 else if (d > -EPSILON_A) {
1645 /* See above regarding break flags. */
1646 left_x1 = art_svp_intersect_break(ctx, left_seg,
1647 right_x1, right_y1,
1648 ART_BREAK_LEFT);
1649 if (left_x1 <= right_x1)
1650 return false;
1651 }
1652 } else { /* left_y1 == right_y1 */
1653 left_x1 = left_seg->x[1];
1654 double right_x1 = right_seg->x[1];
1655
1656 if (left_x1 <= right_x1)
1657 return false;
1658 }
1659 art_svp_intersect_swap_active(ctx, left_seg, right_seg);
1660 return true;
1661 }
1662
1663 if (left_y1 < right_y1) {
1664 /* Test left (x1, y1) against right segment */
1665 left_x1 = left_seg->x[1];
1666
1667 if (left_x1 <
1668 right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1] ||
1669 left_y1 == right_seg->y0)
1670 return false;
1671 d = left_x1 * right_seg->a + left_y1 * right_seg->b + right_seg->c;
1672 if (d < -EPSILON_A)
1673 return false;
1674 else if (d < EPSILON_A) {
1675 double right_x1 = art_svp_intersect_break(ctx, right_seg,
1676 left_x1, left_y1,
1677 ART_BREAK_RIGHT);
1678 if (left_x1 <= right_x1)
1679 return false;
1680 }
1681 } else if (left_y1 > right_y1) {
1682 /* Test right (x1, y1) against left segment */
1683 double right_x1 = right_seg->x[1];
1684
1685 if (right_x1 > left_seg->x[left_seg->flags & ART_ACTIVE_FLAGS_BNEG] ||
1686 right_y1 == left_seg->y0)
1687 return false;
1688 d = right_x1 * left_seg->a + right_y1 * left_seg->b + left_seg->c;
1689 if (d > EPSILON_A)
1690 return false;
1691 else if (d > -EPSILON_A) {
1692 left_x1 = art_svp_intersect_break(ctx, left_seg,
1693 right_x1, right_y1,
1694 ART_BREAK_LEFT);
1695 if (left_x1 <= right_x1)
1696 return false;
1697 }
1698 } else { /* left_y1 == right_y1 */
1699 left_x1 = left_seg->x[1];
1700 double right_x1 = right_seg->x[1];
1701
1702 if (left_x1 <= right_x1)
1703 return false;
1704 }
1705
1706 /* The segments cross. Find the intersection point. */
1707
1708 in_seg = left_seg->in_seg;
1709 in_curs = left_seg->in_curs;
1710 left_x0 = in_seg->points[in_curs - 1].x;
1711 left_y0 = in_seg->points[in_curs - 1].y;
1712 left_x1 = in_seg->points[in_curs].x;
1713 left_y1 = in_seg->points[in_curs].y;
1714 d0 = left_x0 * right_seg->a + left_y0 * right_seg->b + right_seg->c;
1715 d1 = left_x1 * right_seg->a + left_y1 * right_seg->b + right_seg->c;
1716 if (d0 == d1) {
1717 x = left_x0;
1718 y = left_y0;
1719 } else {
1720 /* Is this division always safe? It could possibly overflow. */
1721 t = d0 / (d0 - d1);
1722 if (t <= 0) {
1723 x = left_x0;
1724 y = left_y0;
1725 } else if (t >= 1) {
1726 x = left_x1;
1727 y = left_y1;
1728 } else {
1729 x = left_x0 + t * (left_x1 - left_x0);
1730 y = left_y0 + t * (left_y1 - left_y0);
1731 }
1732 }
1733
1734 /* Make sure intersection point is within bounds of right seg. */
1735 if (y < right_seg->y0) {
1736 x = right_seg->x[0];
1737 y = right_seg->y0;
1738 } else if (y > right_seg->y1) {
1739 x = right_seg->x[1];
1740 y = right_seg->y1;
1741 } else if (x < right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1])
1742 x = right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1];
1743 else if (x > right_seg->x[right_seg->flags & ART_ACTIVE_FLAGS_BNEG])
1744 x = right_seg->x[right_seg->flags & ART_ACTIVE_FLAGS_BNEG];
1745
1746 if (y == left_seg->y0) {
1747 if (y != right_seg->y0) {
1748 art_svp_intersect_push_pt(ctx, right_seg, x, y);
1749 if ((break_flags & ART_BREAK_RIGHT) && right_seg->right != NULL)
1750 art_svp_intersect_add_point(ctx, x, y, right_seg->right,
1751 break_flags);
1752 } else {
1753 /* Intersection takes place at current scan line; process
1754 immediately rather than queueing intersection point into
1755 priq. */
1756 ArtActiveSeg *winner, *loser;
1757
1758 /* Choose "most vertical" segement */
1759 if (left_seg->a > right_seg->a) {
1760 winner = left_seg;
1761 loser = right_seg;
1762 } else {
1763 winner = right_seg;
1764 loser = left_seg;
1765 }
1766
1767 loser->x[0] = winner->x[0];
1768 loser->horiz_x = loser->x[0];
1769 loser->horiz_delta_wind += loser->delta_wind;
1770 winner->horiz_delta_wind -= loser->delta_wind;
1771
1772 art_svp_intersect_swap_active(ctx, left_seg, right_seg);
1773 return true;
1774 }
1775 } else if (y == right_seg->y0) {
1776 art_svp_intersect_push_pt(ctx, left_seg, x, y);
1777 if ((break_flags & ART_BREAK_LEFT) && left_seg->left != NULL)
1778 art_svp_intersect_add_point(ctx, x, y, left_seg->left,
1779 break_flags);
1780 } else {
1781 /* Insert the intersection point into both segments. */
1782 art_svp_intersect_push_pt(ctx, left_seg, x, y);
1783 art_svp_intersect_push_pt(ctx, right_seg, x, y);
1784 if ((break_flags & ART_BREAK_LEFT) && left_seg->left != NULL)
1785 art_svp_intersect_add_point(ctx, x, y, left_seg->left, break_flags);
1786 if ((break_flags & ART_BREAK_RIGHT) && right_seg->right != NULL)
1787 art_svp_intersect_add_point(ctx, x, y, right_seg->right, break_flags);
1788 }
1789 return false;
1790 }
1791
1792 /**
1793 * art_svp_intersect_active_delete: Delete segment from active list.
1794 * @ctx: Intersection context.
1795 * @seg: Segment to delete.
1796 *
1797 * Deletes @seg from the active list.
1798 **/
art_svp_intersect_active_delete(ArtIntersectCtx * ctx,ArtActiveSeg * seg)1799 static void art_svp_intersect_active_delete(ArtIntersectCtx *ctx, ArtActiveSeg *seg) {
1800 ArtActiveSeg *left = seg->left, *right = seg->right;
1801
1802 if (left != NULL)
1803 left->right = right;
1804 else
1805 ctx->active_head = right;
1806 if (right != NULL)
1807 right->left = left;
1808 }
1809
1810 /**
1811 * art_svp_intersect_active_free: Free an active segment.
1812 * @seg: Segment to delete.
1813 *
1814 * Frees @seg.
1815 **/
art_svp_intersect_active_free(ArtActiveSeg * seg)1816 static void art_svp_intersect_active_free(ArtActiveSeg *seg) {
1817 free(seg->stack);
1818 free(seg);
1819 }
1820
1821 /**
1822 * art_svp_intersect_insert_cross: Test crossings of newly inserted line.
1823 *
1824 * Tests @seg against its left and right neighbors for intersections.
1825 * Precondition: the line in @seg is not purely horizontal.
1826 **/
art_svp_intersect_insert_cross(ArtIntersectCtx * ctx,ArtActiveSeg * seg)1827 static void art_svp_intersect_insert_cross(ArtIntersectCtx *ctx,
1828 ArtActiveSeg *seg) {
1829 ArtActiveSeg *left = seg, *right = seg;
1830
1831 for (;;) {
1832 if (left != NULL) {
1833 ArtActiveSeg *leftc;
1834
1835 for (leftc = left->left; leftc != NULL; leftc = leftc->left)
1836 if (!(leftc->flags & ART_ACTIVE_FLAGS_DEL))
1837 break;
1838 if (leftc != NULL &&
1839 art_svp_intersect_test_cross(ctx, leftc, left,
1840 ART_BREAK_LEFT)) {
1841 if (left == right || right == NULL)
1842 right = left->right;
1843 } else {
1844 left = NULL;
1845 }
1846 } else if (right != NULL && right->right != NULL) {
1847 ArtActiveSeg *rightc;
1848
1849 for (rightc = right->right; rightc != NULL; rightc = rightc->right)
1850 if (!(rightc->flags & ART_ACTIVE_FLAGS_DEL))
1851 break;
1852 if (rightc != NULL &&
1853 art_svp_intersect_test_cross(ctx, right, rightc,
1854 ART_BREAK_RIGHT)) {
1855 if (left == right || left == NULL)
1856 left = right->left;
1857 } else {
1858 right = NULL;
1859 }
1860 } else
1861 break;
1862 }
1863 }
1864
1865 /**
1866 * art_svp_intersect_horiz: Add horizontal line segment.
1867 * @ctx: Intersector context.
1868 * @seg: Segment on which to add horizontal line.
1869 * @x0: Old x position.
1870 * @x1: New x position.
1871 *
1872 * Adds a horizontal line from @x0 to @x1, and updates the current
1873 * location of @seg to @x1.
1874 **/
art_svp_intersect_horiz(ArtIntersectCtx * ctx,ArtActiveSeg * seg,double x0,double x1)1875 static void art_svp_intersect_horiz(ArtIntersectCtx *ctx, ArtActiveSeg *seg,
1876 double x0, double x1) {
1877 ArtActiveSeg *hs;
1878
1879 if (x0 == x1)
1880 return;
1881
1882 hs = art_new(ArtActiveSeg, 1);
1883 if (!hs)
1884 error("[art_svp_intersect_horiz] Cannot allocate memory");
1885
1886 hs->flags = ART_ACTIVE_FLAGS_DEL | (seg->flags & ART_ACTIVE_FLAGS_OUT);
1887 if (seg->flags & ART_ACTIVE_FLAGS_OUT) {
1888 ArtSvpWriter *swr = ctx->out;
1889
1890 swr->add_point(swr, seg->seg_id, x0, ctx->y);
1891 }
1892 hs->seg_id = seg->seg_id;
1893 hs->horiz_x = x0;
1894 hs->horiz_delta_wind = seg->delta_wind;
1895 hs->stack = NULL;
1896
1897 /* Ideally, the (a, b, c) values will never be read. However, there
1898 are probably some tests remaining that don't check for _DEL
1899 before evaluating the line equation. For those, these
1900 initializations will at least prevent a UMR of the values, which
1901 can crash on some platforms. */
1902 hs->a = 0.0;
1903 hs->b = 0.0;
1904 hs->c = 0.0;
1905
1906 seg->horiz_delta_wind -= seg->delta_wind;
1907
1908 art_svp_intersect_add_horiz(ctx, hs);
1909
1910 if (x0 > x1) {
1911 ArtActiveSeg *left;
1912 bool first = true;
1913
1914 for (left = seg->left; left != NULL; left = seg->left) {
1915 int left_bneg = left->flags & ART_ACTIVE_FLAGS_BNEG;
1916
1917 if (left->x[left_bneg] <= x1)
1918 break;
1919 if (left->x[left_bneg ^ 1] <= x1 &&
1920 x1 *left->a + ctx->y *left->b + left->c >= 0)
1921 break;
1922 if (left->y0 != ctx->y && left->y1 != ctx->y) {
1923 art_svp_intersect_break(ctx, left, x1, ctx->y, ART_BREAK_LEFT);
1924 }
1925 art_svp_intersect_swap_active(ctx, left, seg);
1926 if (first && left->right != NULL) {
1927 art_svp_intersect_test_cross(ctx, left, left->right,
1928 ART_BREAK_RIGHT);
1929 first = false;
1930 }
1931 }
1932 } else {
1933 ArtActiveSeg *right;
1934 bool first = true;
1935
1936 for (right = seg->right; right != NULL; right = seg->right) {
1937 int right_bneg = right->flags & ART_ACTIVE_FLAGS_BNEG;
1938
1939 if (right->x[right_bneg ^ 1] >= x1)
1940 break;
1941 if (right->x[right_bneg] >= x1 &&
1942 x1 *right->a + ctx->y *right->b + right->c <= 0)
1943 break;
1944 if (right->y0 != ctx->y && right->y1 != ctx->y) {
1945 art_svp_intersect_break(ctx, right, x1, ctx->y,
1946 ART_BREAK_LEFT);
1947 }
1948 art_svp_intersect_swap_active(ctx, seg, right);
1949 if (first && right->left != NULL) {
1950 art_svp_intersect_test_cross(ctx, right->left, right,
1951 ART_BREAK_RIGHT);
1952 first = false;
1953 }
1954 }
1955 }
1956
1957 seg->x[0] = x1;
1958 seg->x[1] = x1;
1959 seg->horiz_x = x1;
1960 seg->flags &= ~ART_ACTIVE_FLAGS_OUT;
1961 }
1962
1963 /**
1964 * art_svp_intersect_insert_line: Insert a line into the active list.
1965 * @ctx: Intersector context.
1966 * @seg: Segment containing line to insert.
1967 *
1968 * Inserts the line into the intersector context, taking care of any
1969 * intersections, and adding the appropriate horizontal points to the
1970 * active list.
1971 **/
art_svp_intersect_insert_line(ArtIntersectCtx * ctx,ArtActiveSeg * seg)1972 static void art_svp_intersect_insert_line(ArtIntersectCtx *ctx, ArtActiveSeg *seg) {
1973 if (seg->y1 == seg->y0) {
1974 art_svp_intersect_horiz(ctx, seg, seg->x[0], seg->x[1]);
1975 } else {
1976 art_svp_intersect_insert_cross(ctx, seg);
1977 art_svp_intersect_add_horiz(ctx, seg);
1978 }
1979 }
1980
art_svp_intersect_process_intersection(ArtIntersectCtx * ctx,ArtActiveSeg * seg)1981 static void art_svp_intersect_process_intersection(ArtIntersectCtx *ctx,
1982 ArtActiveSeg *seg) {
1983 int n_stack = --seg->n_stack;
1984 seg->x[1] = seg->stack[n_stack - 1].x;
1985 seg->y1 = seg->stack[n_stack - 1].y;
1986 seg->x[0] = seg->stack[n_stack].x;
1987 seg->y0 = seg->stack[n_stack].y;
1988 seg->horiz_x = seg->x[0];
1989 art_svp_intersect_insert_line(ctx, seg);
1990 }
1991
art_svp_intersect_advance_cursor(ArtIntersectCtx * ctx,ArtActiveSeg * seg,ArtPriPoint * pri_pt)1992 static void art_svp_intersect_advance_cursor(ArtIntersectCtx *ctx, ArtActiveSeg *seg,
1993 ArtPriPoint *pri_pt) {
1994 const ArtSVPSeg *in_seg = seg->in_seg;
1995 int in_curs = seg->in_curs;
1996 ArtSvpWriter *swr = seg->flags & ART_ACTIVE_FLAGS_OUT ? ctx->out : NULL;
1997
1998 if (swr != NULL)
1999 swr->add_point(swr, seg->seg_id, seg->x[1], seg->y1);
2000 if (in_curs + 1 == in_seg->n_points) {
2001 ArtActiveSeg *left = seg->left, *right = seg->right;
2002
2003 seg->flags |= ART_ACTIVE_FLAGS_DEL;
2004 art_svp_intersect_add_horiz(ctx, seg);
2005 art_svp_intersect_active_delete(ctx, seg);
2006 if (left != NULL && right != NULL)
2007 art_svp_intersect_test_cross(ctx, left, right,
2008 (ArtBreakFlags)(ART_BREAK_LEFT | ART_BREAK_RIGHT));
2009 free(pri_pt);
2010 } else {
2011 seg->horiz_x = seg->x[1];
2012
2013 art_svp_intersect_setup_seg(seg, pri_pt);
2014 art_pri_insert(ctx->pq, pri_pt);
2015 art_svp_intersect_insert_line(ctx, seg);
2016 }
2017 }
2018
art_svp_intersect_add_seg(ArtIntersectCtx * ctx,const ArtSVPSeg * in_seg)2019 static void art_svp_intersect_add_seg(ArtIntersectCtx *ctx, const ArtSVPSeg *in_seg) {
2020 ArtActiveSeg *seg = art_new(ArtActiveSeg, 1);
2021 ArtActiveSeg *test;
2022 double x0, y0;
2023 ArtActiveSeg *last = NULL;
2024 ArtActiveSeg *left, *right;
2025 ArtPriPoint *pri_pt = art_new(ArtPriPoint, 1);
2026 if (!pri_pt)
2027 error("[art_svp_intersect_add_seg] Cannot allocate memory");
2028
2029 seg->flags = 0;
2030 seg->in_seg = in_seg;
2031 seg->in_curs = 0;
2032
2033 seg->n_stack_max = 4;
2034 seg->stack = art_new(ArtPoint, seg->n_stack_max);
2035
2036 seg->horiz_delta_wind = 0;
2037
2038 seg->wind_left = 0;
2039
2040 pri_pt->user_data = seg;
2041 art_svp_intersect_setup_seg(seg, pri_pt);
2042 art_pri_insert(ctx->pq, pri_pt);
2043
2044 /* Find insertion place for new segment */
2045 /* This is currently a left-to-right scan, but should be replaced
2046 with a binary search as soon as it's validated. */
2047
2048 x0 = in_seg->points[0].x;
2049 y0 = in_seg->points[0].y;
2050 for (test = ctx->active_head; test != NULL; test = test->right) {
2051 double d;
2052 int test_bneg = test->flags & ART_ACTIVE_FLAGS_BNEG;
2053
2054 if (x0 < test->x[test_bneg]) {
2055 if (x0 < test->x[test_bneg ^ 1])
2056 break;
2057 d = x0 * test->a + y0 * test->b + test->c;
2058 if (d < 0)
2059 break;
2060 }
2061 last = test;
2062 }
2063
2064 left = art_svp_intersect_add_point(ctx, x0, y0, last, (ArtBreakFlags)(ART_BREAK_LEFT | ART_BREAK_RIGHT));
2065 seg->left = left;
2066 if (left == NULL) {
2067 right = ctx->active_head;
2068 ctx->active_head = seg;
2069 } else {
2070 right = left->right;
2071 left->right = seg;
2072 }
2073 seg->right = right;
2074 if (right != NULL)
2075 right->left = seg;
2076
2077 seg->delta_wind = in_seg->dir ? 1 : -1;
2078 seg->horiz_x = x0;
2079
2080 art_svp_intersect_insert_line(ctx, seg);
2081 }
2082
2083 /**
2084 * art_svp_intersect_horiz_commit: Commit points in horiz list to output.
2085 * @ctx: Intersection context.
2086 *
2087 * The main function of the horizontal commit is to output new
2088 * points to the output writer.
2089 *
2090 * This "commit" pass is also where winding numbers are assigned,
2091 * because doing it here provides much greater tolerance for inputs
2092 * which are not in strict SVP order.
2093 *
2094 * Each cluster in the horiz_list contains both segments that are in
2095 * the active list (ART_ACTIVE_FLAGS_DEL is false) and that are not,
2096 * and are scheduled to be deleted (ART_ACTIVE_FLAGS_DEL is true). We
2097 * need to deal with both.
2098 **/
art_svp_intersect_horiz_commit(ArtIntersectCtx * ctx)2099 static void art_svp_intersect_horiz_commit(ArtIntersectCtx *ctx) {
2100 ArtActiveSeg *seg;
2101 int winding_number = 0; /* initialization just to avoid warning */
2102 int horiz_wind = 0;
2103 double last_x = 0; /* initialization just to avoid warning */
2104
2105 /* Output points to svp writer. */
2106 for (seg = ctx->horiz_first; seg != NULL;) {
2107 /* Find a cluster with common horiz_x, */
2108 ArtActiveSeg *curs;
2109 double x = seg->horiz_x;
2110
2111 /* Generate any horizontal segments. */
2112 if (horiz_wind != 0) {
2113 ArtSvpWriter *swr = ctx->out;
2114 int seg_id;
2115
2116 seg_id = swr->add_segment(swr, winding_number, horiz_wind,
2117 last_x, ctx->y);
2118 swr->add_point(swr, seg_id, x, ctx->y);
2119 swr->close_segment(swr, seg_id);
2120 }
2121
2122 /* Find first active segment in cluster. */
2123
2124 for (curs = seg; curs != NULL && curs->horiz_x == x;
2125 curs = curs->horiz_right)
2126 if (!(curs->flags & ART_ACTIVE_FLAGS_DEL))
2127 break;
2128
2129 if (curs != NULL && curs->horiz_x == x) {
2130 /* There exists at least one active segment in this cluster. */
2131
2132 /* Find beginning of cluster. */
2133 for (; curs->left != NULL; curs = curs->left)
2134 if (curs->left->horiz_x != x)
2135 break;
2136
2137 if (curs->left != NULL)
2138 winding_number = curs->left->wind_left + curs->left->delta_wind;
2139 else
2140 winding_number = 0;
2141
2142 do {
2143 if (!(curs->flags & ART_ACTIVE_FLAGS_OUT) ||
2144 curs->wind_left != winding_number) {
2145 ArtSvpWriter *swr = ctx->out;
2146
2147 if (curs->flags & ART_ACTIVE_FLAGS_OUT) {
2148 swr->add_point(swr, curs->seg_id,
2149 curs->horiz_x, ctx->y);
2150 swr->close_segment(swr, curs->seg_id);
2151 }
2152
2153 curs->seg_id = swr->add_segment(swr, winding_number,
2154 curs->delta_wind,
2155 x, ctx->y);
2156 curs->flags |= ART_ACTIVE_FLAGS_OUT;
2157 }
2158 curs->wind_left = winding_number;
2159 winding_number += curs->delta_wind;
2160 curs = curs->right;
2161 } while (curs != NULL && curs->horiz_x == x);
2162 }
2163
2164 /* Skip past cluster. */
2165 do {
2166 ArtActiveSeg *next = seg->horiz_right;
2167
2168 seg->flags &= ~ART_ACTIVE_FLAGS_IN_HORIZ;
2169 horiz_wind += seg->horiz_delta_wind;
2170 seg->horiz_delta_wind = 0;
2171 if (seg->flags & ART_ACTIVE_FLAGS_DEL) {
2172 if (seg->flags & ART_ACTIVE_FLAGS_OUT) {
2173 ArtSvpWriter *swr = ctx->out;
2174 swr->close_segment(swr, seg->seg_id);
2175 }
2176 art_svp_intersect_active_free(seg);
2177 }
2178 seg = next;
2179 } while (seg != NULL && seg->horiz_x == x);
2180
2181 last_x = x;
2182 }
2183 ctx->horiz_first = NULL;
2184 ctx->horiz_last = NULL;
2185 }
2186
art_svp_intersector(const ArtSVP * in,ArtSvpWriter * out)2187 void art_svp_intersector(const ArtSVP *in, ArtSvpWriter *out) {
2188 ArtIntersectCtx *ctx;
2189 ArtPriQ *pq;
2190 ArtPriPoint *first_point;
2191
2192 if (in->n_segs == 0)
2193 return;
2194
2195 ctx = art_new(ArtIntersectCtx, 1);
2196 if (!ctx)
2197 error("[art_svp_intersector] Cannot allocate memory");
2198
2199 ctx->in = in;
2200 ctx->out = out;
2201 pq = art_pri_new();
2202 ctx->pq = pq;
2203
2204 ctx->active_head = NULL;
2205
2206 ctx->horiz_first = NULL;
2207 ctx->horiz_last = NULL;
2208
2209 ctx->in_curs = 0;
2210 first_point = art_new(ArtPriPoint, 1);
2211 if (!first_point)
2212 error("[art_svp_intersector] Cannot allocate memory");
2213
2214 first_point->x = in->segs[0].points[0].x;
2215 first_point->y = in->segs[0].points[0].y;
2216 first_point->user_data = NULL;
2217 ctx->y = first_point->y;
2218 art_pri_insert(pq, first_point);
2219
2220 while (!art_pri_empty(pq)) {
2221 ArtPriPoint *pri_point = art_pri_choose(pq);
2222 ArtActiveSeg *seg = (ArtActiveSeg *)pri_point->user_data;
2223
2224 if (ctx->y != pri_point->y) {
2225 art_svp_intersect_horiz_commit(ctx);
2226 ctx->y = pri_point->y;
2227 }
2228
2229 if (seg == NULL) {
2230 /* Insert new segment from input */
2231 const ArtSVPSeg *in_seg = &in->segs[ctx->in_curs++];
2232 art_svp_intersect_add_seg(ctx, in_seg);
2233 if (ctx->in_curs < in->n_segs) {
2234 const ArtSVPSeg *next_seg = &in->segs[ctx->in_curs];
2235 pri_point->x = next_seg->points[0].x;
2236 pri_point->y = next_seg->points[0].y;
2237 /* user_data is already NULL */
2238 art_pri_insert(pq, pri_point);
2239 } else
2240 free(pri_point);
2241 } else {
2242 int n_stack = seg->n_stack;
2243
2244 if (n_stack > 1) {
2245 art_svp_intersect_process_intersection(ctx, seg);
2246 free(pri_point);
2247 } else {
2248 art_svp_intersect_advance_cursor(ctx, seg, pri_point);
2249 }
2250 }
2251 }
2252
2253 art_svp_intersect_horiz_commit(ctx);
2254
2255 art_pri_free(pq);
2256 free(ctx);
2257 }
2258
2259
2260 /* The spiffy antialiased renderer for sorted vector paths. */
2261
2262 typedef double artfloat;
2263
2264 struct ArtSVPRenderAAIter {
2265 const ArtSVP *svp;
2266 int x0, x1;
2267 int y;
2268 int seg_ix;
2269
2270 int *active_segs;
2271 int n_active_segs;
2272 int *cursor;
2273 artfloat *seg_x;
2274 artfloat *seg_dx;
2275
2276 ArtSVPRenderAAStep *steps;
2277 };
2278
art_svp_render_insert_active(int i,int * active_segs,int n_active_segs,artfloat * seg_x,artfloat * seg_dx)2279 static void art_svp_render_insert_active(int i, int *active_segs, int n_active_segs,
2280 artfloat *seg_x, artfloat *seg_dx) {
2281 int j;
2282 artfloat x;
2283 int tmp1, tmp2;
2284
2285 /* this is a cheap hack to get ^'s sorted correctly */
2286 x = seg_x[i] + 0.001 * seg_dx[i];
2287 for (j = 0; j < n_active_segs && seg_x[active_segs[j]] < x; j++)
2288 ;
2289
2290 tmp1 = i;
2291 while (j < n_active_segs) {
2292 tmp2 = active_segs[j];
2293 active_segs[j] = tmp1;
2294 tmp1 = tmp2;
2295 j++;
2296 }
2297 active_segs[j] = tmp1;
2298 }
2299
art_svp_render_delete_active(int * active_segs,int j,int n_active_segs)2300 static void art_svp_render_delete_active(int *active_segs, int j, int n_active_segs) {
2301 int k;
2302
2303 for (k = j; k < n_active_segs; k++)
2304 active_segs[k] = active_segs[k + 1];
2305 }
2306
2307 /* Render the sorted vector path in the given rectangle, antialiased.
2308
2309 This interface uses a callback for the actual pixel rendering. The
2310 callback is called y1 - y0 times (once for each scan line). The y
2311 coordinate is given as an argument for convenience (it could be
2312 stored in the callback's private data and incremented on each
2313 call).
2314
2315 The rendered polygon is represented in a semi-runlength format: a
2316 start value and a sequence of "steps". Each step has an x
2317 coordinate and a value delta. The resulting value at position x is
2318 equal to the sum of the start value and all step delta values for
2319 which the step x coordinate is less than or equal to x. An
2320 efficient algorithm will traverse the steps left to right, keeping
2321 a running sum.
2322
2323 All x coordinates in the steps are guaranteed to be x0 <= x < x1.
2324 (This guarantee is a change from the gfonted vpaar renderer, and is
2325 designed to simplify the callback).
2326
2327 There is now a further guarantee that no two steps will have the
2328 same x value. This may allow for further speedup and simplification
2329 of renderers.
2330
2331 The value 0x8000 represents 0% coverage by the polygon, while
2332 0xff8000 represents 100% coverage. This format is designed so that
2333 >> 16 results in a standard 0x00..0xff value range, with nice
2334 rounding.
2335
2336 Status of this routine:
2337
2338 Basic correctness: OK
2339
2340 Numerical stability: pretty good, although probably not
2341 bulletproof.
2342
2343 Speed: Needs more aggressive culling of bounding boxes. Can
2344 probably speed up the [x0,x1) clipping of step values. Can do more
2345 of the step calculation in fixed point.
2346
2347 Precision: No known problems, although it should be tested
2348 thoroughly, especially for symmetry.
2349
2350 */
2351
art_svp_render_aa_iter(const ArtSVP * svp,int x0,int y0,int x1,int y1)2352 ArtSVPRenderAAIter *art_svp_render_aa_iter(const ArtSVP *svp,
2353 int x0, int y0, int x1, int y1) {
2354 ArtSVPRenderAAIter *iter = art_new(ArtSVPRenderAAIter, 1);
2355 if (!iter)
2356 error("[art_svp_render_aa_iter] Cannot allocate memory");
2357
2358 iter->svp = svp;
2359 iter->y = y0;
2360 iter->x0 = x0;
2361 iter->x1 = x1;
2362 iter->seg_ix = 0;
2363
2364 iter->active_segs = art_new(int, svp->n_segs);
2365 iter->cursor = art_new(int, svp->n_segs);
2366 iter->seg_x = art_new(artfloat, svp->n_segs);
2367 iter->seg_dx = art_new(artfloat, svp->n_segs);
2368 iter->steps = art_new(ArtSVPRenderAAStep, x1 - x0);
2369 iter->n_active_segs = 0;
2370
2371 return iter;
2372 }
2373
2374 #define ADD_STEP(xpos, xdelta) \
2375 /* stereotype code fragment for adding a step */ \
2376 if (n_steps == 0 || steps[n_steps - 1].x < xpos) { \
2377 sx = n_steps; \
2378 steps[sx].x = xpos; \
2379 steps[sx].delta = xdelta; \
2380 n_steps++; \
2381 } else { \
2382 for (sx = n_steps; sx > 0; sx--) { \
2383 if (steps[sx - 1].x == xpos) { \
2384 steps[sx - 1].delta += xdelta; \
2385 sx = n_steps; \
2386 break; \
2387 } else if (steps[sx - 1].x < xpos) { \
2388 break; \
2389 } \
2390 } \
2391 if (sx < n_steps) { \
2392 memmove (&steps[sx + 1], &steps[sx], \
2393 (n_steps - sx) * sizeof(steps[0])); \
2394 steps[sx].x = xpos; \
2395 steps[sx].delta = xdelta; \
2396 n_steps++; \
2397 } \
2398 }
2399
art_svp_render_aa_iter_step(ArtSVPRenderAAIter * iter,int * p_start,ArtSVPRenderAAStep ** p_steps,int * p_n_steps)2400 void art_svp_render_aa_iter_step(ArtSVPRenderAAIter *iter, int *p_start,
2401 ArtSVPRenderAAStep **p_steps, int *p_n_steps) {
2402 const ArtSVP *svp = iter->svp;
2403 int *active_segs = iter->active_segs;
2404 int n_active_segs = iter->n_active_segs;
2405 int *cursor = iter->cursor;
2406 artfloat *seg_x = iter->seg_x;
2407 artfloat *seg_dx = iter->seg_dx;
2408 int i = iter->seg_ix;
2409 int j;
2410 int x0 = iter->x0;
2411 int x1 = iter->x1;
2412 int y = iter->y;
2413 int seg_index;
2414
2415 int x;
2416 ArtSVPRenderAAStep *steps = iter->steps;
2417 int n_steps;
2418 artfloat y_top, y_bot;
2419 artfloat x_top, x_bot;
2420 artfloat x_min, x_max;
2421 int ix_min, ix_max;
2422 artfloat delta; /* delta should be int too? */
2423 int last, this_;
2424 int xdelta;
2425 artfloat rslope, drslope;
2426 int start;
2427 const ArtSVPSeg *seg;
2428 int curs;
2429 artfloat dy;
2430
2431 int sx;
2432
2433 /* insert new active segments */
2434 for (; i < svp->n_segs && svp->segs[i].bbox.y0 < y + 1; i++) {
2435 if (svp->segs[i].bbox.y1 > y &&
2436 svp->segs[i].bbox.x0 < x1) {
2437 seg = &svp->segs[i];
2438 /* move cursor to topmost vector which overlaps [y,y+1) */
2439 for (curs = 0; seg->points[curs + 1].y < y; curs++)
2440 ;
2441 cursor[i] = curs;
2442 dy = seg->points[curs + 1].y - seg->points[curs].y;
2443 if (fabs(dy) >= EPSILON_6)
2444 seg_dx[i] = (seg->points[curs + 1].x - seg->points[curs].x) /
2445 dy;
2446 else
2447 seg_dx[i] = 1e12;
2448 seg_x[i] = seg->points[curs].x +
2449 (y - seg->points[curs].y) * seg_dx[i];
2450 art_svp_render_insert_active(i, active_segs, n_active_segs++,
2451 seg_x, seg_dx);
2452 }
2453 }
2454
2455 n_steps = 0;
2456
2457 /* render the runlengths, advancing and deleting as we go */
2458 start = 0x8000;
2459
2460 for (j = 0; j < n_active_segs; j++) {
2461 seg_index = active_segs[j];
2462 seg = &svp->segs[seg_index];
2463 curs = cursor[seg_index];
2464 while (curs != seg->n_points - 1 &&
2465 seg->points[curs].y < y + 1) {
2466 y_top = y;
2467 if (y_top < seg->points[curs].y)
2468 y_top = seg->points[curs].y;
2469 y_bot = y + 1;
2470 if (y_bot > seg->points[curs + 1].y)
2471 y_bot = seg->points[curs + 1].y;
2472 if (y_top != y_bot) {
2473 delta = (seg->dir ? 16711680.0 : -16711680.0) *
2474 (y_bot - y_top);
2475 x_top = seg_x[seg_index] + (y_top - y) * seg_dx[seg_index];
2476 x_bot = seg_x[seg_index] + (y_bot - y) * seg_dx[seg_index];
2477 if (x_top < x_bot) {
2478 x_min = x_top;
2479 x_max = x_bot;
2480 } else {
2481 x_min = x_bot;
2482 x_max = x_top;
2483 }
2484 ix_min = (int)floor(x_min);
2485 ix_max = (int)floor(x_max);
2486 if (ix_min >= x1) {
2487 /* skip; it starts to the right of the render region */
2488 } else if (ix_max < x0)
2489 /* it ends to the left of the render region */
2490 start += (int)delta;
2491 else if (ix_min == ix_max) {
2492 /* case 1, antialias a single pixel */
2493 xdelta = (int)((ix_min + 1 - (x_min + x_max) * 0.5) * delta);
2494
2495 ADD_STEP(ix_min, xdelta)
2496
2497 if (ix_min + 1 < x1) {
2498 xdelta = (int)(delta - xdelta);
2499
2500 ADD_STEP(ix_min + 1, xdelta)
2501 }
2502 } else {
2503 /* case 2, antialias a run */
2504 rslope = 1.0 / fabs(seg_dx[seg_index]);
2505 drslope = delta * rslope;
2506 last =
2507 (int)(drslope * 0.5 *
2508 (ix_min + 1 - x_min) * (ix_min + 1 - x_min));
2509 xdelta = last;
2510 if (ix_min >= x0) {
2511 ADD_STEP(ix_min, xdelta)
2512
2513 x = ix_min + 1;
2514 } else {
2515 start += last;
2516 x = x0;
2517 }
2518 if (ix_max > x1)
2519 ix_max = x1;
2520 for (; x < ix_max; x++) {
2521 this_ = (int)((seg->dir ? 16711680.0 : -16711680.0) * rslope *
2522 (x + 0.5 - x_min));
2523 xdelta = this_ - last;
2524 last = this_;
2525
2526 ADD_STEP(x, xdelta)
2527 }
2528 if (x < x1) {
2529 this_ =
2530 (int)(delta * (1 - 0.5 *
2531 (x_max - ix_max) * (x_max - ix_max) *
2532 rslope));
2533 xdelta = this_ - last;
2534 last = this_;
2535
2536 ADD_STEP(x, xdelta)
2537
2538 if (x + 1 < x1) {
2539 xdelta = (int)(delta - last);
2540
2541 ADD_STEP(x + 1, xdelta)
2542 }
2543 }
2544 }
2545 }
2546 curs++;
2547 if (curs != seg->n_points - 1 &&
2548 seg->points[curs].y < y + 1) {
2549 dy = seg->points[curs + 1].y - seg->points[curs].y;
2550 if (fabs(dy) >= EPSILON_6)
2551 seg_dx[seg_index] = (seg->points[curs + 1].x -
2552 seg->points[curs].x) / dy;
2553 else
2554 seg_dx[seg_index] = 1e12;
2555 seg_x[seg_index] = seg->points[curs].x +
2556 (y - seg->points[curs].y) * seg_dx[seg_index];
2557 }
2558 /* break here, instead of duplicating predicate in while? */
2559 }
2560 if (seg->points[curs].y >= y + 1) {
2561 curs--;
2562 cursor[seg_index] = curs;
2563 seg_x[seg_index] += seg_dx[seg_index];
2564 } else {
2565 art_svp_render_delete_active(active_segs, j--,
2566 --n_active_segs);
2567 }
2568 }
2569
2570 *p_start = start;
2571 *p_steps = steps;
2572 *p_n_steps = n_steps;
2573
2574 iter->seg_ix = i;
2575 iter->n_active_segs = n_active_segs;
2576 iter->y++;
2577 }
2578
art_svp_render_aa_iter_done(ArtSVPRenderAAIter * iter)2579 void art_svp_render_aa_iter_done(ArtSVPRenderAAIter *iter) {
2580 free(iter->steps);
2581
2582 free(iter->seg_dx);
2583 free(iter->seg_x);
2584 free(iter->cursor);
2585 free(iter->active_segs);
2586 free(iter);
2587 }
2588
2589 /**
2590 * art_svp_render_aa: Render SVP antialiased.
2591 * @svp: The #ArtSVP to render.
2592 * @x0: Left coordinate of destination rectangle.
2593 * @y0: Top coordinate of destination rectangle.
2594 * @x1: Right coordinate of destination rectangle.
2595 * @y1: Bottom coordinate of destination rectangle.
2596 * @callback: The callback which actually paints the pixels.
2597 * @callback_data: Private data for @callback.
2598 *
2599 * Renders the sorted vector path in the given rectangle, antialiased.
2600 *
2601 * This interface uses a callback for the actual pixel rendering. The
2602 * callback is called @y1 - @y0 times (once for each scan line). The y
2603 * coordinate is given as an argument for convenience (it could be
2604 * stored in the callback's private data and incremented on each
2605 * call).
2606 *
2607 * The rendered polygon is represented in a semi-runlength format: a
2608 * start value and a sequence of "steps". Each step has an x
2609 * coordinate and a value delta. The resulting value at position x is
2610 * equal to the sum of the start value and all step delta values for
2611 * which the step x coordinate is less than or equal to x. An
2612 * efficient algorithm will traverse the steps left to right, keeping
2613 * a running sum.
2614 *
2615 * All x coordinates in the steps are guaranteed to be @x0 <= x < @x1.
2616 * (This guarantee is a change from the gfonted vpaar renderer from
2617 * which this routine is derived, and is designed to simplify the
2618 * callback).
2619 *
2620 * The value 0x8000 represents 0% coverage by the polygon, while
2621 * 0xff8000 represents 100% coverage. This format is designed so that
2622 * >> 16 results in a standard 0x00..0xff value range, with nice
2623 * rounding.
2624 *
2625 **/
art_svp_render_aa(const ArtSVP * svp,int x0,int y0,int x1,int y1,void (* callback)(void * callback_data,int y,int start,ArtSVPRenderAAStep * steps,int n_steps),void * callback_data)2626 void art_svp_render_aa(const ArtSVP *svp,
2627 int x0, int y0, int x1, int y1,
2628 void (*callback)(void *callback_data,
2629 int y,
2630 int start,
2631 ArtSVPRenderAAStep *steps, int n_steps),
2632 void *callback_data) {
2633 ArtSVPRenderAAIter *iter;
2634 int y;
2635 int start;
2636 ArtSVPRenderAAStep *steps;
2637 int n_steps;
2638
2639 iter = art_svp_render_aa_iter(svp, x0, y0, x1, y1);
2640
2641
2642 for (y = y0; y < y1; y++) {
2643 art_svp_render_aa_iter_step(iter, &start, &steps, &n_steps);
2644 (*callback)(callback_data, y, start, steps, n_steps);
2645 }
2646
2647 art_svp_render_aa_iter_done(iter);
2648 }
2649
2650 } // End of namespace Sword25
2651