fix crashes reported by Debian Cylab Mayhem Team
[swftools.git] / lib / art / art_svp_ops.c
blob50c3c5960eb11c856e6d7f31147f30d3d47f8e25
1 /* Libart_LGPL - library of basic graphic primitives
2 * Copyright (C) 1998-2000 Raph Levien
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
20 #define noVERBOSE
22 /* Vector path set operations, over sorted vpaths. */
24 #include "config.h"
25 #include "art_svp_ops.h"
27 #include "art_misc.h"
29 #include "art_svp.h"
30 #include "art_vpath.h"
31 #include "art_svp_vpath.h"
32 #include "art_svp.h"
33 #ifdef ART_USE_NEW_INTERSECTOR
34 #include "art_svp_intersect.h"
35 #else
36 #include "art_svp_wind.h"
37 #endif
38 #include "art_vpath_svp.h"
40 /* Merge the segments of the two svp's. The resulting svp will share
41 segments with args passed in, so be super-careful with the
42 allocation. */
43 /**
44 * art_svp_merge: Merge the segments of two svp's.
45 * @svp1: One svp to merge.
46 * @svp2: The other svp to merge.
48 * Merges the segments of two SVP's into a new one. The resulting
49 * #ArtSVP data structure will share the segments of the argument
50 * svp's, so it is probably a good idea to free it shallowly,
51 * especially if the arguments will be freed with art_svp_free().
53 * Return value: The merged #ArtSVP.
54 **/
55 ArtSVP * art_svp_merge (const ArtSVP *svp1, const ArtSVP *svp2)
57 ArtSVP *svp_new;
58 int ix;
59 int ix1, ix2;
61 svp_new = (ArtSVP *)art_alloc (sizeof(ArtSVP) +
62 (svp1->n_segs + svp2->n_segs - 1) *
63 sizeof(ArtSVPSeg));
64 ix1 = 0;
65 ix2 = 0;
66 for (ix = 0; ix < svp1->n_segs + svp2->n_segs; ix++)
68 if (ix1 < svp1->n_segs &&
69 (ix2 == svp2->n_segs ||
70 art_svp_seg_compare (&svp1->segs[ix1], &svp2->segs[ix2]) < 1))
71 svp_new->segs[ix] = svp1->segs[ix1++];
72 else
73 svp_new->segs[ix] = svp2->segs[ix2++];
76 svp_new->n_segs = ix;
77 return svp_new;
80 #ifdef VERBOSE
82 #define XOFF 50
83 #define YOFF 700
85 static void
86 print_ps_vpath (ArtVpath *vpath)
88 int i;
90 printf ("gsave %d %d translate 1 -1 scale\n", XOFF, YOFF);
91 for (i = 0; vpath[i].code != ART_END; i++)
93 switch (vpath[i].code)
95 case ART_MOVETO:
96 printf ("%g %g moveto\n", vpath[i].x, vpath[i].y);
97 break;
98 case ART_LINETO:
99 printf ("%g %g lineto\n", vpath[i].x, vpath[i].y);
100 break;
101 default:
102 break;
105 printf ("stroke grestore showpage\n");
108 #define DELT 4
110 static void
111 print_ps_svp (ArtSVP *vpath)
113 int i, j;
115 printf ("%% begin\n");
116 for (i = 0; i < vpath->n_segs; i++)
118 printf ("%g setgray\n", vpath->segs[i].dir ? 0.7 : 0);
119 for (j = 0; j < vpath->segs[i].n_points; j++)
121 printf ("%g %g %s\n",
122 XOFF + vpath->segs[i].points[j].x,
123 YOFF - vpath->segs[i].points[j].y,
124 j ? "lineto" : "moveto");
126 printf ("%g %g moveto %g %g lineto %g %g lineto %g %g lineto stroke\n",
127 XOFF + vpath->segs[i].points[0].x - DELT,
128 YOFF - DELT - vpath->segs[i].points[0].y,
129 XOFF + vpath->segs[i].points[0].x - DELT,
130 YOFF - vpath->segs[i].points[0].y,
131 XOFF + vpath->segs[i].points[0].x + DELT,
132 YOFF - vpath->segs[i].points[0].y,
133 XOFF + vpath->segs[i].points[0].x + DELT,
134 YOFF - DELT - vpath->segs[i].points[0].y);
135 printf ("%g %g moveto %g %g lineto %g %g lineto %g %g lineto stroke\n",
136 XOFF + vpath->segs[i].points[j - 1].x - DELT,
137 YOFF + DELT - vpath->segs[i].points[j - 1].y,
138 XOFF + vpath->segs[i].points[j - 1].x - DELT,
139 YOFF - vpath->segs[i].points[j - 1].y,
140 XOFF + vpath->segs[i].points[j - 1].x + DELT,
141 YOFF - vpath->segs[i].points[j - 1].y,
142 XOFF + vpath->segs[i].points[j - 1].x + DELT,
143 YOFF + DELT - vpath->segs[i].points[j - 1].y);
144 printf ("stroke\n");
147 printf ("showpage\n");
149 #endif
151 #ifndef ART_USE_NEW_INTERSECTOR
152 static ArtSVP *
153 art_svp_merge_perturbed (const ArtSVP *svp1, const ArtSVP *svp2)
155 ArtVpath *vpath1, *vpath2;
156 ArtVpath *vpath1_p, *vpath2_p;
157 ArtSVP *svp1_p, *svp2_p;
158 ArtSVP *svp_new;
160 vpath1 = art_vpath_from_svp (svp1);
161 vpath1_p = art_vpath_perturb (vpath1);
162 art_free (vpath1);
163 svp1_p = art_svp_from_vpath (vpath1_p);
164 art_free (vpath1_p);
166 vpath2 = art_vpath_from_svp (svp2);
167 vpath2_p = art_vpath_perturb (vpath2);
168 art_free (vpath2);
169 svp2_p = art_svp_from_vpath (vpath2_p);
170 art_free (vpath2_p);
172 svp_new = art_svp_merge (svp1_p, svp2_p);
173 #ifdef VERBOSE
174 print_ps_svp (svp1_p);
175 print_ps_svp (svp2_p);
176 print_ps_svp (svp_new);
177 #endif
178 art_free (svp1_p);
179 art_free (svp2_p);
181 return svp_new;
183 #endif
185 /* Compute the union of two vector paths.
187 Status of this routine:
189 Basic correctness: Seems to work.
191 Numerical stability: We cheat (adding random perturbation). Thus,
192 it seems very likely that no numerical stability problems will be
193 seen in practice.
195 Speed: Would be better if we didn't go to unsorted vector path
196 and back to add the perturbation.
198 Precision: The perturbation fuzzes the coordinates slightly. In
199 cases of butting segments, razor thin long holes may appear.
203 * art_svp_union: Compute the union of two sorted vector paths.
204 * @svp1: One sorted vector path.
205 * @svp2: The other sorted vector path.
207 * Computes the union of the two argument svp's. Given two svp's with
208 * winding numbers of 0 and 1 everywhere, the resulting winding number
209 * will be 1 where either (or both) of the argument svp's has a
210 * winding number 1, 0 otherwise. The result is newly allocated.
212 * Currently, this routine has accuracy problems pending the
213 * implementation of the new intersector.
215 * Return value: The union of @svp1 and @svp2.
217 ArtSVP *
218 art_svp_union (const ArtSVP *svp1, const ArtSVP *svp2)
220 #ifdef ART_USE_NEW_INTERSECTOR
221 ArtSVP *svp3, *svp_new;
222 ArtSvpWriter *swr;
224 svp3 = art_svp_merge (svp1, svp2);
225 swr = art_svp_writer_rewind_new (ART_WIND_RULE_POSITIVE);
226 art_svp_intersector (svp3, swr);
227 svp_new = art_svp_writer_rewind_reap (swr);
228 art_free (svp3); /* shallow free because svp3 contains shared segments */
230 return svp_new;
231 #else
232 ArtSVP *svp3, *svp4, *svp_new;
234 svp3 = art_svp_merge_perturbed (svp1, svp2);
235 svp4 = art_svp_uncross (svp3);
236 art_svp_free (svp3);
238 svp_new = art_svp_rewind_uncrossed (svp4, ART_WIND_RULE_POSITIVE);
239 #ifdef VERBOSE
240 print_ps_svp (svp4);
241 print_ps_svp (svp_new);
242 #endif
243 art_svp_free (svp4);
244 return svp_new;
245 #endif
248 /* Compute the intersection of two vector paths.
250 Status of this routine:
252 Basic correctness: Seems to work.
254 Numerical stability: We cheat (adding random perturbation). Thus,
255 it seems very likely that no numerical stability problems will be
256 seen in practice.
258 Speed: Would be better if we didn't go to unsorted vector path
259 and back to add the perturbation.
261 Precision: The perturbation fuzzes the coordinates slightly. In
262 cases of butting segments, razor thin long isolated segments may
263 appear.
268 * art_svp_intersect: Compute the intersection of two sorted vector paths.
269 * @svp1: One sorted vector path.
270 * @svp2: The other sorted vector path.
272 * Computes the intersection of the two argument svp's. Given two
273 * svp's with winding numbers of 0 and 1 everywhere, the resulting
274 * winding number will be 1 where both of the argument svp's has a
275 * winding number 1, 0 otherwise. The result is newly allocated.
277 * Currently, this routine has accuracy problems pending the
278 * implementation of the new intersector.
280 * Return value: The intersection of @svp1 and @svp2.
282 ArtSVP *
283 art_svp_intersect (const ArtSVP *svp1, const ArtSVP *svp2)
285 #ifdef ART_USE_NEW_INTERSECTOR
286 ArtSVP *svp3, *svp_new;
287 ArtSvpWriter *swr;
289 svp3 = art_svp_merge (svp1, svp2);
290 swr = art_svp_writer_rewind_new (ART_WIND_RULE_INTERSECT);
291 art_svp_intersector (svp3, swr);
292 svp_new = art_svp_writer_rewind_reap (swr);
293 art_free (svp3); /* shallow free because svp3 contains shared segments */
295 return svp_new;
296 #else
297 ArtSVP *svp3, *svp4, *svp_new;
299 svp3 = art_svp_merge_perturbed (svp1, svp2);
300 svp4 = art_svp_uncross (svp3);
301 art_svp_free (svp3);
303 svp_new = art_svp_rewind_uncrossed (svp4, ART_WIND_RULE_INTERSECT);
304 art_svp_free (svp4);
305 return svp_new;
306 #endif
309 /* Compute the symmetric difference of two vector paths.
311 Status of this routine:
313 Basic correctness: Seems to work.
315 Numerical stability: We cheat (adding random perturbation). Thus,
316 it seems very likely that no numerical stability problems will be
317 seen in practice.
319 Speed: We could do a lot better by scanning through the svp
320 representations and culling out any segments that are exactly
321 identical. It would also be better if we didn't go to unsorted
322 vector path and back to add the perturbation.
324 Precision: Awful. In the case of inputs which are similar (the
325 common case for canvas display), the entire outline is "hairy." In
326 addition, the perturbation fuzzes the coordinates slightly. It can
327 be used as a conservative approximation.
332 * art_svp_diff: Compute the symmetric difference of two sorted vector paths.
333 * @svp1: One sorted vector path.
334 * @svp2: The other sorted vector path.
336 * Computes the symmetric of the two argument svp's. Given two svp's
337 * with winding numbers of 0 and 1 everywhere, the resulting winding
338 * number will be 1 where either, but not both, of the argument svp's
339 * has a winding number 1, 0 otherwise. The result is newly allocated.
341 * Currently, this routine has accuracy problems pending the
342 * implementation of the new intersector.
344 * Return value: The symmetric difference of @svp1 and @svp2.
346 ArtSVP *
347 art_svp_diff (const ArtSVP *svp1, const ArtSVP *svp2)
349 #ifdef ART_USE_NEW_INTERSECTOR
350 ArtSVP *svp3, *svp_new;
351 ArtSvpWriter *swr;
353 svp3 = art_svp_merge (svp1, svp2);
354 swr = art_svp_writer_rewind_new (ART_WIND_RULE_ODDEVEN);
355 art_svp_intersector (svp3, swr);
356 svp_new = art_svp_writer_rewind_reap (swr);
357 art_free (svp3); /* shallow free because svp3 contains shared segments */
359 return svp_new;
360 #else
361 ArtSVP *svp3, *svp4, *svp_new;
363 svp3 = art_svp_merge_perturbed (svp1, svp2);
364 svp4 = art_svp_uncross (svp3);
365 art_svp_free (svp3);
367 svp_new = art_svp_rewind_uncrossed (svp4, ART_WIND_RULE_ODDEVEN);
368 art_svp_free (svp4);
369 return svp_new;
370 #endif
373 #ifdef ART_USE_NEW_INTERSECTOR
374 ArtSVP *
375 art_svp_minus (const ArtSVP *svp1, const ArtSVP *svp2)
377 ArtSVP *svp2_mod;
378 ArtSVP *svp3, *svp_new;
379 ArtSvpWriter *swr;
380 int i;
382 svp2_mod = (ArtSVP *) svp2; /* get rid of the const for a while */
384 /* First invert svp2 to "turn it inside out" */
385 for (i = 0; i < svp2_mod->n_segs; i++)
386 svp2_mod->segs[i].dir = !svp2_mod->segs[i].dir;
388 svp3 = art_svp_merge (svp1, svp2_mod);
389 swr = art_svp_writer_rewind_new (ART_WIND_RULE_POSITIVE);
390 art_svp_intersector (svp3, swr);
391 svp_new = art_svp_writer_rewind_reap (swr);
392 art_free (svp3); /* shallow free because svp3 contains shared segments */
394 /* Flip svp2 back to its original state */
395 for (i = 0; i < svp2_mod->n_segs; i++)
396 svp2_mod->segs[i].dir = !svp2_mod->segs[i].dir;
398 return svp_new;
400 #endif /* ART_USE_NEW_INTERSECTOR */