Coverage Report

Created: 2026-08-22 06:39

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
63.9M
           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
63.9M
  *xo = xi * matrix[0] + yi * matrix[2] + matrix[4];
53
63.9M
  *yo = xi * matrix[1] + yi * matrix[3] + matrix[5];
54
63.9M
}
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
313M
#define maxCoord 100000000.0
64
65
63.9M
void SplashXPath::clampCoords(SplashCoord *x, SplashCoord *y) {
66
63.9M
#if !USE_FIXEDPOINT
67
63.9M
  if (*x > maxCoord) {
68
26.6M
    *x = maxCoord;
69
37.2M
  } else if (*x < -maxCoord) {
70
31.5M
    *x = -maxCoord;
71
31.5M
  }
72
63.9M
  if (*y > maxCoord) {
73
22.6M
    *y = maxCoord;
74
41.2M
  } else if (*y < -maxCoord) {
75
26.0M
    *y = -maxCoord;
76
26.0M
  }
77
63.9M
#endif
78
63.9M
}
79
80
SplashXPath::SplashXPath(SplashPath *path, SplashCoord *matrix,
81
       SplashCoord flatness, GBool closeSubpaths,
82
       GBool simplify,
83
       SplashStrokeAdjustMode strokeAdjMode,
84
222k
       SplashClip *clip) {
85
222k
  SplashXPathPoint *pts;
86
222k
  SplashCoord x0, y0, x1, y1, x2, y2, x3, y3, xsp, ysp, t;
87
222k
  int nSubpaths, curSubpath, firstSegInSubpath, i;
88
222k
  GBool adjusted;
89
90
  //--- transform the points
91
222k
  pts = (SplashXPathPoint *)gmallocn(path->length, sizeof(SplashXPathPoint));
92
64.1M
  for (i = 0; i < path->length; ++i) {
93
63.9M
    transform(matrix, path->pts[i].x, path->pts[i].y, &pts[i].x, &pts[i].y);
94
63.9M
    clampCoords(&pts[i].x, &pts[i].y);
95
63.9M
  }
96
97
  //--- do stroke adjustment
98
222k
  if (path->hints) {
99
30.7k
    adjusted = strokeAdjust(pts, path->hints, path->hintsLength,
100
30.7k
          strokeAdjMode, clip);
101
191k
  } else {
102
191k
    adjusted = gFalse;
103
191k
  }
104
105
  //--- construct the segments
106
107
222k
  segs = NULL;
108
222k
  length = size = 0;
109
110
222k
  x0 = y0 = xsp = ysp = 0; // make gcc happy
111
222k
  nSubpaths = 0;
112
222k
  curSubpath = 0;
113
222k
  firstSegInSubpath = 0;
114
222k
  i = 0;
115
61.6M
  while (i < path->length) {
116
117
    // first point in subpath - skip it
118
61.4M
    if (path->flags[i] & splashPathFirst) {
119
12.8M
      x0 = pts[i].x;
120
12.8M
      y0 = pts[i].y;
121
12.8M
      xsp = x0;
122
12.8M
      ysp = y0;
123
12.8M
      curSubpath = i;
124
12.8M
      ++i;
125
126
48.6M
    } else {
127
128
      // curve segment
129
48.6M
      if (path->flags[i] & splashPathCurve) {
130
1.22M
  x1 = pts[i].x;
131
1.22M
  y1 = pts[i].y;
132
1.22M
  x2 = pts[i+1].x;
133
1.22M
  y2 = pts[i+1].y;
134
1.22M
  x3 = pts[i+2].x;
135
1.22M
  y3 = pts[i+2].y;
136
1.22M
  addCurve(x0, y0, x1, y1, x2, y2, x3, y3,
137
1.22M
     flatness,
138
1.22M
     (path->flags[i-1] & splashPathFirst),
139
1.22M
     (path->flags[i+2] & splashPathLast),
140
1.22M
     !closeSubpaths &&
141
0
       (path->flags[i-1] & splashPathFirst) &&
142
0
       !(path->flags[i-1] & splashPathClosed),
143
1.22M
     !closeSubpaths &&
144
0
       (path->flags[i+2] & splashPathLast) &&
145
0
       !(path->flags[i+2] & splashPathClosed));
146
1.22M
  x0 = x3;
147
1.22M
  y0 = y3;
148
1.22M
  i += 3;
149
150
      // line segment
151
47.4M
      } else {
152
47.4M
  x1 = pts[i].x;
153
47.4M
  y1 = pts[i].y;
154
47.4M
  addSegment(x0, y0, x1, y1);
155
47.4M
  x0 = x1;
156
47.4M
  y0 = y1;
157
47.4M
  ++i;
158
47.4M
      }
159
160
      // end a subpath
161
48.6M
      if (path->flags[i-1] & splashPathLast) {
162
12.8M
  ++nSubpaths;
163
12.8M
  if (closeSubpaths &&
164
12.2M
      (pts[i-1].x != pts[curSubpath].x ||
165
12.2M
       pts[i-1].y != pts[curSubpath].y)) {
166
2.04M
    addSegment(x0, y0, xsp, ysp);
167
2.04M
  }
168
12.8M
  if (simplify && !adjusted) {
169
0
    mergeSegments(firstSegInSubpath);
170
0
  }
171
12.8M
  firstSegInSubpath = length;
172
12.8M
      }
173
48.6M
    }
174
61.4M
  }
175
176
222k
  gfree(pts);
177
178
222k
  finishSegments();
179
180
  //--- check for a rectangle
181
222k
  isRect = gFalse;
182
222k
  rectX0 = rectY0 = rectX1 = rectY1 = 0;
183
222k
  if (nSubpaths == 1 && length == 4) {
184
120k
#if HAVE_STD_SORT
185
120k
    std::sort(segs, segs + length, SplashXPathSeg::cmpY);
186
#else
187
    qsort(segs, length, sizeof(SplashXPathSeg), &SplashXPathSeg::cmpY);
188
#endif
189
120k
    if (segs[0].y0 == segs[0].y1 &&
190
69.8k
  segs[1].x0 == segs[1].x1 &&
191
68.2k
  segs[2].x0 == segs[2].x1 &&
192
50.8k
  segs[3].y0 == segs[3].y1) {
193
46.8k
      isRect = gTrue;
194
46.8k
      rectX0 = segs[1].x0;
195
46.8k
      rectX1 = segs[2].x0;
196
46.8k
      rectY0 = segs[0].y0;
197
46.8k
      rectY1 = segs[3].y0;
198
73.8k
    } else if (segs[0].x0 == segs[0].x1 &&
199
54.3k
         segs[1].y0 == segs[1].y1 &&
200
46.7k
         segs[2].x0 == segs[2].x1 &&
201
45.9k
         segs[3].y0 == segs[3].y1) {
202
45.3k
      isRect = gTrue;
203
45.3k
      rectX0 = segs[0].x0;
204
45.3k
      rectX1 = segs[2].x0;
205
45.3k
      rectY0 = segs[1].y0;
206
45.3k
      rectY1 = segs[3].y0;
207
45.3k
    } else if (segs[0].x0 == segs[0].x1 &&
208
8.94k
         segs[1].x0 == segs[1].x1 &&
209
8.26k
         segs[2].y0 == segs[2].y1 &&
210
2.63k
         segs[3].y0 == segs[3].y1) {
211
2.39k
      isRect = gTrue;
212
2.39k
      rectX0 = segs[0].x0;
213
2.39k
      rectX1 = segs[1].x0;
214
2.39k
      rectY0 = segs[2].y0;
215
2.39k
      rectY1 = segs[3].y0;
216
2.39k
    }
217
120k
    if (isRect) {
218
94.6k
      if (rectX0 > rectX1) {
219
29.0k
  t = rectX0;  rectX0 = rectX1;  rectX1 = t;
220
29.0k
      }
221
94.6k
      if (rectY0 > rectY1) {
222
0
  t = rectY0;  rectY0 = rectY1;  rectY1 = t;
223
0
      }
224
94.6k
    }
225
120k
  }
226
222k
}
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
23.3k
    cx0 = clip->getXMin();
