Coverage Report

Created: 2026-05-30 06:25

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