Coverage Report

Created: 2025-07-11 07:47

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