253
23.3k
    cx1 = clip->getXMax();
254
23.3k
    cy0 = clip->getYMin();
255
23.3k
    cy1 = clip->getYMax();
256
23.3k
    cxi0 = clip->getXMinI(strokeAdjMode);
257
23.3k
    cxi1 = clip->getXMaxI(strokeAdjMode);
258
23.3k
    cyi0 = clip->getYMinI(strokeAdjMode);
259
23.3k
    cyi1 = clip->getYMaxI(strokeAdjMode);
260
23.3k
  }
261
262
  // set up the stroke adjustment hints
263
30.7k
  adjusts = (SplashXPathAdjust *)gmallocn(nHints, sizeof(SplashXPathAdjust));
264
12.1M
  for (i = 0; i < nHints; ++i) {
265
12.1M
    hint = &hints[i];
266
12.1M
    x0 = pts[hint->ctrl0    ].x;    y0 = pts[hint->ctrl0    ].y;
267
12.1M
    x1 = pts[hint->ctrl0 + 1].x;    y1 = pts[hint->ctrl0 + 1].y;
268
12.1M
    x2 = pts[hint->ctrl1    ].x;    y2 = pts[hint->ctrl1    ].y;
269
12.1M
    x3 = pts[hint->ctrl1 + 1].x;    y3 = pts[hint->ctrl1 + 1].y;
270
12.1M
    w = -1;
271
12.1M
    if (splashAbs(x0 - x1) < 0.01 && splashAbs(x2 - x3) < 0.01) {
272
12.0M
      adjusts[i].vert = gTrue;
273
12.0M
      adj0 = x0;
274
12.0M
      adj1 = x2;
275
12.0M
      if (hint->projectingCap) {
276
2.42k
  w = splashAbs(y1 - y0);
277
2.42k
      }
278
12.0M
    } else if (splashAbs(y0 - y1) < 0.01 && splashAbs(y2 - y3) < 0.01) {
279
123k
      adjusts[i].vert = gFalse;
280
123k
      adj0 = y0;
281
123k
      adj1 = y2;
282
123k
      if (hint->projectingCap) {
283
625
  w = splashAbs(x1 - x0);
284
625
      }
285
123k
    } else {
286
6.25k
      goto done;
287
6.25k
    }
288
12.1M
    if (adj0 > adj1) {
289
548k
      x0 = adj0;
290
548k
      adj0 = adj1;
291
548k
      adj1 = x0;
292
548k
    }
293
12.1M
    d = adj1 - adj0;
294
12.1M
    if (d > 0.04) {
295
767k
      d = 0.01;
296
11.3M
    } else {
297
11.3M
      d *= 0.25;
298
11.3M
    }
299
12.1M
    adjusts[i].x0a = adj0 - d;
300
12.1M
    adjusts[i].x0b = adj0 + d;
301
12.1M
    adjusts[i].xma = (SplashCoord)0.5 * (adj0 + adj1) - d;
302
12.1M
    adjusts[i].xmb = (SplashCoord)0.5 * (adj0 + adj1) + d;
303
12.1M
    adjusts[i].x1a = adj1 - d;
304
12.1M
    adjusts[i].x1b = adj1 + d;
305
12.1M
    splashStrokeAdjust(adj0, adj1, &xi0, &xi1, strokeAdjMode, w);
306
12.1M
    if (clipTweak) {
307
6.93M
      SplashCoord c0, c1;
308
6.93M
      int ci0, ci1;
309
6.93M
      if (adjusts[i].vert) {
310
6.81M
  c0 = cx0;
311
6.81M
  c1 = cx1;
312
6.81M
  ci0 = cxi0;
313
6.81M
  ci1 = cxi1;
314
6.81M
      } else {
315
111k
  c0 = cy0;
316
111k
  c1 = cy1;
317
111k
  ci0 = cyi0;
318
111k
  ci1 = cyi1;
319
111k
      }
320
6.93M
      if (adj0 < c0 && c0 < adj1 && adj1 < c1 &&
321
4.27k
    adj1 - c0 > (adj1 - adj0) * 0.2 &&
322
4.10k
    xi1 <= ci0) {
323
503
  xi0 = ci0;
324
503
  xi1 = xi0 + 1;
325
6.93M
      } else if (c0 < adj0 && adj0 < c1 && c1 < adj1 &&
326
1.23k
     c1 - adj0 > (adj1 - adj0) * 0.2 &&
327
743
     ci1 < xi0) {
328
360
  xi0 = ci1;
329
360
  xi1 = ci1 + 1;
330
360
      }
331
6.93M
    }
332
12.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
12.1M
    adjusts[i].x1 = (SplashCoord)xi1 - 0.001;
337
12.1M
    adjusts[i].xm = (SplashCoord)0.5 * (adjusts[i].x0 + adjusts[i].x1);
338
12.1M
    adjusts[i].firstPt = hint->firstPt;
339
12.1M
    adjusts[i].lastPt = hint->lastPt;
340
12.1M
  }
