Coverage Report

Created: 2026-02-26 07:15

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