Coverage Report

Created: 2026-10-31 06:21

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
65.0M
           SplashCoord *xo, SplashCoord *yo) {
49
  //                          [ m[0] m[1] 0 ]
50
  // [xo yo 1] = [xi yi 1] *  [ m[2] m[3] 0 ]
51
  //                          [ m[4] m[5] 1 ]
52
65.0M
  *xo = xi * matrix[0] + yi * matrix[2] + matrix[4];
53
65.0M
  *yo = xi * matrix[1] + yi * matrix[3] + matrix[5];
54
65.0M
}
55
56
//------------------------------------------------------------------------
57
// SplashXPath
58
//------------------------------------------------------------------------
59
60
// SplashXPath segment coords are clipped to +/-maxCoord to avoid
61
// problems.  The xMin/yMin/xMax/yMax fields are 32-bit integers, so
62
// coords need to be < 2^31 / aa{Horiz,Vert}.
63
310M
#define maxCoord 100000000.0
64
65
65.0M
void SplashXPath::clampCoords(SplashCoord *x, SplashCoord *y) {
66
65.0M
#if !USE_FIXEDPOINT
67
65.0M
  if (*x > maxCoord) {
68
24.0M
    *x = maxCoord;
69
41.0M
  } else if (*x < -maxCoord) {
70
28.1M
    *x = -maxCoord;
71
28.1M
  }
72
65.0M
  if (*y > maxCoord) {
73
23.4M
    *y = maxCoord;
74
41.6M
  } else if (*y < -maxCoord) {
75
21.6M
    *y = -maxCoord;
76
21.6M
  }
77
65.0M
#endif
78
65.0M
}
79
80
SplashXPath::SplashXPath(SplashPath *path, SplashCoord *matrix,
81
       SplashCoord flatness, GBool closeSubpaths,
82
       GBool simplify,
83
       SplashStrokeAdjustMode strokeAdjMode,
84
329k
       SplashClip *clip) {
85
329k
  SplashXPathPoint *pts;
86
329k
  SplashCoord x0, y0, x1, y1, x2, y2, x3, y3, xsp, ysp, t;
87
329k
  int nSubpaths, curSubpath, firstSegInSubpath, i;
88
329k
  GBool adjusted;
89
90
  //--- transform the points
91
329k
  pts = (SplashXPathPoint *)gmallocn(path->length, sizeof(SplashXPathPoint));
92
65.4M
  for (i = 0; i < path->length; ++i) {
93
65.0M
    transform(matrix, path->pts[i].x, path->pts[i].y, &pts[i].x, &pts[i].y);
94
65.0M
    clampCoords(&pts[i].x, &pts[i].y);
95
65.0M
  }
96
97
  //--- do stroke adjustment
98
329k
  if (path->hints) {
99
48.3k
    adjusted = strokeAdjust(pts, path->hints, path->hintsLength,
100
48.3k
          strokeAdjMode, clip);
101
281k
  } else {
102
281k
    adjusted = gFalse;
103
281k
  }
104
105
  //--- construct the segments
106
107
329k
  segs = NULL;
108
329k
  length = size = 0;
109
110
329k
  x0 = y0 = xsp = ysp = 0; // make gcc happy
111
329k
  nSubpaths = 0;
112
329k
  curSubpath = 0;
113
329k
  firstSegInSubpath = 0;
114
329k
  i = 0;
115
64.2M
  while (i < path->length) {
116
117
    // first point in subpath - skip it
118
63.9M
    if (path->flags[i] & splashPathFirst) {
119
13.3M
      x0 = pts[i].x;
120
13.3M
      y0 = pts[i].y;
121
13.3M
      xsp = x0;
122
13.3M
      ysp = y0;
123
13.3M
      curSubpath = i;
124
13.3M
      ++i;
125
126
50.5M
    } else {
127
128
      // curve segment
129
50.5M
      if (path->flags[i] & splashPathCurve) {
130
588k
  x1 = pts[i].x;
131
588k
  y1 = pts[i].y;
132
588k
  x2 = pts[i+1].x;
133
588k
  y2 = pts[i+1].y;
134
588k
  x3 = pts[i+2].x;
135
588k
  y3 = pts[i+2].y;
136
588k
  addCurve(x0, y0, x1, y1, x2, y2, x3, y3,
137
588k
     flatness,
138
588k
     (path->flags[i-1] & splashPathFirst),
139
588k
     (path->flags[i+2] & splashPathLast),
140
588k
     !closeSubpaths &&
141
0
       (path->flags[i-1] & splashPathFirst) &&
142
0
       !(path->flags[i-1] & splashPathClosed),
143
588k
     !closeSubpaths &&
144
0
       (path->flags[i+2] & splashPathLast) &&
145
0
       !(path->flags[i+2] & splashPathClosed));
146
588k
  x0 = x3;
147
588k
  y0 = y3;
148
588k
  i += 3;
149
150
      // line segment
151
49.9M
      } else {
152
49.9M
  x1 = pts[i].x;
153
49.9M
  y1 = pts[i].y;
154
49.9M
  addSegment(x0, y0, x1, y1);
155
49.9M
  x0 = x1;
156
49.9M
  y0 = y1;
157
49.9M
  ++i;
158
49.9M
      }
159
160
      // end a subpath
161
50.5M
      if (path->flags[i-1] & splashPathLast) {
162
13.3M
  ++nSubpaths;
163
13.3M
  if (closeSubpaths &&
164
12.5M
      (pts[i-1].x != pts[curSubpath].x ||
165
12.5M
       pts[i-1].y != pts[curSubpath].y)) {
166
2.84M
    addSegment(x0, y0, xsp, ysp);
167
2.84M
  }
168
13.3M
  if (simplify && !adjusted) {
169
0
    mergeSegments(firstSegInSubpath);
170
0
  }
171
13.3M
  firstSegInSubpath = length;
172
13.3M
      }
173
50.5M
    }
174
63.9M
  }
175
176
329k
  gfree(pts);
177
178
329k
  finishSegments();
179
180
  //--- check for a rectangle
181
329k
  isRect = gFalse;
182
329k
  rectX0 = rectY0 = rectX1 = rectY1 = 0;
183
329k
  if (nSubpaths == 1 && length == 4) {
184
224k
#if HAVE_STD_SORT
185
224k
    std::sort(segs, segs + length, SplashXPathSeg::cmpY);
186
#else
187
    qsort(segs, length, sizeof(SplashXPathSeg), &SplashXPathSeg::cmpY);
188
#endif
189
224k
    if (segs[0].y0 == segs[0].y1 &&
190
125k
  segs[1].x0 == segs[1].x1 &&
191
123k
  segs[2].x0 == segs[2].x1 &&
192
83.9k
  segs[3].y0 == segs[3].y1) {
193
81.3k
      isRect = gTrue;
194
81.3k
      rectX0 = segs[1].x0;
195
81.3k
      rectX1 = segs[2].x0;
196
81.3k
      rectY0 = segs[0].y0;
197
81.3k
      rectY1 = segs[3].y0;
198
143k
    } else if (segs[0].x0 == segs[0].x1 &&
199
100k
         segs[1].y0 == segs[1].y1 &&
200
91.0k
         segs[2].x0 == segs[2].x1 &&
201
88.8k
         segs[3].y0 == segs[3].y1) {
202
88.0k
      isRect = gTrue;
203
88.0k
      rectX0 = segs[0].x0;
204
88.0k
      rectX1 = segs[2].x0;
205
88.0k
      rectY0 = segs[1].y0;
206
88.0k
      rectY1 = segs[3].y0;
207
88.0k
    } else if (segs[0].x0 == segs[0].x1 &&
208
12.8k
         segs[1].x0 == segs[1].x1 &&
209
11.8k
         segs[2].y0 == segs[2].y1 &&
210
7.19k
         segs[3].y0 == segs[3].y1) {
211
6.95k
      isRect = gTrue;
212
6.95k
      rectX0 = segs[0].x0;
213
6.95k
      rectX1 = segs[1].x0;
214
6.95k
      rectY0 = segs[2].y0;
215
6.95k
      rectY1 = segs[3].y0;
216
6.95k
    }
217
224k
    if (isRect) {
218
176k
      if (rectX0 > rectX1) {
219
41.3k
  t = rectX0;  rectX0 = rectX1;  rectX1 = t;
220
41.3k
      }
221
176k
      if (rectY0 > rectY1) {
222
0
  t = rectY0;  rectY0 = rectY1;  rectY1 = t;
223
0
      }
224
176k
    }
225
224k
  }
226
329k
}
227
228
GBool SplashXPath::strokeAdjust(SplashXPathPoint *pts,
229
        SplashPathHint *hints, int nHints,
230
        SplashStrokeAdjustMode strokeAdjMode,
231
48.3k
        SplashClip *clip) {
232
48.3k
  SplashXPathAdjust *adjusts, *adjust;
233
48.3k
  SplashPathHint *hint;
234
48.3k
  SplashCoord x0, y0, x1, y1, x2, y2, x3, y3;
235
48.3k
  SplashCoord adj0, adj1, w, d;
236
48.3k
  int xi0, xi1;
237
48.3k
  int i, j;
238
48.3k
  GBool adjusted;
239
240
48.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
48.3k
  GBool clipTweak = clip && clip->getIsSimple();
249
48.3k
  SplashCoord cx0 = 0, cx1 = 0, cy0 = 0, cy1 = 0;
250
48.3k
  int cxi0 = 0, cxi1 = 0, cyi0 = 0, cyi1 = 0;
251
48.3k
  if (clipTweak) {
252
36.1k
    cx0 = clip->getXMin();
253
36.1k
    cx1 = clip->getXMax();
254
36.1k
    cy0 = clip->getYMin();
255
36.1k
    cy1 = clip->getYMax();
256
36.1k
    cxi0 = clip->getXMinI(strokeAdjMode);
257
36.1k
    cxi1 = clip->getXMaxI(strokeAdjMode);
258
36.1k
    cyi0 = clip->getYMinI(strokeAdjMode);
259
36.1k
    cyi1 = clip->getYMaxI(strokeAdjMode);
260
36.1k
  }
261
262
  // set up the stroke adjustment hints
263
48.3k
  adjusts = (SplashXPathAdjust *)gmallocn(nHints, sizeof(SplashXPathAdjust));
264
13.4M
  for (i = 0; i < nHints; ++i) {
265
13.4M
    hint = &hints[i];
266
13.4M
    x0 = pts[hint->ctrl0    ].x;    y0 = pts[hint->ctrl0    ].y;
267
13.4M
    x1 = pts[hint->ctrl0 + 1].x;    y1 = pts[hint->ctrl0 + 1].y;
268
13.4M
    x2 = pts[hint->ctrl1    ].x;    y2 = pts[hint->ctrl1    ].y;
269
13.4M
    x3 = pts[hint->ctrl1 + 1].x;    y3 = pts[hint->ctrl1 + 1].y;
270
13.4M
    w = -1;
271
13.4M
    if (splashAbs(x0 - x1) < 0.01 && splashAbs(x2 - x3) < 0.01) {
272
13.2M
      adjusts[i].vert = gTrue;
273
13.2M
      adj0 = x0;
274
13.2M
      adj1 = x2;
275
13.2M
      if (hint->projectingCap) {
276
4.48k
  w = splashAbs(y1 - y0);
277
4.48k
      }
278
13.2M
    } else if (splashAbs(y0 - y1) < 0.01 && splashAbs(y2 - y3) < 0.01) {
279
159k
      adjusts[i].vert = gFalse;
280
159k
      adj0 = y0;
281
159k
      adj1 = y2;
282
159k
      if (hint->projectingCap) {
283
381
  w = splashAbs(x1 - x0);
284
381
      }
285
159k
    } else {
286
5.41k
      goto done;
287
5.41k
    }
288
13.4M
    if (adj0 > adj1) {
289
293k
      x0 = adj0;
290
293k
      adj0 = adj1;
291
293k
      adj1 = x0;
292
293k
    }
293
13.4M
    d = adj1 - adj0;
294
13.4M
    if (d > 0.04) {
295
463k
      d = 0.01;
296
12.9M
    } else {
297
12.9M
      d *= 0.25;
298
12.9M
    }
299
13.4M
    adjusts[i].x0a = adj0 - d;
300
13.4M
    adjusts[i].x0b = adj0 + d;
301
13.4M
    adjusts[i].xma = (SplashCoord)0.5 * (adj0 + adj1) - d;
302
13.4M
    adjusts[i].xmb = (SplashCoord)0.5 * (adj0 + adj1) + d;
303
13.4M
    adjusts[i].x1a = adj1 - d;
304
13.4M
    adjusts[i].x1b = adj1 + d;
305
13.4M
    splashStrokeAdjust(adj0, adj1, &xi0, &xi1, strokeAdjMode, w);
306
13.4M
    if (clipTweak) {
307
5.48M
      SplashCoord c0, c1;
308
5.48M
      int ci0, ci1;
309
5.48M
      if (adjusts[i].vert) {
310
5.36M
  c0 = cx0;
311
5.36M
  c1 = cx1;
312
5.36M
  ci0 = cxi0;
313
5.36M
  ci1 = cxi1;
314
5.36M
      } else {
315
118k
  c0 = cy0;
316
118k
  c1 = cy1;
317
118k
  ci0 = cyi0;
318
118k
  ci1 = cyi1;
319
118k
      }
320
5.48M
      if (adj0 < c0 && c0 < adj1 && adj1 < c1 &&
321
14.8k
    adj1 - c0 > (adj1 - adj0) * 0.2 &&
322
14.6k
    xi1 <= ci0) {
323
3.51k
  xi0 = ci0;
324
3.51k
  xi1 = xi0 + 1;
325
5.47M
      } else if (c0 < adj0 && adj0 < c1 && c1 < adj1 &&
326
2.46k
     c1 - adj0 > (adj1 - adj0) * 0.2 &&
327
1.75k
     ci1 < xi0) {
328
877
  xi0 = ci1;
329
877
  xi1 = ci1 + 1;
330
877
      }
331
5.48M
    }
332
13.4M
    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.4M
    adjusts[i].x1 = (SplashCoord)xi1 - 0.001;
337
13.4M
    adjusts[i].xm = (SplashCoord)0.5 * (adjusts[i].x0 + adjusts[i].x1);
338
13.4M
    adjusts[i].firstPt = hint->firstPt;
339
13.4M
    adjusts[i].lastPt = hint->lastPt;
340
13.4M
  }
341
342
  // perform stroke adjustment
343
11.2M
  for (i = 0, adjust = adjusts; i < nHints; ++i, ++adjust) {
344
81.2M
    for (j = adjust->firstPt; j <= adjust->lastPt; ++j) {
345
70.0M
      if (adjust->vert) {
346
69.2M
  x0 = pts[j].x;
347
69.2M
  if (x0 > adjust->x0a && x0 < adjust->x0b) {
348
1.21M
    pts[j].x = adjust->x0;
349
68.0M
  } else if (x0 > adjust->xma && x0 < adjust->xmb) {
350
62.6k
    pts[j].x = adjust->xm;
351
68.0M
  } else if (x0 > adjust->x1a && x0 < adjust->x1b) {
352
1.02M
    pts[j].x = adjust->x1;
353
1.02M
  }
354
69.2M
      } else {
355
744k
  y0 = pts[j].y;
356
744k
  if (y0 > adjust->x0a && y0 < adjust->x0b) {
357
41.4k
    pts[j].y = adjust->x0;
358
702k
  } else if (y0 > adjust->xma && y0 < adjust->xmb) {
359
1.47k
    pts[j].y = adjust->xm;
360
701k
  } else if (y0 > adjust->x1a && y0 < adjust->x1b) {
361
39.4k
    pts[j].y = adjust->x1;
362
39.4k
  }
363
744k
      }
364
70.0M
    }
365
11.1M
  }
366
42.9k
  adjusted = gTrue;
367
368
48.3k
 done:
369
48.3k
  gfree(adjusts);
370
48.3k
  return adjusted;
371
42.9k
}
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
329k
SplashXPath::~SplashXPath() {
385
329k
  gfree(segs);
386
329k
}
387
388
// Add space for <nSegs> more segments
389
98.3M
void SplashXPath::grow(int nSegs) {
390
98.3M
  if (length + nSegs > size) {
391
461k
    if (size == 0) {
392
316k
      size = 32;
393
316k
    }
394
605k
    while (size < length + nSegs) {
395
144k
      size *= 2;
396
144k
    }
397
461k
    segs = (SplashXPathSeg *)greallocn(segs, size, sizeof(SplashXPathSeg));
398
461k
  }
399
98.3M
}
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
588k
         GBool first, GBool last, GBool end0, GBool end1) {
407
588k
  SplashCoord cx[splashMaxCurveSplits + 1][3];
408
588k
  SplashCoord cy[splashMaxCurveSplits + 1][3];
409
588k
  int cNext[splashMaxCurveSplits + 1];
410
588k
  SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh;
411
588k
  SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh;
412
588k
  SplashCoord dx, dy, mx, my, d1, d2, flatness2;
413
588k
  int p1, p2, p3;
414
415
#if USE_FIXEDPOINT
416
  flatness2 = flatness;
417
#else
418
588k
  flatness2 = flatness * flatness;
419
588k
#endif
420
421
  // initial segment
422
588k
  p1 = 0;
423
588k
  p2 = splashMaxCurveSplits;
424
588k
  cx[p1][0] = x0;  cy[p1][0] = y0;
425
588k
  cx[p1][1] = x1;  cy[p1][1] = y1;
426
588k
  cx[p1][2] = x2;  cy[p1][2] = y2;
427
588k
  cx[p2][0] = x3;  cy[p2][0] = y3;
428
588k
  cNext[p1] = p2;
429
430
90.9M
  while (p1 < splashMaxCurveSplits) {
431
432
    // get the next segment
433
90.3M
    xl0 = cx[p1][0];  yl0 = cy[p1][0];
434
90.3M
    xx1 = cx[p1][1];  yy1 = cy[p1][1];
435
90.3M
    xx2 = cx[p1][2];  yy2 = cy[p1][2];
436
90.3M
    p2 = cNext[p1];
437
90.3M
    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
90.3M
    mx = (xl0 + xr3) * 0.5;
444
90.3M
    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
90.3M
    dx = xx1 - mx;
450
90.3M
    dy = yy1 - my;
451
90.3M
    d1 = dx*dx + dy*dy;
452
90.3M
    dx = xx2 - mx;
453
90.3M
    dy = yy2 - my;
454
90.3M
    d2 = dx*dx + dy*dy;
455
90.3M
#endif
456
457
    // if the curve is flat enough, or no more subdivisions are
458
    // allowed, add the straight line segment
459
90.3M
    if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) {
460
45.4M
      addSegment(xl0, yl0, xr3, yr3);
461
45.4M
      p1 = p2;
462
463
    // otherwise, subdivide the curve
464
45.4M
    } else {
465
44.8M
      xl1 = (xl0 + xx1) * 0.5;
466
44.8M
      yl1 = (yl0 + yy1) * 0.5;
467
44.8M
      xh = (xx1 + xx2) * 0.5;
468
44.8M
      yh = (yy1 + yy2) * 0.5;
469
44.8M
      xl2 = (xl1 + xh) * 0.5;
470
44.8M
      yl2 = (yl1 + yh) * 0.5;
471
44.8M
      xr2 = (xx2 + xr3) * 0.5;
472
44.8M
      yr2 = (yy2 + yr3) * 0.5;
473
44.8M
      xr1 = (xh + xr2) * 0.5;
474
44.8M
      yr1 = (yh + yr2) * 0.5;
475
44.8M
      xr0 = (xl2 + xr1) * 0.5;
476
44.8M
      yr0 = (yl2 + yr1) * 0.5;
477
      // add the new subdivision points
478
44.8M
      p3 = (p1 + p2) / 2;
479
44.8M
      cx[p1][1] = xl1;  cy[p1][1] = yl1;
480
44.8M
      cx[p1][2] = xl2;  cy[p1][2] = yl2;
481
44.8M
      cNext[p1] = p3;
482
44.8M
      cx[p3][0] = xr0;  cy[p3][0] = yr0;
483
44.8M
      cx[p3][1] = xr1;  cy[p3][1] = yr1;
484
44.8M
      cx[p3][2] = xr2;  cy[p3][2] = yr2;
485
44.8M
      cNext[p3] = p2;
486
44.8M
    }
487
90.3M
  }
