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