Coverage Report

Created: 2026-10-19 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
18.9M
           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
18.9M
  *xo = xi * matrix[0] + yi * matrix[2] + matrix[4];
53
18.9M
  *yo = xi * matrix[1] + yi * matrix[3] + matrix[5];
54
18.9M
}
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
90.4M
#define maxCoord 100000000.0
64
65
18.9M
void SplashXPath::clampCoords(SplashCoord *x, SplashCoord *y) {
66
18.9M
#if !USE_FIXEDPOINT
67
18.9M
  if (*x > maxCoord) {
68
8.37M
    *x = maxCoord;
69
10.5M
  } else if (*x < -maxCoord) {
70
10.2M
    *x = -maxCoord;
71
10.2M
  }
72
18.9M
  if (*y > maxCoord) {
73
6.83M
    *y = maxCoord;
74
12.1M
  } else if (*y < -maxCoord) {
75
4.43M
    *y = -maxCoord;
76
4.43M
  }
77
18.9M
#endif
78
18.9M
}
79
80
SplashXPath::SplashXPath(SplashPath *path, SplashCoord *matrix,
81
       SplashCoord flatness, GBool closeSubpaths,
82
       GBool simplify,
83
       SplashStrokeAdjustMode strokeAdjMode,
84
131k
       SplashClip *clip) {
85
131k
  SplashXPathPoint *pts;
86
131k
  SplashCoord x0, y0, x1, y1, x2, y2, x3, y3, xsp, ysp, t;
87
131k
  int nSubpaths, curSubpath, firstSegInSubpath, i;
88
131k
  GBool adjusted;
89
90
  //--- transform the points
91
131k
  pts = (SplashXPathPoint *)gmallocn(path->length, sizeof(SplashXPathPoint));
92
19.0M
  for (i = 0; i < path->length; ++i) {
93
18.9M
    transform(matrix, path->pts[i].x, path->pts[i].y, &pts[i].x, &pts[i].y);
94
18.9M
    clampCoords(&pts[i].x, &pts[i].y);
95
18.9M
  }
96
97
  //--- do stroke adjustment
98
131k
  if (path->hints) {
99
7.87k
    adjusted = strokeAdjust(pts, path->hints, path->hintsLength,
100
7.87k
          strokeAdjMode, clip);
101
123k
  } else {
102
123k
    adjusted = gFalse;
103
123k
  }
104
105
  //--- construct the segments
106
107
131k
  segs = NULL;
108
131k
  length = size = 0;
109
110
131k
  x0 = y0 = xsp = ysp = 0; // make gcc happy
111
131k
  nSubpaths = 0;
112
131k
  curSubpath = 0;
113
131k
  firstSegInSubpath = 0;
114
131k
  i = 0;
115
18.6M
  while (i < path->length) {
116
117
    // first point in subpath - skip it
118
18.5M
    if (path->flags[i] & splashPathFirst) {
119
3.71M
      x0 = pts[i].x;
120
3.71M
      y0 = pts[i].y;
121
3.71M
      xsp = x0;
122
3.71M
      ysp = y0;
123
3.71M
      curSubpath = i;
124
3.71M
      ++i;
125
126
14.7M
    } else {
127
128
      // curve segment
129
14.7M
      if (path->flags[i] & splashPathCurve) {
130
227k
  x1 = pts[i].x;
131
227k
  y1 = pts[i].y;
132
227k
  x2 = pts[i+1].x;
133
227k
  y2 = pts[i+1].y;
134
227k
  x3 = pts[i+2].x;
135
227k
  y3 = pts[i+2].y;
136
227k
  addCurve(x0, y0, x1, y1, x2, y2, x3, y3,
137
227k
     flatness,
138
227k
     (path->flags[i-1] & splashPathFirst),
139
227k
     (path->flags[i+2] & splashPathLast),
140
227k
     !closeSubpaths &&
141
0
       (path->flags[i-1] & splashPathFirst) &&
142
0
       !(path->flags[i-1] & splashPathClosed),
143
227k
     !closeSubpaths &&
144
0
       (path->flags[i+2] & splashPathLast) &&
145
0
       !(path->flags[i+2] & splashPathClosed));
146
227k
  x0 = x3;
147
227k
  y0 = y3;
148
227k
  i += 3;
149
150
      // line segment
151
14.5M
      } else {
152
14.5M
  x1 = pts[i].x;
153
14.5M
  y1 = pts[i].y;
154
14.5M
  addSegment(x0, y0, x1, y1);
155
14.5M
  x0 = x1;
156
14.5M
  y0 = y1;
157
14.5M
  ++i;
158
14.5M
      }
159
160
      // end a subpath
161
14.7M
      if (path->flags[i-1] & splashPathLast) {
162
3.71M
  ++nSubpaths;
163
3.71M
  if (closeSubpaths &&
164
3.63M
      (pts[i-1].x != pts[curSubpath].x ||
165
3.63M
       pts[i-1].y != pts[curSubpath].y)) {
166
1.49M
    addSegment(x0, y0, xsp, ysp);
167
1.49M
  }
168
3.71M
  if (simplify && !adjusted) {
169
0
    mergeSegments(firstSegInSubpath);
170
0
  }
171
3.71M
  firstSegInSubpath = length;
172
3.71M
      }
173
14.7M
    }
174
18.5M
  }
175
176
131k
  gfree(pts);
177
178
131k
  finishSegments();
179
180
  //--- check for a rectangle
181
131k
  isRect = gFalse;
182
131k
  rectX0 = rectY0 = rectX1 = rectY1 = 0;
183
131k
  if (nSubpaths == 1 && length == 4) {
184
89.5k
#if HAVE_STD_SORT
185
89.5k
    std::sort(segs, segs + length, SplashXPathSeg::cmpY);
186
#else
187
    qsort(segs, length, sizeof(SplashXPathSeg), &SplashXPathSeg::cmpY);
188
#endif
189
89.5k
    if (segs[0].y0 == segs[0].y1 &&
190
41.9k
  segs[1].x0 == segs[1].x1 &&
191
41.0k
  segs[2].x0 == segs[2].x1 &&
192
31.3k
  segs[3].y0 == segs[3].y1) {
193
28.7k
      isRect = gTrue;
194
28.7k
      rectX0 = segs[1].x0;
195
28.7k
      rectX1 = segs[2].x0;
196
28.7k
      rectY0 = segs[0].y0;
197
28.7k
      rectY1 = segs[3].y0;
198
60.7k
    } else if (segs[0].x0 == segs[0].x1 &&
199
46.5k
         segs[1].y0 == segs[1].y1 &&
200
39.2k
         segs[2].x0 == segs[2].x1 &&
201
36.0k
         segs[3].y0 == segs[3].y1) {
202
35.7k
      isRect = gTrue;
203
35.7k
      rectX0 = segs[0].x0;
204
35.7k
      rectX1 = segs[2].x0;
205
35.7k
      rectY0 = segs[1].y0;
206
35.7k
      rectY1 = segs[3].y0;
207
35.7k
    } else if (segs[0].x0 == segs[0].x1 &&
208
10.8k
         segs[1].x0 == segs[1].x1 &&
209
10.5k
         segs[2].y0 == segs[2].y1 &&
210
7.89k
         segs[3].y0 == segs[3].y1) {
211
7.77k
      isRect = gTrue;
212
7.77k
      rectX0 = segs[0].x0;
213
7.77k
      rectX1 = segs[1].x0;
214
7.77k
      rectY0 = segs[2].y0;
215
7.77k
      rectY1 = segs[3].y0;
216
7.77k
    }
217
89.5k
    if (isRect) {
218
72.2k
      if (rectX0 > rectX1) {
219
26.4k
  t = rectX0;  rectX0 = rectX1;  rectX1 = t;
220
26.4k
      }
221
72.2k
      if (rectY0 > rectY1) {
222
0
  t = rectY0;  rectY0 = rectY1;  rectY1 = t;
223
0
      }
224
72.2k
    }
225
89.5k
  }
226
131k
}
227
228
GBool SplashXPath::strokeAdjust(SplashXPathPoint *pts,
229
        SplashPathHint *hints, int nHints,
230
        SplashStrokeAdjustMode strokeAdjMode,
231
7.87k
        SplashClip *clip) {
232
7.87k
  SplashXPathAdjust *adjusts, *adjust;
233
7.87k
  SplashPathHint *hint;
234
7.87k
  SplashCoord x0, y0, x1, y1, x2, y2, x3, y3;
235
7.87k
  SplashCoord adj0, adj1, w, d;
236
7.87k
  int xi0, xi1;
237
7.87k
  int i, j;
238
7.87k
  GBool adjusted;
239
240
7.87k
  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
7.87k
  GBool clipTweak = clip && clip->getIsSimple();
249
7.87k
  SplashCoord cx0 = 0, cx1 = 0, cy0 = 0, cy1 = 0;
250
7.87k
  int cxi0 = 0, cxi1 = 0, cyi0 = 0, cyi1 = 0;
251
7.87k
  if (clipTweak) {
252
6.47k
    cx0 = clip->getXMin();
253
6.47k
    cx1 = clip->getXMax();
254
6.47k
    cy0 = clip->getYMin();
255
6.47k
    cy1 = clip->getYMax();
256
6.47k
    cxi0 = clip->getXMinI(strokeAdjMode);
257
6.47k
    cxi1 = clip->getXMaxI(strokeAdjMode);
258
6.47k
    cyi0 = clip->getYMinI(strokeAdjMode);
259
6.47k
    cyi1 = clip->getYMaxI(strokeAdjMode);
260
6.47k
  }
261
262
  // set up the stroke adjustment hints
263
7.87k
  adjusts = (SplashXPathAdjust *)gmallocn(nHints, sizeof(SplashXPathAdjust));
264
2.53M
  for (i = 0; i < nHints; ++i) {
265
2.53M
    hint = &hints[i];
266
2.53M
    x0 = pts[hint->ctrl0    ].x;    y0 = pts[hint->ctrl0    ].y;
267
2.53M
    x1 = pts[hint->ctrl0 + 1].x;    y1 = pts[hint->ctrl0 + 1].y;
268
2.53M
    x2 = pts[hint->ctrl1    ].x;    y2 = pts[hint->ctrl1    ].y;
269
2.53M
    x3 = pts[hint->ctrl1 + 1].x;    y3 = pts[hint->ctrl1 + 1].y;
270
2.53M
    w = -1;
271
2.53M
    if (splashAbs(x0 - x1) < 0.01 && splashAbs(x2 - x3) < 0.01) {
272
2.51M
      adjusts[i].vert = gTrue;
273
2.51M
      adj0 = x0;
274
2.51M
      adj1 = x2;
275
2.51M
      if (hint->projectingCap) {
276
462
  w = splashAbs(y1 - y0);
277
462
      }
278
2.51M
    } else if (splashAbs(y0 - y1) < 0.01 && splashAbs(y2 - y3) < 0.01) {
279
13.1k
      adjusts[i].vert = gFalse;
280
13.1k
      adj0 = y0;
281
13.1k
      adj1 = y2;
282
13.1k
      if (hint->projectingCap) {
283
99
  w = splashAbs(x1 - x0);
284
99
      }
285
13.1k
    } else {
286
4.11k
      goto done;
287
4.11k
    }
288
2.52M
    if (adj0 > adj1) {
289
24.4k
      x0 = adj0;
290
24.4k
      adj0 = adj1;
291
24.4k
      adj1 = x0;
292
24.4k
    }
293
2.52M
    d = adj1 - adj0;
294
2.52M
    if (d > 0.04) {
295
83.4k
      d = 0.01;
296
2.44M
    } else {
297
2.44M
      d *= 0.25;
298
2.44M
    }
299
2.52M
    adjusts[i].x0a = adj0 - d;
300
2.52M
    adjusts[i].x0b = adj0 + d;
301
2.52M
    adjusts[i].xma = (SplashCoord)0.5 * (adj0 + adj1) - d;
302
2.52M
    adjusts[i].xmb = (SplashCoord)0.5 * (adj0 + adj1) + d;
303
2.52M
    adjusts[i].x1a = adj1 - d;
304
2.52M
    adjusts[i].x1b = adj1 + d;
305
2.52M
    splashStrokeAdjust(adj0, adj1, &xi0, &xi1, strokeAdjMode, w);
306
2.52M
    if (clipTweak) {
307
1.22M
      SplashCoord c0, c1;
308
1.22M
      int ci0, ci1;
309
1.22M
      if (adjusts[i].vert) {
310
1.21M
  c0 = cx0;
311
1.21M
  c1 = cx1;
312
1.21M
  ci0 = cxi0;
313
1.21M
  ci1 = cxi1;
314
1.21M
      } else {
315
12.2k
  c0 = cy0;
316
12.2k
  c1 = cy1;
317
12.2k
  ci0 = cyi0;
318
12.2k
  ci1 = cyi1;
319
12.2k
      }
320
1.22M
      if (adj0 < c0 && c0 < adj1 && adj1 < c1 &&
321
1.20k
    adj1 - c0 > (adj1 - adj0) * 0.2 &&
322
1.20k
    xi1 <= ci0) {
323
137
  xi0 = ci0;
324
137
  xi1 = xi0 + 1;
325
1.22M
      } else if (c0 < adj0 && adj0 < c1 && c1 < adj1 &&
326
2.14k
     c1 - adj0 > (adj1 - adj0) * 0.2 &&
327
1.47k
     ci1 < xi0) {
328
634
  xi0 = ci1;
329
634
  xi1 = ci1 + 1;
330
634
      }
331
1.22M
    }
332
2.52M
    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
2.52M
    adjusts[i].x1 = (SplashCoord)xi1 - 0.001;
337
2.52M
    adjusts[i].xm = (SplashCoord)0.5 * (adjusts[i].x0 + adjusts[i].x1);
338
2.52M
    adjusts[i].firstPt = hint->firstPt;
339
2.52M
    adjusts[i].lastPt = hint->lastPt;
340
2.52M
  }
341
342
  // perform stroke adjustment
343
1.78M
  for (i = 0, adjust = adjusts; i < nHints; ++i, ++adjust) {
344
13.4M
    for (j = adjust->firstPt; j <= adjust->lastPt; ++j) {
345
11.6M
      if (adjust->vert) {
346
11.6M
  x0 = pts[j].x;
347
11.6M
  if (x0 > adjust->x0a && x0 < adjust->x0b) {
348
147k
    pts[j].x = adjust->x0;
349
11.4M
  } else if (x0 > adjust->xma && x0 < adjust->xmb) {
350
5.02k
    pts[j].x = adjust->xm;
351
11.4M
  } else if (x0 > adjust->x1a && x0 < adjust->x1b) {
352
215k
    pts[j].x = adjust->x1;
353
215k
  }
354
11.6M
      } else {
355
72.0k
  y0 = pts[j].y;
356
72.0k
  if (y0 > adjust->x0a && y0 < adjust->x0b) {
357
9.80k
    pts[j].y = adjust->x0;
358
62.2k
  } else if (y0 > adjust->xma && y0 < adjust->xmb) {
359
5.23k
    pts[j].y = adjust->xm;
360
57.0k
  } else if (y0 > adjust->x1a && y0 < adjust->x1b) {
361
15.4k
    pts[j].y = adjust->x1;
362
15.4k
  }
363
72.0k
      }
364
11.6M
    }
365
1.78M
  }
366
3.75k
  adjusted = gTrue;
367
368
7.87k
 done:
369
7.87k
  gfree(adjusts);
370
7.87k
  return adjusted;
371
3.75k
}
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
131k
SplashXPath::~SplashXPath() {
385
131k
  gfree(segs);
386
131k
}
387
388
// Add space for <nSegs> more segments
389
30.3M
void SplashXPath::grow(int nSegs) {
390
30.3M
  if (length + nSegs > size) {
391
163k
    if (size == 0) {
392
122k
      size = 32;
393
122k
    }
394
205k
    while (size < length + nSegs) {
395
41.6k
      size *= 2;
396
41.6k
    }
397
163k
    segs = (SplashXPathSeg *)greallocn(segs, size, sizeof(SplashXPathSeg));
398
163k
  }
399
30.3M
}
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
227k
         GBool first, GBool last, GBool end0, GBool end1) {
407
227k
  SplashCoord cx[splashMaxCurveSplits + 1][3];
408
227k
  SplashCoord cy[splashMaxCurveSplits + 1][3];
409
227k
  int cNext[splashMaxCurveSplits + 1];
410
227k
  SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh;
411
227k
  SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh;
412
227k
  SplashCoord dx, dy, mx, my, d1, d2, flatness2;
413
227k
  int p1, p2, p3;
414
415
#if USE_FIXEDPOINT
416
  flatness2 = flatness;
417
#else
418
227k
  flatness2 = flatness * flatness;
419
227k
#endif
420
421
  // initial segment
422
227k
  p1 = 0;
423
227k
  p2 = splashMaxCurveSplits;
424
227k
  cx[p1][0] = x0;  cy[p1][0] = y0;
425
227k
  cx[p1][1] = x1;  cy[p1][1] = y1;
426
227k
  cx[p1][2] = x2;  cy[p1][2] = y2;
427
227k
  cx[p2][0] = x3;  cy[p2][0] = y3;
428
227k
  cNext[p1] = p2;
429
430
28.5M
  while (p1 < splashMaxCurveSplits) {
431
432
    // get the next segment
433
28.3M
    xl0 = cx[p1][0];  yl0 = cy[p1][0];
434
28.3M
    xx1 = cx[p1][1];  yy1 = cy[p1][1];
435
28.3M
    xx2 = cx[p1][2];  yy2 = cy[p1][2];
436
28.3M
    p2 = cNext[p1];
437
28.3M
    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
28.3M
    mx = (xl0 + xr3) * 0.5;
444
28.3M
    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
28.3M
    dx = xx1 - mx;
450
28.3M
    dy = yy1 - my;
451
28.3M
    d1 = dx*dx + dy*dy;
452
28.3M
    dx = xx2 - mx;
453
28.3M
    dy = yy2 - my;
454
28.3M
    d2 = dx*dx + dy*dy;
455
28.3M
#endif
456
457
    // if the curve is flat enough, or no more subdivisions are
458
    // allowed, add the straight line segment
459
28.3M
    if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) {
460
14.2M
      addSegment(xl0, yl0, xr3, yr3);
461
14.2M
      p1 = p2;
462
463
    // otherwise, subdivide the curve
464
14.2M
    } else {
465
14.0M
      xl1 = (xl0 + xx1) * 0.5;
466
14.0M
      yl1 = (yl0 + yy1) * 0.5;
467
14.0M
      xh = (xx1 + xx2) * 0.5;
468
14.0M
      yh = (yy1 + yy2) * 0.5;
469
14.0M
      xl2 = (xl1 + xh) * 0.5;
470
14.0M
      yl2 = (yl1 + yh) * 0.5;
471
14.0M
      xr2 = (xx2 + xr3) * 0.5;
472
14.0M
      yr2 = (yy2 + yr3) * 0.5;
473
14.0M
      xr1 = (xh + xr2) * 0.5;
474
14.0M
      yr1 = (yh + yr2) * 0.5;
475
14.0M
      xr0 = (xl2 + xr1) * 0.5;
476
14.0M
      yr0 = (yl2 + yr1) * 0.5;
477
      // add the new subdivision points
478
14.0M
      p3 = (p1 + p2) / 2;
479
14.0M
      cx[p1][1] = xl1;  cy[p1][1] = yl1;
480
14.0M
      cx[p1][2] = xl2;  cy[p1][2] = yl2;
481
14.0M
      cNext[p1] = p3;
482
14.0M
      cx[p3][0] = xr0;  cy[p3][0] = yr0;
483
14.0M
      cx[p3][1] = xr1;  cy[p3][1] = yr1;
484
14.0M
      cx[p3][2] = xr2;  cy[p3][2] = yr2;
485
14.0M
      cNext[p3] = p2;
486
14.0M
    }
487
28.3M
  }
