Coverage Report

Created: 2026-08-13 06:56

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