488
588k
}
489
490
void SplashXPath::addSegment(SplashCoord x0, SplashCoord y0,
491
98.3M
           SplashCoord x1, SplashCoord y1) {
492
98.3M
  grow(1);
493
98.3M
  segs[length].x0 = x0;
494
98.3M
  segs[length].y0 = y0;
495
98.3M
  segs[length].x1 = x1;
496
98.3M
  segs[length].y1 = y1;
497
98.3M
  ++length;
498
98.3M
}
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
329k
void SplashXPath::finishSegments() {
616
329k
  SplashXPathSeg *seg;
617
329k
  SplashCoord xMinFP, xMaxFP, yMinFP, yMaxFP, t;
618
329k
  int i;
619
620
329k
  xMinFP = yMinFP = xMaxFP = yMaxFP = 0;
621
622
98.6M
  for (i = 0; i < length; ++i) {
623
98.3M
    seg = &segs[i];
624
625
    //--- compute the slopes
626
98.3M
    if (seg->y0 <= seg->y1) {
627
52.4M
      seg->count = 1;
628
52.4M
    } else {
629
45.8M
      t = seg->x0;  seg->x0 = seg->x1;  seg->x1 = t;
630
45.8M
      t = seg->y0;  seg->y0 = seg->y1;  seg->y1 = t;
631
45.8M
      seg->count = -1;
632
45.8M
    }
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.3M
    if (splashAbs(seg->y1 - seg->y0) < 1e-200 ||
644
81.7M
  splashAbs(seg->x1 - seg->x0) < 1e-200) {
645
81.7M
      seg->dxdy = 0;
646
81.7M
      seg->dydx = 0;
647
81.7M
    } else {
648
16.5M
      seg->dxdy = (seg->x1 - seg->x0) / (seg->y1 - seg->y0);
649
16.5M
      if (seg->dxdy == 0) {
650
0
  seg->dydx = 0;
651
16.5M
      } else {
652
16.5M
  seg->dydx = 1 / seg->dxdy;
653
16.5M
      }
654
16.5M
    }
655
98.3M
#endif
656
657
    //--- update bbox
658
98.3M
    if (i == 0) {
659
316k
      if (seg->x0 <= seg->x1) {
660
234k
  xMinFP = seg->x0;
661
234k
  xMaxFP = seg->x1;
662
234k
      } else {
663
82.0k
  xMinFP = seg->x1;
664
82.0k
  xMaxFP = seg->x0;
665
82.0k
      }
666
316k
      yMinFP = seg->y0;
667
316k
      yMaxFP = seg->y1;
668
97.9M
    } else {
669
97.9M
      if (seg->x0 < xMinFP) {
670
269k
  xMinFP = seg->x0;
671
97.7M
      } else if (seg->x0 > xMaxFP) {
672
2.09M
  xMaxFP = seg->x0;
673
2.09M
      }
674
97.9M
      if (seg->x1 < xMinFP) {
675
749k
  xMinFP = seg->x1;
676
97.2M
      } else if (seg->x1 > xMaxFP) {
677
1.00M
  xMaxFP = seg->x1;
678
1.00M
      }
679
97.9M
      if (seg->y0 < yMinFP) {
680
7.07M
  yMinFP = seg->y0;
681
7.07M
      }
682
97.9M
      if (seg->y1 > yMaxFP) {
683
4.26M
  yMaxFP = seg->y1;
684
4.26M
      }
685
97.9M
    }
686
98.3M
  }
687
688
329k
  xMin = splashFloor(xMinFP);
689
329k
  yMin = splashFloor(yMinFP);
690
329k
  xMax = splashFloor(xMaxFP);
691
329k
  yMax = splashFloor(yMaxFP);
692
329k
}