Coverage Report

Created: 2026-09-03 07: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
71.3M
           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
71.3M
  *xo = xi * matrix[0] + yi * matrix[2] + matrix[4];
53
71.3M
  *yo = xi * matrix[1] + yi * matrix[3] + matrix[5];
54
71.3M
}
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
344M
#define maxCoord 100000000.0
64
65
71.3M
void SplashXPath::clampCoords(SplashCoord *x, SplashCoord *y) {
66
71.3M
#if !USE_FIXEDPOINT
67
71.3M
  if (*x > maxCoord) {
68
30.8M
    *x = maxCoord;
69
40.4M
  } else if (*x < -maxCoord) {
70
32.2M
    *x = -maxCoord;
71
32.2M
  }
72
71.3M
  if (*y > maxCoord) {
73
29.7M
    *y = maxCoord;
74
41.5M
  } else if (*y < -maxCoord) {
75
26.7M
    *y = -maxCoord;
76
26.7M
  }
77
71.3M
#endif
78
71.3M
}
79
80
SplashXPath::SplashXPath(SplashPath *path, SplashCoord *matrix,
81
       SplashCoord flatness, GBool closeSubpaths,
82
       GBool simplify,
83
       SplashStrokeAdjustMode strokeAdjMode,
84
276k
       SplashClip *clip) {
85
276k
  SplashXPathPoint *pts;
86
276k
  SplashCoord x0, y0, x1, y1, x2, y2, x3, y3, xsp, ysp, t;
87
276k
  int nSubpaths, curSubpath, firstSegInSubpath, i;
88
276k
  GBool adjusted;
89
90
  //--- transform the points
91
276k
  pts = (SplashXPathPoint *)gmallocn(path->length, sizeof(SplashXPathPoint));
92
71.5M
  for (i = 0; i < path->length; ++i) {
93
71.3M
    transform(matrix, path->pts[i].x, path->pts[i].y, &pts[i].x, &pts[i].y);
94
71.3M
    clampCoords(&pts[i].x, &pts[i].y);
95
71.3M
  }
96
97
  //--- do stroke adjustment
98
276k
  if (path->hints) {
99
32.0k
    adjusted = strokeAdjust(pts, path->hints, path->hintsLength,
100
32.0k
          strokeAdjMode, clip);
101
244k
  } else {
102
244k
    adjusted = gFalse;
103
244k
  }
104
105
  //--- construct the segments
106
107
276k
  segs = NULL;
108
276k
  length = size = 0;
109
110
276k
  x0 = y0 = xsp = ysp = 0; // make gcc happy
111
276k
  nSubpaths = 0;
112
276k
  curSubpath = 0;
113
276k
  firstSegInSubpath = 0;
114
276k
  i = 0;
115
69.4M
  while (i < path->length) {
116
117
    // first point in subpath - skip it
118
69.1M
    if (path->flags[i] & splashPathFirst) {
119
14.2M
      x0 = pts[i].x;
120
14.2M
      y0 = pts[i].y;
121
14.2M
      xsp = x0;
122
14.2M
      ysp = y0;
123
14.2M
      curSubpath = i;
124
14.2M
      ++i;
125
126
54.9M
    } else {
127
128
      // curve segment
129
54.9M
      if (path->flags[i] & splashPathCurve) {
130
1.08M
  x1 = pts[i].x;
131
1.08M
  y1 = pts[i].y;
132
1.08M
  x2 = pts[i+1].x;
133
1.08M
  y2 = pts[i+1].y;
134
1.08M
  x3 = pts[i+2].x;
135
1.08M
  y3 = pts[i+2].y;
136
1.08M
  addCurve(x0, y0, x1, y1, x2, y2, x3, y3,
137
1.08M
     flatness,
138
1.08M
     (path->flags[i-1] & splashPathFirst),
139
1.08M
     (path->flags[i+2] & splashPathLast),
140
1.08M
     !closeSubpaths &&
141
0
       (path->flags[i-1] & splashPathFirst) &&
142
0
       !(path->flags[i-1] & splashPathClosed),
143
1.08M
     !closeSubpaths &&
144
0
       (path->flags[i+2] & splashPathLast) &&
145
0
       !(path->flags[i+2] & splashPathClosed));
146
1.08M
  x0 = x3;
147
1.08M
  y0 = y3;
148
1.08M
  i += 3;
149
150
      // line segment
151
53.8M
      } else {
152
53.8M
  x1 = pts[i].x;
153
53.8M
  y1 = pts[i].y;
154
53.8M
  addSegment(x0, y0, x1, y1);
155
53.8M
  x0 = x1;
156
53.8M
  y0 = y1;
157
53.8M
  ++i;
158
53.8M
      }
159
160
      // end a subpath
161
54.9M
      if (path->flags[i-1] & splashPathLast) {
162
14.2M
  ++nSubpaths;
163
14.2M
  if (closeSubpaths &&
164
13.5M
      (pts[i-1].x != pts[curSubpath].x ||
165
13.5M
       pts[i-1].y != pts[curSubpath].y)) {
166
1.74M
    addSegment(x0, y0, xsp, ysp);
167
1.74M
  }
168
14.2M
  if (simplify && !adjusted) {
169
0
    mergeSegments(firstSegInSubpath);
170
0
  }
171
14.2M
  firstSegInSubpath = length;
172
14.2M
      }
173
54.9M
    }
174
69.1M
  }
175
176
276k
  gfree(pts);
177
178
276k
  finishSegments();
179
180
  //--- check for a rectangle
181
276k
  isRect = gFalse;
182
276k
  rectX0 = rectY0 = rectX1 = rectY1 = 0;
183
276k
  if (nSubpaths == 1 && length == 4) {
184
170k
#if HAVE_STD_SORT
185
170k
    std::sort(segs, segs + length, SplashXPathSeg::cmpY);
186
#else
187
    qsort(segs, length, sizeof(SplashXPathSeg), &SplashXPathSeg::cmpY);
188
#endif
189
170k
    if (segs[0].y0 == segs[0].y1 &&
190
75.9k
  segs[1].x0 == segs[1].x1 &&
191
73.9k
  segs[2].x0 == segs[2].x1 &&
192
54.3k
  segs[3].y0 == segs[3].y1) {
193
49.9k
      isRect = gTrue;
194
49.9k
      rectX0 = segs[1].x0;
195
49.9k
      rectX1 = segs[2].x0;
196
49.9k
      rectY0 = segs[0].y0;
197
49.9k
      rectY1 = segs[3].y0;
198
120k
    } else if (segs[0].x0 == segs[0].x1 &&
199
99.3k
         segs[1].y0 == segs[1].y1 &&
200
90.5k
         segs[2].x0 == segs[2].x1 &&
201
87.2k
         segs[3].y0 == segs[3].y1) {
202
86.6k
      isRect = gTrue;
203
86.6k
      rectX0 = segs[0].x0;
204
86.6k
      rectX1 = segs[2].x0;
205
86.6k
      rectY0 = segs[1].y0;
206
86.6k
      rectY1 = segs[3].y0;
207
86.6k
    } else if (segs[0].x0 == segs[0].x1 &&
208
12.6k
         segs[1].x0 == segs[1].x1 &&
209
9.28k
         segs[2].y0 == segs[2].y1 &&
210
3.13k
         segs[3].y0 == segs[3].y1) {
211
2.89k
      isRect = gTrue;
212
2.89k
      rectX0 = segs[0].x0;
213
2.89k
      rectX1 = segs[1].x0;
214
2.89k
      rectY0 = segs[2].y0;
215
2.89k
      rectY1 = segs[3].y0;
216
2.89k
    }
217
170k
    if (isRect) {
218
139k
      if (rectX0 > rectX1) {
219
51.7k
  t = rectX0;  rectX0 = rectX1;  rectX1 = t;
220
51.7k
      }
221
139k
      if (rectY0 > rectY1) {
222
0
  t = rectY0;  rectY0 = rectY1;  rectY1 = t;
223
0
      }
224
139k
    }
225
170k
  }
226
276k
}
227
228
GBool SplashXPath::strokeAdjust(SplashXPathPoint *pts,
229
        SplashPathHint *hints, int nHints,
230
        SplashStrokeAdjustMode strokeAdjMode,
231
32.0k
        SplashClip *clip) {
232
32.0k
  SplashXPathAdjust *adjusts, *adjust;
233
32.0k
  SplashPathHint *hint;
234
32.0k
  SplashCoord x0, y0, x1, y1, x2, y2, x3, y3;
235
32.0k
  SplashCoord adj0, adj1, w, d;
236
32.0k
  int xi0, xi1;
237
32.0k
  int i, j;
238
32.0k
  GBool adjusted;
239
240
32.0k
  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
32.0k
  GBool clipTweak = clip && clip->getIsSimple();
249
32.0k
  SplashCoord cx0 = 0, cx1 = 0, cy0 = 0, cy1 = 0;
250
32.0k
  int cxi0 = 0, cxi1 = 0, cyi0 = 0, cyi1 = 0;
251
32.0k
  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
32.0k
  adjusts = (SplashXPathAdjust *)gmallocn(nHints, sizeof(SplashXPathAdjust));
264
13.3M
  for (i = 0; i < nHints; ++i) {
265
13.3M
    hint = &hints[i];
266
13.3M
    x0 = pts[hint->ctrl0    ].x;    y0 = pts[hint->ctrl0    ].y;
267
13.3M
    x1 = pts[hint->ctrl0 + 1].x;    y1 = pts[hint->ctrl0 + 1].y;
268
13.3M
    x2 = pts[hint->ctrl1    ].x;    y2 = pts[hint->ctrl1    ].y;
269
13.3M
    x3 = pts[hint->ctrl1 + 1].x;    y3 = pts[hint->ctrl1 + 1].y;
270
13.3M
    w = -1;
271
13.3M
    if (splashAbs(x0 - x1) < 0.01 && splashAbs(x2 - x3) < 0.01) {
272
13.0M
      adjusts[i].vert = gTrue;
273
13.0M
      adj0 = x0;
274
13.0M
      adj1 = x2;
275
13.0M
      if (hint->projectingCap) {
276
2.39k
  w = splashAbs(y1 - y0);
277
2.39k
      }
278
13.0M
    } else if (splashAbs(y0 - y1) < 0.01 && splashAbs(y2 - y3) < 0.01) {
279
214k
      adjusts[i].vert = gFalse;
280
214k
      adj0 = y0;
281
214k
      adj1 = y2;
282
214k
      if (hint->projectingCap) {
283
711
  w = splashAbs(x1 - x0);
284
711
      }
285
214k
    } else {
286
7.72k
      goto done;
287
7.72k
    }
288
13.3M
    if (adj0 > adj1) {
289
576k
      x0 = adj0;
290
576k
      adj0 = adj1;
291
576k
      adj1 = x0;
292
576k
    }
293
13.3M
    d = adj1 - adj0;
294
13.3M
    if (d > 0.04) {
295
683k
      d = 0.01;
296
12.6M
    } else {
297
12.6M
      d *= 0.25;
298
12.6M
    }
299
13.3M
    adjusts[i].x0a = adj0 - d;
300
13.3M
    adjusts[i].x0b = adj0 + d;
301
13.3M
    adjusts[i].xma = (SplashCoord)0.5 * (adj0 + adj1) - d;
302
13.3M
    adjusts[i].xmb = (SplashCoord)0.5 * (adj0 + adj1) + d;
303
13.3M
    adjusts[i].x1a = adj1 - d;
304
13.3M
    adjusts[i].x1b = adj1 + d;
305
13.3M
    splashStrokeAdjust(adj0, adj1, &xi0, &xi1, strokeAdjMode, w);
306
13.3M
    if (clipTweak) {
307
8.07M
      SplashCoord c0, c1;
308
8.07M
      int ci0, ci1;
309
8.07M
      if (adjusts[i].vert) {
310
7.87M
  c0 = cx0;
311
7.87M
  c1 = cx1;
312
7.87M
  ci0 = cxi0;
313
7.87M
  ci1 = cxi1;
314
7.87M
      } else {
315
203k
  c0 = cy0;
316
203k
  c1 = cy1;
317
203k
  ci0 = cyi0;
318
203k
  ci1 = cyi1;
319
203k
      }
320
8.07M
      if (adj0 < c0 && c0 < adj1 && adj1 < c1 &&
321
11.7k
    adj1 - c0 > (adj1 - adj0) * 0.2 &&
322
11.1k
    xi1 <= ci0) {
323
2.57k
  xi0 = ci0;
324
2.57k
  xi1 = xi0 + 1;
325
8.07M
      } else if (c0 < adj0 && adj0 < c1 && c1 < adj1 &&
326
1.50k
     c1 - adj0 > (adj1 - adj0) * 0.2 &&
327
963
     ci1 < xi0) {
328
416
  xi0 = ci1;
329
416
  xi1 = ci1 + 1;
330
416
      }
331
8.07M
    }
332
13.3M
    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.3M
    adjusts[i].x1 = (SplashCoord)xi1 - 0.001;
337
13.3M
    adjusts[i].xm = (SplashCoord)0.5 * (adjusts[i].x0 + adjusts[i].x1);
338
13.3M
    adjusts[i].firstPt = hint->firstPt;
339
13.3M
    adjusts[i].lastPt = hint->lastPt;
340
13.3M
  }
341
342
  // perform stroke adjustment
343
12.0M
  for (i = 0, adjust = adjusts; i < nHints; ++i, ++adjust) {
344
88.1M
    for (j = adjust->firstPt; j <= adjust->lastPt; ++j) {
345
76.0M
      if (adjust->vert) {
346
74.8M
  x0 = pts[j].x;
347
74.8M
  if (x0 > adjust->x0a && x0 < adjust->x0b) {
348
2.73M
    pts[j].x = adjust->x0;
349
72.1M
  } else if (x0 > adjust->xma && x0 < adjust->xmb) {
350
57.0k
    pts[j].x = adjust->xm;
351
72.0M
  } else if (x0 > adjust->x1a && x0 < adjust->x1b) {
352
1.36M
    pts[j].x = adjust->x1;
353
1.36M
  }
354
74.8M
      } else {
355
1.21M
  y0 = pts[j].y;
356
1.21M
  if (y0 > adjust->x0a && y0 < adjust->x0b) {
357
147k
    pts[j].y = adjust->x0;
358
1.07M
  } else if (y0 > adjust->xma && y0 < adjust->xmb) {
359
90.3k
    pts[j].y = adjust->xm;
360
980k
  } else if (y0 > adjust->x1a && y0 < adjust->x1b) {
361
175k
    pts[j].y = adjust->x1;
362
175k
  }
363
1.21M
      }
364
76.0M
    }
365
12.0M
  }
366
24.3k
  adjusted = gTrue;
367
368
32.0k
 done:
369
32.0k
  gfree(adjusts);
370
32.0k
  return adjusted;
371
24.3k
}
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
276k
SplashXPath::~SplashXPath() {
385
276k
  gfree(segs);
386
276k
}
387
388
// Add space for <nSegs> more segments
389
97.7M
void SplashXPath::grow(int nSegs) {
390
97.7M
  if (length + nSegs > size) {
391
373k
    if (size == 0) {
392
261k
      size = 32;
393
261k
    }
394
485k
    while (size < length + nSegs) {
395
111k
      size *= 2;
396
111k
    }
397
373k
    segs = (SplashXPathSeg *)greallocn(segs, size, sizeof(SplashXPathSeg));
398
373k
  }
399
97.7M
}
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.08M
         GBool first, GBool last, GBool end0, GBool end1) {
407
1.08M
  SplashCoord cx[splashMaxCurveSplits + 1][3];
408
1.08M
  SplashCoord cy[splashMaxCurveSplits + 1][3];
409
1.08M
  int cNext[splashMaxCurveSplits + 1];
410
1.08M
  SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh;
411
1.08M
  SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh;
412
1.08M
  SplashCoord dx, dy, mx, my, d1, d2, flatness2;
413
1.08M
  int p1, p2, p3;
414
415
#if USE_FIXEDPOINT
416
  flatness2 = flatness;
417
#else
418
1.08M
  flatness2 = flatness * flatness;
419
1.08M
#endif
420
421
  // initial segment
422
1.08M
  p1 = 0;
423
1.08M
  p2 = splashMaxCurveSplits;
424
1.08M
  cx[p1][0] = x0;  cy[p1][0] = y0;
425
1.08M
  cx[p1][1] = x1;  cy[p1][1] = y1;
426
1.08M
  cx[p1][2] = x2;  cy[p1][2] = y2;
427
1.08M
  cx[p2][0] = x3;  cy[p2][0] = y3;
428
1.08M
  cNext[p1] = p2;
429
430
84.3M
  while (p1 < splashMaxCurveSplits) {
431
432
    // get the next segment
433
83.2M
    xl0 = cx[p1][0];  yl0 = cy[p1][0];
434
83.2M
    xx1 = cx[p1][1];  yy1 = cy[p1][1];
435
83.2M
    xx2 = cx[p1][2];  yy2 = cy[p1][2];
436
83.2M
    p2 = cNext[p1];
437
83.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
83.2M
    mx = (xl0 + xr3) * 0.5;
444
83.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
83.2M
    dx = xx1 - mx;
450
83.2M
    dy = yy1 - my;
451
83.2M
    d1 = dx*dx + dy*dy;
452
83.2M
    dx = xx2 - mx;
453
83.2M
    dy = yy2 - my;
454
83.2M
    d2 = dx*dx + dy*dy;
455
83.2M
#endif
456
457
    // if the curve is flat enough, or no more subdivisions are
458
    // allowed, add the straight line segment
459
83.2M
    if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) {
460
42.1M
      addSegment(xl0, yl0, xr3, yr3);
461
42.1M
      p1 = p2;
462
463
    // otherwise, subdivide the curve
464
42.1M
    } else {
465
41.0M
      xl1 = (xl0 + xx1) * 0.5;
466
41.0M
      yl1 = (yl0 + yy1) * 0.5;
467
41.0M
      xh = (xx1 + xx2) * 0.5;
468
41.0M
      yh = (yy1 + yy2) * 0.5;
469
41.0M
      xl2 = (xl1 + xh) * 0.5;
470
41.0M
      yl2 = (yl1 + yh) * 0.5;
471
41.0M
      xr2 = (xx2 + xr3) * 0.5;
472
41.0M
      yr2 = (yy2 + yr3) * 0.5;
473
41.0M
      xr1 = (xh + xr2) * 0.5;
474
41.0M
      yr1 = (yh + yr2) * 0.5;
475
41.0M
      xr0 = (xl2 + xr1) * 0.5;
476
41.0M
      yr0 = (yl2 + yr1) * 0.5;
477
      // add the new subdivision points
478
41.0M
      p3 = (p1 + p2) / 2;
479
41.0M
      cx[p1][1] = xl1;  cy[p1][1] = yl1;
480
41.0M
      cx[p1][2] = xl2;  cy[p1][2] = yl2;
481
41.0M
      cNext[p1] = p3;
482
41.0M
      cx[p3][0] = xr0;  cy[p3][0] = yr0;
483
41.0M
      cx[p3][1] = xr1;  cy[p3][1] = yr1;
484
41.0M
      cx[p3][2] = xr2;  cy[p3][2] = yr2;
485
41.0M
      cNext[p3] = p2;
486
41.0M
    }
487
83.2M
  }
