Coverage Report

Created: 2026-03-31 07:04

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
80.0M
           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
80.0M
  *xo = xi * matrix[0] + yi * matrix[2] + matrix[4];
53
80.0M
  *yo = xi * matrix[1] + yi * matrix[3] + matrix[5];
54
80.0M
}
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
388M
#define maxCoord 100000000.0
64
65
80.0M
void SplashXPath::clampCoords(SplashCoord *x, SplashCoord *y) {
66
80.0M
#if !USE_FIXEDPOINT
67
80.0M
  if (*x > maxCoord) {
68
38.8M
    *x = maxCoord;
69
41.1M
  } else if (*x < -maxCoord) {
70
37.6M
    *x = -maxCoord;
71
37.6M
  }
72
80.0M
  if (*y > maxCoord) {
73
39.2M
    *y = maxCoord;
74
40.8M
  } else if (*y < -maxCoord) {
75
30.7M
    *y = -maxCoord;
76
30.7M
  }
77
80.0M
#endif
78
80.0M
}
79
80
SplashXPath::SplashXPath(SplashPath *path, SplashCoord *matrix,
81
       SplashCoord flatness, GBool closeSubpaths,
82
       GBool simplify,
83
       SplashStrokeAdjustMode strokeAdjMode,
84
251k
       SplashClip *clip) {
85
251k
  SplashXPathPoint *pts;
86
251k
  SplashCoord x0, y0, x1, y1, x2, y2, x3, y3, xsp, ysp, t;
87
251k
  int nSubpaths, curSubpath, firstSegInSubpath, i;
88
251k
  GBool adjusted;
89
90
  //--- transform the points
91
251k
  pts = (SplashXPathPoint *)gmallocn(path->length, sizeof(SplashXPathPoint));
92
80.2M
  for (i = 0; i < path->length; ++i) {
93
80.0M
    transform(matrix, path->pts[i].x, path->pts[i].y, &pts[i].x, &pts[i].y);
94
80.0M
    clampCoords(&pts[i].x, &pts[i].y);
95
80.0M
  }
96
97
  //--- do stroke adjustment
98
251k
  if (path->hints) {
99
30.7k
    adjusted = strokeAdjust(pts, path->hints, path->hintsLength,
100
30.7k
          strokeAdjMode, clip);
101
220k
  } else {
102
220k
    adjusted = gFalse;
103
220k
  }
104
105
  //--- construct the segments
106
107
251k
  segs = NULL;
108
251k
  length = size = 0;
109
110
251k
  x0 = y0 = xsp = ysp = 0; // make gcc happy
111
251k
  nSubpaths = 0;
112
251k
  curSubpath = 0;
113
251k
  firstSegInSubpath = 0;
114
251k
  i = 0;
115
77.3M
  while (i < path->length) {
116
117
    // first point in subpath - skip it
118
77.0M
    if (path->flags[i] & splashPathFirst) {
119
15.7M
      x0 = pts[i].x;
120
15.7M
      y0 = pts[i].y;
121
15.7M
      xsp = x0;
122
15.7M
      ysp = y0;
123
15.7M
      curSubpath = i;
124
15.7M
      ++i;
125
126
61.3M
    } else {
127
128
      // curve segment
129
61.3M
      if (path->flags[i] & splashPathCurve) {
130
1.47M
  x1 = pts[i].x;
131
1.47M
  y1 = pts[i].y;
132
1.47M
  x2 = pts[i+1].x;
133
1.47M
  y2 = pts[i+1].y;
134
1.47M
  x3 = pts[i+2].x;
135
1.47M
  y3 = pts[i+2].y;
136
1.47M
  addCurve(x0, y0, x1, y1, x2, y2, x3, y3,
137
1.47M
     flatness,
138
1.47M
     (path->flags[i-1] & splashPathFirst),
139
1.47M
     (path->flags[i+2] & splashPathLast),
140
1.47M
     !closeSubpaths &&
141
0
       (path->flags[i-1] & splashPathFirst) &&
142
0
       !(path->flags[i-1] & splashPathClosed),
143
1.47M
     !closeSubpaths &&
144
0
       (path->flags[i+2] & splashPathLast) &&
145
0
       !(path->flags[i+2] & splashPathClosed));
146
1.47M
  x0 = x3;
147
1.47M
  y0 = y3;
148
1.47M
  i += 3;
149
150
      // line segment
151
59.8M
      } else {
152
59.8M
  x1 = pts[i].x;
153
59.8M
  y1 = pts[i].y;
154
59.8M
  addSegment(x0, y0, x1, y1);
155
59.8M
  x0 = x1;
156
59.8M
  y0 = y1;
157
59.8M
  ++i;
158
59.8M
      }
159
160
      // end a subpath
161
61.3M
      if (path->flags[i-1] & splashPathLast) {
162
15.7M
  ++nSubpaths;
163
15.7M
  if (closeSubpaths &&
164
15.6M
      (pts[i-1].x != pts[curSubpath].x ||
165
15.6M
       pts[i-1].y != pts[curSubpath].y)) {
166
1.66M
    addSegment(x0, y0, xsp, ysp);
167
1.66M
  }
168
15.7M
  if (simplify && !adjusted) {
169
0
    mergeSegments(firstSegInSubpath);
170
0
  }
171
15.7M
  firstSegInSubpath = length;
172
15.7M
      }
173
61.3M
    }
174
77.0M
  }
175
176
251k
  gfree(pts);
177
178
251k
  finishSegments();
179
180
  //--- check for a rectangle
181
251k
  isRect = gFalse;
182
251k
  rectX0 = rectY0 = rectX1 = rectY1 = 0;
183
251k
  if (nSubpaths == 1 && length == 4) {
184
141k
#if HAVE_STD_SORT
185
141k
    std::sort(segs, segs + length, SplashXPathSeg::cmpY);
186
#else
187
    qsort(segs, length, sizeof(SplashXPathSeg), &SplashXPathSeg::cmpY);
188
#endif
189
141k
    if (segs[0].y0 == segs[0].y1 &&
190
96.8k
  segs[1].x0 == segs[1].x1 &&
191
94.1k
  segs[2].x0 == segs[2].x1 &&
192
76.4k
  segs[3].y0 == segs[3].y1) {
193
73.2k
      isRect = gTrue;
194
73.2k
      rectX0 = segs[1].x0;
195
73.2k
      rectX1 = segs[2].x0;
196
73.2k
      rectY0 = segs[0].y0;
197
73.2k
      rectY1 = segs[3].y0;
198
73.2k
    } else if (segs[0].x0 == segs[0].x1 &&
199
48.5k
         segs[1].y0 == segs[1].y1 &&
200
41.0k
         segs[2].x0 == segs[2].x1 &&
201
37.5k
         segs[3].y0 == segs[3].y1) {
202
37.3k
      isRect = gTrue;
203
37.3k
      rectX0 = segs[0].x0;
204
37.3k
      rectX1 = segs[2].x0;
205
37.3k
      rectY0 = segs[1].y0;
206
37.3k
      rectY1 = segs[3].y0;
207
37.3k
    } else if (segs[0].x0 == segs[0].x1 &&
208
11.2k
         segs[1].x0 == segs[1].x1 &&
209
10.2k
         segs[2].y0 == segs[2].y1 &&
210
6.18k
         segs[3].y0 == segs[3].y1) {
211
6.10k
      isRect = gTrue;
212
6.10k
      rectX0 = segs[0].x0;
213
6.10k
      rectX1 = segs[1].x0;
214
6.10k
      rectY0 = segs[2].y0;
215
6.10k
      rectY1 = segs[3].y0;
216
6.10k
    }
217
141k
    if (isRect) {
218
116k
      if (rectX0 > rectX1) {
219
24.1k
  t = rectX0;  rectX0 = rectX1;  rectX1 = t;
220
24.1k
      }
221
116k
      if (rectY0 > rectY1) {
222
0
  t = rectY0;  rectY0 = rectY1;  rectY1 = t;
223
0
      }
224
116k
    }
225
141k
  }
226
251k
}
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.5k
    cx0 = clip->getXMin();
