Coverage Report

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