341
342
  // perform stroke adjustment
343
10.8M
  for (i = 0, adjust = adjusts; i < nHints; ++i, ++adjust) {
344
80.1M
    for (j = adjust->firstPt; j <= adjust->lastPt; ++j) {
345
69.4M
      if (adjust->vert) {
346
68.8M
  x0 = pts[j].x;
347
68.8M
  if (x0 > adjust->x0a && x0 < adjust->x0b) {
348
2.65M
    pts[j].x = adjust->x0;
349
66.1M
  } else if (x0 > adjust->xma && x0 < adjust->xmb) {
350
37.3k
    pts[j].x = adjust->xm;
351
66.1M
  } else if (x0 > adjust->x1a && x0 < adjust->x1b) {
352
1.92M
    pts[j].x = adjust->x1;
353
1.92M
  }
354
68.8M
      } else {
355
598k
  y0 = pts[j].y;
356
598k
  if (y0 > adjust->x0a && y0 < adjust->x0b) {
357
70.0k
    pts[j].y = adjust->x0;
358
528k
  } else if (y0 > adjust->xma && y0 < adjust->xmb) {
359
30.1k
    pts[j].y = adjust->xm;
360
498k
  } else if (y0 > adjust->x1a && y0 < adjust->x1b) {
361
50.6k
    pts[j].y = adjust->x1;
362
50.6k
  }
363
598k
      }
364
69.4M
    }
365
10.7M
  }
