Coverage Report

Created: 2026-08-31 06:45

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