/src/xpdf-4.05/splash/SplashXPath.cc
Line | Count | Source (jump to first uncovered line) |
1 | | //======================================================================== |
2 | | // |
3 | | // SplashXPath.cc |
4 | | // |
5 | | // Copyright 2003-2013 Glyph & Cog, LLC |
6 | | // |
7 | | //======================================================================== |
8 | | |
9 | | #include <aconf.h> |
10 | | |
11 | | #include <stdlib.h> |
12 | | #include <string.h> |
13 | | #if HAVE_STD_SORT |
14 | | #include <algorithm> |
15 | | #endif |
16 | | #include "gmem.h" |
17 | | #include "gmempp.h" |
18 | | #include "SplashMath.h" |
19 | | #include "SplashPath.h" |
20 | | #include "SplashClip.h" |
21 | | #include "SplashXPath.h" |
22 | | |
23 | | //------------------------------------------------------------------------ |
24 | | |
25 | 0 | #define minCosSquaredJoinAngle 0.75 |
26 | 0 | #define maxPointToLineDistanceSquared 0.04 |
27 | | |
28 | | //------------------------------------------------------------------------ |
29 | | |
30 | | struct SplashXPathPoint { |
31 | | SplashCoord x, y; |
32 | | }; |
33 | | |
34 | | struct SplashXPathAdjust { |
35 | | int firstPt, lastPt; // range of points |
36 | | GBool vert; // vertical or horizontal hint |
37 | | SplashCoord x0a, x0b, // hint boundaries |
38 | | xma, xmb, |
39 | | x1a, x1b; |
40 | | SplashCoord x0, x1, xm; // adjusted coordinates |
41 | | }; |
42 | | |
43 | | //------------------------------------------------------------------------ |
44 | | |
45 | | // Transform a point from user space to device space. |
46 | | inline void SplashXPath::transform(SplashCoord *matrix, |
47 | | SplashCoord xi, SplashCoord yi, |
48 | 0 | SplashCoord *xo, SplashCoord *yo) { |
49 | | // [ m[0] m[1] 0 ] |
50 | | // [xo yo 1] = [xi yi 1] * [ m[2] m[3] 0 ] |
51 | | // [ m[4] m[5] 1 ] |
52 | 0 | *xo = xi * matrix[0] + yi * matrix[2] + matrix[4]; |
53 | 0 | *yo = xi * matrix[1] + yi * matrix[3] + matrix[5]; |
54 | 0 | } |
55 | | |
56 | | //------------------------------------------------------------------------ |
57 | | // SplashXPath |
58 | | //------------------------------------------------------------------------ |
59 | | |
60 | | // SplashXPath segment coords are clipped to +/-maxCoord to avoid |
61 | | // problems. The xMin/yMin/xMax/yMax fields are 32-bit integers, so |
62 | | // coords need to be < 2^31 / aa{Horiz,Vert}. |
63 | 0 | #define maxCoord 100000000.0 |
64 | | |
65 | 0 | void SplashXPath::clampCoords(SplashCoord *x, SplashCoord *y) { |
66 | 0 | #if !USE_FIXEDPOINT |
67 | 0 | if (*x > maxCoord) { |
68 | 0 | *x = maxCoord; |
69 | 0 | } else if (*x < -maxCoord) { |
70 | 0 | *x = -maxCoord; |
71 | 0 | } |
72 | 0 | if (*y > maxCoord) { |
73 | 0 | *y = maxCoord; |
74 | 0 | } else if (*y < -maxCoord) { |
75 | 0 | *y = -maxCoord; |
76 | 0 | } |
77 | 0 | #endif |
78 | 0 | } |
79 | | |
80 | | SplashXPath::SplashXPath(SplashPath *path, SplashCoord *matrix, |
81 | | SplashCoord flatness, GBool closeSubpaths, |
82 | | GBool simplify, |
83 | | SplashStrokeAdjustMode strokeAdjMode, |
84 | 0 | SplashClip *clip) { |
85 | 0 | SplashXPathPoint *pts; |
86 | 0 | SplashCoord x0, y0, x1, y1, x2, y2, x3, y3, xsp, ysp, t; |
87 | 0 | int curSubpath, firstSegInSubpath, i; |
88 | 0 | GBool adjusted; |
89 | | |
90 | | //--- transform the points |
91 | 0 | pts = (SplashXPathPoint *)gmallocn(path->length, sizeof(SplashXPathPoint)); |
92 | 0 | for (i = 0; i < path->length; ++i) { |
93 | 0 | transform(matrix, path->pts[i].x, path->pts[i].y, &pts[i].x, &pts[i].y); |
94 | 0 | clampCoords(&pts[i].x, &pts[i].y); |
95 | 0 | } |
96 | | |
97 | | //--- do stroke adjustment |
98 | 0 | if (path->hints) { |
99 | 0 | adjusted = strokeAdjust(pts, path->hints, path->hintsLength, |
100 | 0 | strokeAdjMode, clip); |
101 | 0 | } else { |
102 | 0 | adjusted = gFalse; |
103 | 0 | } |
104 | | |
105 | | //--- construct the segments |
106 | |
|
107 | 0 | segs = NULL; |
108 | 0 | length = size = 0; |
109 | |
|
110 | 0 | x0 = y0 = xsp = ysp = 0; // make gcc happy |
111 | 0 | curSubpath = 0; |
112 | 0 | firstSegInSubpath = 0; |
113 | 0 | i = 0; |
114 | 0 | while (i < path->length) { |
115 | | |
116 | | // first point in subpath - skip it |
117 | 0 | if (path->flags[i] & splashPathFirst) { |
118 | 0 | x0 = pts[i].x; |
119 | 0 | y0 = pts[i].y; |
120 | 0 | xsp = x0; |
121 | 0 | ysp = y0; |
122 | 0 | curSubpath = i; |
123 | 0 | ++i; |
124 | |
|
125 | 0 | } else { |
126 | | |
127 | | // curve segment |
128 | 0 | if (path->flags[i] & splashPathCurve) { |
129 | 0 | x1 = pts[i].x; |
130 | 0 | y1 = pts[i].y; |
131 | 0 | x2 = pts[i+1].x; |
132 | 0 | y2 = pts[i+1].y; |
133 | 0 | x3 = pts[i+2].x; |
134 | 0 | y3 = pts[i+2].y; |
135 | 0 | addCurve(x0, y0, x1, y1, x2, y2, x3, y3, |
136 | 0 | flatness, |
137 | 0 | (path->flags[i-1] & splashPathFirst), |
138 | 0 | (path->flags[i+2] & splashPathLast), |
139 | 0 | !closeSubpaths && |
140 | 0 | (path->flags[i-1] & splashPathFirst) && |
141 | 0 | !(path->flags[i-1] & splashPathClosed), |
142 | 0 | !closeSubpaths && |
143 | 0 | (path->flags[i+2] & splashPathLast) && |
144 | 0 | !(path->flags[i+2] & splashPathClosed)); |
145 | 0 | x0 = x3; |
146 | 0 | y0 = y3; |
147 | 0 | i += 3; |
148 | | |
149 | | // line segment |
150 | 0 | } else { |
151 | 0 | x1 = pts[i].x; |
152 | 0 | y1 = pts[i].y; |
153 | 0 | addSegment(x0, y0, x1, y1); |
154 | 0 | x0 = x1; |
155 | 0 | y0 = y1; |
156 | 0 | ++i; |
157 | 0 | } |
158 | | |
159 | | // end a subpath |
160 | 0 | if (path->flags[i-1] & splashPathLast) { |
161 | 0 | if (closeSubpaths && |
162 | 0 | (pts[i-1].x != pts[curSubpath].x || |
163 | 0 | pts[i-1].y != pts[curSubpath].y)) { |
164 | 0 | addSegment(x0, y0, xsp, ysp); |
165 | 0 | } |
166 | 0 | if (simplify && !adjusted) { |
167 | 0 | mergeSegments(firstSegInSubpath); |
168 | 0 | } |
169 | 0 | firstSegInSubpath = length; |
170 | 0 | } |
171 | 0 | } |
172 | 0 | } |
173 | |
|
174 | 0 | gfree(pts); |
175 | |
|
176 | 0 | finishSegments(); |
177 | | |
178 | | //--- check for a rectangle |
179 | 0 | isRect = gFalse; |
180 | 0 | rectX0 = rectY0 = rectX1 = rectY1 = 0; |
181 | 0 | if (length == 4) { |
182 | 0 | #if HAVE_STD_SORT |
183 | 0 | std::sort(segs, segs + length, SplashXPathSeg::cmpY); |
184 | | #else |
185 | | qsort(segs, length, sizeof(SplashXPathSeg), &SplashXPathSeg::cmpY); |
186 | | #endif |
187 | 0 | if (segs[0].y0 == segs[0].y1 && |
188 | 0 | segs[1].x0 == segs[1].x1 && |
189 | 0 | segs[2].x0 == segs[2].x1 && |
190 | 0 | segs[3].y0 == segs[3].y1) { |
191 | 0 | isRect = gTrue; |
192 | 0 | rectX0 = segs[1].x0; |
193 | 0 | rectX1 = segs[2].x0; |
194 | 0 | rectY0 = segs[0].y0; |
195 | 0 | rectY1 = segs[3].y0; |
196 | 0 | } else if (segs[0].x0 == segs[0].x1 && |
197 | 0 | segs[1].y0 == segs[1].y1 && |
198 | 0 | segs[2].x0 == segs[2].x1 && |
199 | 0 | segs[3].y0 == segs[3].y1) { |
200 | 0 | isRect = gTrue; |
201 | 0 | rectX0 = segs[0].x0; |
202 | 0 | rectX1 = segs[2].x0; |
203 | 0 | rectY0 = segs[1].y0; |
204 | 0 | rectY1 = segs[3].y0; |
205 | 0 | } else if (segs[0].x0 == segs[0].x1 && |
206 | 0 | segs[1].x0 == segs[1].x1 && |
207 | 0 | segs[2].y0 == segs[2].y1 && |
208 | 0 | segs[3].y0 == segs[3].y1) { |
209 | 0 | isRect = gTrue; |
210 | 0 | rectX0 = segs[0].x0; |
211 | 0 | rectX1 = segs[1].x0; |
212 | 0 | rectY0 = segs[2].y0; |
213 | 0 | rectY1 = segs[3].y0; |
214 | 0 | } |
215 | 0 | if (isRect) { |
216 | 0 | if (rectX0 > rectX1) { |
217 | 0 | t = rectX0; rectX0 = rectX1; rectX1 = t; |
218 | 0 | } |
219 | 0 | if (rectY0 > rectY1) { |
220 | 0 | t = rectY0; rectY0 = rectY1; rectY1 = t; |
221 | 0 | } |
222 | 0 | } |
223 | 0 | } |
224 | 0 | } |
225 | | |
226 | | GBool SplashXPath::strokeAdjust(SplashXPathPoint *pts, |
227 | | SplashPathHint *hints, int nHints, |
228 | | SplashStrokeAdjustMode strokeAdjMode, |
229 | 0 | SplashClip *clip) { |
230 | 0 | SplashXPathAdjust *adjusts, *adjust; |
231 | 0 | SplashPathHint *hint; |
232 | 0 | SplashCoord x0, y0, x1, y1, x2, y2, x3, y3; |
233 | 0 | SplashCoord adj0, adj1, w, d; |
234 | 0 | int xi0, xi1; |
235 | 0 | int i, j; |
236 | 0 | GBool adjusted; |
237 | |
|
238 | 0 | adjusted = gFalse; |
239 | | |
240 | | // With CAD-mode stroke adjustment, and a simple rectangular clip |
241 | | // region, stroke-adjusted edges that fall slightly outside the clip |
242 | | // region are adjusted back inside the clip region. This avoids |
243 | | // problems with narrow lines in slightly mismatched clip |
244 | | // rectangles, which appear to be generated somewhat commonly by |
245 | | // buggy CAD software. (Note: [clip] is NULL when called to build a |
246 | | // clip path.) |
247 | 0 | GBool clipTweak = clip && clip->getIsSimple() && |
248 | 0 | strokeAdjMode == splashStrokeAdjustCAD; |
249 | 0 | SplashCoord cx0 = 0, cx1 = 0, cy0 = 0, cy1 = 0; |
250 | 0 | int cxi0 = 0, cxi1 = 0, cyi0 = 0, cyi1 = 0; |
251 | 0 | if (clipTweak) { |
252 | 0 | cx0 = clip->getXMin(); |
253 | 0 | cx1 = clip->getXMax(); |
254 | 0 | cy0 = clip->getYMin(); |
255 | 0 | cy1 = clip->getYMax(); |
256 | 0 | cxi0 = clip->getXMinI(strokeAdjMode); |
257 | 0 | cxi1 = clip->getXMaxI(strokeAdjMode); |
258 | 0 | cyi0 = clip->getYMinI(strokeAdjMode); |
259 | 0 | cyi1 = clip->getYMaxI(strokeAdjMode); |
260 | 0 | } |
261 | | |
262 | | // set up the stroke adjustment hints |
263 | 0 | adjusts = (SplashXPathAdjust *)gmallocn(nHints, sizeof(SplashXPathAdjust)); |
264 | 0 | for (i = 0; i < nHints; ++i) { |
265 | 0 | hint = &hints[i]; |
266 | 0 | x0 = pts[hint->ctrl0 ].x; y0 = pts[hint->ctrl0 ].y; |
267 | 0 | x1 = pts[hint->ctrl0 + 1].x; y1 = pts[hint->ctrl0 + 1].y; |
268 | 0 | x2 = pts[hint->ctrl1 ].x; y2 = pts[hint->ctrl1 ].y; |
269 | 0 | x3 = pts[hint->ctrl1 + 1].x; y3 = pts[hint->ctrl1 + 1].y; |
270 | 0 | w = -1; |
271 | 0 | if (splashAbs(x0 - x1) < 0.01 && splashAbs(x2 - x3) < 0.01) { |
272 | 0 | adjusts[i].vert = gTrue; |
273 | 0 | adj0 = x0; |
274 | 0 | adj1 = x2; |
275 | 0 | if (hint->projectingCap) { |
276 | 0 | w = splashAbs(y1 - y0); |
277 | 0 | } |
278 | 0 | } else if (splashAbs(y0 - y1) < 0.01 && splashAbs(y2 - y3) < 0.01) { |
279 | 0 | adjusts[i].vert = gFalse; |
280 | 0 | adj0 = y0; |
281 | 0 | adj1 = y2; |
282 | 0 | if (hint->projectingCap) { |
283 | 0 | w = splashAbs(x1 - x0); |
284 | 0 | } |
285 | 0 | } else { |
286 | 0 | goto done; |
287 | 0 | } |
288 | 0 | if (adj0 > adj1) { |
289 | 0 | x0 = adj0; |
290 | 0 | adj0 = adj1; |
291 | 0 | adj1 = x0; |
292 | 0 | } |
293 | 0 | d = adj1 - adj0; |
294 | 0 | if (d > 0.04) { |
295 | 0 | d = 0.01; |
296 | 0 | } else { |
297 | 0 | d *= 0.25; |
298 | 0 | } |
299 | 0 | adjusts[i].x0a = adj0 - d; |
300 | 0 | adjusts[i].x0b = adj0 + d; |
301 | 0 | adjusts[i].xma = (SplashCoord)0.5 * (adj0 + adj1) - d; |
302 | 0 | adjusts[i].xmb = (SplashCoord)0.5 * (adj0 + adj1) + d; |
303 | 0 | adjusts[i].x1a = adj1 - d; |
304 | 0 | adjusts[i].x1b = adj1 + d; |
305 | 0 | splashStrokeAdjust(adj0, adj1, &xi0, &xi1, strokeAdjMode, w); |
306 | 0 | if (clipTweak) { |
307 | 0 | SplashCoord c0, c1; |
308 | 0 | int ci0, ci1; |
309 | 0 | if (adjusts[i].vert) { |
310 | 0 | c0 = cx0; |
311 | 0 | c1 = cx1; |
312 | 0 | ci0 = cxi0; |
313 | 0 | ci1 = cxi1; |
314 | 0 | } else { |
315 | 0 | c0 = cy0; |
316 | 0 | c1 = cy1; |
317 | 0 | ci0 = cyi0; |
318 | 0 | ci1 = cyi1; |
319 | 0 | } |
320 | 0 | if (adj0 < c0 && c0 < adj1 && adj1 < c1 && |
321 | 0 | adj1 - c0 > (adj1 - adj0) * 0.2 && |
322 | 0 | xi1 <= ci0) { |
323 | 0 | xi0 = ci0; |
324 | 0 | xi1 = xi0 + 1; |
325 | 0 | } else if (c0 < adj0 && adj0 < c1 && c1 < adj1 && |
326 | 0 | c1 - adj0 > (adj1 - adj0) * 0.2 && |
327 | 0 | ci1 < xi0) { |
328 | 0 | xi0 = ci1; |
329 | 0 | xi1 = ci1 + 1; |
330 | 0 | } |
331 | 0 | } |
332 | 0 | adjusts[i].x0 = (SplashCoord)xi0; |
333 | | // the "minus epsilon" thing here is needed when vector |
334 | | // antialiasing is turned off -- otherwise stroke adjusted lines |
335 | | // will touch an extra pixel on one edge |
336 | 0 | adjusts[i].x1 = (SplashCoord)xi1 - 0.001; |
337 | 0 | adjusts[i].xm = (SplashCoord)0.5 * (adjusts[i].x0 + adjusts[i].x1); |
338 | 0 | adjusts[i].firstPt = hint->firstPt; |
339 | 0 | adjusts[i].lastPt = hint->lastPt; |
340 | 0 | } |
341 | | |
342 | | // perform stroke adjustment |
343 | 0 | for (i = 0, adjust = adjusts; i < nHints; ++i, ++adjust) { |
344 | 0 | for (j = adjust->firstPt; j <= adjust->lastPt; ++j) { |
345 | 0 | if (adjust->vert) { |
346 | 0 | x0 = pts[j].x; |
347 | 0 | if (x0 > adjust->x0a && x0 < adjust->x0b) { |
348 | 0 | pts[j].x = adjust->x0; |
349 | 0 | } else if (x0 > adjust->xma && x0 < adjust->xmb) { |
350 | 0 | pts[j].x = adjust->xm; |
351 | 0 | } else if (x0 > adjust->x1a && x0 < adjust->x1b) { |
352 | 0 | pts[j].x = adjust->x1; |
353 | 0 | } |
354 | 0 | } else { |
355 | 0 | y0 = pts[j].y; |
356 | 0 | if (y0 > adjust->x0a && y0 < adjust->x0b) { |
357 | 0 | pts[j].y = adjust->x0; |
358 | 0 | } else if (y0 > adjust->xma && y0 < adjust->xmb) { |
359 | 0 | pts[j].y = adjust->xm; |
360 | 0 | } else if (y0 > adjust->x1a && y0 < adjust->x1b) { |
361 | 0 | pts[j].y = adjust->x1; |
362 | 0 | } |
363 | 0 | } |
364 | 0 | } |
365 | 0 | } |
366 | 0 | adjusted = gTrue; |
367 | |
|
368 | 0 | done: |
369 | 0 | gfree(adjusts); |
370 | 0 | return adjusted; |
371 | 0 | } |
372 | | |
373 | 0 | SplashXPath::SplashXPath(SplashXPath *xPath) { |
374 | 0 | length = xPath->length; |
375 | 0 | size = xPath->size; |
376 | 0 | segs = (SplashXPathSeg *)gmallocn(size, sizeof(SplashXPathSeg)); |
377 | 0 | memcpy(segs, xPath->segs, length * sizeof(SplashXPathSeg)); |
378 | 0 | xMin = xPath->xMin; |
379 | 0 | yMin = xPath->yMin; |
380 | 0 | xMax = xPath->xMax; |
381 | 0 | yMax = xPath->yMax; |
382 | 0 | } |
383 | | |
384 | 0 | SplashXPath::~SplashXPath() { |
385 | 0 | gfree(segs); |
386 | 0 | } |
387 | | |
388 | | // Add space for <nSegs> more segments |
389 | 0 | void SplashXPath::grow(int nSegs) { |
390 | 0 | if (length + nSegs > size) { |
391 | 0 | if (size == 0) { |
392 | 0 | size = 32; |
393 | 0 | } |
394 | 0 | while (size < length + nSegs) { |
395 | 0 | size *= 2; |
396 | 0 | } |
397 | 0 | segs = (SplashXPathSeg *)greallocn(segs, size, sizeof(SplashXPathSeg)); |
398 | 0 | } |
399 | 0 | } |
400 | | |
401 | | void SplashXPath::addCurve(SplashCoord x0, SplashCoord y0, |
402 | | SplashCoord x1, SplashCoord y1, |
403 | | SplashCoord x2, SplashCoord y2, |
404 | | SplashCoord x3, SplashCoord y3, |
405 | | SplashCoord flatness, |
406 | 0 | GBool first, GBool last, GBool end0, GBool end1) { |
407 | 0 | SplashCoord cx[splashMaxCurveSplits + 1][3]; |
408 | 0 | SplashCoord cy[splashMaxCurveSplits + 1][3]; |
409 | 0 | int cNext[splashMaxCurveSplits + 1]; |
410 | 0 | SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh; |
411 | 0 | SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh; |
412 | 0 | SplashCoord dx, dy, mx, my, d1, d2, flatness2; |
413 | 0 | int p1, p2, p3; |
414 | |
|
415 | | #if USE_FIXEDPOINT |
416 | | flatness2 = flatness; |
417 | | #else |
418 | 0 | flatness2 = flatness * flatness; |
419 | 0 | #endif |
420 | | |
421 | | // initial segment |
422 | 0 | p1 = 0; |
423 | 0 | p2 = splashMaxCurveSplits; |
424 | 0 | cx[p1][0] = x0; cy[p1][0] = y0; |
425 | 0 | cx[p1][1] = x1; cy[p1][1] = y1; |
426 | 0 | cx[p1][2] = x2; cy[p1][2] = y2; |
427 | 0 | cx[p2][0] = x3; cy[p2][0] = y3; |
428 | 0 | cNext[p1] = p2; |
429 | |
|
430 | 0 | while (p1 < splashMaxCurveSplits) { |
431 | | |
432 | | // get the next segment |
433 | 0 | xl0 = cx[p1][0]; yl0 = cy[p1][0]; |
434 | 0 | xx1 = cx[p1][1]; yy1 = cy[p1][1]; |
435 | 0 | xx2 = cx[p1][2]; yy2 = cy[p1][2]; |
436 | 0 | p2 = cNext[p1]; |
437 | 0 | xr3 = cx[p2][0]; yr3 = cy[p2][0]; |
438 | | |
439 | | // compute the distances from the control points to the |
440 | | // midpoint of the straight line (this is a bit of a hack, but |
441 | | // it's much faster than computing the actual distances to the |
442 | | // line) |
443 | 0 | mx = (xl0 + xr3) * 0.5; |
444 | 0 | my = (yl0 + yr3) * 0.5; |
445 | | #if USE_FIXEDPOINT |
446 | | d1 = splashDist(xx1, yy1, mx, my); |
447 | | d2 = splashDist(xx2, yy2, mx, my); |
448 | | #else |
449 | 0 | dx = xx1 - mx; |
450 | 0 | dy = yy1 - my; |
451 | 0 | d1 = dx*dx + dy*dy; |
452 | 0 | dx = xx2 - mx; |
453 | 0 | dy = yy2 - my; |
454 | 0 | d2 = dx*dx + dy*dy; |
455 | 0 | #endif |
456 | | |
457 | | // if the curve is flat enough, or no more subdivisions are |
458 | | // allowed, add the straight line segment |
459 | 0 | if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) { |
460 | 0 | addSegment(xl0, yl0, xr3, yr3); |
461 | 0 | p1 = p2; |
462 | | |
463 | | // otherwise, subdivide the curve |
464 | 0 | } else { |
465 | 0 | xl1 = (xl0 + xx1) * 0.5; |
466 | 0 | yl1 = (yl0 + yy1) * 0.5; |
467 | 0 | xh = (xx1 + xx2) * 0.5; |
468 | 0 | yh = (yy1 + yy2) * 0.5; |
469 | 0 | xl2 = (xl1 + xh) * 0.5; |
470 | 0 | yl2 = (yl1 + yh) * 0.5; |
471 | 0 | xr2 = (xx2 + xr3) * 0.5; |
472 | 0 | yr2 = (yy2 + yr3) * 0.5; |
473 | 0 | xr1 = (xh + xr2) * 0.5; |
474 | 0 | yr1 = (yh + yr2) * 0.5; |
475 | 0 | xr0 = (xl2 + xr1) * 0.5; |
476 | 0 | yr0 = (yl2 + yr1) * 0.5; |
477 | | // add the new subdivision points |
478 | 0 | p3 = (p1 + p2) / 2; |
479 | 0 | cx[p1][1] = xl1; cy[p1][1] = yl1; |
480 | 0 | cx[p1][2] = xl2; cy[p1][2] = yl2; |
481 | 0 | cNext[p1] = p3; |
482 | 0 | cx[p3][0] = xr0; cy[p3][0] = yr0; |
483 | 0 | cx[p3][1] = xr1; cy[p3][1] = yr1; |
484 | 0 | cx[p3][2] = xr2; cy[p3][2] = yr2; |
485 | 0 | cNext[p3] = p2; |
486 | 0 | } |
487 | 0 | } |
488 | 0 | } |
489 | | |
490 | | void SplashXPath::addSegment(SplashCoord x0, SplashCoord y0, |
491 | 0 | SplashCoord x1, SplashCoord y1) { |
492 | 0 | grow(1); |
493 | 0 | segs[length].x0 = x0; |
494 | 0 | segs[length].y0 = y0; |
495 | 0 | segs[length].x1 = x1; |
496 | 0 | segs[length].y1 = y1; |
497 | 0 | ++length; |
498 | 0 | } |
499 | | |
500 | | // Returns true if the angle between (x0,y0)-(x1,y1) and |
501 | | // (x1,y1)-(x2,y2) is close to 180 degrees. |
502 | | static GBool joinAngleIsFlat(SplashCoord x0, SplashCoord y0, |
503 | | SplashCoord x1, SplashCoord y1, |
504 | 0 | SplashCoord x2, SplashCoord y2) { |
505 | 0 | SplashCoord dx1, dy1, dx2, dy2, d, len1, len2; |
506 | |
|
507 | 0 | dx1 = x1 - x0; |
508 | 0 | dy1 = y1 - y0; |
509 | 0 | dx2 = x2 - x1; |
510 | 0 | dy2 = y2 - y1; |
511 | 0 | d = dx1 * dx2 + dy1 * dy2; |
512 | 0 | len1 = dx1 * dx1 + dy1 * dy1; |
513 | 0 | len2 = dx2 * dx2 + dy2 * dy2; |
514 | 0 | return d > 0 && d * d > len1 * len2 * minCosSquaredJoinAngle; |
515 | 0 | } |
516 | | |
517 | | // Returns true if (x1,y1) is sufficiently close to the segment |
518 | | // (x0,y0)-(x2,y2), looking at the perpendicular point-to-line |
519 | | // distance. |
520 | | static GBool pointCloseToSegment(SplashCoord x0, SplashCoord y0, |
521 | | SplashCoord x1, SplashCoord y1, |
522 | 0 | SplashCoord x2, SplashCoord y2) { |
523 | 0 | SplashCoord t1, t2, dx, dy; |
524 | | |
525 | | // compute the perpendicular distance from the point to the segment, |
526 | | // i.e., the projection of (x0,y0)-(x1,y1) onto a unit normal to the |
527 | | // segment (this actually computes the square of the distance) |
528 | 0 | dx = x2 - x0; |
529 | 0 | dy = y2 - y0; |
530 | 0 | t1 = dx*dx + dy*dy; |
531 | 0 | if (t1 < 0.0001) { |
532 | | // degenerate case: (x0,y0) and (x2,y2) are (nearly) identical -- |
533 | | // just compute the distance to (x1,y1) |
534 | 0 | dx = x0 - x1; |
535 | 0 | dy = y0 - y1; |
536 | 0 | t2 = dx*dx + dy*dy; |
537 | 0 | return t2 < maxPointToLineDistanceSquared; |
538 | 0 | } |
539 | 0 | t2 = x1 * dy - dx * y1 - x0 * y2 + x2 * y0; |
540 | | // actual distance = t2 / sqrt(t1) |
541 | 0 | return t2 * t2 < t1 * maxPointToLineDistanceSquared; |
542 | 0 | } |
543 | | |
544 | | // Attempt to simplify the path by merging sequences of consecutive |
545 | | // segments in [first] .. [length]-1. |
546 | 0 | void SplashXPath::mergeSegments(int first) { |
547 | 0 | GBool horiz, vert; |
548 | 0 | int in, out, prev, i, j; |
549 | |
|
550 | 0 | in = out = first; |
551 | 0 | while (in < length) { |
552 | | |
553 | | // skip zero-length segments |
554 | 0 | if (segs[in].x0 == segs[in].x1 && segs[in].y0 == segs[in].y1) { |
555 | 0 | ++in; |
556 | 0 | continue; |
557 | 0 | } |
558 | | |
559 | 0 | horiz = segs[in].y0 == segs[in].y1; |
560 | 0 | vert = segs[in].x0 == segs[in].x1; |
561 | | |
562 | | // check for a sequence of mergeable segments: in .. i |
563 | 0 | prev = in; |
564 | 0 | for (i = in + 1; i < length; ++i) { |
565 | | |
566 | | // skip zero-length segments |
567 | 0 | if (segs[i].x0 == segs[i].x1 && segs[i].y0 == segs[i].y1) { |
568 | 0 | continue; |
569 | 0 | } |
570 | | |
571 | | // check for a horizontal or vertical segment |
572 | 0 | if ((horiz && segs[in].y0 != segs[in].y1) || |
573 | 0 | (vert && segs[in].x0 != segs[in].x1)) { |
574 | 0 | break; |
575 | 0 | } |
576 | | |
577 | | // check the angle between segs i-1 and i |
578 | | // (actually, we compare seg i to the previous non-zero-length |
579 | | // segment, which may not be i-1) |
580 | 0 | if (!joinAngleIsFlat(segs[prev].x0, segs[prev].y0, |
581 | 0 | segs[i].x0, segs[i].y0, |
582 | 0 | segs[i].x1, segs[i].y1)) { |
583 | 0 | break; |
584 | 0 | } |
585 | | |
586 | | // check the distances from the ends of segs in .. i-1 to the |
587 | | // proposed new segment |
588 | 0 | for (j = in; j < i; ++j) { |
589 | 0 | if (!pointCloseToSegment(segs[in].x0, segs[in].y0, |
590 | 0 | segs[j].x1, segs[j].y1, |
591 | 0 | segs[i].x1, segs[i].y1)) { |
592 | 0 | break; |
593 | 0 | } |
594 | 0 | } |
595 | 0 | if (j < i) { |
596 | 0 | break; |
597 | 0 | } |
598 | | |
599 | 0 | prev = i; |
600 | 0 | } |
601 | | |
602 | | // we can merge segs: in .. i-1 |
603 | | // (this may be the single segment: in) |
604 | 0 | segs[out].x0 = segs[in].x0; |
605 | 0 | segs[out].y0 = segs[in].y0; |
606 | 0 | segs[out].x1 = segs[i-1].x1; |
607 | 0 | segs[out].y1 = segs[i-1].y1; |
608 | 0 | in = i; |
609 | 0 | ++out; |
610 | 0 | } |
611 | |
|
612 | 0 | length = out; |
613 | 0 | } |
614 | | |
615 | 0 | void SplashXPath::finishSegments() { |
616 | 0 | SplashXPathSeg *seg; |
617 | 0 | SplashCoord xMinFP, xMaxFP, yMinFP, yMaxFP, t; |
618 | 0 | int i; |
619 | |
|
620 | 0 | xMinFP = yMinFP = xMaxFP = yMaxFP = 0; |
621 | |
|
622 | 0 | for (i = 0; i < length; ++i) { |
623 | 0 | seg = &segs[i]; |
624 | | |
625 | | //--- compute the slopes |
626 | 0 | if (seg->y0 <= seg->y1) { |
627 | 0 | seg->count = 1; |
628 | 0 | } else { |
629 | 0 | t = seg->x0; seg->x0 = seg->x1; seg->x1 = t; |
630 | 0 | t = seg->y0; seg->y0 = seg->y1; seg->y1 = t; |
631 | 0 | seg->count = -1; |
632 | 0 | } |
633 | | #if USE_FIXEDPOINT |
634 | | if (seg->y0 == seg->y1 || seg->x0 == seg->x1 || |
635 | | !FixedPoint::divCheck(seg->x1 - seg->x0, seg->y1 - seg->y0, |
636 | | &seg->dxdy) || |
637 | | !FixedPoint::divCheck(seg->y1 - seg->y0, seg->x1 - seg->x0, |
638 | | &seg->dydx)) { |
639 | | seg->dxdy = 0; |
640 | | seg->dydx = 0; |
641 | | } |
642 | | #else |
643 | 0 | if (splashAbs(seg->y1 - seg->y0) < 1e-200 || |
644 | 0 | splashAbs(seg->x1 - seg->x0) < 1e-200) { |
645 | 0 | seg->dxdy = 0; |
646 | 0 | seg->dydx = 0; |
647 | 0 | } else { |
648 | 0 | seg->dxdy = (seg->x1 - seg->x0) / (seg->y1 - seg->y0); |
649 | 0 | if (seg->dxdy == 0) { |
650 | 0 | seg->dydx = 0; |
651 | 0 | } else { |
652 | 0 | seg->dydx = 1 / seg->dxdy; |
653 | 0 | } |
654 | 0 | } |
655 | 0 | #endif |
656 | | |
657 | | //--- update bbox |
658 | 0 | if (i == 0) { |
659 | 0 | if (seg->x0 <= seg->x1) { |
660 | 0 | xMinFP = seg->x0; |
661 | 0 | xMaxFP = seg->x1; |
662 | 0 | } else { |
663 | 0 | xMinFP = seg->x1; |
664 | 0 | xMaxFP = seg->x0; |
665 | 0 | } |
666 | 0 | yMinFP = seg->y0; |
667 | 0 | yMaxFP = seg->y1; |
668 | 0 | } else { |
669 | 0 | if (seg->x0 < xMinFP) { |
670 | 0 | xMinFP = seg->x0; |
671 | 0 | } else if (seg->x0 > xMaxFP) { |
672 | 0 | xMaxFP = seg->x0; |
673 | 0 | } |
674 | 0 | if (seg->x1 < xMinFP) { |
675 | 0 | xMinFP = seg->x1; |
676 | 0 | } else if (seg->x1 > xMaxFP) { |
677 | 0 | xMaxFP = seg->x1; |
678 | 0 | } |
679 | 0 | if (seg->y0 < yMinFP) { |
680 | 0 | yMinFP = seg->y0; |
681 | 0 | } |
682 | 0 | if (seg->y1 > yMaxFP) { |
683 | 0 | yMaxFP = seg->y1; |
684 | 0 | } |
685 | 0 | } |
686 | 0 | } |
687 | |
|
688 | 0 | xMin = splashFloor(xMinFP); |
689 | 0 | yMin = splashFloor(yMinFP); |
690 | 0 | xMax = splashFloor(xMaxFP); |
691 | 0 | yMax = splashFloor(yMaxFP); |
692 | 0 | } |