Coverage Report

Created: 2026-04-12 06:43

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