253
22.5k
    cx1 = clip->getXMax();
254
22.5k
    cy0 = clip->getYMin();
255
22.5k
    cy1 = clip->getYMax();
256
22.5k
    cxi0 = clip->getXMinI(strokeAdjMode);
257
22.5k
    cxi1 = clip->getXMaxI(strokeAdjMode);
258
22.5k
    cyi0 = clip->getYMinI(strokeAdjMode);
259
22.5k
    cyi1 = clip->getYMaxI(strokeAdjMode);
260
22.5k
  }
261
262
  // set up the stroke adjustment hints
263
30.7k
  adjusts = (SplashXPathAdjust *)gmallocn(nHints, sizeof(SplashXPathAdjust));
264
16.5M
  for (i = 0; i < nHints; ++i) {
265
16.5M
    hint = &hints[i];
266
16.5M
    x0 = pts[hint->ctrl0    ].x;    y0 = pts[hint->ctrl0    ].y;
267
16.5M
    x1 = pts[hint->ctrl0 + 1].x;    y1 = pts[hint->ctrl0 + 1].y;
268
16.5M
    x2 = pts[hint->ctrl1    ].x;    y2 = pts[hint->ctrl1    ].y;
269
16.5M
    x3 = pts[hint->ctrl1 + 1].x;    y3 = pts[hint->ctrl1 + 1].y;
270
16.5M
    w = -1;
271
16.5M
    if (splashAbs(x0 - x1) < 0.01 && splashAbs(x2 - x3) < 0.01) {
272
16.3M
      adjusts[i].vert = gTrue;
273
16.3M
      adj0 = x0;
274
16.3M
      adj1 = x2;
275
16.3M
      if (hint->projectingCap) {
276
1.81k
  w = splashAbs(y1 - y0);
277
1.81k
      }
278
16.3M
    } else if (splashAbs(y0 - y1) < 0.01 && splashAbs(y2 - y3) < 0.01) {
279
122k
      adjusts[i].vert = gFalse;
280
122k
      adj0 = y0;
281
122k
      adj1 = y2;
282
122k
      if (hint->projectingCap) {
283
467
  w = splashAbs(x1 - x0);
284
467
      }
285
122k
    } else {
286
8.05k
      goto done;
287
8.05k
    }
288
16.5M
    if (adj0 > adj1) {
289
483k
      x0 = adj0;
290
483k
      adj0 = adj1;
291
483k
      adj1 = x0;
292
483k
    }
293
16.5M
    d = adj1 - adj0;
294
16.5M
    if (d > 0.04) {
295
864k
      d = 0.01;
296
15.6M
    } else {
297
15.6M
      d *= 0.25;
298
15.6M
    }
299
16.5M
    adjusts[i].x0a = adj0 - d;
300
16.5M
    adjusts[i].x0b = adj0 + d;
301
16.5M
    adjusts[i].xma = (SplashCoord)0.5 * (adj0 + adj1) - d;
302
16.5M
    adjusts[i].xmb = (SplashCoord)0.5 * (adj0 + adj1) + d;
303
16.5M
    adjusts[i].x1a = adj1 - d;
304
16.5M
    adjusts[i].x1b = adj1 + d;
305
16.5M
    splashStrokeAdjust(adj0, adj1, &xi0, &xi1, strokeAdjMode, w);
306
16.5M
    if (clipTweak) {
307
9.57M
      SplashCoord c0, c1;
308
9.57M
      int ci0, ci1;
309
9.57M
      if (adjusts[i].vert) {
310
9.47M
  c0 = cx0;
311
9.47M
  c1 = cx1;
312
9.47M
  ci0 = cxi0;
313
9.47M
  ci1 = cxi1;
314
9.47M
      } else {
315
100k
  c0 = cy0;
316
100k
  c1 = cy1;
317
100k
  ci0 = cyi0;
318
100k
  ci1 = cyi1;
319
100k
      }
320
9.57M
      if (adj0 < c0 && c0 < adj1 && adj1 < c1 &&
321
85
    adj1 - c0 > (adj1 - adj0) * 0.2 &&
322
85
    xi1 <= ci0) {
323
0
  xi0 = ci0;
324
0
  xi1 = xi0 + 1;
325
9.57M
      } else if (c0 < adj0 && adj0 < c1 && c1 < adj1 &&
326
15
     c1 - adj0 > (adj1 - adj0) * 0.2 &&
327
0
     ci1 < xi0) {
328
0
  xi0 = ci1;
329
0
  xi1 = ci1 + 1;
330
0
      }
331
9.57M
    }
332
16.5M
    adjusts[i].x0 = (SplashCoord)xi0;
333
    // the "minus epsilon" thing here is needed when vector
334
    // antialiasing is turned off -- otherwise stroke adjusted lines
335
    // will touch an extra pixel on one edge
336
16.5M
    adjusts[i].x1 = (SplashCoord)xi1 - 0.001;
337
16.5M
    adjusts[i].xm = (SplashCoord)0.5 * (adjusts[i].x0 + adjusts[i].x1);
338
16.5M
    adjusts[i].firstPt = hint->firstPt;
339
16.5M
    adjusts[i].lastPt = hint->lastPt;
340
16.5M
  }
