Coverage Report

Created: 2026-08-22 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/xpdf-4.06/splash/SplashClip.cc
Line
Count
Source
1
//========================================================================
2
//
3
// SplashClip.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
#include "gmem.h"
14
#include "gmempp.h"
15
#include "SplashErrorCodes.h"
16
#include "SplashPath.h"
17
#include "SplashXPath.h"
18
#include "SplashXPathScanner.h"
19
#include "SplashClip.h"
20
21
//------------------------------------------------------------------------
22
23
// Compute x * y / 255, where x and y are in [0, 255].
24
60.9k
static inline Guchar mul255(Guchar x, Guchar y) {
25
60.9k
  int z;
26
27
60.9k
  z = (int)x * (int)y;
28
60.9k
  return (Guchar)((z + (z >> 8) + 0x80) >> 8);
29
60.9k
}
30
31
//------------------------------------------------------------------------
32
// SplashClip
33
//------------------------------------------------------------------------
34
35
SplashClip::SplashClip(int hardXMinA, int hardYMinA,
36
194k
           int hardXMaxA, int hardYMaxA) {
37
194k
  int w;
38
39
194k
  hardXMin = hardXMinA;
40
194k
  hardYMin = hardYMinA;
41
194k
  hardXMax = hardXMaxA;
42
194k
  hardYMax = hardYMaxA;
43
194k
  xMin = hardXMin;
44
194k
  yMin = hardYMin;
45
194k
  xMax = hardXMax;
46
194k
  yMax = hardYMax;
47
194k
  intBoundsValid = gFalse;
48
194k
  paths = NULL;
49
194k
  eo = NULL;
50
194k
  scanners = NULL;
51
194k
  length = size = 0;
52
194k
  isSimple = gTrue;
53
194k
  prev = NULL;
54
194k
  if ((w = hardXMax + 1) <= 0) {
55
0
    w = 1;
56
0
  }
57
194k
  buf = (Guchar *)gmalloc(w);
58
194k
}
59
60
128k
SplashClip::SplashClip(SplashClip *clip) {
61
128k
  int w;
62
63
128k
  hardXMin = clip->hardXMin;
64
128k
  hardYMin = clip->hardYMin;
65
128k
  hardXMax = clip->hardXMax;
66
128k
  hardYMax = clip->hardYMax;
67
128k
  xMin = clip->xMin;
68
128k
  yMin = clip->yMin;
69
128k
  xMax = clip->xMax;
70
128k
  yMax = clip->yMax;
71
128k
  xMinI = clip->xMinI;
72
128k
  yMinI = clip->yMinI;
73
128k
  xMaxI = clip->xMaxI;
74
128k
  yMaxI = clip->yMaxI;
75
128k
  intBoundsValid = clip->intBoundsValid;
76
128k
  intBoundsStrokeAdjust = clip->intBoundsStrokeAdjust;
77
128k
  paths = NULL;
78
128k
  eo = NULL;
79
128k
  scanners = NULL;
80
128k
  length = size = 0;
81
128k
  isSimple = clip->isSimple;
82
128k
  prev = clip;
83
128k
  if ((w = splashCeil(xMax)) <= 0) {
84
25.8k
    w = 1;
85
25.8k
  }
86
128k
  buf = (Guchar *)gmalloc(w);
87
128k
}
88
89
323k
SplashClip::~SplashClip() {
90
323k
  int i;
91
92
400k
  for (i = 0; i < length; ++i) {
93
77.2k
    delete scanners[i];
94
77.2k
    delete paths[i];
95
77.2k
  }
96
323k
  gfree(paths);
97
323k
  gfree(eo);
98
323k
  gfree(scanners);
99
323k
  gfree(buf);
100
323k
}
101
102
77.2k
void SplashClip::grow(int nPaths) {
103
77.2k
  if (length + nPaths > size) {
104
70.4k
    if (size == 0) {
105
70.4k
      size = 32;
106
70.4k
    }
107
70.4k
    while (size < length + nPaths) {
108
9
      size *= 2;
109
9
    }
110
70.4k
    paths = (SplashXPath **)greallocn(paths, size, sizeof(SplashXPath *));
111
70.4k
    eo = (Guchar *)greallocn(eo, size, sizeof(Guchar));
112
70.4k
    scanners = (SplashXPathScanner **)
113
70.4k
                   greallocn(scanners, size, sizeof(SplashXPathScanner *));
114
70.4k
  }
115
77.2k
}
116
117
void SplashClip::resetToRect(SplashCoord x0, SplashCoord y0,
118
0
           SplashCoord x1, SplashCoord y1) {
119
0
  int w, i;
120
121
0
  for (i = 0; i < length; ++i) {
122
0
    delete paths[i];
123
0
    delete scanners[i];
124
0
  }
125
0
  gfree(paths);
126
0
  gfree(eo);
127
0
  gfree(scanners);
128
0
  gfree(buf);
129
0
  paths = NULL;
130
0
  eo = NULL;
131
0
  scanners = NULL;
132
0
  length = size = 0;
133
0
  isSimple = gTrue;
134
0
  prev = NULL;
135
136
0
  if (x0 < x1) {
137
0
    xMin = x0;
138
0
    xMax = x1;
139
0
  } else {
140
0
    xMin = x1;
141
0
    xMax = x0;
142
0
  }
143
0
  if (y0 < y1) {
144
0
    yMin = y0;
145
0
    yMax = y1;
146
0
  } else {
147
0
    yMin = y1;
148
0
    yMax = y0;
149
0
  }
150
0
  intBoundsValid = gFalse;
151
0
  if ((w = splashCeil(xMax)) <= 0) {
152
0
    w = 1;
153
0
  }
154
0
  buf = (Guchar *)gmalloc(w);
155
0
}
156
157
SplashError SplashClip::clipToRect(SplashCoord x0, SplashCoord y0,
158
76.2k
           SplashCoord x1, SplashCoord y1) {
159
76.2k
  if (x0 < x1) {
160
36.2k
    if (x0 > xMin) {
161
639
      xMin = x0;
162
639
      intBoundsValid = gFalse;
163
639
    }
164
36.2k
    if (x1 < xMax) {
165
5.84k
      xMax = x1;
166
5.84k
      intBoundsValid = gFalse;
167
5.84k
    }
168
39.9k
  } else {
169
39.9k
    if (x1 > xMin) {
170
13.7k
      xMin = x1;
171
13.7k
      intBoundsValid = gFalse;
172
13.7k
    }
173
39.9k
    if (x0 < xMax) {
174
9.07k
      xMax = x0;
175
9.07k
      intBoundsValid = gFalse;
176
9.07k
    }
177
39.9k
  }
178
76.2k
  if (y0 < y1) {
179
49.1k
    if (y0 > yMin) {
180
10.5k
      yMin = y0;
181
10.5k
      intBoundsValid = gFalse;
182
10.5k
    }
183
49.1k
    if (y1 < yMax) {
184
1.51k
      yMax = y1;
185
1.51k
      intBoundsValid = gFalse;
186
1.51k
    }
187
49.1k
  } else {
188
27.0k
    if (y1 > yMin) {
189
4.55k
      yMin = y1;
190
4.55k
      intBoundsValid = gFalse;
191
4.55k
    }
192
27.0k
    if (y0 < yMax) {
193
4.65k
      yMax = y0;
194
4.65k
      intBoundsValid = gFalse;
195
4.65k
    }
196
27.0k
  }
197
76.2k
  return splashOk;
198
76.2k
}
199
200
SplashError SplashClip::clipToPath(SplashPath *path, SplashCoord *matrix,
201
           SplashCoord flatness, GBool eoA,
202
           GBool enablePathSimplification,
203
165k
           SplashStrokeAdjustMode strokeAdjust) {
204
165k
  SplashXPath *xPath;
205
165k
  SplashCoord t;
206
207
165k
  xPath = new SplashXPath(path, matrix, flatness, gTrue,
208
165k
        enablePathSimplification,
209
165k
        strokeAdjust, NULL);
210
211
  // check for an empty path
212
165k
  if (xPath->length == 0) {
213
11.6k
    xMin = yMin = 1;
214
11.6k
    xMax = yMax = 0;
215
11.6k
    intBoundsValid = gFalse;
216
11.6k
    delete xPath;
217
11.6k
    return splashOk;
218
11.6k
  }
219
220
  // check for a rectangle
221
153k
  if (xPath->isRect) {
222
76.2k
    clipToRect(xPath->rectX0, xPath->rectY0, xPath->rectX1, xPath->rectY1);
223
76.2k
    delete xPath;
224
76.2k
    return splashOk;
225
76.2k
  }
226
227
77.2k
  grow(1);
228
77.2k
  paths[length] = xPath;
229
77.2k
  eo[length] = (Guchar)eoA;
230
77.2k
  if ((t = xPath->getXMin()) > xMin) {
231
2.15k
    xMin = t;
232
2.15k
  }
233
77.2k
  if ((t = xPath->getYMin()) > yMin) {
234
10.9k
    yMin = t;
235
10.9k
  }
236
77.2k
  if ((t = xPath->getXMax() + 1) < xMax) {
237
38.6k
    xMax = t;
238
38.6k
  }
239
77.2k
  if ((t = xPath->getYMax() + 1) < yMax) {
240
46.4k
    yMax = t;
241
46.4k
  }
242
77.2k
  intBoundsValid = gFalse;
243
77.2k
  scanners[length] = new SplashXPathScanner(xPath, eoA, splashFloor(yMin),
244
77.2k
              splashCeil(yMax) - 1);
245
77.2k
  ++length;
246
77.2k
  isSimple = gFalse;
247
248
77.2k
  return splashOk;
249
153k
}
250
251
SplashClipResult SplashClip::testRect(int rectXMin, int rectYMin,
252
              int rectXMax, int rectYMax,
253
2.33M
              SplashStrokeAdjustMode strokeAdjust) {
254
  // In general, this function tests the rectangle:
255
  //     x = [rectXMin, rectXMax + 1)    (note: coords are ints)
256
  //     y = [rectYMin, rectYMax + 1)
257
  // against the clipping region:
258
  //     x = [xMin, xMax)                (note: coords are fp)
259
  //     y = [yMin, yMax)
260
261
2.33M
  if (strokeAdjust != splashStrokeAdjustOff && isSimple) {
262
    // special case for stroke adjustment with a simple clipping
263
    // rectangle -- the clipping region is:
264
    //     x = [xMinI, xMaxI + 1)
265
    //     y = [yMinI, yMaxI + 1)
266
1.05M
    updateIntBounds(strokeAdjust);
267
1.05M
    if (xMinI > xMaxI || yMinI > yMaxI) {
268
9.04k
      return splashClipAllOutside;
269
9.04k
    }
270
1.04M
    if (rectXMax + 1 <= xMinI ||
271
556k
  rectXMin >= xMaxI + 1 ||
272
263k
  rectYMax + 1 <= yMinI ||
273
1.02M
  rectYMin >= yMaxI + 1) {
274
1.02M
      return splashClipAllOutside;
275
1.02M
    }
276
27.1k
    if (rectXMin >= xMinI &&
277
18.4k
  rectXMax <= xMaxI &&
278
12.5k
  rectYMin >= yMinI &&
279
7.12k
  rectYMax <= yMaxI) {
280
6.87k
      return splashClipAllInside;
281
6.87k
    }
282
1.28M
  } else {
283
1.28M
    if (xMin >= xMax || yMin >= yMax) {
284
749k
      return splashClipAllOutside;
285
749k
    }
286
531k
    if ((SplashCoord)(rectXMax + 1) <= xMin ||
287
203k
  (SplashCoord)rectXMin >= xMax ||
288
58.1k
  (SplashCoord)(rectYMax + 1) <= yMin ||
289
517k
  (SplashCoord)rectYMin >= yMax) {
290
517k
      return splashClipAllOutside;
291
517k
    }
292
13.8k
    if (isSimple &&
293
6.61k
  (SplashCoord)rectXMin >= xMin &&
294
2.52k
  (SplashCoord)(rectXMax + 1) <= xMax &&
295
2.29k
  (SplashCoord)rectYMin >= yMin &&
296
401
  (SplashCoord)(rectYMax + 1) <= yMax) {
297
395
      return splashClipAllInside;
298
395
    }
299
13.8k
  }
300
33.7k
  return splashClipPartial;
301
2.33M
}
302
303
void SplashClip::clipSpan(Guchar *line, int y, int x0, int x1,
304
27.9k
        SplashStrokeAdjustMode strokeAdjust) {
305
27.9k
  SplashClip *clip;
306
27.9k
  SplashCoord d;
307
27.9k
  int x0a, x1a, x0b, x1b, x, i;
308
309
27.9k
  updateIntBounds(strokeAdjust);
310
311
  //--- clip to the integer rectangle
312
313
27.9k
  if (y < yMinI || y > yMaxI ||
314
27.9k
      x1 < xMinI || x0 > xMaxI) {
315
0
    memset(line + x0, 0, x1 - x0 + 1);
316
0
    return;
317
0
  }
318
319
27.9k
  if (x0 > xMinI) {
320
324
    x0a = x0;
321
27.5k
  } else {
322
27.5k
    x0a = xMinI;
323
27.5k
    memset(line + x0, 0, x0a - x0);
324
27.5k
  }
325
326
27.9k
  if (x1 < xMaxI) {
327
28
    x1a = x1;
328
27.8k
  } else {
329
27.8k
    x1a = xMaxI;
330
27.8k
    memset(line + x1a + 1, 0, x1 - x1a);
331
27.8k
  }
332
333
27.9k
  if (x0a > x1a) {
334
0
    return;
335
0
  }
336
337
  //--- clip to the floating point rectangle
338
  //    (if stroke adjustment is disabled)
339
340
27.9k
  if (strokeAdjust == splashStrokeAdjustOff) {
341
342
    // clip left edge (xMin)
343
6.60k
    if (x0a == xMinI) {
344
6.27k
      d = (SplashCoord)(xMinI + 1) - xMin;
345
6.27k
      line[x0a] = (Guchar)(int)((SplashCoord)line[x0a] * d);
346
6.27k
    }
347
348
    // clip right edge (xMax)
349
6.60k
    if (x1a == xMaxI) {
350
6.57k
      d = xMax - (SplashCoord)xMaxI;
351
6.57k
      line[x1a] = (Guchar)(int)((SplashCoord)line[x1a] * d);
352
6.57k
    }
353
354
    // clip top edge (yMin)
355
6.60k
    if (y == yMinI) {
356
6.26k
      d = (SplashCoord)(yMinI + 1) - yMin;
357
12.9k
      for (x = x0a; x <= x1a; ++x) {
358
6.63k
  line[x] = (Guchar)(int)((SplashCoord)line[x] * d);
359
6.63k
      }
360
6.26k
    }
361
362
    // clip bottom edge (yMax)
363
6.60k
    if (y == yMaxI) {
364
6.25k
      d = yMax - (SplashCoord)yMaxI;
365
12.8k
      for (x = x0a; x <= x1a; ++x) {
366
6.62k
  line[x] = (Guchar)(int)((SplashCoord)line[x] * d);
367
6.62k
      }
368
6.25k
    }
369
6.60k
  }
370
371
27.9k
  if (isSimple) {
372
22.7k
    return;
373
22.7k
  }
374
375
  //--- clip to the paths
376
377
10.7k
  for (clip = this; clip; clip = clip->prev) {
378
66.7k
    for (i = 0; i < clip->length; ++i) {
379
61.1k
      clip->scanners[i]->getSpan(buf, y, x0a, x1a, &x0b, &x1b);
380
61.1k
      if (x0a < x0b) {
381
53
  memset(line + x0a, 0, x0b - x0a);
382
53
      }
383
122k
      for (x = x0b; x <= x1b; ++x) {
384
60.9k
  line[x] = mul255(line[x], buf[x]);
385
60.9k
      }
386
61.1k
      if (x1b < x1a) {
387
128
  memset(line + x1b + 1, 0, x1a - x1b);
388
128
      }
389
61.1k
    }
390
5.65k
  }
391
5.12k
}
392
393
GBool SplashClip::clipSpanBinary(Guchar *line, int y, int x0, int x1,
394
4.42k
         SplashStrokeAdjustMode strokeAdjust) {
395
4.42k
  SplashClip *clip;
396
4.42k
  int x0a, x1a, x0b, x1b, x, i;
397
4.42k
  Guchar any;
398
399
4.42k
  updateIntBounds(strokeAdjust);
400
401
4.42k
  if (y < yMinI || y > yMaxI ||
402
4.42k
      x1 < xMinI || x0 > xMaxI) {
403
0
    if (x0 <= x1) {
404
0
      memset(line + x0, 0, x1 - x0 + 1);
405
0
    }
406
0
    return gFalse;
407
0
  }
408
409
4.42k
  if (x0 > xMinI) {
410
0
    x0a = x0;
411
4.42k
  } else {
412
4.42k
    x0a = xMinI;
413
4.42k
    memset(line + x0, 0, x0a - x0);
414
4.42k
  }
415
416
4.42k
  if (x1 < xMaxI) {
417
0
    x1a = x1;
418
4.42k
  } else {
419
4.42k
    x1a = xMaxI;
420
4.42k
    memset(line + x1a + 1, 0, x1 - x1a);
421
4.42k
  }
422
423
4.42k
  if (x0a > x1a) {
424
0
    return gFalse;
425
0
  }
426
427
4.42k
  if (isSimple) {
428
2.82k
    for (x = x0a; x <= x1a; ++x) {
429
2.82k
      if (line[x]) {
430
2.82k
  return gTrue;
431
2.82k
      }
432
2.82k
    }
433
0
    return gFalse;
434
2.82k
  }
435
436
1.59k
  any = 0;
437
3.35k
  for (clip = this; clip; clip = clip->prev) {
438
5.82k
    for (i = 0; i < clip->length; ++i) {
439
4.07k
      clip->scanners[i]->getSpanBinary(buf, y, x0a, x1a, &x0b, &x1b);
440
4.07k
      if (x0a < x0b) {
441
158
  memset(line + x0a, 0, x0b - x0a);
442
158
      }
443
7.93k
      for (x = x0b; x <= x1b; ++x) {
444
3.86k
  line[x] &= buf[x];
445
3.86k
  any |= line[x];
446
3.86k
      }
447
4.07k
      if (x1b < x1a) {
448
208
  memset(line + x1b + 1, 0, x1a - x1b);
449
208
      }
450
4.07k
    }
451
1.75k
  }
452
453
1.59k
  return any != 0;
454
4.42k
}
455
456
72.6k
int SplashClip::getXMinI(SplashStrokeAdjustMode strokeAdjust) {
457
72.6k
  updateIntBounds(strokeAdjust);
458
72.6k
  return xMinI;
459
72.6k
}
460
461
72.6k
int SplashClip::getXMaxI(SplashStrokeAdjustMode strokeAdjust) {
462
72.6k
  updateIntBounds(strokeAdjust);
463
72.6k
  return xMaxI;
464
72.6k
}
465
466
70.9k
int SplashClip::getYMinI(SplashStrokeAdjustMode strokeAdjust) {
467
70.9k
  updateIntBounds(strokeAdjust);
468
70.9k
  return yMinI;
469
70.9k
}
470
471
70.9k
int SplashClip::getYMaxI(SplashStrokeAdjustMode strokeAdjust) {
472
70.9k
  updateIntBounds(strokeAdjust);
473
70.9k
  return yMaxI;
474
70.9k
}
475
476
0
int SplashClip::getNumPaths() {
477
0
  SplashClip *clip;
478
0
  int n;
479
480
0
  n = 0;
481
0
  for (clip = this; clip; clip = clip->prev) {
482
0
    n += clip->length;
483
0
  }
484
0
  return n;
485
0
}
486
487
1.37M
void SplashClip::updateIntBounds(SplashStrokeAdjustMode strokeAdjust) {
488
1.37M
  if (intBoundsValid && strokeAdjust == intBoundsStrokeAdjust) {
489
1.36M
    return;
490
1.36M
  }
491
17.5k
  if (strokeAdjust != splashStrokeAdjustOff && isSimple) {
492
14.2k
    splashStrokeAdjust(xMin, xMax, &xMinI, &xMaxI, strokeAdjust);
493
14.2k
    splashStrokeAdjust(yMin, yMax, &yMinI, &yMaxI, strokeAdjust);
494
14.2k
  } else {
495
3.33k
    xMinI = splashFloor(xMin);
496
3.33k
    yMinI = splashFloor(yMin);
497
3.33k
    xMaxI = splashCeil(xMax);
498
3.33k
    yMaxI = splashCeil(yMax);
499
3.33k
  }
500
17.5k
  if (xMinI < hardXMin) {
501
1
    xMinI = hardXMin;
502
1
  }
503
17.5k
  if (yMinI < hardYMin) {
504
1
    yMinI = hardYMin;
505
1
  }
506
17.5k
  if (xMaxI > hardXMax) {
507
1
    xMaxI = hardXMax;
508
1
  }
509
17.5k
  if (yMaxI > hardYMax) {
510
1
    yMaxI = hardYMax;
511
1
  }
512
  // the clipping code uses [xMinI, xMaxI] instead of [xMinI, xMaxI)
513
17.5k
  --xMaxI;
514
17.5k
  --yMaxI;
515
17.5k
  intBoundsValid = gTrue;
516
17.5k
  intBoundsStrokeAdjust = strokeAdjust;
517
17.5k
}