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