341
342
  // perform stroke adjustment
343
13.8M
  for (i = 0, adjust = adjusts; i < nHints; ++i, ++adjust) {
344
100M
    for (j = adjust->firstPt; j <= adjust->lastPt; ++j) {
345
86.8M
      if (adjust->vert) {
346
86.2M
  x0 = pts[j].x;
347
86.2M
  if (x0 > adjust->x0a && x0 < adjust->x0b) {
348
2.51M
    pts[j].x = adjust->x0;
349
83.6M
  } else if (x0 > adjust->xma && x0 < adjust->xmb) {
350
6.64k
    pts[j].x = adjust->xm;
351
83.6M
  } else if (x0 > adjust->x1a && x0 < adjust->x1b) {
352
2.55M
    pts[j].x = adjust->x1;
353
2.55M
  }
354
86.2M
      } else {
355
607k
  y0 = pts[j].y;
356
607k
  if (y0 > adjust->x0a && y0 < adjust->x0b) {
357
19.7k
    pts[j].y = adjust->x0;
358
587k
  } else if (y0 > adjust->xma && y0 < adjust->xmb) {
359
1.12k
    pts[j].y = adjust->xm;
360
586k
  } else if (y0 > adjust->x1a && y0 < adjust->x1b) {
361
20.6k
    pts[j].y = adjust->x1;
362
20.6k
  }
363
607k
      }
364
86.8M
    }
365
13.8M
  }
