Coverage Report

Created: 2026-04-04 06:06

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