Coverage Report

Created: 2026-07-12 06:50

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