Coverage Report

Created: 2026-06-22 07:14

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
62.7M
           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
62.7M
  *xo = xi * matrix[0] + yi * matrix[2] + matrix[4];
53
62.7M
  *yo = xi * matrix[1] + yi * matrix[3] + matrix[5];
54
62.7M
}
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
312M
#define maxCoord 100000000.0
64
65
62.7M
void SplashXPath::clampCoords(SplashCoord *x, SplashCoord *y) {
66
62.7M
#if !USE_FIXEDPOINT
67
62.7M
  if (*x > maxCoord) {
68
27.7M
    *x = maxCoord;
69
34.9M
  } else if (*x < -maxCoord) {
70
31.3M
    *x = -maxCoord;
71
31.3M
  }
72
62.7M
  if (*y > maxCoord) {
73
22.7M
    *y = maxCoord;
74
39.9M
  } else if (*y < -maxCoord) {
75
30.1M
    *y = -maxCoord;
76
30.1M
  }
77
62.7M
#endif
78
62.7M
}
79
80
SplashXPath::SplashXPath(SplashPath *path, SplashCoord *matrix,
81
       SplashCoord flatness, GBool closeSubpaths,
82
       GBool simplify,
83
       SplashStrokeAdjustMode strokeAdjMode,
84
237k
       SplashClip *clip) {
85
237k
  SplashXPathPoint *pts;
86
237k
  SplashCoord x0, y0, x1, y1, x2, y2, x3, y3, xsp, ysp, t;
87
237k
  int nSubpaths, curSubpath, firstSegInSubpath, i;
88
237k
  GBool adjusted;
89
90
  //--- transform the points
91
237k
  pts = (SplashXPathPoint *)gmallocn(path->length, sizeof(SplashXPathPoint));
92
62.9M
  for (i = 0; i < path->length; ++i) {
93
62.7M
    transform(matrix, path->pts[i].x, path->pts[i].y, &pts[i].x, &pts[i].y);
94
62.7M
    clampCoords(&pts[i].x, &pts[i].y);
95
62.7M
  }
96
97
  //--- do stroke adjustment
98
237k
  if (path->hints) {
99
25.1k
    adjusted = strokeAdjust(pts, path->hints, path->hintsLength,
100
25.1k
          strokeAdjMode, clip);
101
212k
  } else {
102
212k
    adjusted = gFalse;
103
212k
  }
104
105
  //--- construct the segments
106
107
237k
  segs = NULL;
108
237k
  length = size = 0;
109
110
237k
  x0 = y0 = xsp = ysp = 0; // make gcc happy
111
237k
  nSubpaths = 0;
112
237k
  curSubpath = 0;
113
237k
  firstSegInSubpath = 0;
114
237k
  i = 0;
115
60.3M
  while (i < path->length) {
116
117
    // first point in subpath - skip it
118
60.0M
    if (path->flags[i] & splashPathFirst) {
119
12.2M
      x0 = pts[i].x;
120
12.2M
      y0 = pts[i].y;
121
12.2M
      xsp = x0;
122
12.2M
      ysp = y0;
123
12.2M
      curSubpath = i;
124
12.2M
      ++i;
125
126
47.8M
    } else {
127
128
      // curve segment
129
47.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
46.5M
      } else {
152
46.5M
  x1 = pts[i].x;
153
46.5M
  y1 = pts[i].y;
154
46.5M
  addSegment(x0, y0, x1, y1);
155
46.5M
  x0 = x1;
156
46.5M
  y0 = y1;
157
46.5M
  ++i;
158
46.5M
      }
159
160
      // end a subpath
161
47.8M
      if (path->flags[i-1] & splashPathLast) {
162
12.2M
  ++nSubpaths;
163
12.2M
  if (closeSubpaths &&
164
12.2M
      (pts[i-1].x != pts[curSubpath].x ||
165
12.1M
       pts[i-1].y != pts[curSubpath].y)) {
166
1.65M
    addSegment(x0, y0, xsp, ysp);
167
1.65M
  }
168
12.2M
  if (simplify && !adjusted) {
169
0
    mergeSegments(firstSegInSubpath);
170
0
  }
171
12.2M
  firstSegInSubpath = length;
172
12.2M
      }
173
47.8M
    }
174
60.0M
  }
175
176
237k
  gfree(pts);
177
178
237k
  finishSegments();
179
180
  //--- check for a rectangle
