Coverage Report

Created: 2026-06-10 06:37

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