366
24.5k
  adjusted = gTrue;
367
368
30.7k
 done:
369
30.7k
  gfree(adjusts);
370
30.7k
  return adjusted;
371
24.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
222k
SplashXPath::~SplashXPath() {
385
222k
  gfree(segs);
386
222k
}
387
388
// Add space for <nSegs> more segments
389
86.6M
void SplashXPath::grow(int nSegs) {
390
86.6M
  if (length + nSegs > size) {
391
307k
    if (size == 0) {
392
207k
      size = 32;
393
207k
    }
394
407k
    while (size < length + nSegs) {
395
99.7k
      size *= 2;
396
99.7k
    }
397
307k
    segs = (SplashXPathSeg *)greallocn(segs, size, sizeof(SplashXPathSeg));
398
307k
  }
399
86.6M
}
400
401
void SplashXPath::addCurve(SplashCoord x0, SplashCoord y0,
402
         SplashCoord x1, SplashCoord y1,
403
         SplashCoord x2, SplashCoord y2,
404
         SplashCoord x3, SplashCoord y3,
405
         SplashCoord flatness,
406
1.22M
         GBool first, GBool last, GBool end0, GBool end1) {
407
1.22M
  SplashCoord cx[splashMaxCurveSplits + 1][3];
408
1.22M
  SplashCoord cy[splashMaxCurveSplits + 1][3];
409
1.22M
  int cNext[splashMaxCurveSplits + 1];
410
1.22M
  SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh;
411
1.22M
  SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh;
412
1.22M
  SplashCoord dx, dy, mx, my, d1, d2, flatness2;
413
1.22M
  int p1, p2, p3;
414
415
#if USE_FIXEDPOINT
416
  flatness2 = flatness;
417
#else
418
1.22M
  flatness2 = flatness * flatness;
419
1.22M
#endif
420
421
  // initial segment
422
1.22M
  p1 = 0;
423
1.22M
  p2 = splashMaxCurveSplits;
424
1.22M
  cx[p1][0] = x0;  cy[p1][0] = y0;
425
1.22M
  cx[p1][1] = x1;  cy[p1][1] = y1;
426
1.22M
  cx[p1][2] = x2;  cy[p1][2] = y2;
427
1.22M
  cx[p2][0] = x3;  cy[p2][0] = y3;
428
1.22M
  cNext[p1] = p2;
429
430
74.3M
  while (p1 < splashMaxCurveSplits) {
431
432
    // get the next segment
433
73.1M
    xl0 = cx[p1][0];  yl0 = cy[p1][0];
434
73.1M
    xx1 = cx[p1][1];  yy1 = cy[p1][1];
435
73.1M
    xx2 = cx[p1][2];  yy2 = cy[p1][2];
436
73.1M
    p2 = cNext[p1];
437
73.1M
    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
73.1M
    mx = (xl0 + xr3) * 0.5;
444
73.1M
    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
73.1M
    dx = xx1 - mx;
450
73.1M
    dy = yy1 - my;
451
73.1M
    d1 = dx*dx + dy*dy;
452
73.1M
    dx = xx2 - mx;
453
73.1M
    dy = yy2 - my;
454
73.1M
    d2 = dx*dx + dy*dy;
455
73.1M
#endif
456
457
    // if the curve is flat enough, or no more subdivisions are
458
    // allowed, add the straight line segment
459
73.1M
    if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) {
460
37.1M
      addSegment(xl0, yl0, xr3, yr3);
461
37.1M
      p1 = p2;
462
463
    // otherwise, subdivide the curve
464
37.1M
    } else {
465
35.9M
      xl1 = (xl0 + xx1) * 0.5;
466
35.9M
      yl1 = (yl0 + yy1) * 0.5;
467
35.9M
      xh = (xx1 + xx2) * 0.5;
468
35.9M
      yh = (yy1 + yy2) * 0.5;
469
35.9M
      xl2 = (xl1 + xh) * 0.5;
470
35.9M
      yl2 = (yl1 + yh) * 0.5;
471
35.9M
      xr2 = (xx2 + xr3) * 0.5;
472
35.9M
      yr2 = (yy2 + yr3) * 0.5;
473
35.9M
      xr1 = (xh + xr2) * 0.5;
474
35.9M
      yr1 = (yh + yr2) * 0.5;
475
35.9M
      xr0 = (xl2 + xr1) * 0.5;
476
35.9M
      yr0 = (yl2 + yr1) * 0.5;
477
      // add the new subdivision points
478
35.9M
      p3 = (p1 + p2) / 2;
479
35.9M
      cx[p1][1] = xl1;  cy[p1][1] = yl1;
480
35.9M
      cx[p1][2] = xl2;  cy[p1][2] = yl2;
481
35.9M
      cNext[p1] = p3;
482
35.9M
      cx[p3][0] = xr0;  cy[p3][0] = yr0;
483
35.9M
      cx[p3][1] = xr1;  cy[p3][1] = yr1;
484
35.9M
      cx[p3][2] = xr2;  cy[p3][2] = yr2;
485
35.9M
      cNext[p3] = p2;
486
35.9M
    }
487
73.1M
  }