181
237k
  isRect = gFalse;
182
237k
  rectX0 = rectY0 = rectX1 = rectY1 = 0;
183
237k
  if (nSubpaths == 1 && length == 4) {
184
139k
#if HAVE_STD_SORT
185
139k
    std::sort(segs, segs + length, SplashXPathSeg::cmpY);
186
#else
187
    qsort(segs, length, sizeof(SplashXPathSeg), &SplashXPathSeg::cmpY);
188
#endif
189
139k
    if (segs[0].y0 == segs[0].y1 &&
190
77.5k
  segs[1].x0 == segs[1].x1 &&
191
74.3k
  segs[2].x0 == segs[2].x1 &&
192
55.1k
  segs[3].y0 == segs[3].y1) {
193
52.3k
      isRect = gTrue;
194
52.3k
      rectX0 = segs[1].x0;
195
52.3k
      rectX1 = segs[2].x0;
196
52.3k
      rectY0 = segs[0].y0;
197
52.3k
      rectY1 = segs[3].y0;
198
87.0k
    } else if (segs[0].x0 == segs[0].x1 &&
199
67.7k
         segs[1].y0 == segs[1].y1 &&
200
59.3k
         segs[2].x0 == segs[2].x1 &&
201
54.9k
         segs[3].y0 == segs[3].y1) {
202
54.6k
      isRect = gTrue;
203
54.6k
      rectX0 = segs[0].x0;
204
54.6k
      rectX1 = segs[2].x0;
205
54.6k
      rectY0 = segs[1].y0;
206
54.6k
      rectY1 = segs[3].y0;
207
54.6k
    } else if (segs[0].x0 == segs[0].x1 &&
208
13.1k
         segs[1].x0 == segs[1].x1 &&
209
12.0k
         segs[2].y0 == segs[2].y1 &&
210
7.57k
         segs[3].y0 == segs[3].y1) {
211
7.48k
      isRect = gTrue;
212
7.48k
      rectX0 = segs[0].x0;
213
7.48k
      rectX1 = segs[1].x0;
214
7.48k
      rectY0 = segs[2].y0;
215
7.48k
      rectY1 = segs[3].y0;
216
7.48k
    }
217
139k
    if (isRect) {
218
114k
      if (rectX0 > rectX1) {
219
31.4k
  t = rectX0;  rectX0 = rectX1;  rectX1 = t;
220
31.4k
      }
221
114k
      if (rectY0 > rectY1) {
222
0
  t = rectY0;  rectY0 = rectY1;  rectY1 = t;
223
0
      }
224
114k
    }
225
139k
  }
