Coverage Report

Created: 2026-10-17 07:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/xpdf-4.06/splash/SplashXPath.cc
Line
Count
Source
1
//========================================================================
2
//
3
// SplashXPath.cc
4
//
5
// Copyright 2003-2013 Glyph & Cog, LLC
6
//
7
//========================================================================
8
9
#include <aconf.h>
10
11
#include <stdlib.h>
12
#include <string.h>
13
#if HAVE_STD_SORT
14
#include <algorithm>
15
#endif
16
#include "gmem.h"
17
#include "gmempp.h"
18
#include "SplashMath.h"
19
#include "SplashPath.h"
20
#include "SplashClip.h"
21
#include "SplashXPath.h"
22
23
//------------------------------------------------------------------------
24
25
0
#define minCosSquaredJoinAngle 0.75
26
0
#define maxPointToLineDistanceSquared 0.04
27
28
//------------------------------------------------------------------------
29
30
struct SplashXPathPoint {
31
  SplashCoord x, y;
32
};
33
34
struct SplashXPathAdjust {
35
  int firstPt, lastPt;    // range of points
36
  GBool vert;     // vertical or horizontal hint
37
  SplashCoord x0a, x0b,   // hint boundaries
38
              xma, xmb,
39
              x1a, x1b;
40
  SplashCoord x0, x1, xm; // adjusted coordinates
41
};
42
43
//------------------------------------------------------------------------
44
45
// Transform a point from user space to device space.
46
inline void SplashXPath::transform(SplashCoord *matrix,
47
           SplashCoord xi, SplashCoord yi,
48
18.9M
           SplashCoord *xo, SplashCoord *yo) {
49
  //                          [ m[0] m[1] 0 ]
50
  // [xo yo 1] = [xi yi 1] *  [ m[2] m[3] 0 ]
51
  //                          [ m[4] m[5] 1 ]
52
18.9M
  *xo = xi * matrix[0] + yi * matrix[2] + matrix[4];
53
18.9M
  *yo = xi * matrix[1] + yi * matrix[3] + matrix[5];
54
18.9M
}
55
56
//------------------------------------------------------------------------
57
// SplashXPath
58
//------------------------------------------------------------------------
59
60
// SplashXPath segment coords are clipped to +/-maxCoord to avoid
61
// problems.  The xMin/yMin/xMax/yMax fields are 32-bit integers, so
62
// coords need to be < 2^31 / aa{Horiz,Vert}.
63
90.5M
#define maxCoord 100000000.0
64
65
18.9M
void SplashXPath::clampCoords(SplashCoord *x, SplashCoord *y) {
66
18.9M
#if !USE_FIXEDPOINT
67
18.9M
  if (*x > maxCoord) {
68
8.39M
    *x = maxCoord;
69
10.5M
  } else if (*x < -maxCoord) {
70
10.1M
    *x = -maxCoord;
71
10.1M
  }
72
18.9M
  if (*y > maxCoord) {
73
6.85M
    *y = maxCoord;
74
12.1M
  } else if (*y < -maxCoord) {
75
4.47M
    *y = -maxCoord;
76
4.47M
  }
77
18.9M
#endif
78
18.9M
}
79
80
SplashXPath::SplashXPath(SplashPath *path, SplashCoord *matrix,
81
       SplashCoord flatness, GBool closeSubpaths,
82
       GBool simplify,
83
       SplashStrokeAdjustMode strokeAdjMode,
84
130k
       SplashClip *clip) {
85
130k
  SplashXPathPoint *pts;
86
130k
  SplashCoord x0, y0, x1, y1, x2, y2, x3, y3, xsp, ysp, t;
87
130k
  int nSubpaths, curSubpath, firstSegInSubpath, i;
88
130k
  GBool adjusted;
89
90
  //--- transform the points
91
130k
  pts = (SplashXPathPoint *)gmallocn(path->length, sizeof(SplashXPathPoint));
92
19.0M
  for (i = 0; i < path->length; ++i) {
93
18.9M
    transform(matrix, path->pts[i].x, path->pts[i].y, &pts[i].x, &pts[i].y);
94
18.9M
    clampCoords(&pts[i].x, &pts[i].y);
95
18.9M
  }
96
97
  //--- do stroke adjustment
98
130k
  if (path->hints) {
99
7.82k
    adjusted = strokeAdjust(pts, path->hints, path->hintsLength,
100
7.82k
          strokeAdjMode, clip);
101
123k
  } else {
102
123k
    adjusted = gFalse;
103
123k
  }
104
105
  //--- construct the segments
106
107
130k
  segs = NULL;
108
130k
  length = size = 0;
109
110
130k
  x0 = y0 = xsp = ysp = 0; // make gcc happy
111
130k
  nSubpaths = 0;
112
130k
  curSubpath = 0;
113
130k
  firstSegInSubpath = 0;
114
130k
  i = 0;
115
18.6M
  while (i < path->length) {
116
117
    // first point in subpath - skip it
118
18.5M
    if (path->flags[i] & splashPathFirst) {
119
3.72M
      x0 = pts[i].x;
120
3.72M
      y0 = pts[i].y;
121
3.72M
      xsp = x0;
122
3.72M
      ysp = y0;
123
3.72M
      curSubpath = i;
124
3.72M
      ++i;
125
126
14.7M
    } else {
127
128
      // curve segment
129
14.7M
      if (path->flags[i] & splashPathCurve) {
130
229k
  x1 = pts[i].x;
131
229k
  y1 = pts[i].y;
132
229k
  x2 = pts[i+1].x;
133
229k
  y2 = pts[i+1].y;
134
229k
  x3 = pts[i+2].x;
135
229k
  y3 = pts[i+2].y;
136
229k
  addCurve(x0, y0, x1, y1, x2, y2, x3, y3,
137
229k
     flatness,
138
229k
     (path->flags[i-1] & splashPathFirst),
139
229k
     (path->flags[i+2] & splashPathLast),
140
229k
     !closeSubpaths &&
141
0
       (path->flags[i-1] & splashPathFirst) &&
142
0
       !(path->flags[i-1] & splashPathClosed),
143
229k
     !closeSubpaths &&
144
0
       (path->flags[i+2] & splashPathLast) &&
145
0
       !(path->flags[i+2] & splashPathClosed));
146
229k
  x0 = x3;
147
229k
  y0 = y3;
148
229k
  i += 3;
149
150
      // line segment
151
14.5M
      } else {
152
14.5M
  x1 = pts[i].x;
153
14.5M
  y1 = pts[i].y;
154
14.5M
  addSegment(x0, y0, x1, y1);
155
14.5M
  x0 = x1;
156
14.5M
  y0 = y1;
157
14.5M
  ++i;
158
14.5M
      }
159
160
      // end a subpath
161
14.7M
      if (path->flags[i-1] & splashPathLast) {
162
3.72M
  ++nSubpaths;
163
3.72M
  if (closeSubpaths &&
164
3.63M
      (pts[i-1].x != pts[curSubpath].x ||
165
3.63M
       pts[i-1].y != pts[curSubpath].y)) {
166
1.48M
    addSegment(x0, y0, xsp, ysp);
167
1.48M
  }
168
3.72M
  if (simplify && !adjusted) {
169
0
    mergeSegments(firstSegInSubpath);
170
0
  }
171
3.72M
  firstSegInSubpath = length;
172
3.72M
      }
173
14.7M
    }
174
18.5M
  }
175
176
130k
  gfree(pts);
177
178
130k
  finishSegments();
179
180
  //--- check for a rectangle
181
130k
  isRect = gFalse;
182
130k
  rectX0 = rectY0 = rectX1 = rectY1 = 0;
183
130k
  if (nSubpaths == 1 && length == 4) {
184
88.3k
#if HAVE_STD_SORT
185
88.3k
    std::sort(segs, segs + length, SplashXPathSeg::cmpY);
186
#else
187
    qsort(segs, length, sizeof(SplashXPathSeg), &SplashXPathSeg::cmpY);
188
#endif
189
88.3k
    if (segs[0].y0 == segs[0].y1 &&
190
40.7k
  segs[1].x0 == segs[1].x1 &&
191
39.9k
  segs[2].x0 == segs[2].x1 &&
192
30.5k
  segs[3].y0 == segs[3].y1) {
193
27.9k
      isRect = gTrue;
194
27.9k
      rectX0 = segs[1].x0;
195
27.9k
      rectX1 = segs[2].x0;
196
27.9k
      rectY0 = segs[0].y0;
197
27.9k
      rectY1 = segs[3].y0;
198
60.4k
    } else if (segs[0].x0 == segs[0].x1 &&
199
46.5k
         segs[1].y0 == segs[1].y1 &&
200
39.1k
         segs[2].x0 == segs[2].x1 &&
201
35.9k
         segs[3].y0 == segs[3].y1) {
202
35.6k
      isRect = gTrue;
203
35.6k
      rectX0 = segs[0].x0;
204
35.6k
      rectX1 = segs[2].x0;
205
35.6k
      rectY0 = segs[1].y0;
206
35.6k
      rectY1 = segs[3].y0;
207
35.6k
    } else if (segs[0].x0 == segs[0].x1 &&
208
10.8k
         segs[1].x0 == segs[1].x1 &&
209
10.6k
         segs[2].y0 == segs[2].y1 &&
210
7.89k
         segs[3].y0 == segs[3].y1) {
211
7.77k
      isRect = gTrue;
212
7.77k
      rectX0 = segs[0].x0;
213
7.77k
      rectX1 = segs[1].x0;
214
7.77k
      rectY0 = segs[2].y0;
215
7.77k
      rectY1 = segs[3].y0;
216
7.77k
    }
217
88.3k
    if (isRect) {
218
71.3k
      if (rectX0 > rectX1) {
219
26.4k
  t = rectX0;  rectX0 = rectX1;  rectX1 = t;
220
26.4k
      }
221
71.3k
      if (rectY0 > rectY1) {
222
0
  t = rectY0;  rectY0 = rectY1;  rectY1 = t;
223
0
      }
224
71.3k
    }
225
88.3k
  }
226
130k
}
227
228
GBool SplashXPath::strokeAdjust(SplashXPathPoint *pts,
229
        SplashPathHint *hints, int nHints,
230
        SplashStrokeAdjustMode strokeAdjMode,
231
7.82k
        SplashClip *clip) {
232
7.82k
  SplashXPathAdjust *adjusts, *adjust;
233
7.82k
  SplashPathHint *hint;
234
7.82k
  SplashCoord x0, y0, x1, y1, x2, y2, x3, y3;
235
7.82k
  SplashCoord adj0, adj1, w, d;
236
7.82k
  int xi0, xi1;
237
7.82k
  int i, j;
238
7.82k
  GBool adjusted;
239
240
7.82k
  adjusted = gFalse;
241
242
  // If there is a simple rectangular clip region, stroke-adjusted
243
  // edges that fall slightly outside the clip region are adjusted
244
  // back inside the clip region. This avoids problems with narrow
245
  // lines in slightly mismatched clip rectangles, which appear to be
246
  // generated somewhat commonly by buggy CAD software. (Note: [clip]
247
  // is NULL when called to build a clip path.)
248
7.82k
  GBool clipTweak = clip && clip->getIsSimple();
249
7.82k
  SplashCoord cx0 = 0, cx1 = 0, cy0 = 0, cy1 = 0;
250
7.82k
  int cxi0 = 0, cxi1 = 0, cyi0 = 0, cyi1 = 0;
251
7.82k
  if (clipTweak) {
252
6.44k
    cx0 = clip->getXMin();
253
6.44k
    cx1 = clip->getXMax();
254
6.44k
    cy0 = clip->getYMin();
255
6.44k
    cy1 = clip->getYMax();
256
6.44k
    cxi0 = clip->getXMinI(strokeAdjMode);
257
6.44k
    cxi1 = clip->getXMaxI(strokeAdjMode);
258
6.44k
    cyi0 = clip->getYMinI(strokeAdjMode);
259
6.44k
    cyi1 = clip->getYMaxI(strokeAdjMode);
260
6.44k
  }
261
262
  // set up the stroke adjustment hints
263
7.82k
  adjusts = (SplashXPathAdjust *)gmallocn(nHints, sizeof(SplashXPathAdjust));
264
2.54M
  for (i = 0; i < nHints; ++i) {
265
2.53M
    hint = &hints[i];
266
2.53M
    x0 = pts[hint->ctrl0    ].x;    y0 = pts[hint->ctrl0    ].y;
267
2.53M
    x1 = pts[hint->ctrl0 + 1].x;    y1 = pts[hint->ctrl0 + 1].y;
268
2.53M
    x2 = pts[hint->ctrl1    ].x;    y2 = pts[hint->ctrl1    ].y;
269
2.53M
    x3 = pts[hint->ctrl1 + 1].x;    y3 = pts[hint->ctrl1 + 1].y;
270
2.53M
    w = -1;
271
2.53M
    if (splashAbs(x0 - x1) < 0.01 && splashAbs(x2 - x3) < 0.01) {
272
2.52M
      adjusts[i].vert = gTrue;
273
2.52M
      adj0 = x0;
274
2.52M
      adj1 = x2;
275
2.52M
      if (hint->projectingCap) {
276
462
  w = splashAbs(y1 - y0);
277
462
      }
278
2.52M
    } else if (splashAbs(y0 - y1) < 0.01 && splashAbs(y2 - y3) < 0.01) {
279
13.1k
      adjusts[i].vert = gFalse;
280
13.1k
      adj0 = y0;
281
13.1k
      adj1 = y2;
282
13.1k
      if (hint->projectingCap) {
283
99
  w = splashAbs(x1 - x0);
284
99
      }
285
13.1k
    } else {
286
4.08k
      goto done;
287
4.08k
    }
288
2.53M
    if (adj0 > adj1) {
289
24.3k
      x0 = adj0;
290
24.3k
      adj0 = adj1;
291
24.3k
      adj1 = x0;
292
24.3k
    }
293
2.53M
    d = adj1 - adj0;
294
2.53M
    if (d > 0.04) {
295
83.4k
      d = 0.01;
296
2.45M
    } else {
297
2.45M
      d *= 0.25;
298
2.45M
    }
299
2.53M
    adjusts[i].x0a = adj0 - d;
300
2.53M
    adjusts[i].x0b = adj0 + d;
301
2.53M
    adjusts[i].xma = (SplashCoord)0.5 * (adj0 + adj1) - d;
302
2.53M
    adjusts[i].xmb = (SplashCoord)0.5 * (adj0 + adj1) + d;
303
2.53M
    adjusts[i].x1a = adj1 - d;
304
2.53M
    adjusts[i].x1b = adj1 + d;
305
2.53M
    splashStrokeAdjust(adj0, adj1, &xi0, &xi1, strokeAdjMode, w);
306
2.53M
    if (clipTweak) {
307
1.23M
      SplashCoord c0, c1;
308
1.23M
      int ci0, ci1;
309
1.23M
      if (adjusts[i].vert) {
310
1.22M
  c0 = cx0;
311
1.22M
  c1 = cx1;
312
1.22M
  ci0 = cxi0;
313
1.22M
  ci1 = cxi1;
314
1.22M
      } else {
315
12.2k
  c0 = cy0;
316
12.2k
  c1 = cy1;
317
12.2k
  ci0 = cyi0;
318
12.2k
  ci1 = cyi1;
319
12.2k
      }
320
1.23M
      if (adj0 < c0 && c0 < adj1 && adj1 < c1 &&
321
1.20k
    adj1 - c0 > (adj1 - adj0) * 0.2 &&
322
1.20k
    xi1 <= ci0) {
323
137
  xi0 = ci0;
324
137
  xi1 = xi0 + 1;
325
1.23M
      } else if (c0 < adj0 && adj0 < c1 && c1 < adj1 &&
326
2.13k
     c1 - adj0 > (adj1 - adj0) * 0.2 &&
327
1.46k
     ci1 < xi0) {
328
622
  xi0 = ci1;
329
622
  xi1 = ci1 + 1;
330
622
      }
331
1.23M
    }
332
2.53M
    adjusts[i].x0 = (SplashCoord)xi0;
333
    // the "minus epsilon" thing here is needed when vector
334
    // antialiasing is turned off -- otherwise stroke adjusted lines
335
    // will touch an extra pixel on one edge
336
2.53M
    adjusts[i].x1 = (SplashCoord)xi1 - 0.001;
337
2.53M
    adjusts[i].xm = (SplashCoord)0.5 * (adjusts[i].x0 + adjusts[i].x1);
338
2.53M
    adjusts[i].firstPt = hint->firstPt;
339
2.53M
    adjusts[i].lastPt = hint->lastPt;
340
2.53M
  }
341
342
  // perform stroke adjustment
343
1.78M
  for (i = 0, adjust = adjusts; i < nHints; ++i, ++adjust) {
344
13.5M
    for (j = adjust->firstPt; j <= adjust->lastPt; ++j) {
345
11.7M
      if (adjust->vert) {
346
11.6M
  x0 = pts[j].x;
347
11.6M
  if (x0 > adjust->x0a && x0 < adjust->x0b) {
348
147k
    pts[j].x = adjust->x0;
349
11.5M
  } else if (x0 > adjust->xma && x0 < adjust->xmb) {
350
4.99k
    pts[j].x = adjust->xm;
351
11.5M
  } else if (x0 > adjust->x1a && x0 < adjust->x1b) {
352
215k
    pts[j].x = adjust->x1;
353
215k
  }
354
11.6M
      } else {
355
72.0k
  y0 = pts[j].y;
356
72.0k
  if (y0 > adjust->x0a && y0 < adjust->x0b) {
357
9.78k
    pts[j].y = adjust->x0;
358
62.2k
  } else if (y0 > adjust->xma && y0 < adjust->xmb) {
359
5.19k
    pts[j].y = adjust->xm;
360
57.0k
  } else if (y0 > adjust->x1a && y0 < adjust->x1b) {
361
15.4k
    pts[j].y = adjust->x1;
362
15.4k
  }
363
72.0k
      }
364
11.7M
    }
365
1.78M
  }
366
3.74k
  adjusted = gTrue;
367
368
7.82k
 done:
369
7.82k
  gfree(adjusts);
370
7.82k
  return adjusted;
371
3.74k
}
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
130k
SplashXPath::~SplashXPath() {
385
130k
  gfree(segs);
386
130k
}
387
388
// Add space for <nSegs> more segments
389
30.2M
void SplashXPath::grow(int nSegs) {
390
30.2M
  if (length + nSegs > size) {
391
162k
    if (size == 0) {
392
120k
      size = 32;
393
120k
    }
394
204k
    while (size < length + nSegs) {
395
41.5k
      size *= 2;
396
41.5k
    }
397
162k
    segs = (SplashXPathSeg *)greallocn(segs, size, sizeof(SplashXPathSeg));
398
162k
  }
399
30.2M
}
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
229k
         GBool first, GBool last, GBool end0, GBool end1) {
407
229k
  SplashCoord cx[splashMaxCurveSplits + 1][3];
408
229k
  SplashCoord cy[splashMaxCurveSplits + 1][3];
409
229k
  int cNext[splashMaxCurveSplits + 1];
410
229k
  SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh;
411
229k
  SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh;
412
229k
  SplashCoord dx, dy, mx, my, d1, d2, flatness2;
413
229k
  int p1, p2, p3;
414
415
#if USE_FIXEDPOINT
416
  flatness2 = flatness;
417
#else
418
229k
  flatness2 = flatness * flatness;
419
229k
#endif
420
421
  // initial segment
422
229k
  p1 = 0;
423
229k
  p2 = splashMaxCurveSplits;
424
229k
  cx[p1][0] = x0;  cy[p1][0] = y0;
425
229k
  cx[p1][1] = x1;  cy[p1][1] = y1;
426
229k
  cx[p1][2] = x2;  cy[p1][2] = y2;
427
229k
  cx[p2][0] = x3;  cy[p2][0] = y3;
428
229k
  cNext[p1] = p2;
429
430
28.4M
  while (p1 < splashMaxCurveSplits) {
431
432
    // get the next segment
433
28.2M
    xl0 = cx[p1][0];  yl0 = cy[p1][0];
434
28.2M
    xx1 = cx[p1][1];  yy1 = cy[p1][1];
435
28.2M
    xx2 = cx[p1][2];  yy2 = cy[p1][2];
436
28.2M
    p2 = cNext[p1];
437
28.2M
    xr3 = cx[p2][0];  yr3 = cy[p2][0];
438
439
    // compute the distances from the control points to the
440
    // midpoint of the straight line (this is a bit of a hack, but
441
    // it's much faster than computing the actual distances to the
442
    // line)
443
28.2M
    mx = (xl0 + xr3) * 0.5;
444
28.2M
    my = (yl0 + yr3) * 0.5;
445
#if USE_FIXEDPOINT
446
    d1 = splashDist(xx1, yy1, mx, my);
447
    d2 = splashDist(xx2, yy2, mx, my);
448
#else
449
28.2M
    dx = xx1 - mx;
450
28.2M
    dy = yy1 - my;
451
28.2M
    d1 = dx*dx + dy*dy;
452
28.2M
    dx = xx2 - mx;
453
28.2M
    dy = yy2 - my;
454
28.2M
    d2 = dx*dx + dy*dy;
455
28.2M
#endif
456
457
    // if the curve is flat enough, or no more subdivisions are
458
    // allowed, add the straight line segment
459
28.2M
    if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) {
460
14.2M
      addSegment(xl0, yl0, xr3, yr3);
461
14.2M
      p1 = p2;
462
463
    // otherwise, subdivide the curve
464
14.2M
    } else {
465
14.0M
      xl1 = (xl0 + xx1) * 0.5;
466
14.0M
      yl1 = (yl0 + yy1) * 0.5;
467
14.0M
      xh = (xx1 + xx2) * 0.5;
468
14.0M
      yh = (yy1 + yy2) * 0.5;
469
14.0M
      xl2 = (xl1 + xh) * 0.5;
470
14.0M
      yl2 = (yl1 + yh) * 0.5;
471
14.0M
      xr2 = (xx2 + xr3) * 0.5;
472
14.0M
      yr2 = (yy2 + yr3) * 0.5;
473
14.0M
      xr1 = (xh + xr2) * 0.5;
474
14.0M
      yr1 = (yh + yr2) * 0.5;
475
14.0M
      xr0 = (xl2 + xr1) * 0.5;
476
14.0M
      yr0 = (yl2 + yr1) * 0.5;
477
      // add the new subdivision points
478
14.0M
      p3 = (p1 + p2) / 2;
479
14.0M
      cx[p1][1] = xl1;  cy[p1][1] = yl1;
480
14.0M
      cx[p1][2] = xl2;  cy[p1][2] = yl2;
481
14.0M
      cNext[p1] = p3;
482
14.0M
      cx[p3][0] = xr0;  cy[p3][0] = yr0;
483
14.0M
      cx[p3][1] = xr1;  cy[p3][1] = yr1;
484
14.0M
      cx[p3][2] = xr2;  cy[p3][2] = yr2;
485
14.0M
      cNext[p3] = p2;
486
14.0M
    }
487
28.2M
  }