488
1.08M
}
489
490
void SplashXPath::addSegment(SplashCoord x0, SplashCoord y0,
491
97.7M
           SplashCoord x1, SplashCoord y1) {
492
97.7M
  grow(1);
493
97.7M
  segs[length].x0 = x0;
494
97.7M
  segs[length].y0 = y0;
495
97.7M
  segs[length].x1 = x1;
496
97.7M
  segs[length].y1 = y1;
497
97.7M
  ++length;
498
97.7M
}
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
276k
void SplashXPath::finishSegments() {
616
276k
  SplashXPathSeg *seg;
617
276k
  SplashCoord xMinFP, xMaxFP, yMinFP, yMaxFP, t;
618
276k
  int i;
619
620
276k
  xMinFP = yMinFP = xMaxFP = yMaxFP = 0;
621
622
98.0M
  for (i = 0; i < length; ++i) {
623
97.7M
    seg = &segs[i];
624
625
    //--- compute the slopes
626
97.7M
    if (seg->y0 <= seg->y1) {
627
54.6M
      seg->count = 1;
628
54.6M
    } else {
629
43.1M
      t = seg->x0;  seg->x0 = seg->x1;  seg->x1 = t;
630
43.1M
      t = seg->y0;  seg->y0 = seg->y1;  seg->y1 = t;
631
43.1M
      seg->count = -1;
632
43.1M
    }
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
97.7M
    if (splashAbs(seg->y1 - seg->y0) < 1e-200 ||
644
80.2M
  splashAbs(seg->x1 - seg->x0) < 1e-200) {
645
80.2M
      seg->dxdy = 0;
646
80.2M
      seg->dydx = 0;
647
80.2M
    } else {
648
17.5M
      seg->dxdy = (seg->x1 - seg->x0) / (seg->y1 - seg->y0);
649
17.5M
      if (seg->dxdy == 0) {
650
0
  seg->dydx = 0;
651
17.5M
      } else {
652
17.5M
  seg->dydx = 1 / seg->dxdy;
653
17.5M
      }
654
17.5M
    }
655
97.7M
#endif
656
657
    //--- update bbox
658
97.7M
    if (i == 0) {
659
261k
      if (seg->x0 <= seg->x1) {
660
218k
  xMinFP = seg->x0;
661
218k
  xMaxFP = seg->x1;
662
218k
      } else {
663
42.6k
  xMinFP = seg->x1;
664
42.6k
  xMaxFP = seg->x0;
665
42.6k
      }
666
261k
      yMinFP = seg->y0;
667
261k
      yMaxFP = seg->y1;
668
97.4M
    } else {
669
97.4M
      if (seg->x0 < xMinFP) {
670
382k
  xMinFP = seg->x0;
671
97.1M
      } else if (seg->x0 > xMaxFP) {
672
635k
  xMaxFP = seg->x0;
673
635k
      }
674
97.4M
      if (seg->x1 < xMinFP) {
675
940k
  xMinFP = seg->x1;
676
96.5M
      } else if (seg->x1 > xMaxFP) {
677
1.61M
  xMaxFP = seg->x1;
678
1.61M
      }
679
97.4M
      if (seg->y0 < yMinFP) {
680
2.58M
  yMinFP = seg->y0;
681
2.58M
      }
682
97.4M
      if (seg->y1 > yMaxFP) {
683
2.67M
  yMaxFP = seg->y1;
684
2.67M
      }
685
97.4M
    }
686
97.7M
  }
687
688
276k
  xMin = splashFloor(xMinFP);
689
276k
  yMin = splashFloor(yMinFP);
690
276k
  xMax = splashFloor(xMaxFP);
691
276k
  yMax = splashFloor(yMaxFP);
692
276k
}