226
237k
}
227
228
GBool SplashXPath::strokeAdjust(SplashXPathPoint *pts,
229
        SplashPathHint *hints, int nHints,
230
        SplashStrokeAdjustMode strokeAdjMode,
231
25.1k
        SplashClip *clip) {
232
25.1k
  SplashXPathAdjust *adjusts, *adjust;
233
25.1k
  SplashPathHint *hint;
234
25.1k
  SplashCoord x0, y0, x1, y1, x2, y2, x3, y3;
235
25.1k
  SplashCoord adj0, adj1, w, d;
236
25.1k
  int xi0, xi1;
237
25.1k
  int i, j;
238
25.1k
  GBool adjusted;
239
240
25.1k
  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
25.1k
  GBool clipTweak = clip && clip->getIsSimple();
249
25.1k
  SplashCoord cx0 = 0, cx1 = 0, cy0 = 0, cy1 = 0;
250
25.1k
  int cxi0 = 0, cxi1 = 0, cyi0 = 0, cyi1 = 0;
251
25.1k
  if (clipTweak) {
252
18.8k
    cx0 = clip->getXMin();
253
18.8k
    cx1 = clip->getXMax();
254
18.8k
    cy0 = clip->getYMin();
255
18.8k
    cy1 = clip->getYMax();
256
18.8k
    cxi0 = clip->getXMinI(strokeAdjMode);
257
18.8k
    cxi1 = clip->getXMaxI(strokeAdjMode);
258
18.8k
    cyi0 = clip->getYMinI(strokeAdjMode);
259
18.8k
    cyi1 = clip->getYMaxI(strokeAdjMode);
260
18.8k
  }
261
262
  // set up the stroke adjustment hints
263
25.1k
  adjusts = (SplashXPathAdjust *)gmallocn(nHints, sizeof(SplashXPathAdjust));
264
12.8M
  for (i = 0; i < nHints; ++i) {
265
12.7M
    hint = &hints[i];
266
12.7M
    x0 = pts[hint->ctrl0    ].x;    y0 = pts[hint->ctrl0    ].y;
267
12.7M
    x1 = pts[hint->ctrl0 + 1].x;    y1 = pts[hint->ctrl0 + 1].y;
268
12.7M
    x2 = pts[hint->ctrl1    ].x;    y2 = pts[hint->ctrl1    ].y;
269
12.7M
    x3 = pts[hint->ctrl1 + 1].x;    y3 = pts[hint->ctrl1 + 1].y;
270
12.7M
    w = -1;
271
12.7M
    if (splashAbs(x0 - x1) < 0.01 && splashAbs(x2 - x3) < 0.01) {
272
12.7M
      adjusts[i].vert = gTrue;
273
12.7M
      adj0 = x0;
274
12.7M
      adj1 = x2;
275
12.7M
      if (hint->projectingCap) {
276
1.43k
  w = splashAbs(y1 - y0);
277
1.43k
      }
278
12.7M
    } else if (splashAbs(y0 - y1) < 0.01 && splashAbs(y2 - y3) < 0.01) {
279
79.6k
      adjusts[i].vert = gFalse;
280
79.6k
      adj0 = y0;
281
79.6k
      adj1 = y2;
282
79.6k
      if (hint->projectingCap) {
283
699
  w = splashAbs(x1 - x0);
284
699
      }
285
79.6k
    } else {
286
6.05k
      goto done;
287
6.05k
    }
288
12.7M
    if (adj0 > adj1) {
289
558k
      x0 = adj0;
290
558k
      adj0 = adj1;
291
558k
      adj1 = x0;
292
558k
    }
293
12.7M
    d = adj1 - adj0;
294
12.7M
    if (d > 0.04) {
295
734k
      d = 0.01;
296
12.0M
    } else {
297
12.0M
      d *= 0.25;
298
12.0M
    }
299
12.7M
    adjusts[i].x0a = adj0 - d;
300
12.7M
    adjusts[i].x0b = adj0 + d;
301
12.7M
    adjusts[i].xma = (SplashCoord)0.5 * (adj0 + adj1) - d;
302
12.7M
    adjusts[i].xmb = (SplashCoord)0.5 * (adj0 + adj1) + d;
303
12.7M
    adjusts[i].x1a = adj1 - d;
304
12.7M
    adjusts[i].x1b = adj1 + d;
305
12.7M
    splashStrokeAdjust(adj0, adj1, &xi0, &xi1, strokeAdjMode, w);
306
12.7M
    if (clipTweak) {
307
6.34M
      SplashCoord c0, c1;
308
6.34M
      int ci0, ci1;
309
6.34M
      if (adjusts[i].vert) {
310
6.28M
  c0 = cx0;
311
6.28M
  c1 = cx1;
312
6.28M
  ci0 = cxi0;
313
6.28M
  ci1 = cxi1;
314
6.28M
      } else {
315
60.0k
  c0 = cy0;
316
60.0k
  c1 = cy1;
317
60.0k
  ci0 = cyi0;
318
60.0k
  ci1 = cyi1;
319
60.0k
      }
320
6.34M
      if (adj0 < c0 && c0 < adj1 && adj1 < c1 &&
321
423
    adj1 - c0 > (adj1 - adj0) * 0.2 &&
322
423
    xi1 <= ci0) {
323
0
  xi0 = ci0;
324
0
  xi1 = xi0 + 1;
325
6.34M
      } else if (c0 < adj0 && adj0 < c1 && c1 < adj1 &&
326
1.14k
     c1 - adj0 > (adj1 - adj0) * 0.2 &&
327
651
     ci1 < xi0) {
328
312
  xi0 = ci1;
329
312
  xi1 = ci1 + 1;
330
312
      }
331
6.34M
    }
332
12.7M
    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
12.7M
    adjusts[i].x1 = (SplashCoord)xi1 - 0.001;
337
12.7M
    adjusts[i].xm = (SplashCoord)0.5 * (adjusts[i].x0 + adjusts[i].x1);
338
12.7M
    adjusts[i].firstPt = hint->firstPt;
339
12.7M
    adjusts[i].lastPt = hint->lastPt;
340
12.7M
  }
341
342
  // perform stroke adjustment
343
10.9M
  for (i = 0, adjust = adjusts; i < nHints; ++i, ++adjust) {
344
80.3M
    for (j = adjust->firstPt; j <= adjust->lastPt; ++j) {
345
69.4M
      if (adjust->vert) {
346
69.0M
  x0 = pts[j].x;
347
69.0M
  if (x0 > adjust->x0a && x0 < adjust->x0b) {
348
2.98M
    pts[j].x = adjust->x0;
349
66.0M
  } else if (x0 > adjust->xma && x0 < adjust->xmb) {
350
39.3k
    pts[j].x = adjust->xm;
351
66.0M
  } else if (x0 > adjust->x1a && x0 < adjust->x1b) {
352
1.54M
    pts[j].x = adjust->x1;
353
1.54M
  }
354
69.0M
      } else {
355
405k
  y0 = pts[j].y;
356
405k
  if (y0 > adjust->x0a && y0 < adjust->x0b) {
357
11.6k
    pts[j].y = adjust->x0;
358
394k
  } else if (y0 > adjust->xma && y0 < adjust->xmb) {
359
180
    pts[j].y = adjust->xm;
360
394k
  } else if (y0 > adjust->x1a && y0 < adjust->x1b) {
361
11.8k
    pts[j].y = adjust->x1;
362
11.8k
  }
363
405k
      }
364
69.4M
    }
365
10.8M
  }
366
19.0k
  adjusted = gTrue;
367
368
25.1k
 done:
369
25.1k
  gfree(adjusts);
370
25.1k
  return adjusted;
371
19.0k
}
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
237k
SplashXPath::~SplashXPath() {
385
237k
  gfree(segs);
386
237k
}
387
388
// Add space for <nSegs> more segments
389
73.5M
void SplashXPath::grow(int nSegs) {
390
73.5M
  if (length + nSegs > size) {
391
315k
    if (size == 0) {
392
225k
      size = 32;
393
225k
    }
394
404k
    while (size < length + nSegs) {
395
89.5k
      size *= 2;
396
89.5k
    }
397
315k
    segs = (SplashXPathSeg *)greallocn(segs, size, sizeof(SplashXPathSeg));
398
315k
  }
399
73.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.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
50.7M
  while (p1 < splashMaxCurveSplits) {
431
432
    // get the next segment
433
49.4M
    xl0 = cx[p1][0];  yl0 = cy[p1][0];
434
49.4M
    xx1 = cx[p1][1];  yy1 = cy[p1][1];
435
49.4M
    xx2 = cx[p1][2];  yy2 = cy[p1][2];
436
49.4M
    p2 = cNext[p1];
437
49.4M
    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
49.4M
    mx = (xl0 + xr3) * 0.5;
444
49.4M
    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
49.4M
    dx = xx1 - mx;
450
49.4M
    dy = yy1 - my;
451
49.4M
    d1 = dx*dx + dy*dy;
452
49.4M
    dx = xx2 - mx;
453
49.4M
    dy = yy2 - my;
454
49.4M
    d2 = dx*dx + dy*dy;
455
49.4M
#endif
456
457
    // if the curve is flat enough, or no more subdivisions are
458
    // allowed, add the straight line segment
459
49.4M
    if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) {
460
25.3M
      addSegment(xl0, yl0, xr3, yr3);
461
25.3M
      p1 = p2;
462
463
    // otherwise, subdivide the curve
464
25.3M
    } else {
465
24.0M
      xl1 = (xl0 + xx1) * 0.5;
466
24.0M
      yl1 = (yl0 + yy1) * 0.5;
467
24.0M
      xh = (xx1 + xx2) * 0.5;
468
24.0M
      yh = (yy1 + yy2) * 0.5;
469
24.0M
      xl2 = (xl1 + xh) * 0.5;
470
24.0M
      yl2 = (yl1 + yh) * 0.5;
471
24.0M
      xr2 = (xx2 + xr3) * 0.5;
472
24.0M
      yr2 = (yy2 + yr3) * 0.5;
473
24.0M
      xr1 = (xh + xr2) * 0.5;
474
24.0M
      yr1 = (yh + yr2) * 0.5;
475
24.0M
      xr0 = (xl2 + xr1) * 0.5;
476
24.0M
      yr0 = (yl2 + yr1) * 0.5;
477
      // add the new subdivision points
478
24.0M
      p3 = (p1 + p2) / 2;
479
24.0M
      cx[p1][1] = xl1;  cy[p1][1] = yl1;
480
24.0M
      cx[p1][2] = xl2;  cy[p1][2] = yl2;
481
24.0M
      cNext[p1] = p3;
482
24.0M
      cx[p3][0] = xr0;  cy[p3][0] = yr0;
483
24.0M
      cx[p3][1] = xr1;  cy[p3][1] = yr1;
484
24.0M
      cx[p3][2] = xr2;  cy[p3][2] = yr2;
485
24.0M
      cNext[p3] = p2;
486
24.0M
    }
487
49.4M
  }
