Coverage Report

Created: 2026-10-13 06:47

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