Coverage Report

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