488
1.31M
}
489
490
void SplashXPath::addSegment(SplashCoord x0, SplashCoord y0,
491
73.5M
           SplashCoord x1, SplashCoord y1) {
492
73.5M
  grow(1);
493
73.5M
  segs[length].x0 = x0;
494
73.5M
  segs[length].y0 = y0;
495
73.5M
  segs[length].x1 = x1;
496
73.5M
  segs[length].y1 = y1;
497
73.5M
  ++length;
498
73.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
237k
void SplashXPath::finishSegments() {
616
237k
  SplashXPathSeg *seg;
617
237k
  SplashCoord xMinFP, xMaxFP, yMinFP, yMaxFP, t;
618
237k
  int i;
619
620
237k
  xMinFP = yMinFP = xMaxFP = yMaxFP = 0;
621
622
73.8M
  for (i = 0; i < length; ++i) {
623
73.5M
    seg = &segs[i];
624
625
    //--- compute the slopes
626
73.5M
    if (seg->y0 <= seg->y1) {
627
48.5M
      seg->count = 1;
628
48.5M
    } else {
629
25.0M
      t = seg->x0;  seg->x0 = seg->x1;  seg->x1 = t;
630
25.0M
      t = seg->y0;  seg->y0 = seg->y1;  seg->y1 = t;
631
25.0M
      seg->count = -1;
632
25.0M
    }
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
73.5M
    if (splashAbs(seg->y1 - seg->y0) < 1e-200 ||
644
64.9M
  splashAbs(seg->x1 - seg->x0) < 1e-200) {
645
64.9M
      seg->dxdy = 0;
646
64.9M
      seg->dydx = 0;
647
64.9M
    } else {
648
8.60M
      seg->dxdy = (seg->x1 - seg->x0) / (seg->y1 - seg->y0);
649
8.60M
      if (seg->dxdy == 0) {
650
0
  seg->dydx = 0;
651
8.60M
      } else {
652
8.60M
  seg->dydx = 1 / seg->dxdy;
653
8.60M
      }
654
8.60M
    }
655
73.5M
#endif
656
657
    //--- update bbox
658
73.5M
    if (i == 0) {
659
225k
      if (seg->x0 <= seg->x1) {
660
190k
  xMinFP = seg->x0;
661
190k
  xMaxFP = seg->x1;
662
190k
      } else {
663
34.9k
  xMinFP = seg->x1;
664
34.9k
  xMaxFP = seg->x0;
665
34.9k
      }
666
225k
      yMinFP = seg->y0;
667
225k
      yMaxFP = seg->y1;
668
73.3M
    } else {
669
73.3M
      if (seg->x0 < xMinFP) {
670
237k
  xMinFP = seg->x0;
671
73.1M
      } else if (seg->x0 > xMaxFP) {
672
409k
  xMaxFP = seg->x0;
673
409k
      }
674
73.3M
      if (seg->x1 < xMinFP) {
675
337k
  xMinFP = seg->x1;
676
73.0M
      } else if (seg->x1 > xMaxFP) {
677
1.50M
  xMaxFP = seg->x1;
678
1.50M
      }
679
73.3M
      if (seg->y0 < yMinFP) {
680
2.59M
  yMinFP = seg->y0;
681
2.59M
      }
682
73.3M
      if (seg->y1 > yMaxFP) {
683
2.20M
  yMaxFP = seg->y1;
684
2.20M
      }
685
73.3M
    }
686
73.5M
  }
687
688
237k
  xMin = splashFloor(xMinFP);
689
237k
  yMin = splashFloor(yMinFP);
690
237k
  xMax = splashFloor(xMaxFP);
691
237k
  yMax = splashFloor(yMaxFP);
692
237k
}