/src/xpdf-4.06/splash/SplashXPath.cc
Line | Count | Source |
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 | 82.4M | 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 | 82.4M | *xo = xi * matrix[0] + yi * matrix[2] + matrix[4]; |
53 | 82.4M | *yo = xi * matrix[1] + yi * matrix[3] + matrix[5]; |
54 | 82.4M | } |
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 | 393M | #define maxCoord 100000000.0 |
64 | | |
65 | 82.4M | void SplashXPath::clampCoords(SplashCoord *x, SplashCoord *y) { |
66 | 82.4M | #if !USE_FIXEDPOINT |
67 | 82.4M | if (*x > maxCoord) { |
68 | 47.0M | *x = maxCoord; |
69 | 47.0M | } else if (*x < -maxCoord) { |
70 | 34.6M | *x = -maxCoord; |
71 | 34.6M | } |
72 | 82.4M | if (*y > maxCoord) { |
73 | 40.5M | *y = maxCoord; |
74 | 41.8M | } else if (*y < -maxCoord) { |
75 | 29.2M | *y = -maxCoord; |
76 | 29.2M | } |
77 | 82.4M | #endif |
78 | 82.4M | } |
79 | | |
80 | | SplashXPath::SplashXPath(SplashPath *path, SplashCoord *matrix, |
81 | | SplashCoord flatness, GBool closeSubpaths, |
82 | | GBool simplify, |
83 | | SplashStrokeAdjustMode strokeAdjMode, |
84 | 194k | SplashClip *clip) { |
85 | 194k | SplashXPathPoint *pts; |
86 | 194k | SplashCoord x0, y0, x1, y1, x2, y2, x3, y3, xsp, ysp, t; |
87 | 194k | int nSubpaths, curSubpath, firstSegInSubpath, i; |
88 | 194k | GBool adjusted; |
89 | | |
90 | | //--- transform the points |
91 | 194k | pts = (SplashXPathPoint *)gmallocn(path->length, sizeof(SplashXPathPoint)); |
92 | 82.5M | for (i = 0; i < path->length; ++i) { |
93 | 82.4M | transform(matrix, path->pts[i].x, path->pts[i].y, &pts[i].x, &pts[i].y); |
94 | 82.4M | clampCoords(&pts[i].x, &pts[i].y); |
95 | 82.4M | } |
96 | | |
97 | | //--- do stroke adjustment |
98 | 194k | if (path->hints) { |
99 | 34.6k | adjusted = strokeAdjust(pts, path->hints, path->hintsLength, |
100 | 34.6k | strokeAdjMode, clip); |
101 | 159k | } else { |
102 | 159k | adjusted = gFalse; |
103 | 159k | } |
104 | | |
105 | | //--- construct the segments |
106 | | |
107 | 194k | segs = NULL; |
108 | 194k | length = size = 0; |
109 | | |
110 | 194k | x0 = y0 = xsp = ysp = 0; // make gcc happy |
111 | 194k | nSubpaths = 0; |
112 | 194k | curSubpath = 0; |
113 | 194k | firstSegInSubpath = 0; |
114 | 194k | i = 0; |
115 | 79.3M | while (i < path->length) { |
116 | | |
117 | | // first point in subpath - skip it |
118 | 79.1M | if (path->flags[i] & splashPathFirst) { |
119 | 16.2M | x0 = pts[i].x; |
120 | 16.2M | y0 = pts[i].y; |
121 | 16.2M | xsp = x0; |
122 | 16.2M | ysp = y0; |
123 | 16.2M | curSubpath = i; |
124 | 16.2M | ++i; |
125 | | |
126 | 62.8M | } else { |
127 | | |
128 | | // curve segment |
129 | 62.8M | if (path->flags[i] & splashPathCurve) { |
130 | 1.62M | x1 = pts[i].x; |
131 | 1.62M | y1 = pts[i].y; |
132 | 1.62M | x2 = pts[i+1].x; |
133 | 1.62M | y2 = pts[i+1].y; |
134 | 1.62M | x3 = pts[i+2].x; |
135 | 1.62M | y3 = pts[i+2].y; |
136 | 1.62M | addCurve(x0, y0, x1, y1, x2, y2, x3, y3, |
137 | 1.62M | flatness, |
138 | 1.62M | (path->flags[i-1] & splashPathFirst), |
139 | 1.62M | (path->flags[i+2] & splashPathLast), |
140 | 1.62M | !closeSubpaths && |
141 | 0 | (path->flags[i-1] & splashPathFirst) && |
142 | 0 | !(path->flags[i-1] & splashPathClosed), |
143 | 1.62M | !closeSubpaths && |
144 | 0 | (path->flags[i+2] & splashPathLast) && |
145 | 0 | !(path->flags[i+2] & splashPathClosed)); |
146 | 1.62M | x0 = x3; |
147 | 1.62M | y0 = y3; |
148 | 1.62M | i += 3; |
149 | | |
150 | | // line segment |
151 | 61.2M | } else { |
152 | 61.2M | x1 = pts[i].x; |
153 | 61.2M | y1 = pts[i].y; |
154 | 61.2M | addSegment(x0, y0, x1, y1); |
155 | 61.2M | x0 = x1; |
156 | 61.2M | y0 = y1; |
157 | 61.2M | ++i; |
158 | 61.2M | } |
159 | | |
160 | | // end a subpath |
161 | 62.8M | if (path->flags[i-1] & splashPathLast) { |
162 | 16.2M | ++nSubpaths; |
163 | 16.2M | if (closeSubpaths && |
164 | 16.1M | (pts[i-1].x != pts[curSubpath].x || |
165 | 16.1M | pts[i-1].y != pts[curSubpath].y)) { |
166 | 2.11M | addSegment(x0, y0, xsp, ysp); |
167 | 2.11M | } |
168 | 16.2M | if (simplify && !adjusted) { |
169 | 0 | mergeSegments(firstSegInSubpath); |
170 | 0 | } |
171 | 16.2M | firstSegInSubpath = length; |
172 | 16.2M | } |
173 | 62.8M | } |
174 | 79.1M | } |
175 | | |
176 | 194k | gfree(pts); |
177 | | |
178 | 194k | finishSegments(); |
179 | | |
180 | | //--- check for a rectangle |
181 | 194k | isRect = gFalse; |
182 | 194k | rectX0 = rectY0 = rectX1 = rectY1 = 0; |
183 | 194k | if (nSubpaths == 1 && length == 4) { |
184 | 137k | #if HAVE_STD_SORT |
185 | 137k | std::sort(segs, segs + length, SplashXPathSeg::cmpY); |
186 | | #else |
187 | | qsort(segs, length, sizeof(SplashXPathSeg), &SplashXPathSeg::cmpY); |
188 | | #endif |
189 | 137k | if (segs[0].y0 == segs[0].y1 && |
190 | 95.1k | segs[1].x0 == segs[1].x1 && |
191 | 92.8k | segs[2].x0 == segs[2].x1 && |
192 | 76.6k | segs[3].y0 == segs[3].y1) { |
193 | 74.4k | isRect = gTrue; |
194 | 74.4k | rectX0 = segs[1].x0; |
195 | 74.4k | rectX1 = segs[2].x0; |
196 | 74.4k | rectY0 = segs[0].y0; |
197 | 74.4k | rectY1 = segs[3].y0; |
198 | 74.4k | } else if (segs[0].x0 == segs[0].x1 && |
199 | 44.1k | segs[1].y0 == segs[1].y1 && |
200 | 37.7k | segs[2].x0 == segs[2].x1 && |
201 | 35.9k | segs[3].y0 == segs[3].y1) { |
202 | 35.4k | isRect = gTrue; |
203 | 35.4k | rectX0 = segs[0].x0; |
204 | 35.4k | rectX1 = segs[2].x0; |
205 | 35.4k | rectY0 = segs[1].y0; |
206 | 35.4k | rectY1 = segs[3].y0; |
207 | 35.4k | } else if (segs[0].x0 == segs[0].x1 && |
208 | 8.73k | segs[1].x0 == segs[1].x1 && |
209 | 7.99k | segs[2].y0 == segs[2].y1 && |
210 | 5.25k | segs[3].y0 == segs[3].y1) { |
211 | 4.96k | isRect = gTrue; |
212 | 4.96k | rectX0 = segs[0].x0; |
213 | 4.96k | rectX1 = segs[1].x0; |
214 | 4.96k | rectY0 = segs[2].y0; |
215 | 4.96k | rectY1 = segs[3].y0; |
216 | 4.96k | } |
217 | 137k | if (isRect) { |
218 | 114k | if (rectX0 > rectX1) { |
219 | 21.3k | t = rectX0; rectX0 = rectX1; rectX1 = t; |
220 | 21.3k | } |
221 | 114k | if (rectY0 > rectY1) { |
222 | 0 | t = rectY0; rectY0 = rectY1; rectY1 = t; |
223 | 0 | } |
224 | 114k | } |
225 | 137k | } |
226 | 194k | } |
227 | | |
228 | | GBool SplashXPath::strokeAdjust(SplashXPathPoint *pts, |
229 | | SplashPathHint *hints, int nHints, |
230 | | SplashStrokeAdjustMode strokeAdjMode, |
231 | 34.6k | SplashClip *clip) { |
232 | 34.6k | SplashXPathAdjust *adjusts, *adjust; |
233 | 34.6k | SplashPathHint *hint; |
234 | 34.6k | SplashCoord x0, y0, x1, y1, x2, y2, x3, y3; |
235 | 34.6k | SplashCoord adj0, adj1, w, d; |
236 | 34.6k | int xi0, xi1; |
237 | 34.6k | int i, j; |
238 | 34.6k | GBool adjusted; |
239 | | |
240 | 34.6k | adjusted = gFalse; |
241 | | |
242 | | // If there is a simple rectangular clip region, stroke-adjusted |
243 | | // edges that fall slightly outside the clip region are adjusted |
244 | | // back inside the clip region. This avoids problems with narrow |
245 | | // lines in slightly mismatched clip rectangles, which appear to be |
246 | | // generated somewhat commonly by buggy CAD software. (Note: [clip] |
247 | | // is NULL when called to build a clip path.) |
248 | 34.6k | GBool clipTweak = clip && clip->getIsSimple(); |
249 | 34.6k | SplashCoord cx0 = 0, cx1 = 0, cy0 = 0, cy1 = 0; |
250 | 34.6k | int cxi0 = 0, cxi1 = 0, cyi0 = 0, cyi1 = 0; |
251 | 34.6k | if (clipTweak) { |
252 | 25.1k | cx0 = clip->getXMin(); |
253 | 25.1k | cx1 = clip->getXMax(); |
254 | 25.1k | cy0 = clip->getYMin(); |
255 | 25.1k | cy1 = clip->getYMax(); |
256 | 25.1k | cxi0 = clip->getXMinI(strokeAdjMode); |
257 | 25.1k | cxi1 = clip->getXMaxI(strokeAdjMode); |
258 | 25.1k | cyi0 = clip->getYMinI(strokeAdjMode); |
259 | 25.1k | cyi1 = clip->getYMaxI(strokeAdjMode); |
260 | 25.1k | } |
261 | | |
262 | | // set up the stroke adjustment hints |
263 | 34.6k | adjusts = (SplashXPathAdjust *)gmallocn(nHints, sizeof(SplashXPathAdjust)); |
264 | 15.8M | for (i = 0; i < nHints; ++i) { |
265 | 15.8M | hint = &hints[i]; |
266 | 15.8M | x0 = pts[hint->ctrl0 ].x; y0 = pts[hint->ctrl0 ].y; |
267 | 15.8M | x1 = pts[hint->ctrl0 + 1].x; y1 = pts[hint->ctrl0 + 1].y; |
268 | 15.8M | x2 = pts[hint->ctrl1 ].x; y2 = pts[hint->ctrl1 ].y; |
269 | 15.8M | x3 = pts[hint->ctrl1 + 1].x; y3 = pts[hint->ctrl1 + 1].y; |
270 | 15.8M | w = -1; |
271 | 15.8M | if (splashAbs(x0 - x1) < 0.01 && splashAbs(x2 - x3) < 0.01) { |
272 | 15.7M | adjusts[i].vert = gTrue; |
273 | 15.7M | adj0 = x0; |
274 | 15.7M | adj1 = x2; |
275 | 15.7M | if (hint->projectingCap) { |
276 | 1.41k | w = splashAbs(y1 - y0); |
277 | 1.41k | } |
278 | 15.7M | } else if (splashAbs(y0 - y1) < 0.01 && splashAbs(y2 - y3) < 0.01) { |
279 | 109k | adjusts[i].vert = gFalse; |
280 | 109k | adj0 = y0; |
281 | 109k | adj1 = y2; |
282 | 109k | if (hint->projectingCap) { |
283 | 461 | w = splashAbs(x1 - x0); |
284 | 461 | } |
285 | 109k | } else { |
286 | 9.38k | goto done; |
287 | 9.38k | } |
288 | 15.8M | if (adj0 > adj1) { |
289 | 431k | x0 = adj0; |
290 | 431k | adj0 = adj1; |
291 | 431k | adj1 = x0; |
292 | 431k | } |
293 | 15.8M | d = adj1 - adj0; |
294 | 15.8M | if (d > 0.04) { |
295 | 693k | d = 0.01; |
296 | 15.1M | } else { |
297 | 15.1M | d *= 0.25; |
298 | 15.1M | } |
299 | 15.8M | adjusts[i].x0a = adj0 - d; |
300 | 15.8M | adjusts[i].x0b = adj0 + d; |
301 | 15.8M | adjusts[i].xma = (SplashCoord)0.5 * (adj0 + adj1) - d; |
302 | 15.8M | adjusts[i].xmb = (SplashCoord)0.5 * (adj0 + adj1) + d; |
303 | 15.8M | adjusts[i].x1a = adj1 - d; |
304 | 15.8M | adjusts[i].x1b = adj1 + d; |
305 | 15.8M | splashStrokeAdjust(adj0, adj1, &xi0, &xi1, strokeAdjMode, w); |
306 | 15.8M | if (clipTweak) { |
307 | 9.37M | SplashCoord c0, c1; |
308 | 9.37M | int ci0, ci1; |
309 | 9.37M | if (adjusts[i].vert) { |
310 | 9.27M | c0 = cx0; |
311 | 9.27M | c1 = cx1; |
312 | 9.27M | ci0 = cxi0; |
313 | 9.27M | ci1 = cxi1; |
314 | 9.27M | } else { |
315 | 98.2k | c0 = cy0; |
316 | 98.2k | c1 = cy1; |
317 | 98.2k | ci0 = cyi0; |
318 | 98.2k | ci1 = cyi1; |
319 | 98.2k | } |
320 | 9.37M | if (adj0 < c0 && c0 < adj1 && adj1 < c1 && |
321 | 102 | adj1 - c0 > (adj1 - adj0) * 0.2 && |
322 | 102 | xi1 <= ci0) { |
323 | 0 | xi0 = ci0; |
324 | 0 | xi1 = xi0 + 1; |
325 | 9.37M | } 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 | 9.37M | } |
332 | 15.8M | 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 | 15.8M | adjusts[i].x1 = (SplashCoord)xi1 - 0.001; |
337 | 15.8M | adjusts[i].xm = (SplashCoord)0.5 * (adjusts[i].x0 + adjusts[i].x1); |
338 | 15.8M | adjusts[i].firstPt = hint->firstPt; |
339 | 15.8M | adjusts[i].lastPt = hint->lastPt; |
340 | 15.8M | } |
341 | | |
342 | | // perform stroke adjustment |
343 | 13.4M | for (i = 0, adjust = adjusts; i < nHints; ++i, ++adjust) { |
344 | 97.8M | for (j = adjust->firstPt; j <= adjust->lastPt; ++j) { |
345 | 84.4M | if (adjust->vert) { |
346 | 83.9M | x0 = pts[j].x; |
347 | 83.9M | if (x0 > adjust->x0a && x0 < adjust->x0b) { |
348 | 1.56M | pts[j].x = adjust->x0; |
349 | 82.3M | } else if (x0 > adjust->xma && x0 < adjust->xmb) { |
350 | 6.03k | pts[j].x = adjust->xm; |
351 | 82.3M | } else if (x0 > adjust->x1a && x0 < adjust->x1b) { |
352 | 1.51M | pts[j].x = adjust->x1; |
353 | 1.51M | } |
354 | 83.9M | } else { |
355 | 537k | y0 = pts[j].y; |
356 | 537k | if (y0 > adjust->x0a && y0 < adjust->x0b) { |
357 | 25.3k | pts[j].y = adjust->x0; |
358 | 512k | } else if (y0 > adjust->xma && y0 < adjust->xmb) { |
359 | 1.45k | pts[j].y = adjust->xm; |
360 | 510k | } else if (y0 > adjust->x1a && y0 < adjust->x1b) { |
361 | 24.9k | pts[j].y = adjust->x1; |
362 | 24.9k | } |
363 | 537k | } |
364 | 84.4M | } |
365 | 13.4M | } |
366 | 25.2k | adjusted = gTrue; |
367 | | |
368 | 34.6k | done: |
369 | 34.6k | gfree(adjusts); |
370 | 34.6k | return adjusted; |
371 | 25.2k | } |
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 | 194k | SplashXPath::~SplashXPath() { |
385 | 194k | gfree(segs); |
386 | 194k | } |
387 | | |
388 | | // Add space for <nSegs> more segments |
389 | 95.5M | void SplashXPath::grow(int nSegs) { |
390 | 95.5M | if (length + nSegs > size) { |
391 | 265k | if (size == 0) { |
392 | 179k | size = 32; |
393 | 179k | } |
394 | 351k | while (size < length + nSegs) { |
395 | 85.8k | size *= 2; |
396 | 85.8k | } |
397 | 265k | segs = (SplashXPathSeg *)greallocn(segs, size, sizeof(SplashXPathSeg)); |
398 | 265k | } |
399 | 95.5M | } |
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 | 1.62M | GBool first, GBool last, GBool end0, GBool end1) { |
407 | 1.62M | SplashCoord cx[splashMaxCurveSplits + 1][3]; |
408 | 1.62M | SplashCoord cy[splashMaxCurveSplits + 1][3]; |
409 | 1.62M | int cNext[splashMaxCurveSplits + 1]; |
410 | 1.62M | SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh; |
411 | 1.62M | SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh; |
412 | 1.62M | SplashCoord dx, dy, mx, my, d1, d2, flatness2; |
413 | 1.62M | int p1, p2, p3; |
414 | | |
415 | | #if USE_FIXEDPOINT |
416 | | flatness2 = flatness; |
417 | | #else |
418 | 1.62M | flatness2 = flatness * flatness; |
419 | 1.62M | #endif |
420 | | |
421 | | // initial segment |
422 | 1.62M | p1 = 0; |
423 | 1.62M | p2 = splashMaxCurveSplits; |
424 | 1.62M | cx[p1][0] = x0; cy[p1][0] = y0; |
425 | 1.62M | cx[p1][1] = x1; cy[p1][1] = y1; |
426 | 1.62M | cx[p1][2] = x2; cy[p1][2] = y2; |
427 | 1.62M | cx[p2][0] = x3; cy[p2][0] = y3; |
428 | 1.62M | cNext[p1] = p2; |
429 | | |
430 | 64.4M | while (p1 < splashMaxCurveSplits) { |
431 | | |
432 | | // get the next segment |
433 | 62.7M | xl0 = cx[p1][0]; yl0 = cy[p1][0]; |
434 | 62.7M | xx1 = cx[p1][1]; yy1 = cy[p1][1]; |
435 | 62.7M | xx2 = cx[p1][2]; yy2 = cy[p1][2]; |
436 | 62.7M | p2 = cNext[p1]; |
437 | 62.7M | 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 | 62.7M | mx = (xl0 + xr3) * 0.5; |
444 | 62.7M | 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 | 62.7M | dx = xx1 - mx; |
450 | 62.7M | dy = yy1 - my; |
451 | 62.7M | d1 = dx*dx + dy*dy; |
452 | 62.7M | dx = xx2 - mx; |
453 | 62.7M | dy = yy2 - my; |
454 | 62.7M | d2 = dx*dx + dy*dy; |
455 | 62.7M | #endif |
456 | | |
457 | | // if the curve is flat enough, or no more subdivisions are |
458 | | // allowed, add the straight line segment |
459 | 62.7M | if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) { |
460 | 32.2M | addSegment(xl0, yl0, xr3, yr3); |
461 | 32.2M | p1 = p2; |
462 | | |
463 | | // otherwise, subdivide the curve |
464 | 32.2M | } else { |
465 | 30.5M | xl1 = (xl0 + xx1) * 0.5; |
466 | 30.5M | yl1 = (yl0 + yy1) * 0.5; |
467 | 30.5M | xh = (xx1 + xx2) * 0.5; |
468 | 30.5M | yh = (yy1 + yy2) * 0.5; |
469 | 30.5M | xl2 = (xl1 + xh) * 0.5; |
470 | 30.5M | yl2 = (yl1 + yh) * 0.5; |
471 | 30.5M | xr2 = (xx2 + xr3) * 0.5; |
472 | 30.5M | yr2 = (yy2 + yr3) * 0.5; |
473 | 30.5M | xr1 = (xh + xr2) * 0.5; |
474 | 30.5M | yr1 = (yh + yr2) * 0.5; |
475 | 30.5M | xr0 = (xl2 + xr1) * 0.5; |
476 | 30.5M | yr0 = (yl2 + yr1) * 0.5; |
477 | | // add the new subdivision points |
478 | 30.5M | p3 = (p1 + p2) / 2; |
479 | 30.5M | cx[p1][1] = xl1; cy[p1][1] = yl1; |
480 | 30.5M | cx[p1][2] = xl2; cy[p1][2] = yl2; |
481 | 30.5M | cNext[p1] = p3; |
482 | 30.5M | cx[p3][0] = xr0; cy[p3][0] = yr0; |
483 | 30.5M | cx[p3][1] = xr1; cy[p3][1] = yr1; |
484 | 30.5M | cx[p3][2] = xr2; cy[p3][2] = yr2; |
485 | 30.5M | cNext[p3] = p2; |
486 | 30.5M | } |
487 | 62.7M | } |
488 | 1.62M | } |
489 | | |
490 | | void SplashXPath::addSegment(SplashCoord x0, SplashCoord y0, |
491 | 95.5M | SplashCoord x1, SplashCoord y1) { |
492 | 95.5M | grow(1); |
493 | 95.5M | segs[length].x0 = x0; |
494 | 95.5M | segs[length].y0 = y0; |
495 | 95.5M | segs[length].x1 = x1; |
496 | 95.5M | segs[length].y1 = y1; |
497 | 95.5M | ++length; |
498 | 95.5M | } |
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 | 194k | void SplashXPath::finishSegments() { |
616 | 194k | SplashXPathSeg *seg; |
617 | 194k | SplashCoord xMinFP, xMaxFP, yMinFP, yMaxFP, t; |
618 | 194k | int i; |
619 | | |
620 | 194k | xMinFP = yMinFP = xMaxFP = yMaxFP = 0; |
621 | | |
622 | 95.7M | for (i = 0; i < length; ++i) { |
623 | 95.5M | seg = &segs[i]; |
624 | | |
625 | | //--- compute the slopes |
626 | 95.5M | if (seg->y0 <= seg->y1) { |
627 | 60.8M | seg->count = 1; |
628 | 60.8M | } else { |
629 | 34.7M | t = seg->x0; seg->x0 = seg->x1; seg->x1 = t; |
630 | 34.7M | t = seg->y0; seg->y0 = seg->y1; seg->y1 = t; |
631 | 34.7M | seg->count = -1; |
632 | 34.7M | } |
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 | 95.5M | if (splashAbs(seg->y1 - seg->y0) < 1e-200 || |
644 | 84.9M | splashAbs(seg->x1 - seg->x0) < 1e-200) { |
645 | 84.9M | seg->dxdy = 0; |
646 | 84.9M | seg->dydx = 0; |
647 | 84.9M | } else { |
648 | 10.6M | seg->dxdy = (seg->x1 - seg->x0) / (seg->y1 - seg->y0); |
649 | 10.6M | if (seg->dxdy == 0) { |
650 | 0 | seg->dydx = 0; |
651 | 10.6M | } else { |
652 | 10.6M | seg->dydx = 1 / seg->dxdy; |
653 | 10.6M | } |
654 | 10.6M | } |
655 | 95.5M | #endif |
656 | | |
657 | | //--- update bbox |
658 | 95.5M | if (i == 0) { |
659 | 179k | if (seg->x0 <= seg->x1) { |
660 | 148k | xMinFP = seg->x0; |
661 | 148k | xMaxFP = seg->x1; |
662 | 148k | } else { |
663 | 31.1k | xMinFP = seg->x1; |
664 | 31.1k | xMaxFP = seg->x0; |
665 | 31.1k | } |
666 | 179k | yMinFP = seg->y0; |
667 | 179k | yMaxFP = seg->y1; |
668 | 95.4M | } else { |
669 | 95.4M | if (seg->x0 < xMinFP) { |
670 | 311k | xMinFP = seg->x0; |
671 | 95.0M | } else if (seg->x0 > xMaxFP) { |
672 | 569k | xMaxFP = seg->x0; |
673 | 569k | } |
674 | 95.4M | if (seg->x1 < xMinFP) { |
675 | 645k | xMinFP = seg->x1; |
676 | 94.7M | } else if (seg->x1 > xMaxFP) { |
677 | 896k | xMaxFP = seg->x1; |
678 | 896k | } |
679 | 95.4M | if (seg->y0 < yMinFP) { |
680 | 1.86M | yMinFP = seg->y0; |
681 | 1.86M | } |
682 | 95.4M | if (seg->y1 > yMaxFP) { |
683 | 1.04M | yMaxFP = seg->y1; |
684 | 1.04M | } |
685 | 95.4M | } |
686 | 95.5M | } |
687 | | |
688 | 194k | xMin = splashFloor(xMinFP); |
689 | 194k | yMin = splashFloor(yMinFP); |
690 | 194k | xMax = splashFloor(xMaxFP); |
691 | 194k | yMax = splashFloor(yMaxFP); |
692 | 194k | } |