488
227k
}
489
490
void SplashXPath::addSegment(SplashCoord x0, SplashCoord y0,
491
30.3M
           SplashCoord x1, SplashCoord y1) {
492
30.3M
  grow(1);
493
30.3M
  segs[length].x0 = x0;
494
30.3M
  segs[length].y0 = y0;
495
30.3M
  segs[length].x1 = x1;
496
30.3M
  segs[length].y1 = y1;
497
30.3M
  ++length;
498
30.3M
}
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
131k
void SplashXPath::finishSegments() {
616
131k
  SplashXPathSeg *seg;
617
131k
  SplashCoord xMinFP, xMaxFP, yMinFP, yMaxFP, t;
618
131k
  int i;
619
620
131k
  xMinFP = yMinFP = xMaxFP = yMaxFP = 0;
621
622
30.4M
  for (i = 0; i < length; ++i) {
623
30.3M
    seg = &segs[i];
624
625
    //--- compute the slopes
626
30.3M
    if (seg->y0 <= seg->y1) {
627
12.9M
      seg->count = 1;
628
17.3M
    } else {
629
17.3M
      t = seg->x0;  seg->x0 = seg->x1;  seg->x1 = t;
630
17.3M
      t = seg->y0;  seg->y0 = seg->y1;  seg->y1 = t;
631
17.3M
      seg->count = -1;
632
17.3M
    }
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
30.3M
    if (splashAbs(seg->y1 - seg->y0) < 1e-200 ||
644
25.2M
  splashAbs(seg->x1 - seg->x0) < 1e-200) {
645
25.2M
      seg->dxdy = 0;
646
25.2M
      seg->dydx = 0;
647
25.2M
    } else {
648
5.14M
      seg->dxdy = (seg->x1 - seg->x0) / (seg->y1 - seg->y0);
649
5.14M
      if (seg->dxdy == 0) {
650
0
  seg->dydx = 0;
651
5.14M
      } else {
652
5.14M
  seg->dydx = 1 / seg->dxdy;
653
5.14M
      }
654
5.14M
    }
655
30.3M
#endif
656
657
    //--- update bbox
658
30.3M
    if (i == 0) {
659
122k
      if (seg->x0 <= seg->x1) {
660
100k
  xMinFP = seg->x0;
661
100k
  xMaxFP = seg->x1;
662
100k
      } else {
663
21.1k
  xMinFP = seg->x1;
664
21.1k
  xMaxFP = seg->x0;
665
21.1k
      }
666
122k
      yMinFP = seg->y0;
667
122k
      yMaxFP = seg->y1;
668
30.2M
    } else {
669
30.2M
      if (seg->x0 < xMinFP) {
670
82.0k
  xMinFP = seg->x0;
671
30.1M
      } else if (seg->x0 > xMaxFP) {
672
1.02M
  xMaxFP = seg->x0;
673
1.02M
      }
674
30.2M
      if (seg->x1 < xMinFP) {
675
365k
  xMinFP = seg->x1;
676
29.8M
      } else if (seg->x1 > xMaxFP) {
677
770k
  xMaxFP = seg->x1;
678
770k
      }
679
30.2M
      if (seg->y0 < yMinFP) {
680
1.43M
  yMinFP = seg->y0;
681
1.43M
      }
682
30.2M
      if (seg->y1 > yMaxFP) {
683
1.56M
  yMaxFP = seg->y1;
684
1.56M
      }
685
30.2M
    }
686
30.3M
  }
687
688
131k
  xMin = splashFloor(xMinFP);
689
131k
  yMin = splashFloor(yMinFP);
690
131k
  xMax = splashFloor(xMaxFP);
691
131k
  yMax = splashFloor(yMaxFP);
692
131k
}