366
22.7k
  adjusted = gTrue;
367
368
30.7k
 done:
369
30.7k
  gfree(adjusts);
370
30.7k
  return adjusted;
371
22.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
251k
SplashXPath::~SplashXPath() {
385
251k
  gfree(segs);
386
251k
}
387
388
// Add space for <nSegs> more segments
389
93.5M
void SplashXPath::grow(int nSegs) {
390
93.5M
  if (length + nSegs > size) {
391
348k
    if (size == 0) {
392
238k
      size = 32;
393
238k
    }
394
458k
    while (size < length + nSegs) {
395
109k
      size *= 2;
396
109k
    }
397
348k
    segs = (SplashXPathSeg *)greallocn(segs, size, sizeof(SplashXPathSeg));
398
348k
  }
399
93.5M
}
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.47M
         GBool first, GBool last, GBool end0, GBool end1) {
407
1.47M
  SplashCoord cx[splashMaxCurveSplits + 1][3];
408
1.47M
  SplashCoord cy[splashMaxCurveSplits + 1][3];
409
1.47M
  int cNext[splashMaxCurveSplits + 1];
410
1.47M
  SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh;
411
1.47M
  SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh;
412
1.47M
  SplashCoord dx, dy, mx, my, d1, d2, flatness2;
413
1.47M
  int p1, p2, p3;
414
415
#if USE_FIXEDPOINT
416
  flatness2 = flatness;
417
#else
418
1.47M
  flatness2 = flatness * flatness;
419
1.47M
#endif
420
421
  // initial segment
422
1.47M
  p1 = 0;
423
1.47M
  p2 = splashMaxCurveSplits;
424
1.47M
  cx[p1][0] = x0;  cy[p1][0] = y0;
425
1.47M
  cx[p1][1] = x1;  cy[p1][1] = y1;
426
1.47M
  cx[p1][2] = x2;  cy[p1][2] = y2;
427
1.47M
  cx[p2][0] = x3;  cy[p2][0] = y3;
428
1.47M
  cNext[p1] = p2;
429
430
64.0M
  while (p1 < splashMaxCurveSplits) {
431
432
    // get the next segment
433
62.5M
    xl0 = cx[p1][0];  yl0 = cy[p1][0];
434
62.5M
    xx1 = cx[p1][1];  yy1 = cy[p1][1];
435
62.5M
    xx2 = cx[p1][2];  yy2 = cy[p1][2];
436
62.5M
    p2 = cNext[p1];
437
62.5M
    xr3 = cx[p2][0];  yr3 = cy[p2][0];
438
439
    // compute the distances from the control points to the
440
    // midpoint of the straight line (this is a bit of a hack, but
441
    // it's much faster than computing the actual distances to the
442
    // line)
443
62.5M
    mx = (xl0 + xr3) * 0.5;
444
62.5M
    my = (yl0 + yr3) * 0.5;
445
#if USE_FIXEDPOINT
446
    d1 = splashDist(xx1, yy1, mx, my);
447
    d2 = splashDist(xx2, yy2, mx, my);
448
#else
449
62.5M
    dx = xx1 - mx;
450
62.5M
    dy = yy1 - my;
451
62.5M
    d1 = dx*dx + dy*dy;
452
62.5M
    dx = xx2 - mx;
453
62.5M
    dy = yy2 - my;
454
62.5M
    d2 = dx*dx + dy*dy;
455
62.5M
#endif
456
457
    // if the curve is flat enough, or no more subdivisions are
458
    // allowed, add the straight line segment
459
62.5M
    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.5M
      xl1 = (xl0 + xx1) * 0.5;
466
30.5M
      yl1 = (yl0 + yy1) * 0.5;
467
30.5M
      xh = (xx1 + xx2) * 0.5;
468
30.5M
      yh = (yy1 + yy2) * 0.5;
469
30.5M
      xl2 = (xl1 + xh) * 0.5;
470
30.5M
      yl2 = (yl1 + yh) * 0.5;
471
30.5M
      xr2 = (xx2 + xr3) * 0.5;
472
30.5M
      yr2 = (yy2 + yr3) * 0.5;
473
30.5M
      xr1 = (xh + xr2) * 0.5;
474
30.5M
      yr1 = (yh + yr2) * 0.5;
475
30.5M
      xr0 = (xl2 + xr1) * 0.5;
476
30.5M
      yr0 = (yl2 + yr1) * 0.5;
477
      // add the new subdivision points
478
30.5M
      p3 = (p1 + p2) / 2;
479
30.5M
      cx[p1][1] = xl1;  cy[p1][1] = yl1;
480
30.5M
      cx[p1][2] = xl2;  cy[p1][2] = yl2;
481
30.5M
      cNext[p1] = p3;
482
30.5M
      cx[p3][0] = xr0;  cy[p3][0] = yr0;
483
30.5M
      cx[p3][1] = xr1;  cy[p3][1] = yr1;
484
30.5M
      cx[p3][2] = xr2;  cy[p3][2] = yr2;
485
30.5M
      cNext[p3] = p2;
486
30.5M
    }
487
62.5M
  }
