Coverage Report

Created: 2026-07-16 06:50

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