Coverage Report

Created: 2026-03-15 06:39

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