488
1.47M
}
489
490
void SplashXPath::addSegment(SplashCoord x0, SplashCoord y0,
491
93.5M
           SplashCoord x1, SplashCoord y1) {
492
93.5M
  grow(1);
493
93.5M
  segs[length].x0 = x0;
494
93.5M
  segs[length].y0 = y0;
495
93.5M
  segs[length].x1 = x1;
496
93.5M
  segs[length].y1 = y1;
497
93.5M
  ++length;
498
93.5M
}
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
251k
void SplashXPath::finishSegments() {
616
251k
  SplashXPathSeg *seg;
617
251k
  SplashCoord xMinFP, xMaxFP, yMinFP, yMaxFP, t;
618
251k
  int i;
619
620
251k
  xMinFP = yMinFP = xMaxFP = yMaxFP = 0;
621
622
93.7M
  for (i = 0; i < length; ++i) {
623
93.5M
    seg = &segs[i];
624
625
    //--- compute the slopes
626
93.5M
    if (seg->y0 <= seg->y1) {
627
64.1M
      seg->count = 1;
628
64.1M
    } else {
629
29.3M
      t = seg->x0;  seg->x0 = seg->x1;  seg->x1 = t;
630
29.3M
      t = seg->y0;  seg->y0 = seg->y1;  seg->y1 = t;
631
29.3M
      seg->count = -1;
632
29.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
93.5M
    if (splashAbs(seg->y1 - seg->y0) < 1e-200 ||
644
83.6M
  splashAbs(seg->x1 - seg->x0) < 1e-200) {
645
83.6M
      seg->dxdy = 0;
646
83.6M
      seg->dydx = 0;
647
83.6M
    } else {
648
9.93M
      seg->dxdy = (seg->x1 - seg->x0) / (seg->y1 - seg->y0);
649
9.93M
      if (seg->dxdy == 0) {
650
0
  seg->dydx = 0;
651
9.93M
      } else {
652
9.93M
  seg->dydx = 1 / seg->dxdy;
653
9.93M
      }
654
9.93M
    }
655
93.5M
#endif
656
657
    //--- update bbox
658
93.5M
    if (i == 0) {
659
238k
      if (seg->x0 <= seg->x1) {
660
204k
  xMinFP = seg->x0;
661
204k
  xMaxFP = seg->x1;
662
204k
      } else {
663
34.4k
  xMinFP = seg->x1;
664
34.4k
  xMaxFP = seg->x0;
665
34.4k
      }
666
238k
      yMinFP = seg->y0;
667
238k
      yMaxFP = seg->y1;
668
93.2M
    } else {
669
93.2M
      if (seg->x0 < xMinFP) {
670
312k
  xMinFP = seg->x0;
671
92.9M
      } else if (seg->x0 > xMaxFP) {
672
485k
  xMaxFP = seg->x0;
673
485k
      }
674
93.2M
      if (seg->x1 < xMinFP) {
675
554k
  xMinFP = seg->x1;
676
92.7M
      } else if (seg->x1 > xMaxFP) {
677
1.38M
  xMaxFP = seg->x1;
678
1.38M
      }
679
93.2M
      if (seg->y0 < yMinFP) {
680
3.33M
  yMinFP = seg->y0;
681
3.33M
      }
682
93.2M
      if (seg->y1 > yMaxFP) {
683
3.40M
  yMaxFP = seg->y1;
684
3.40M
      }
685
93.2M
    }
686
93.5M
  }
687
688
251k
  xMin = splashFloor(xMinFP);
689
251k
  yMin = splashFloor(yMinFP);
690
251k
  xMax = splashFloor(xMaxFP);
691
251k
  yMax = splashFloor(yMaxFP);
692
251k
}