488
1.22M
}
489
490
void SplashXPath::addSegment(SplashCoord x0, SplashCoord y0,
491
86.6M
           SplashCoord x1, SplashCoord y1) {
492
86.6M
  grow(1);
493
86.6M
  segs[length].x0 = x0;
494
86.6M
  segs[length].y0 = y0;
495
86.6M
  segs[length].x1 = x1;
496
86.6M
  segs[length].y1 = y1;
497
86.6M
  ++length;
498
86.6M
}
499
500
// Returns true if the angle between (x0,y0)-(x1,y1) and
501
// (x1,y1)-(x2,y2) is close to 180 degrees.
502
static GBool joinAngleIsFlat(SplashCoord x0, SplashCoord y0,
503
           SplashCoord x1, SplashCoord y1,
504
0
           SplashCoord x2, SplashCoord y2) {
505
0
  SplashCoord dx1, dy1, dx2, dy2, d, len1, len2;
506
507
0
  dx1 = x1 - x0;
508
0
  dy1 = y1 - y0;
509
0
  dx2 = x2 - x1;
510
0
  dy2 = y2 - y1;
511
0
  d = dx1 * dx2 + dy1 * dy2;
512
0
  len1 = dx1 * dx1 + dy1 * dy1;
513
0
  len2 = dx2 * dx2 + dy2 * dy2;
514
0
  return d > 0 && d * d > len1 * len2 * minCosSquaredJoinAngle;
515
0
}
516
517
// Returns true if (x1,y1) is sufficiently close to the segment
518
// (x0,y0)-(x2,y2), looking at the perpendicular point-to-line
519
// distance.
520
static GBool pointCloseToSegment(SplashCoord x0, SplashCoord y0,
521
         SplashCoord x1, SplashCoord y1,
522
0
         SplashCoord x2, SplashCoord y2) {
523
0
  SplashCoord t1, t2, dx, dy;
524
525
  // compute the perpendicular distance from the point to the segment,
526
  // i.e., the projection of (x0,y0)-(x1,y1) onto a unit normal to the
527
  // segment (this actually computes the square of the distance)
528
0
  dx = x2 - x0;
529
0
  dy = y2 - y0;
530
0
  t1 = dx*dx + dy*dy;
531
0
  if (t1 < 0.0001) {
532
    // degenerate case: (x0,y0) and (x2,y2) are (nearly) identical --
533
    // just compute the distance to (x1,y1)
534
0
    dx = x0 - x1;
535
0
    dy = y0 - y1;
536
0
    t2 = dx*dx + dy*dy;
537
0
    return t2 < maxPointToLineDistanceSquared;
538
0
  }
539
0
  t2 = x1 * dy - dx * y1 - x0 * y2 + x2 * y0;
540
  // actual distance = t2 / sqrt(t1)
541
0
  return t2 * t2 < t1 * maxPointToLineDistanceSquared;
542
0
}
543
544
// Attempt to simplify the path by merging sequences of consecutive
545
// segments in [first] .. [length]-1.
546
0
void SplashXPath::mergeSegments(int first) {
547
0
  GBool horiz, vert;
548
0
  int in, out, prev, i, j;
549
550
0
  in = out = first;
551
0
  while (in < length) {
552
553
    // skip zero-length segments
554
0
    if (segs[in].x0 == segs[in].x1 && segs[in].y0 == segs[in].y1) {
555
0
      ++in;
556
0
      continue;
557
0
    }
558
559
0
    horiz = segs[in].y0 == segs[in].y1;
560
0
    vert = segs[in].x0 == segs[in].x1;
561
562
    // check for a sequence of mergeable segments: in .. i
563
0
    prev = in;
564
0
    for (i = in + 1; i < length; ++i) {
565
566
      // skip zero-length segments
567
0
      if (segs[i].x0 == segs[i].x1 && segs[i].y0 == segs[i].y1) {
568
0
  continue;
569
0
      }
570
571
      // check for a horizontal or vertical segment
572
0
      if ((horiz && segs[in].y0 != segs[in].y1) ||
573
0
    (vert && segs[in].x0 != segs[in].x1)) {
574
0
  break;
575
0
      }
576
577
      // check the angle between segs i-1 and i
578
      // (actually, we compare seg i to the previous non-zero-length
579
      // segment, which may not be i-1)
580
0
      if (!joinAngleIsFlat(segs[prev].x0, segs[prev].y0,
581
0
         segs[i].x0, segs[i].y0,
582
0
         segs[i].x1, segs[i].y1)) {
583
0
  break;
584
0
      }
585
586
      // check the distances from the ends of segs in .. i-1 to the
587
      // proposed new segment
588
0
      for (j = in; j < i; ++j) {
589
0
  if (!pointCloseToSegment(segs[in].x0, segs[in].y0,
590
0
         segs[j].x1, segs[j].y1,
591
0
         segs[i].x1, segs[i].y1)) {
592
0
    break;
593
0
  }
594
0
      }
595
0
      if (j < i) {
596
0
  break;
597
0
      }
598
599
0
      prev = i;
600
0
    }
601
602
    // we can merge segs: in .. i-1
603
    // (this may be the single segment: in)
604
0
    segs[out].x0 = segs[in].x0;
605
0
    segs[out].y0 = segs[in].y0;
606
0
    segs[out].x1 = segs[i-1].x1;
607
0
    segs[out].y1 = segs[i-1].y1;
608
0
    in = i;
609
0
    ++out;
610
0
  }
611
612
0
  length = out;
613
0
}
614
615
222k
void SplashXPath::finishSegments() {
616
222k
  SplashXPathSeg *seg;
617
222k
  SplashCoord xMinFP, xMaxFP, yMinFP, yMaxFP, t;
618
222k
  int i;
619
620
222k
  xMinFP = yMinFP = xMaxFP = yMaxFP = 0;
621
622
86.9M
  for (i = 0; i < length; ++i) {
623
86.6M
    seg = &segs[i];
624
625
    //--- compute the slopes
626
86.6M
    if (seg->y0 <= seg->y1) {
627
46.2M
      seg->count = 1;
628
46.2M
    } else {
629
40.4M
      t = seg->x0;  seg->x0 = seg->x1;  seg->x1 = t;
630
40.4M
      t = seg->y0;  seg->y0 = seg->y1;  seg->y1 = t;
631
40.4M
      seg->count = -1;
632
40.4M
    }
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
86.6M
    if (splashAbs(seg->y1 - seg->y0) < 1e-200 ||
644
72.5M
  splashAbs(seg->x1 - seg->x0) < 1e-200) {
645
72.5M
      seg->dxdy = 0;
646
72.5M
      seg->dydx = 0;
647
72.5M
    } else {
648
14.0M
      seg->dxdy = (seg->x1 - seg->x0) / (seg->y1 - seg->y0);
649
14.0M
      if (seg->dxdy == 0) {
650
0
  seg->dydx = 0;
651
14.0M
      } else {
652
14.0M
  seg->dydx = 1 / seg->dxdy;
653
14.0M
      }
654
14.0M
    }
655
86.6M
#endif
656
657
    //--- update bbox
658
86.6M
    if (i == 0) {
659
207k
      if (seg->x0 <= seg->x1) {
660
171k
  xMinFP = seg->x0;
661
171k
  xMaxFP = seg->x1;
662
171k
      } else {
663
36.9k
  xMinFP = seg->x1;
664
36.9k
  xMaxFP = seg->x0;
665
36.9k
      }
666
207k
      yMinFP = seg->y0;
667
207k
      yMaxFP = seg->y1;
668
86.4M
    } else {
669
86.4M
      if (seg->x0 < xMinFP) {
670
394k
  xMinFP = seg->x0;
671
86.0M
      } else if (seg->x0 > xMaxFP) {
672
535k
  xMaxFP = seg->x0;
673
535k
      }
674
86.4M
      if (seg->x1 < xMinFP) {
675
325k
  xMinFP = seg->x1;
676
86.1M
      } else if (seg->x1 > xMaxFP) {
677
1.17M
  xMaxFP = seg->x1;
678
1.17M
      }
679
86.4M
      if (seg->y0 < yMinFP) {
680
2.19M
  yMinFP = seg->y0;
681
2.19M
      }
682
86.4M
      if (seg->y1 > yMaxFP) {
683
2.12M
  yMaxFP = seg->y1;
684
2.12M
      }
685
86.4M
    }
686
86.6M
  }
687
688
222k
  xMin = splashFloor(xMinFP);
689
222k
  yMin = splashFloor(yMinFP);
690
222k
  xMax = splashFloor(xMaxFP);
691
222k
  yMax = splashFloor(yMaxFP);
692
222k
}