488
229k
}
489
490
void SplashXPath::addSegment(SplashCoord x0, SplashCoord y0,
491
30.2M
           SplashCoord x1, SplashCoord y1) {
492
30.2M
  grow(1);
493
30.2M
  segs[length].x0 = x0;
494
30.2M
  segs[length].y0 = y0;
495
30.2M
  segs[length].x1 = x1;
496
30.2M
  segs[length].y1 = y1;
497
30.2M
  ++length;
498
30.2M
}
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
130k
void SplashXPath::finishSegments() {
616
130k
  SplashXPathSeg *seg;
617
130k
  SplashCoord xMinFP, xMaxFP, yMinFP, yMaxFP, t;
618
130k
  int i;
619
620
130k
  xMinFP = yMinFP = xMaxFP = yMaxFP = 0;
621
622
30.4M
  for (i = 0; i < length; ++i) {
623
30.2M
    seg = &segs[i];
624
625
    //--- compute the slopes
626
30.2M
    if (seg->y0 <= seg->y1) {
627
12.9M
      seg->count = 1;
628
17.3M
    } else {
629
17.3M
      t = seg->x0;  seg->x0 = seg->x1;  seg->x1 = t;
630
17.3M
      t = seg->y0;  seg->y0 = seg->y1;  seg->y1 = t;
631
17.3M
      seg->count = -1;
632
17.3M
    }
633
#if USE_FIXEDPOINT
634
    if (seg->y0 == seg->y1 || seg->x0 == seg->x1 ||
635
  !FixedPoint::divCheck(seg->x1 - seg->x0, seg->y1 - seg->y0,
636
            &seg->dxdy) ||
637
  !FixedPoint::divCheck(seg->y1 - seg->y0, seg->x1 - seg->x0,
638
            &seg->dydx)) {
639
      seg->dxdy = 0;
640
      seg->dydx = 0;
641
    }
642
#else
643
30.2M
    if (splashAbs(seg->y1 - seg->y0) < 1e-200 ||
644
25.1M
  splashAbs(seg->x1 - seg->x0) < 1e-200) {
645
25.1M
      seg->dxdy = 0;
646
25.1M
      seg->dydx = 0;
647
25.1M
    } else {
648
5.12M
      seg->dxdy = (seg->x1 - seg->x0) / (seg->y1 - seg->y0);
649
5.12M
      if (seg->dxdy == 0) {
650
0
  seg->dydx = 0;
651
5.12M
      } else {
652
5.12M
  seg->dydx = 1 / seg->dxdy;
653
5.12M
      }
654
5.12M
    }
655
30.2M
#endif
656
657
    //--- update bbox
658
30.2M
    if (i == 0) {
659
120k
      if (seg->x0 <= seg->x1) {
660
99.9k
  xMinFP = seg->x0;
661
99.9k
  xMaxFP = seg->x1;
662
99.9k
      } else {
663
20.9k
  xMinFP = seg->x1;
664
20.9k
  xMaxFP = seg->x0;
665
20.9k
      }
666
120k
      yMinFP = seg->y0;
667
120k
      yMaxFP = seg->y1;
668
30.1M
    } else {
669
30.1M
      if (seg->x0 < xMinFP) {
670
82.3k
  xMinFP = seg->x0;
671
30.0M
      } else if (seg->x0 > xMaxFP) {
672
1.02M
  xMaxFP = seg->x0;
673
1.02M
      }
674
30.1M
      if (seg->x1 < xMinFP) {
675
363k
  xMinFP = seg->x1;
676
29.7M
      } else if (seg->x1 > xMaxFP) {
677
767k
  xMaxFP = seg->x1;
678
767k
      }
679
30.1M
      if (seg->y0 < yMinFP) {
680
1.42M
  yMinFP = seg->y0;
681
1.42M
      }
682
30.1M
      if (seg->y1 > yMaxFP) {
683
1.55M
  yMaxFP = seg->y1;
684
1.55M
      }
685
30.1M
    }
686
30.2M
  }
687
688
130k
  xMin = splashFloor(xMinFP);
689
130k
  yMin = splashFloor(yMinFP);
690
130k
  xMax = splashFloor(xMaxFP);
691
130k
  yMax = splashFloor(yMaxFP);
692
130k
}