Coverage Report

Created: 2026-07-04 07:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/xpdf-4.06/splash/Splash.cc
Line
Count
Source
1
//========================================================================
2
//
3
// Splash.cc
4
//
5
// Copyright 2003-2020 Glyph & Cog, LLC
6
//
7
//========================================================================
8
9
#include <aconf.h>
10
11
#include <stdlib.h>
12
#include <string.h>
13
#include <limits.h>
14
#include <math.h>
15
#include "gmem.h"
16
#include "gmempp.h"
17
#include "GString.h"
18
#include "SplashErrorCodes.h"
19
#include "SplashMath.h"
20
#include "SplashBitmap.h"
21
#include "SplashState.h"
22
#include "SplashPath.h"
23
#include "SplashXPath.h"
24
#include "SplashXPathScanner.h"
25
#include "SplashPattern.h"
26
#include "SplashScreen.h"
27
#include "SplashFont.h"
28
#include "SplashGlyphBitmap.h"
29
#include "Splash.h"
30
31
// the MSVC math.h doesn't define this
32
#ifndef M_PI
33
#define M_PI 3.14159265358979323846
34
#endif
35
36
//------------------------------------------------------------------------
37
38
// distance of Bezier control point from center for circle approximation
39
// = (4 * (sqrt(2) - 1) / 3) * r
40
1.43M
#define bezierCircle ((SplashCoord)0.55228475)
41
10.8k
#define bezierCircle2 ((SplashCoord)(0.5 * 0.55228475))
42
43
// Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result.
44
75.3k
static inline Guchar div255(int x) {
45
75.3k
  return (Guchar)((x + (x >> 8) + 0x80) >> 8);
46
75.3k
}
47
48
// Clip x to lie in [0, 255].
49
4.96k
static inline Guchar clip255(int x) {
50
4.96k
  return x < 0 ? 0 : x > 255 ? 255 : (Guchar)x;
51
4.96k
}
52
53
// Used by drawImage and fillImageMask to divide the target
54
// quadrilateral into sections.
55
struct ImageSection {
56
  int y0, y1;       // actual y range
57
  int ia0, ia1;       // vertex indices for edge A
58
  int ib0, ib1;       // vertex indices for edge B
59
  SplashCoord xa0, ya0, xa1, ya1; // edge A
60
  SplashCoord dxdya;      // slope of edge A
61
  SplashCoord xb0, yb0, xb1, yb1; // edge B
62
  SplashCoord dxdyb;      // slope of edge B
63
};
64
65
//------------------------------------------------------------------------
66
// SplashPipe
67
//------------------------------------------------------------------------
68
69
#define splashPipeMaxStages 9
70
71
struct SplashPipe {
72
  // source pattern
73
  SplashPattern *pattern;
74
75
  // source alpha and color
76
  Guchar aInput;
77
  SplashColor cSrcVal;
78
79
  // source overprint mask
80
  //~ this is a kludge - this pointer should be passed as an arg to the
81
  //~   pipeRun function, but that would require passing in a lot of
82
  //~   null pointers, since it's rarely used
83
  Guint *srcOverprintMaskPtr;
84
85
  // alpha is shape
86
  GBool alphaIsShape;
87
88
  // special cases and result color
89
  GBool noTransparency;
90
  GBool alphaOnly;
91
  SplashPipeResultColorCtrl resultColorCtrl;
92
93
  // non-isolated group correction
94
  // (this is only used when Splash::composite() is called to composite
95
  // a non-isolated group onto the backdrop)
96
  GBool nonIsolatedGroup;
97
98
  // the "run" function
99
  void (Splash::*run)(SplashPipe *pipe, int x0, int x1, int y,
100
          Guchar *shapPetr, Guchar *alphaPtr,
101
          SplashColorPtr cSrcPtr);
102
};
103
104
SplashPipeResultColorCtrl Splash::pipeResultColorNoAlphaBlend[] = {
105
  splashPipeResultColorNoAlphaBlendMono,
106
  splashPipeResultColorNoAlphaBlendMono,
107
  splashPipeResultColorNoAlphaBlendRGB,
108
  splashPipeResultColorNoAlphaBlendRGB
109
#if SPLASH_CMYK
110
  ,
111
  splashPipeResultColorNoAlphaBlendCMYK
112
#endif
113
};
114
115
SplashPipeResultColorCtrl Splash::pipeResultColorAlphaNoBlend[] = {
116
  splashPipeResultColorAlphaNoBlendMono,
117
  splashPipeResultColorAlphaNoBlendMono,
118
  splashPipeResultColorAlphaNoBlendRGB,
119
  splashPipeResultColorAlphaNoBlendRGB
120
#if SPLASH_CMYK
121
  ,
122
  splashPipeResultColorAlphaNoBlendCMYK
123
#endif
124
};
125
126
SplashPipeResultColorCtrl Splash::pipeResultColorAlphaBlend[] = {
127
  splashPipeResultColorAlphaBlendMono,
128
  splashPipeResultColorAlphaBlendMono,
129
  splashPipeResultColorAlphaBlendRGB,
130
  splashPipeResultColorAlphaBlendRGB
131
#if SPLASH_CMYK
132
  ,
133
  splashPipeResultColorAlphaBlendCMYK
134
#endif
135
};
136
137
//------------------------------------------------------------------------
138
// modified region
139
//------------------------------------------------------------------------
140
141
199k
void Splash::clearModRegion() {
142
199k
  modXMin = bitmap->width;
143
199k
  modYMin = bitmap->height;
144
199k
  modXMax = -1;
145
199k
  modYMax = -1;
146
199k
}
147
148
457k
inline void Splash::updateModX(int x) {
149
457k
  if (x < modXMin) {
150
190k
    modXMin = x;
151
190k
  }
152
457k
  if (x > modXMax) {
153
191k
    modXMax = x;
154
191k
  }
155
457k
}
156
157
414k
inline void Splash::updateModY(int y) {
158
414k
  if (y < modYMin) {
159
190k
    modYMin = y;
160
190k
  }
161
414k
  if (y > modYMax) {
162
191k
    modYMax = y;
163
191k
  }
164
414k
}
165
166
//------------------------------------------------------------------------
167
// pipeline
168
//------------------------------------------------------------------------
169
170
inline void Splash::pipeInit(SplashPipe *pipe, SplashPattern *pattern,
171
           Guchar aInput, GBool usesShape, GBool usesAlpha,
172
           GBool nonIsolatedGroup, GBool usesSrcOverprint,
173
50.5k
           GBool alphaIsShape) {
174
50.5k
  SplashColorMode mode;
175
176
50.5k
  mode = bitmap->mode;
177
178
50.5k
  pipe->pattern = NULL;
179
180
  // source color
181
50.5k
  if (pattern && pattern->isStatic()) {
182
43.9k
    pattern->getColor(0, 0, pipe->cSrcVal);
183
43.9k
    pipe->pattern = NULL;
184
43.9k
  } else {
185
6.65k
    pipe->pattern = pattern;
186
6.65k
  }
187
188
  // source alpha
189
50.5k
  pipe->aInput = aInput;
190
191
  // source overprint mask
192
50.5k
  pipe->srcOverprintMaskPtr = NULL;
193
194
  // alpha is shape
195
50.5k
  pipe->alphaIsShape = alphaIsShape;
196
197
  // special cases
198
50.5k
  pipe->noTransparency = aInput == 255 &&
199
50.4k
                         !state->softMask &&
200
49.2k
                         !usesShape &&
201
49.2k
                         !usesAlpha &&
202
0
                         !state->inNonIsolatedGroup &&
203
0
       !state->inKnockoutGroup &&
204
0
                         !nonIsolatedGroup &&
205
0
                         state->overprintMask == 0xffffffff;
206
50.5k
  pipe->alphaOnly = aInput == 255 &&
207
50.4k
                    !state->softMask &&
208
49.2k
                    !usesShape &&
209
49.2k
                    usesAlpha &&
210
49.2k
                    !state->inNonIsolatedGroup &&
211
30.7k
        !state->inKnockoutGroup &&
212
30.7k
                    !nonIsolatedGroup &&
213
27.5k
                    state->overprintMask == 0xffffffff;
214
215
  // result color
216
50.5k
  if (pipe->noTransparency) {
217
    // the !state->blendFunc case is handled separately in pipeRun
218
0
    pipe->resultColorCtrl = pipeResultColorNoAlphaBlend[mode];
219
50.5k
  } else if (!state->blendFunc) {
220
49.3k
    pipe->resultColorCtrl = pipeResultColorAlphaNoBlend[mode];
221
49.3k
  } else {
222
1.20k
    pipe->resultColorCtrl = pipeResultColorAlphaBlend[mode];
223
1.20k
  }
224
225
  // non-isolated group correction
226
50.5k
  pipe->nonIsolatedGroup = nonIsolatedGroup;
227
228
  // select the 'run' function
229
50.5k
  pipe->run = &Splash::pipeRun;
230
50.5k
  if (shapeBitmap || overprintMaskBitmap || usesSrcOverprint) {
231
    // use Splash::pipeRun
232
50.5k
  } else if (!pipe->pattern && pipe->noTransparency && !state->blendFunc) {
233
0
    if (mode == splashModeMono1 && !bitmap->alpha) {
234
0
      pipe->run = &Splash::pipeRunSimpleMono1;
235
0
    } else if (mode == splashModeMono8 && bitmap->alpha) {
236
0
      pipe->run = &Splash::pipeRunSimpleMono8;
237
0
    } else if (mode == splashModeRGB8 && bitmap->alpha) {
238
0
      pipe->run = &Splash::pipeRunSimpleRGB8;
239
0
    } else if (mode == splashModeBGR8 && bitmap->alpha) {
240
0
      pipe->run = &Splash::pipeRunSimpleBGR8;
241
0
#if SPLASH_CMYK
242
0
    } else if (mode == splashModeCMYK8 && bitmap->alpha) {
243
0
      pipe->run = &Splash::pipeRunSimpleCMYK8;
244
0
#endif
245
0
    }
246
50.5k
  } else if (!pipe->pattern && pipe->alphaOnly && !state->blendFunc) {
247
27.2k
    if (mode == splashModeMono1 && !bitmap->alpha) {
248
162
      pipe->run = &Splash::pipeRunShapeMono1;
249
27.0k
    } else if (mode == splashModeMono8 && bitmap->alpha) {
250
375
      pipe->run = &Splash::pipeRunShapeMono8;
251
26.7k
    } else if (mode == splashModeRGB8 && bitmap->alpha) {
252
25.2k
      pipe->run = &Splash::pipeRunShapeRGB8;
253
25.2k
    } else if (mode == splashModeBGR8 && bitmap->alpha) {
254
0
      pipe->run = &Splash::pipeRunShapeBGR8;
255
0
#if SPLASH_CMYK
256
1.42k
    } else if (mode == splashModeCMYK8 && bitmap->alpha) {
257
56
      pipe->run = &Splash::pipeRunShapeCMYK8;
258
56
#endif
259
1.37k
    } else if (mode == splashModeMono8 && !bitmap->alpha) {
260
      // this is used when drawing soft-masked images
261
1.37k
      pipe->run = &Splash::pipeRunShapeNoAlphaMono8;
262
1.37k
    }
263
27.2k
  } else if (!pipe->pattern && !pipe->noTransparency && !state->softMask &&
264
22.0k
       usesAlpha &&
265
22.0k
       !(state->inNonIsolatedGroup && (alpha0Bitmap ||
266
18.4k
               parent->bitmap->alpha)) &&
267
3.66k
       !state->inKnockoutGroup &&
268
3.66k
       !state->blendFunc && !pipe->nonIsolatedGroup) {
269
89
    if (mode == splashModeMono1 && !bitmap->alpha) {
270
0
      pipe->run = &Splash::pipeRunAAMono1;
271
89
    } else if (mode == splashModeMono8 && bitmap->alpha) {
272
0
      pipe->run = &Splash::pipeRunAAMono8;
273
89
    } else if (mode == splashModeRGB8 && bitmap->alpha) {
274
89
      pipe->run = &Splash::pipeRunAARGB8;
275
89
    } else if (mode == splashModeBGR8 && bitmap->alpha) {
276
0
      pipe->run = &Splash::pipeRunAABGR8;
277
0
#if SPLASH_CMYK
278
0
    } else if (mode == splashModeCMYK8 && bitmap->alpha) {
279
0
      pipe->run = &Splash::pipeRunAACMYK8;
280
0
#endif
281
0
    }
282
23.2k
  } else if (!pipe->pattern &&
283
23.2k
             aInput == 255 &&
284
23.2k
             state->softMask &&
285
1.25k
             usesAlpha &&
286
1.25k
             !state->inNonIsolatedGroup &&
287
1.24k
             !state->inKnockoutGroup &&
288
1.24k
             !nonIsolatedGroup &&
289
1.22k
             state->overprintMask == 0xffffffff &&
290
1.22k
             !state->blendFunc) {
291
556
    if (mode == splashModeMono8 && bitmap->alpha) {
292
132
      pipe->run = &Splash::pipeRunSoftMaskMono8;
293
424
    } else if (mode == splashModeRGB8 && bitmap->alpha) {
294
424
      pipe->run = &Splash::pipeRunSoftMaskRGB8;
295
424
    } else if (mode == splashModeBGR8 && bitmap->alpha) {
296
0
      pipe->run = &Splash::pipeRunSoftMaskBGR8;
297
0
#if SPLASH_CMYK
298
0
    } else if (mode == splashModeCMYK8 && bitmap->alpha) {
299
0
      pipe->run = &Splash::pipeRunSoftMaskCMYK8;
300
0
#endif
301
0
    }
302
22.6k
  } else if (!pipe->pattern && !pipe->noTransparency && !state->softMask &&
303
22.0k
       usesAlpha &&
304
22.0k
       state->inNonIsolatedGroup && (alpha0Bitmap ||
305
18.4k
             parent->bitmap->alpha) &&
306
18.4k
       !state->inKnockoutGroup &&
307
18.4k
       !state->blendFunc && !pipe->nonIsolatedGroup) {
308
18.2k
    if (mode == splashModeMono8 && bitmap->alpha) {
309
0
      pipe->run = &Splash::pipeRunNonIsoMono8;
310
18.2k
    } else if (mode == splashModeRGB8 && bitmap->alpha) {
311
18.2k
      pipe->run = &Splash::pipeRunNonIsoRGB8;
312
18.2k
    } else if (mode == splashModeBGR8 && bitmap->alpha) {
313
0
      pipe->run = &Splash::pipeRunNonIsoBGR8;
314
0
#if SPLASH_CMYK
315
0
    } else if (mode == splashModeCMYK8 && bitmap->alpha) {
316
0
      pipe->run = &Splash::pipeRunNonIsoCMYK8;
317
0
#endif
318
0
    }
319
18.2k
  }
320
50.5k
}
321
322
// general case
323
void Splash::pipeRun(SplashPipe *pipe, int x0, int x1, int y,
324
         Guchar *shapePtr, Guchar *alphaPtr,
325
2.66k
         SplashColorPtr cSrcPtr) {
326
2.66k
  Guchar *shapePtr2, *alphaPtr2;
327
2.66k
  Guchar shape, alpha, fSrc, aSrc, fDest, aDest;
328
2.66k
  Guchar alphaI, alphaIm1, alpha0, fResult, aResult;
329
2.66k
  Guchar m1, m2, m3, m4;
330
2.66k
  SplashColor cSrc, cDest, c0, cBlend;
331
2.66k
  Guchar shapeVal, alphaVal, cResult0, cResult1, cResult2, cResult3;
332
2.66k
  int cSrcStride, shapeStride, alphaStride, x, lastX, t;
333
2.66k
  SplashColorPtr cBlendIn, destColorPtr;
334
2.66k
  Guchar destColorMask;
335
2.66k
  Guchar *destShapePtr, *destAlphaPtr;
336
2.66k
  SplashColorPtr color0Ptr;
337
2.66k
  Guchar color0Mask;
338
2.66k
  Guchar *alpha0Ptr;
339
2.66k
  SplashColorPtr softMaskPtr;
340
2.66k
  Guint overprintMask;
341
2.66k
  Guint *overprintMaskPtr;
342
2.66k
#if SPLASH_CMYK
343
2.66k
  Guchar aPrev;
344
2.66k
  SplashColor cSrc2, cBlendIn2;
345
2.66k
#endif
346
2.66k
  int i;
347
348
2.66k
  if (cSrcPtr && !pipe->pattern) {
349
2.46k
    cSrcStride = bitmapComps;
350
2.46k
  } else {
351
208
    cSrcPtr = pipe->cSrcVal;
352
208
    cSrcStride = 0;
353
208
  }
354
2.66k
  if (shapePtr) {
355
3
    shapePtr2 = shapePtr;
356
3
    shapeStride = 1;
357
2.66k
  } else {
358
2.66k
    shapeVal = 0xff;
359
2.66k
    shapePtr2 = &shapeVal;
360
2.66k
    shapeStride = 0;
361
2.66k
  }
362
2.66k
  if (alphaPtr) {
363
2.66k
    alphaPtr2 = alphaPtr;
364
2.66k
    alphaStride = 1;
365
2.66k
  } else {
366
0
    alphaVal = 0xff;
367
0
    alphaPtr2 = &alphaVal;
368
0
    alphaStride = 0;
369
0
  }
370
371
2.66k
  if (shapePtr) {
372
3
    for (; x0 <= x1; ++x0) {
373
3
      if (*shapePtr2) {
374
3
  break;
375
3
      }
376
0
      cSrcPtr += cSrcStride;
377
0
      ++shapePtr2;
378
0
      ++alphaPtr2;
379
0
      if (pipe->srcOverprintMaskPtr) {
380
0
  ++pipe->srcOverprintMaskPtr;
381
0
      }
382
0
    }
383
2.66k
  } else if (alphaPtr) {
384
2.70k
    for (; x0 <= x1; ++x0) {
385
2.66k
      if (*alphaPtr2) {
386
2.62k
  break;
387
2.62k
      }
388
44
      cSrcPtr += cSrcStride;
389
44
      ++alphaPtr2;
390
44
      if (pipe->srcOverprintMaskPtr) {
391
0
  ++pipe->srcOverprintMaskPtr;
392
0
      }
393
44
    }
394
2.66k
  }
395
2.66k
  if (x0 > x1) {
396
44
    return;
397
44
  }
398
2.62k
  updateModX(x0);
399
2.62k
  updateModY(y);
400
2.62k
  lastX = x0;
401
402
2.62k
  useBitmapRow(y);
403
404
2.62k
  if (bitmap->mode == splashModeMono1) {
405
0
    destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)];
406
0
    destColorMask = (Guchar)(0x80 >> (x0 & 7));
407
2.62k
  } else {
408
2.62k
    destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * bitmapComps];
409
2.62k
    destColorMask = 0; // make gcc happy
410
2.62k
  }
411
2.62k
  if (shapeBitmap) {
412
0
    destShapePtr = &shapeBitmap->alpha[y * shapeBitmap->alphaRowSize + x0];
413
2.62k
  } else {
414
2.62k
    destShapePtr = NULL;
415
2.62k
  }
416
2.62k
  if (bitmap->alpha) {
417
2.62k
    destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
418
2.62k
  } else {
419
0
    destAlphaPtr = NULL;
420
0
  }
421
2.62k
  if (state->softMask) {
422
645
    softMaskPtr = &state->softMask->data[y * state->softMask->rowSize + x0];
423
1.97k
  } else {
424
1.97k
    softMaskPtr = NULL;
425
1.97k
  }
426
2.62k
  if (state->inKnockoutGroup) {
427
0
    if (bitmap->mode == splashModeMono1) {
428
0
      color0Ptr =
429
0
          &parent->bitmap->data[(parentOffsetY + y) * parent->bitmap->rowSize +
430
0
        ((parentOffsetX + x0) >> 3)];
431
0
      color0Mask = (Guchar)(0x80 >> ((parentOffsetX + x0) & 7));
432
0
    } else {
433
0
      color0Ptr =
434
0
          &parent->bitmap->data[(parentOffsetY + y) * parent->bitmap->rowSize +
435
0
        (parentOffsetX + x0) * bitmapComps];
436
0
      color0Mask = 0; // make gcc happy
437
0
    }
438
2.62k
  } else {
439
2.62k
    color0Ptr = NULL;
440
2.62k
    color0Mask = 0; // make gcc happy
441
2.62k
  }
442
2.62k
  if (alpha0Bitmap) {
443
0
    alpha0Ptr = &alpha0Bitmap->alpha[y * alpha0Bitmap->alphaRowSize + x0];
444
2.62k
  } else if (state->inNonIsolatedGroup && parent->bitmap->alpha) {
445
139
    alpha0Ptr = &parent->bitmap->alpha[
446
139
        (parentOffsetY + y) * parent->bitmap->alphaRowSize
447
139
        + (parentOffsetX + x0)];
448
2.48k
  } else {
449
2.48k
    alpha0Ptr = NULL;
450
2.48k
  }
451
2.62k
  if (overprintMaskBitmap) {
452
0
    overprintMaskPtr = overprintMaskBitmap + y * bitmap->width + x0;
453
2.62k
  } else {
454
2.62k
    overprintMaskPtr = NULL;
455
2.62k
  }
456
457
  // c0 is only used for non-iso knockout groups, but in some other
458
  // cases we use m2 * c0[i], with m2=0 -- so initialize c0[i]=0 here
459
  // to avoid valgrind warnings
460
10.4k
  for (i = 0; i < bitmapComps; ++i) {
461
7.87k
    c0[i] = 0;
462
7.87k
  }
463
464
5.24k
  for (x = x0; x <= x1; ++x) {
465
466
    //----- shape and alpha
467
468
2.62k
    shape = *shapePtr2;
469
2.62k
    alpha = *alphaPtr2;
470
2.62k
    if (!(shapePtr ? shape : alpha)) {
471
0
      if (bitmap->mode == splashModeMono1) {
472
0
  destColorPtr += destColorMask & 1;
473
0
  destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1));
474
0
      } else {
475
0
  destColorPtr += bitmapComps;
476
0
      }
477
0
      if (destShapePtr) {
478
0
  ++destShapePtr;
479
0
      }
480
0
      if (destAlphaPtr) {
481
0
  ++destAlphaPtr;
482
0
      }
483
0
      if (softMaskPtr) {
484
0
  ++softMaskPtr;
485
0
      }
486
0
      if (color0Ptr) {
487
0
  if (bitmap->mode == splashModeMono1) {
488
0
    color0Ptr += color0Mask & 1;
489
0
    color0Mask = (Guchar)((color0Mask << 7) | (color0Mask >> 1));
490
0
  } else {
491
0
    color0Ptr += bitmapComps;
492
0
  }
493
0
      }
494
0
      if (alpha0Ptr) {
495
0
  ++alpha0Ptr;
496
0
      }
497
0
      cSrcPtr += cSrcStride;
498
0
      shapePtr2 += shapeStride;
499
0
      alphaPtr2 += alphaStride;
500
0
      if (pipe->srcOverprintMaskPtr) {
501
0
  ++pipe->srcOverprintMaskPtr;
502
0
      }
503
0
      if (overprintMaskPtr) {
504
0
  ++overprintMaskPtr;
505
0
      }
506
0
      continue;
507
0
    }
508
2.62k
    lastX = x;
509
510
    //----- source color
511
512
    // static pattern: handled in pipeInit
513
    // fixed color: handled in pipeInit
514
515
    // dynamic pattern
516
2.62k
    if (pipe->pattern) {
517
0
      pipe->pattern->getColor(x, y, pipe->cSrcVal);
518
0
    }
519
520
2.62k
    cResult0 = cResult1 = cResult2 = cResult3 = 0; // make gcc happy
521
522
2.62k
    if (pipe->noTransparency && !state->blendFunc) {
523
524
      //----- handle overprint
525
526
0
      if (overprintMaskPtr) {
527
0
  *overprintMaskPtr++ = 0xffffffff;
528
0
      }
529
530
      //----- result color
531
532
0
      switch (bitmap->mode) {
533
0
      case splashModeMono1:
534
0
      case splashModeMono8:
535
0
  cResult0 = state->grayTransfer[cSrcPtr[0]];
536
0
  break;
537
0
      case splashModeRGB8:
538
0
      case splashModeBGR8:
539
0
  cResult0 = state->rgbTransferR[cSrcPtr[0]];
540
0
  cResult1 = state->rgbTransferG[cSrcPtr[1]];
541
0
  cResult2 = state->rgbTransferB[cSrcPtr[2]];
542
0
  break;
543
0
#if SPLASH_CMYK
544
0
      case splashModeCMYK8:
545
0
  cResult0 = state->cmykTransferC[cSrcPtr[0]];
546
0
  cResult1 = state->cmykTransferM[cSrcPtr[1]];
547
0
  cResult2 = state->cmykTransferY[cSrcPtr[2]];
548
0
  cResult3 = state->cmykTransferK[cSrcPtr[3]];
549
0
  break;
550
0
#endif
551
0
      }
552
0
      fResult = 255;
553
0
      aResult = 255;
554
555
2.62k
    } else { // if (noTransparency && !blendFunc)
556
557
      //----- read destination color
558
559
2.62k
      switch (bitmap->mode) {
560
0
      case splashModeMono1:
561
0
  cDest[0] = (*destColorPtr & destColorMask) ? 0xff : 0x00;
562
0
  break;
563
0
      case splashModeMono8:
564
0
  cDest[0] = *destColorPtr;
565
0
  break;
566
2.62k
      case splashModeRGB8:
567
2.62k
  cDest[0] = destColorPtr[0];
568
2.62k
  cDest[1] = destColorPtr[1];
569
2.62k
  cDest[2] = destColorPtr[2];
570
2.62k
  break;
571
0
      case splashModeBGR8:
572
0
  cDest[0] = destColorPtr[2];
573
0
  cDest[1] = destColorPtr[1];
574
0
  cDest[2] = destColorPtr[0];
575
0
  break;
576
0
#if SPLASH_CMYK
577
0
      case splashModeCMYK8:
578
0
  cDest[0] = destColorPtr[0];
579
0
  cDest[1] = destColorPtr[1];
580
0
  cDest[2] = destColorPtr[2];
581
0
  cDest[3] = destColorPtr[3];
582
0
  break;
583
2.62k
#endif
584
2.62k
      }
585
586
      //----- read backdrop color
587
      //      (only used for non-iso knockout groups)
588
589
2.62k
      if (color0Ptr && alpha0Ptr) {
590
0
  switch (bitmap->mode) {
591
0
  case splashModeMono1:
592
0
    c0[0] = (*color0Ptr & color0Mask) ? 0xff : 0x00;
593
0
    color0Ptr += color0Mask & 1;
594
0
    color0Mask = (Guchar)((color0Mask << 7) | (color0Mask >> 1));
595
0
    break;
596
0
  case splashModeMono8:
597
0
    c0[0] = *color0Ptr++;
598
0
    break;
599
0
  case splashModeRGB8:
600
0
    c0[0] = color0Ptr[0];
601
0
    c0[1] = color0Ptr[1];
602
0
    c0[2] = color0Ptr[2];
603
0
    color0Ptr += 3;
604
0
    break;
605
0
  case splashModeBGR8:
606
0
    c0[2] = color0Ptr[0];
607
0
    c0[1] = color0Ptr[1];
608
0
    c0[0] = color0Ptr[2];
609
0
    color0Ptr += 3;
610
0
    break;
611
0
#if SPLASH_CMYK
612
0
  case splashModeCMYK8:
613
0
    c0[0] = color0Ptr[0];
614
0
    c0[1] = color0Ptr[1];
615
0
    c0[2] = color0Ptr[2];
616
0
    c0[3] = color0Ptr[3];
617
0
    color0Ptr += 4;
618
0
    break;
619
0
#endif
620
0
  }
621
0
  cBlendIn = c0;
622
2.62k
      } else {
623
2.62k
  cBlendIn = cDest;
624
2.62k
      }
625
626
      //----- read destination shape and alpha
627
628
2.62k
      if (destShapePtr) {
629
0
  fDest = *destShapePtr;
630
2.62k
      } else {
631
2.62k
  fDest = 0xff;
632
2.62k
      }
633
634
2.62k
      if (destAlphaPtr) {
635
2.62k
  aDest = *destAlphaPtr;
636
2.62k
      } else {
637
0
  aDest = 0xff;
638
0
      }
639
640
      //----- read source color; handle overprint
641
642
2.62k
      if (pipe->srcOverprintMaskPtr) {
643
0
  overprintMask = *pipe->srcOverprintMaskPtr++;
644
2.62k
      } else {
645
2.62k
  overprintMask = state->overprintMask;
646
2.62k
      }
647
2.62k
      if (overprintMaskPtr) {
648
0
  *overprintMaskPtr++ |= overprintMask;
649
0
      }
650
651
2.62k
      switch (bitmap->mode) {
652
0
      case splashModeMono1:
653
0
      case splashModeMono8:
654
0
  cSrc[0] = state->grayTransfer[cSrcPtr[0]];
655
0
  break;
656
2.62k
      case splashModeRGB8:
657
2.62k
      case splashModeBGR8:
658
2.62k
  cSrc[0] = state->rgbTransferR[cSrcPtr[0]];
659
2.62k
  cSrc[1] = state->rgbTransferG[cSrcPtr[1]];
660
2.62k
  cSrc[2] = state->rgbTransferB[cSrcPtr[2]];
661
2.62k
  break;
662
0
#if SPLASH_CMYK
663
0
      case splashModeCMYK8:
664
0
  if (alpha0Ptr) {   // non-isolated group
665
0
    if (color0Ptr) { //   non-isolated, knockout group
666
0
      aPrev = *alpha0Ptr;
667
0
    } else {         //   non-isolated, non-knockout group
668
0
      aPrev = (Guchar)(*alpha0Ptr + aDest - div255(*alpha0Ptr * aDest));
669
0
    }
670
0
  } else {           // isolated group
671
0
    if (color0Ptr) { //   isolated, knockout group
672
0
      aPrev = 0;
673
0
    } else {         //   isolated, non-knockout group
674
0
      aPrev = aDest;
675
0
    }
676
0
  }
677
0
  if (overprintMask & 0x01) {
678
0
    cSrc[0] = state->cmykTransferC[cSrcPtr[0]];
679
0
  } else {
680
0
    cSrc[0] = div255(aPrev * cDest[0]);
681
0
  }
682
0
  if (overprintMask & 0x02) {
683
0
    cSrc[1] = state->cmykTransferM[cSrcPtr[1]];
684
0
  } else {
685
0
    cSrc[1] = div255(aPrev * cDest[1]);
686
0
  }
687
0
  if (overprintMask & 0x04) {
688
0
    cSrc[2] = state->cmykTransferY[cSrcPtr[2]];
689
0
  } else {
690
0
    cSrc[2] = div255(aPrev * cDest[2]);
691
0
  }
692
0
  if (overprintMask & 0x08) {
693
0
    cSrc[3] = state->cmykTransferK[cSrcPtr[3]];
694
0
  } else {
695
0
    cSrc[3] = div255(aPrev * cDest[3]);
696
0
  }
697
0
  break;
698
2.62k
#endif
699
2.62k
      }
700
701
      //----- source shape and alpha
702
703
2.62k
      fSrc = shape;
704
2.62k
      aSrc = div255(pipe->aInput * alpha);
705
2.62k
      if (softMaskPtr) {
706
645
  if (pipe->alphaIsShape) {
707
0
    fSrc = div255(fSrc * *softMaskPtr);
708
0
  }
709
645
  aSrc = div255(aSrc * *softMaskPtr);
710
645
  ++softMaskPtr;
711
645
      }
712
713
      //----- non-isolated group correction
714
715
2.62k
      if (pipe->nonIsolatedGroup && alpha != 0) {
716
  // This path is only used when Splash::composite() is called to
717
  // composite a non-isolated group onto the backdrop.  In this
718
  // case, alpha is the source (group) alpha.
719
  //
720
  // In a nested non-isolated group, i.e., if the destination is
721
  // also a non-isolated group (state->inNonIsolatedGroup), we
722
  // need to compute the corrected alpha, because the
723
  // destination is is storing group alpha (same computation as
724
  // blitCorrectedAlpha).
725
1.65k
  if (alpha0Ptr) {
726
0
    t = *alpha0Ptr;
727
0
    t = (Guchar)(aDest + t - div255(aDest * t));
728
1.65k
  } else {
729
1.65k
    t = aDest;
730
1.65k
  }
731
1.65k
  t = (t * 255) / alpha - t;
732
1.65k
  switch (bitmap->mode) {
733
0
#if SPLASH_CMYK
734
0
  case splashModeCMYK8:
735
0
    cSrc[3] = clip255(cSrc[3] + ((cSrc[3] - cDest[3]) * t) / 255);
736
0
#endif
737
1.65k
  case splashModeRGB8:
738
1.65k
  case splashModeBGR8:
739
1.65k
    cSrc[2] = clip255(cSrc[2] + ((cSrc[2] - cDest[2]) * t) / 255);
740
1.65k
    cSrc[1] = clip255(cSrc[1] + ((cSrc[1] - cDest[1]) * t) / 255);
741
1.65k
  case splashModeMono1:
742
1.65k
  case splashModeMono8:
743
1.65k
    cSrc[0] = clip255(cSrc[0] + ((cSrc[0] - cDest[0]) * t) / 255);
744
1.65k
    break;
745
1.65k
  }
746
1.65k
      }
747
748
      //----- blend function
749
750
2.62k
      if (state->blendFunc) {
751
1.01k
#if SPLASH_CMYK
752
1.01k
  if (bitmap->mode == splashModeCMYK8) {
753
    // convert colors to additive
754
0
    cSrc2[0] = (Guchar)(0xff - cSrc[0]);
755
0
    cSrc2[1] = (Guchar)(0xff - cSrc[1]);
756
0
    cSrc2[2] = (Guchar)(0xff - cSrc[2]);
757
0
    cSrc2[3] = (Guchar)(0xff - cSrc[3]);
758
0
    cBlendIn2[0] = (Guchar)(0xff - cBlendIn[0]);
759
0
    cBlendIn2[1] = (Guchar)(0xff - cBlendIn[1]);
760
0
    cBlendIn2[2] = (Guchar)(0xff - cBlendIn[2]);
761
0
    cBlendIn2[3] = (Guchar)(0xff - cBlendIn[3]);
762
0
    (*state->blendFunc)(cSrc2, cBlendIn2, cBlend, bitmap->mode);
763
    // convert result back to subtractive
764
0
    cBlend[0] = (Guchar)(0xff - cBlend[0]);
765
0
    cBlend[1] = (Guchar)(0xff - cBlend[1]);
766
0
    cBlend[2] = (Guchar)(0xff - cBlend[2]);
767
0
    cBlend[3] = (Guchar)(0xff - cBlend[3]);
768
0
  } else
769
1.01k
#endif
770
1.01k
  (*state->blendFunc)(cSrc, cBlendIn, cBlend, bitmap->mode);
771
1.01k
      }
772
773
      //----- result shape
774
775
2.62k
      fResult = (Guchar)(fSrc + fDest - div255(fSrc * fDest));
776
777
      //----- result alpha
778
      //      and setup for result color
779
780
      // alphaI = alpha_i
781
      // alphaIm1 = alpha_(i-1)
782
783
2.62k
      if (pipe->noTransparency) {
784
0
  alphaI = aResult = 255;
785
0
  m1 = m2 = m3 = m4 = 0; // make gcc happy
786
2.62k
      } else if (alpha0Ptr) {     // non-isolated
787
139
  alpha0 = *alpha0Ptr++;
788
139
  if (color0Ptr) {          // non-isolated, knockout
789
0
    aResult = (Guchar)(div255((255 - fSrc) * aDest) + aSrc);
790
0
    alphaI = (Guchar)(alpha0 + aResult - div255(alpha0 * aResult));
791
0
    alphaIm1 = (Guchar)(alpha0 + aDest - div255(alpha0 * aDest));
792
0
    m1 = div255((255 - fSrc) * alphaIm1);
793
0
    m2 = div255((fSrc - aSrc) * alpha0);
794
0
    m3 = div255(aSrc * (255 - alpha0));
795
0
    m4 = div255(aSrc * alpha0);
796
139
  } else {                  // non-isolated, non-knockout
797
139
    aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
798
139
    alphaI = (Guchar)(alpha0 + aResult - div255(alpha0 * aResult));
799
139
    alphaIm1 = (Guchar)(alpha0 + aDest - div255(alpha0 * aDest));
800
139
    m1 = alphaI - aSrc;
801
139
    m2 = 0;
802
139
    m3 = div255(aSrc * (255 - alphaIm1));
803
139
    m4 = div255(aSrc * alphaIm1);
804
139
  }
805
2.48k
      } else {
806
2.48k
  if (color0Ptr) {          // isolated, knockout
807
0
    aResult = (Guchar)(div255((255 - fSrc) * aDest) + aSrc);
808
0
    alphaI = aResult;
809
0
    alphaIm1 = aDest;
810
0
    m1 = div255((255 - fSrc) * alphaIm1);
811
0
    m2 = 0;
812
0
    m3 = aSrc;
813
0
    m4 = 0;
814
2.48k
  } else {                  // isolated, non-knockout
815
2.48k
    aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
816
2.48k
    alphaI = aResult;
817
2.48k
    alphaIm1 = aDest;
818
2.48k
    m1 = alphaI - aSrc;
819
2.48k
    m2 = 0;
820
2.48k
    m3 = div255(aSrc * (255 - alphaIm1));
821
2.48k
    m4 = div255(aSrc * alphaIm1);
822
2.48k
  }
823
2.48k
      }
824
825
      //----- result color
826
827
2.62k
      switch (pipe->resultColorCtrl) {
828
829
0
      case splashPipeResultColorNoAlphaBlendMono:
830
0
  cResult0 = div255((255 - aDest) * cSrc[0] + aDest * cBlend[0]);
831
0
  break;
832
0
      case splashPipeResultColorNoAlphaBlendRGB:
833
0
  cResult0 = div255((255 - aDest) * cSrc[0] + aDest * cBlend[0]);
834
0
  cResult1 = div255((255 - aDest) * cSrc[1] + aDest * cBlend[1]);
835
0
  cResult2 = div255((255 - aDest) * cSrc[2] + aDest * cBlend[2]);
836
0
  break;
837
0
#if SPLASH_CMYK
838
0
      case splashPipeResultColorNoAlphaBlendCMYK:
839
0
  cResult0 = div255((255 - aDest) * cSrc[0] + aDest * cBlend[0]);
840
0
  cResult1 = div255((255 - aDest) * cSrc[1] + aDest * cBlend[1]);
841
0
  cResult2 = div255((255 - aDest) * cSrc[2] + aDest * cBlend[2]);
842
0
  cResult3 = div255((255 - aDest) * cSrc[3] + aDest * cBlend[3]);
843
0
  break;
844
0
#endif
845
846
0
      case splashPipeResultColorAlphaNoBlendMono:
847
0
  if (alphaI == 0) {
848
0
    cResult0 = 0;
849
0
  } else {
850
0
    cResult0 = (Guchar)((m1 * cDest[0] + m2 * c0[0] + aSrc * cSrc[0])
851
0
            / alphaI);
852
0
  }
853
0
  break;
854
1.60k
      case splashPipeResultColorAlphaNoBlendRGB:
855
1.60k
  if (alphaI == 0) {
856
22
    cResult0 = 0;
857
22
    cResult1 = 0;
858
22
    cResult2 = 0;
859
1.58k
  } else {
860
1.58k
    cResult0 = (Guchar)((m1 * cDest[0] + m2 * c0[0] + aSrc * cSrc[0])
861
1.58k
            / alphaI);
862
1.58k
    cResult1 = (Guchar)((m1 * cDest[1] + m2 * c0[1] + aSrc * cSrc[1])
863
1.58k
            / alphaI);
864
1.58k
    cResult2 = (Guchar)((m1 * cDest[2] + m2 * c0[2] + aSrc * cSrc[2])
865
1.58k
            / alphaI);
866
1.58k
  }
867
1.60k
  break;
868
0
#if SPLASH_CMYK
869
0
      case splashPipeResultColorAlphaNoBlendCMYK:
870
0
  if (alphaI == 0) {
871
0
    cResult0 = 0;
872
0
    cResult1 = 0;
873
0
    cResult2 = 0;
874
0
    cResult3 = 0;
875
0
  } else {
876
0
    cResult0 = (Guchar)((m1 * cDest[0] + m2 * c0[0] + aSrc * cSrc[0])
877
0
            / alphaI);
878
0
    cResult1 = (Guchar)((m1 * cDest[1] + m2 * c0[1] + aSrc * cSrc[1])
879
0
            / alphaI);
880
0
    cResult2 = (Guchar)((m1 * cDest[2] + m2 * c0[2] + aSrc * cSrc[2])
881
0
            / alphaI);
882
0
    cResult3 = (Guchar)((m1 * cDest[3] + m2 * c0[2] + aSrc * cSrc[3])
883
0
            / alphaI);
884
0
  }
885
0
  break;
886
0
#endif
887
888
0
      case splashPipeResultColorAlphaBlendMono:
889
0
  if (alphaI == 0) {
890
0
    cResult0 = 0;
891
0
  } else {
892
0
    cResult0 = (Guchar)((m1 * cDest[0] + m2 * c0[0] +
893
0
             m3 * cSrc[0] + m4 * cBlend[0])
894
0
            / alphaI);
895
0
  }
896
0
  break;
897
1.01k
      case splashPipeResultColorAlphaBlendRGB:
898
1.01k
  if (alphaI == 0) {
899
144
    cResult0 = 0;
900
144
    cResult1 = 0;
901
144
    cResult2 = 0;
902
875
  } else {
903
875
    cResult0 = (Guchar)((m1 * cDest[0] + m2 * c0[0] +
904
875
             m3 * cSrc[0] + m4 * cBlend[0])
905
875
            / alphaI);
906
875
    cResult1 = (Guchar)((m1 * cDest[1] + m2 * c0[1] +
907
875
             m3 * cSrc[1] + m4 * cBlend[1])
908
875
            / alphaI);
909
875
    cResult2 = (Guchar)((m1 * cDest[2] + m2 * c0[2] +
910
875
             m3 * cSrc[2] + m4 * cBlend[2])
911
875
            / alphaI);
912
875
  }
913
1.01k
  break;
914
0
#if SPLASH_CMYK
915
0
      case splashPipeResultColorAlphaBlendCMYK:
916
0
  if (alphaI == 0) {
917
0
    cResult0 = 0;
918
0
    cResult1 = 0;
919
0
    cResult2 = 0;
920
0
    cResult3 = 0;
921
0
  } else {
922
0
    cResult0 = (Guchar)((m1 * cDest[0] + m2 * c0[0] +
923
0
             m3 * cSrc[0] + m4 * cBlend[0])
924
0
            / alphaI);
925
0
    cResult1 = (Guchar)((m1 * cDest[1] + m2 * c0[1] +
926
0
             m3 * cSrc[1] + m4 * cBlend[1])
927
0
            / alphaI);
928
0
    cResult2 = (Guchar)((m1 * cDest[2] + m2 * c0[2] +
929
0
             m3 * cSrc[2] + m4 * cBlend[2])
930
0
            / alphaI);
931
0
    cResult3 = (Guchar)((m1 * cDest[3] + m2 * c0[3] +
932
0
             m3 * cSrc[3] + m4 * cBlend[3])
933
0
            / alphaI);
934
0
  }
935
0
  break;
936
2.62k
#endif
937
2.62k
      }
938
939
2.62k
    } // if (noTransparency && !blendFunc)
940
941
    //----- write destination pixel
942
943
2.62k
    switch (bitmap->mode) {
944
0
    case splashModeMono1:
945
0
      if (state->screen->test(x, y, cResult0)) {
946
0
  *destColorPtr |= destColorMask;
947
0
      } else {
948
0
  *destColorPtr &= (Guchar)~destColorMask;
949
0
      }
950
0
      destColorPtr += destColorMask & 1;
951
0
      destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1));
952
0
      break;
953
0
    case splashModeMono8:
954
0
      *destColorPtr++ = cResult0;
955
0
      break;
956
2.62k
    case splashModeRGB8:
957
2.62k
      destColorPtr[0] = cResult0;
958
2.62k
      destColorPtr[1] = cResult1;
959
2.62k
      destColorPtr[2] = cResult2;
960
2.62k
      destColorPtr += 3;
961
2.62k
      break;
962
0
    case splashModeBGR8:
963
0
      destColorPtr[0] = cResult2;
964
0
      destColorPtr[1] = cResult1;
965
0
      destColorPtr[2] = cResult0;
966
0
      destColorPtr += 3;
967
0
      break;
968
0
#if SPLASH_CMYK
969
0
    case splashModeCMYK8:
970
0
      destColorPtr[0] = cResult0;
971
0
      destColorPtr[1] = cResult1;
972
0
      destColorPtr[2] = cResult2;
973
0
      destColorPtr[3] = cResult3;
974
0
      destColorPtr += 4;
975
0
      break;
976
2.62k
#endif
977
2.62k
    }
978
2.62k
    if (destShapePtr) {
979
0
      *destShapePtr++ = fResult;
980
0
    }
981
2.62k
    if (destAlphaPtr) {
982
2.62k
      *destAlphaPtr++ = aResult;
983
2.62k
    }
984
985
2.62k
    cSrcPtr += cSrcStride;
986
2.62k
    shapePtr2 += shapeStride;
987
2.62k
    alphaPtr2 += alphaStride;
988
2.62k
  } // for (x ...)
989
990
2.62k
  updateModX(lastX);
991
2.62k
}
992
993
// special case:
994
// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
995
// bitmap->mode == splashModeMono1 && !bitmap->alpha) {
996
void Splash::pipeRunSimpleMono1(SplashPipe *pipe, int x0, int x1, int y,
997
        Guchar *shapePtr, Guchar *alphaPtr,
998
0
        SplashColorPtr cSrcPtr) {
999
0
  Guchar cResult0;
1000
0
  SplashColorPtr destColorPtr;
1001
0
  Guchar destColorMask;
1002
0
  SplashScreenCursor screenCursor;
1003
0
  int cSrcStride, x;
1004
1005
0
  if (cSrcPtr) {
1006
0
    cSrcStride = 1;
1007
0
  } else {
1008
0
    cSrcPtr = pipe->cSrcVal;
1009
0
    cSrcStride = 0;
1010
0
  }
1011
0
  if (x0 > x1) {
1012
0
    return;
1013
0
  }
1014
0
  updateModX(x0);
1015
0
  updateModX(x1);
1016
0
  updateModY(y);
1017
1018
0
  useBitmapRow(y);
1019
1020
0
  destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)];
1021
0
  destColorMask = (Guchar)(0x80 >> (x0 & 7));
1022
1023
0
  screenCursor = state->screen->getTestCursor(y);
1024
1025
0
  for (x = x0; x <= x1; ++x) {
1026
1027
    //----- write destination pixel
1028
0
    cResult0 = state->grayTransfer[cSrcPtr[0]];
1029
0
    if (state->screen->testWithCursor(screenCursor, x, cResult0)) {
1030
0
      *destColorPtr |= destColorMask;
1031
0
    } else {
1032
0
      *destColorPtr &= (Guchar)~destColorMask;
1033
0
    }
1034
0
    destColorPtr += destColorMask & 1;
1035
0
    destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1));
1036
1037
0
    cSrcPtr += cSrcStride;
1038
0
  }
1039
0
}
1040
1041
// special case:
1042
// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
1043
// bitmap->mode == splashModeMono8 && bitmap->alpha) {
1044
void Splash::pipeRunSimpleMono8(SplashPipe *pipe, int x0, int x1, int y,
1045
        Guchar *shapePtr, Guchar *alphaPtr,
1046
0
        SplashColorPtr cSrcPtr) {
1047
0
  SplashColorPtr destColorPtr;
1048
0
  Guchar *destAlphaPtr;
1049
0
  int cSrcStride, x;
1050
1051
0
  if (cSrcPtr) {
1052
0
    cSrcStride = 1;
1053
0
  } else {
1054
0
    cSrcPtr = pipe->cSrcVal;
1055
0
    cSrcStride = 0;
1056
0
  }
1057
0
  if (x0 > x1) {
1058
0
    return;
1059
0
  }
1060
0
  updateModX(x0);
1061
0
  updateModX(x1);
1062
0
  updateModY(y);
1063
1064
0
  useBitmapRow(y);
1065
1066
0
  destColorPtr = &bitmap->data[y * bitmap->rowSize + x0];
1067
0
  destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
1068
1069
0
  for (x = x0; x <= x1; ++x) {
1070
1071
    //----- write destination pixel
1072
0
    *destColorPtr++ = state->grayTransfer[cSrcPtr[0]];
1073
0
    *destAlphaPtr++ = 255;
1074
1075
0
    cSrcPtr += cSrcStride;
1076
0
  }
1077
0
}
1078
1079
// special case:
1080
// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
1081
// bitmap->mode == splashModeRGB8 && bitmap->alpha) {
1082
void Splash::pipeRunSimpleRGB8(SplashPipe *pipe, int x0, int x1, int y,
1083
             Guchar *shapePtr, Guchar *alphaPtr,
1084
0
             SplashColorPtr cSrcPtr) {
1085
0
  SplashColorPtr destColorPtr;
1086
0
  Guchar *destAlphaPtr;
1087
0
  int cSrcStride, x;
1088
1089
0
  if (cSrcPtr) {
1090
0
    cSrcStride = 3;
1091
0
  } else {
1092
0
    cSrcPtr = pipe->cSrcVal;
1093
0
    cSrcStride = 0;
1094
0
  }
1095
0
  if (x0 > x1) {
1096
0
    return;
1097
0
  }
1098
0
  updateModX(x0);
1099
0
  updateModX(x1);
1100
0
  updateModY(y);
1101
1102
0
  useBitmapRow(y);
1103
1104
0
  destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
1105
0
  destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
1106
1107
0
  for (x = x0; x <= x1; ++x) {
1108
1109
    //----- write destination pixel
1110
0
    destColorPtr[0] = state->rgbTransferR[cSrcPtr[0]];
1111
0
    destColorPtr[1] = state->rgbTransferG[cSrcPtr[1]];
1112
0
    destColorPtr[2] = state->rgbTransferB[cSrcPtr[2]];
1113
0
    destColorPtr += 3;
1114
0
    *destAlphaPtr++ = 255;
1115
1116
0
    cSrcPtr += cSrcStride;
1117
0
  }
1118
0
}
1119
1120
// special case:
1121
// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
1122
// bitmap->mode == splashModeBGR8 && bitmap->alpha) {
1123
void Splash::pipeRunSimpleBGR8(SplashPipe *pipe, int x0, int x1, int y,
1124
             Guchar *shapePtr, Guchar *alphaPtr,
1125
0
             SplashColorPtr cSrcPtr) {
1126
0
  SplashColorPtr destColorPtr;
1127
0
  Guchar *destAlphaPtr;
1128
0
  int cSrcStride, x;
1129
1130
0
  if (cSrcPtr) {
1131
0
    cSrcStride = 3;
1132
0
  } else {
1133
0
    cSrcPtr = pipe->cSrcVal;
1134
0
    cSrcStride = 0;
1135
0
  }
1136
0
  if (x0 > x1) {
1137
0
    return;
1138
0
  }
1139
0
  updateModX(x0);
1140
0
  updateModX(x1);
1141
0
  updateModY(y);
1142
1143
0
  useBitmapRow(y);
1144
1145
0
  destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
1146
0
  destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
1147
1148
0
  for (x = x0; x <= x1; ++x) {
1149
1150
    //----- write destination pixel
1151
0
    destColorPtr[0] = state->rgbTransferB[cSrcPtr[2]];
1152
0
    destColorPtr[1] = state->rgbTransferG[cSrcPtr[1]];
1153
0
    destColorPtr[2] = state->rgbTransferR[cSrcPtr[0]];
1154
0
    destColorPtr += 3;
1155
0
    *destAlphaPtr++ = 255;
1156
1157
0
    cSrcPtr += cSrcStride;
1158
0
  }
1159
0
}
1160
1161
#if SPLASH_CMYK
1162
// special case:
1163
// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
1164
// bitmap->mode == splashModeCMYK8 && bitmap->alpha) {
1165
void Splash::pipeRunSimpleCMYK8(SplashPipe *pipe, int x0, int x1, int y,
1166
        Guchar *shapePtr, Guchar *alphaPtr,
1167
0
        SplashColorPtr cSrcPtr) {
1168
0
  SplashColorPtr destColorPtr;
1169
0
  Guchar *destAlphaPtr;
1170
0
  int cSrcStride, x;
1171
1172
0
  if (cSrcPtr) {
1173
0
    cSrcStride = 4;
1174
0
  } else {
1175
0
    cSrcPtr = pipe->cSrcVal;
1176
0
    cSrcStride = 0;
1177
0
  }
1178
0
  if (x0 > x1) {
1179
0
    return;
1180
0
  }
1181
0
  updateModX(x0);
1182
0
  updateModX(x1);
1183
0
  updateModY(y);
1184
1185
0
  useBitmapRow(y);
1186
1187
0
  destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0];
1188
0
  destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
1189
1190
0
  for (x = x0; x <= x1; ++x) {
1191
1192
    //----- write destination pixel
1193
0
    destColorPtr[0] = state->cmykTransferC[cSrcPtr[0]];
1194
0
    destColorPtr[1] = state->cmykTransferM[cSrcPtr[1]];
1195
0
    destColorPtr[2] = state->cmykTransferY[cSrcPtr[2]];
1196
0
    destColorPtr[3] = state->cmykTransferK[cSrcPtr[3]];
1197
0
    destColorPtr += 4;
1198
0
    *destAlphaPtr++ = 255;
1199
1200
0
    cSrcPtr += cSrcStride;
1201
0
  }
1202
0
}
1203
#endif
1204
1205
1206
// special case:
1207
// !pipe->pattern && pipe->alphaOnly && !state->blendFunc &&
1208
// bitmap->mode == splashModeMono1 && !bitmap->alpha
1209
void Splash::pipeRunShapeMono1(SplashPipe *pipe, int x0, int x1, int y,
1210
             Guchar *shapePtr, Guchar *alphaPtr,
1211
8.26k
             SplashColorPtr cSrcPtr) {
1212
8.26k
  Guchar alpha, aSrc, cSrc0, cDest0, cResult0;
1213
8.26k
  SplashColorPtr destColorPtr;
1214
8.26k
  Guchar destColorMask;
1215
8.26k
  SplashScreenCursor screenCursor;
1216
8.26k
  int cSrcStride, x, lastX;
1217
1218
8.26k
  if (cSrcPtr) {
1219
0
    cSrcStride = 1;
1220
8.26k
  } else {
1221
8.26k
    cSrcPtr = pipe->cSrcVal;
1222
8.26k
    cSrcStride = 0;
1223
8.26k
  }
1224
2.88M
  for (; x0 <= x1; ++x0) {
1225
2.88M
    if (*alphaPtr) {
1226
4.06k
      break;
1227
4.06k
    }
1228
2.87M
    cSrcPtr += cSrcStride;
1229
2.87M
    ++alphaPtr;
1230
2.87M
  }
1231
8.26k
  if (x0 > x1) {
1232
4.20k
    return;
1233
4.20k
  }
1234
4.06k
  updateModX(x0);
1235
4.06k
  updateModY(y);
1236
4.06k
  lastX = x0;
1237
1238
4.06k
  useBitmapRow(y);
1239
1240
4.06k
  destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)];
1241
4.06k
  destColorMask = (Guchar)(0x80 >> (x0 & 7));
1242
1243
4.06k
  screenCursor = state->screen->getTestCursor(y);
1244
1245
710k
  for (x = x0; x <= x1; ++x) {
1246
1247
    //----- alpha
1248
706k
    alpha = *alphaPtr;
1249
706k
    if (!alpha) {
1250
220k
      destColorPtr += destColorMask & 1;
1251
220k
      destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1));
1252
220k
      cSrcPtr += cSrcStride;
1253
220k
      ++alphaPtr;
1254
220k
      continue;
1255
220k
    }
1256
485k
    lastX = x;
1257
1258
    //----- source color
1259
485k
    cSrc0 = state->grayTransfer[cSrcPtr[0]];
1260
1261
    //----- source alpha
1262
485k
    aSrc = alpha;
1263
1264
    //----- special case for aSrc = 255
1265
485k
    if (aSrc == 255) {
1266
485k
      cResult0 = cSrc0;
1267
485k
    } else {
1268
1269
      //----- read destination pixel
1270
0
      cDest0 = (*destColorPtr & destColorMask) ? 0xff : 0x00;
1271
1272
      //----- result color
1273
      // note: aDest = alphaI = aResult = 0xff
1274
0
      cResult0 = (Guchar)div255((0xff - aSrc) * cDest0 + aSrc * cSrc0);
1275
0
    }
1276
1277
    //----- write destination pixel
1278
485k
    if (state->screen->testWithCursor(screenCursor, x, cResult0)) {
1279
485k
      *destColorPtr |= destColorMask;
1280
485k
    } else {
1281
0
      *destColorPtr &= (Guchar)~destColorMask;
1282
0
    }
1283
485k
    destColorPtr += destColorMask & 1;
1284
485k
    destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1));
1285
1286
485k
    cSrcPtr += cSrcStride;
1287
485k
    ++alphaPtr;
1288
485k
  }
1289
1290
4.06k
  updateModX(lastX);
1291
4.06k
}
1292
1293
// special case:
1294
// !pipe->pattern && pipe->alphaOnly && !state->blendFunc &&
1295
// bitmap->mode == splashModeMono8 && bitmap->alpha
1296
void Splash::pipeRunShapeMono8(SplashPipe *pipe, int x0, int x1, int y,
1297
             Guchar *shapePtr, Guchar *alphaPtr,
1298
375
             SplashColorPtr cSrcPtr) {
1299
375
  Guchar alpha, aSrc, aDest, alphaI, aResult, cSrc0, cDest0, cResult0;
1300
375
  SplashColorPtr destColorPtr;
1301
375
  Guchar *destAlphaPtr;
1302
375
  int cSrcStride, x, lastX;
1303
1304
375
  if (cSrcPtr) {
1305
0
    cSrcStride = 1;
1306
375
  } else {
1307
375
    cSrcPtr = pipe->cSrcVal;
1308
375
    cSrcStride = 0;
1309
375
  }
1310
410
  for (; x0 <= x1; ++x0) {
1311
375
    if (*alphaPtr) {
1312
340
      break;
1313
340
    }
1314
35
    cSrcPtr += cSrcStride;
1315
35
    ++alphaPtr;
1316
35
  }
1317
375
  if (x0 > x1) {
1318
35
    return;
1319
35
  }
1320
340
  updateModX(x0);
1321
340
  updateModY(y);
1322
340
  lastX = x0;
1323
1324
340
  useBitmapRow(y);
1325
1326
340
  destColorPtr = &bitmap->data[y * bitmap->rowSize + x0];
1327
340
  destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
1328
1329
680
  for (x = x0; x <= x1; ++x) {
1330
1331
    //----- alpha
1332
340
    alpha = *alphaPtr;
1333
340
    if (!alpha) {
1334
0
      ++destColorPtr;
1335
0
      ++destAlphaPtr;
1336
0
      cSrcPtr += cSrcStride;
1337
0
      ++alphaPtr;
1338
0
      continue;
1339
0
    }
1340
340
    lastX = x;
1341
1342
    //----- source color
1343
340
    cSrc0 = state->grayTransfer[cSrcPtr[0]];
1344
1345
    //----- source alpha
1346
340
    aSrc = alpha;
1347
1348
    //----- special case for aSrc = 255
1349
340
    if (aSrc == 255) {
1350
216
      aResult = 255;
1351
216
      cResult0 = cSrc0;
1352
216
    } else {
1353
1354
      //----- read destination alpha
1355
124
      aDest = *destAlphaPtr;
1356
1357
      //----- special case for aDest = 0
1358
124
      if (aDest == 0) {
1359
124
  aResult = aSrc;
1360
124
  cResult0 = cSrc0;
1361
124
      } else {
1362
1363
  //----- read destination pixel
1364
0
  cDest0 = *destColorPtr;
1365
1366
  //----- result alpha and non-isolated group element correction
1367
0
  aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
1368
0
  alphaI = aResult;
1369
1370
  //----- result color
1371
0
  cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
1372
0
      }
1373
124
    }
1374
1375
    //----- write destination pixel
1376
340
    *destColorPtr++ = cResult0;
1377
340
    *destAlphaPtr++ = aResult;
1378
1379
340
    cSrcPtr += cSrcStride;
1380
340
    ++alphaPtr;
1381
340
  }
1382
1383
340
  updateModX(lastX);
1384
340
}
1385
1386
// special case:
1387
// !pipe->pattern && pipe->alphaOnly && !state->blendFunc &&
1388
// bitmap->mode == splashModeRGB8 && bitmap->alpha
1389
void Splash::pipeRunShapeRGB8(SplashPipe *pipe, int x0, int x1, int y,
1390
            Guchar *shapePtr, Guchar *alphaPtr,
1391
21.8k
            SplashColorPtr cSrcPtr) {
1392
21.8k
  Guchar alpha, aSrc, aDest, alphaI, aResult;
1393
21.8k
  Guchar cSrc0, cSrc1, cSrc2;
1394
21.8k
  Guchar cDest0, cDest1, cDest2;
1395
21.8k
  Guchar cResult0, cResult1, cResult2;
1396
21.8k
  SplashColorPtr destColorPtr;
1397
21.8k
  Guchar *destAlphaPtr;
1398
21.8k
  int cSrcStride, x, lastX;
1399
1400
21.8k
  if (cSrcPtr) {
1401
712
    cSrcStride = 3;
1402
21.1k
  } else {
1403
21.1k
    cSrcPtr = pipe->cSrcVal;
1404
21.1k
    cSrcStride = 0;
1405
21.1k
  }
1406
25.8k
  for (; x0 <= x1; ++x0) {
1407
21.8k
    if (*alphaPtr) {
1408
17.9k
      break;
1409
17.9k
    }
1410
3.91k
    cSrcPtr += cSrcStride;
1411
3.91k
    ++alphaPtr;
1412
3.91k
  }
1413
21.8k
  if (x0 > x1) {
1414
3.91k
    return;
1415
3.91k
  }
1416
17.9k
  updateModX(x0);
1417
17.9k
  updateModY(y);
1418
17.9k
  lastX = x0;
1419
1420
17.9k
  useBitmapRow(y);
1421
1422
17.9k
  destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
1423
17.9k
  destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
1424
1425
35.9k
  for (x = x0; x <= x1; ++x) {
1426
1427
    //----- alpha
1428
17.9k
    alpha = *alphaPtr;
1429
17.9k
    if (!alpha) {
1430
0
      destColorPtr += 3;
1431
0
      ++destAlphaPtr;
1432
0
      cSrcPtr += cSrcStride;
1433
0
      ++alphaPtr;
1434
0
      continue;
1435
0
    }
1436
17.9k
    lastX = x;
1437
1438
    //----- source color
1439
17.9k
    cSrc0 = state->rgbTransferR[cSrcPtr[0]];
1440
17.9k
    cSrc1 = state->rgbTransferG[cSrcPtr[1]];
1441
17.9k
    cSrc2 = state->rgbTransferB[cSrcPtr[2]];
1442
1443
    //----- source alpha
1444
17.9k
    aSrc = alpha;
1445
1446
    //----- special case for aSrc = 255
1447
17.9k
    if (aSrc == 255) {
1448
14.7k
      aResult = 255;
1449
14.7k
      cResult0 = cSrc0;
1450
14.7k
      cResult1 = cSrc1;
1451
14.7k
      cResult2 = cSrc2;
1452
14.7k
    } else {
1453
1454
      //----- read destination alpha
1455
3.23k
      aDest = *destAlphaPtr;
1456
1457
      //----- special case for aDest = 0
1458
3.23k
      if (aDest == 0) {
1459
852
  aResult = aSrc;
1460
852
  cResult0 = cSrc0;
1461
852
  cResult1 = cSrc1;
1462
852
  cResult2 = cSrc2;
1463
2.38k
      } else {
1464
1465
  //----- read destination pixel
1466
2.38k
  cDest0 = destColorPtr[0];
1467
2.38k
  cDest1 = destColorPtr[1];
1468
2.38k
  cDest2 = destColorPtr[2];
1469
1470
  //----- result alpha and non-isolated group element correction
1471
2.38k
  aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
1472
2.38k
  alphaI = aResult;
1473
1474
  //----- result color
1475
2.38k
  cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
1476
2.38k
  cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
1477
2.38k
  cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
1478
2.38k
      }
1479
3.23k
    }
1480
1481
    //----- write destination pixel
1482
17.9k
    destColorPtr[0] = cResult0;
1483
17.9k
    destColorPtr[1] = cResult1;
1484
17.9k
    destColorPtr[2] = cResult2;
1485
17.9k
    destColorPtr += 3;
1486
17.9k
    *destAlphaPtr++ = aResult;
1487
1488
17.9k
    cSrcPtr += cSrcStride;
1489
17.9k
    ++alphaPtr;
1490
17.9k
  }
1491
1492
17.9k
  updateModX(lastX);
1493
17.9k
}
1494
1495
// special case:
1496
// !pipe->pattern && pipe->alphaOnly && !state->blendFunc &&
1497
// bitmap->mode == splashModeBGR8 && bitmap->alpha
1498
void Splash::pipeRunShapeBGR8(SplashPipe *pipe, int x0, int x1, int y,
1499
            Guchar *shapePtr, Guchar *alphaPtr,
1500
0
            SplashColorPtr cSrcPtr) {
1501
0
  Guchar alpha, aSrc, aDest, alphaI, aResult;
1502
0
  Guchar cSrc0, cSrc1, cSrc2;
1503
0
  Guchar cDest0, cDest1, cDest2;
1504
0
  Guchar cResult0, cResult1, cResult2;
1505
0
  SplashColorPtr destColorPtr;
1506
0
  Guchar *destAlphaPtr;
1507
0
  int cSrcStride, x, lastX;
1508
1509
0
  if (cSrcPtr) {
1510
0
    cSrcStride = 3;
1511
0
  } else {
1512
0
    cSrcPtr = pipe->cSrcVal;
1513
0
    cSrcStride = 0;
1514
0
  }
1515
0
  for (; x0 <= x1; ++x0) {
1516
0
    if (*alphaPtr) {
1517
0
      break;
1518
0
    }
1519
0
    cSrcPtr += cSrcStride;
1520
0
    ++alphaPtr;
1521
0
  }
1522
0
  if (x0 > x1) {
1523
0
    return;
1524
0
  }
1525
0
  updateModX(x0);
1526
0
  updateModY(y);
1527
0
  lastX = x0;
1528
1529
0
  useBitmapRow(y);
1530
1531
0
  destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
1532
0
  destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
1533
1534
0
  for (x = x0; x <= x1; ++x) {
1535
1536
    //----- alpha
1537
0
    alpha = *alphaPtr;
1538
0
    if (!alpha) {
1539
0
      destColorPtr += 3;
1540
0
      ++destAlphaPtr;
1541
0
      cSrcPtr += cSrcStride;
1542
0
      ++alphaPtr;
1543
0
      continue;
1544
0
    }
1545
0
    lastX = x;
1546
1547
    //----- source color
1548
0
    cSrc0 = state->rgbTransferR[cSrcPtr[0]];
1549
0
    cSrc1 = state->rgbTransferG[cSrcPtr[1]];
1550
0
    cSrc2 = state->rgbTransferB[cSrcPtr[2]];
1551
1552
    //----- source alpha
1553
0
    aSrc = alpha;
1554
1555
    //----- special case for aSrc = 255
1556
0
    if (aSrc == 255) {
1557
0
      aResult = 255;
1558
0
      cResult0 = cSrc0;
1559
0
      cResult1 = cSrc1;
1560
0
      cResult2 = cSrc2;
1561
0
    } else {
1562
1563
      //----- read destination alpha
1564
0
      aDest = *destAlphaPtr;
1565
1566
      //----- special case for aDest = 0
1567
0
      if (aDest == 0) {
1568
0
  aResult = aSrc;
1569
0
  cResult0 = cSrc0;
1570
0
  cResult1 = cSrc1;
1571
0
  cResult2 = cSrc2;
1572
0
      } else {
1573
1574
  //----- read destination pixel
1575
0
  cDest0 = destColorPtr[2];
1576
0
  cDest1 = destColorPtr[1];
1577
0
  cDest2 = destColorPtr[0];
1578
1579
  //----- result alpha and non-isolated group element correction
1580
0
  aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
1581
0
  alphaI = aResult;
1582
1583
  //----- result color
1584
0
  cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
1585
0
  cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
1586
0
  cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
1587
0
      }
1588
0
    }
1589
1590
    //----- write destination pixel
1591
0
    destColorPtr[0] = cResult2;
1592
0
    destColorPtr[1] = cResult1;
1593
0
    destColorPtr[2] = cResult0;
1594
0
    destColorPtr += 3;
1595
0
    *destAlphaPtr++ = aResult;
1596
1597
0
    cSrcPtr += cSrcStride;
1598
0
    ++alphaPtr;
1599
0
  }
1600
1601
0
  updateModX(lastX);
1602
0
}
1603
1604
#if SPLASH_CMYK
1605
// special case:
1606
// !pipe->pattern && pipe->alphaOnly && !state->blendFunc &&
1607
// bitmap->mode == splashModeCMYK8 && bitmap->alpha
1608
void Splash::pipeRunShapeCMYK8(SplashPipe *pipe, int x0, int x1, int y,
1609
             Guchar *shapePtr, Guchar *alphaPtr,
1610
43
             SplashColorPtr cSrcPtr) {
1611
43
  Guchar alpha, aSrc, aDest, alphaI, aResult;
1612
43
  Guchar cSrc0, cSrc1, cSrc2, cSrc3;
1613
43
  Guchar cDest0, cDest1, cDest2, cDest3;
1614
43
  Guchar cResult0, cResult1, cResult2, cResult3;
1615
43
  SplashColorPtr destColorPtr;
1616
43
  Guchar *destAlphaPtr;
1617
43
  int cSrcStride, x, lastX;
1618
1619
43
  if (cSrcPtr) {
1620
43
    cSrcStride = 4;
1621
43
  } else {
1622
0
    cSrcPtr = pipe->cSrcVal;
1623
0
    cSrcStride = 0;
1624
0
  }
1625
43
  for (; x0 <= x1; ++x0) {
1626
43
    if (*alphaPtr) {
1627
43
      break;
1628
43
    }
1629
0
    cSrcPtr += cSrcStride;
1630
0
    ++alphaPtr;
1631
0
  }
1632
43
  if (x0 > x1) {
1633
0
    return;
1634
0
  }
1635
43
  updateModX(x0);
1636
43
  updateModY(y);
1637
43
  lastX = x0;
1638
1639
43
  useBitmapRow(y);
1640
1641
43
  destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0];
1642
43
  destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
1643
1644
86
  for (x = x0; x <= x1; ++x) {
1645
1646
    //----- alpha
1647
43
    alpha = *alphaPtr;
1648
43
    if (!alpha) {
1649
0
      destColorPtr += 4;
1650
0
      ++destAlphaPtr;
1651
0
      cSrcPtr += cSrcStride;
1652
0
      ++alphaPtr;
1653
0
      continue;
1654
0
    }
1655
43
    lastX = x;
1656
1657
    //----- read destination pixel
1658
43
    cDest0 = destColorPtr[0];
1659
43
    cDest1 = destColorPtr[1];
1660
43
    cDest2 = destColorPtr[2];
1661
43
    cDest3 = destColorPtr[3];
1662
43
    aDest = *destAlphaPtr;
1663
1664
    //----- overprint
1665
43
    cSrc0 = state->cmykTransferC[cSrcPtr[0]];
1666
43
    cSrc1 = state->cmykTransferM[cSrcPtr[1]];
1667
43
    cSrc2 = state->cmykTransferY[cSrcPtr[2]];
1668
43
    cSrc3 = state->cmykTransferK[cSrcPtr[3]];
1669
1670
    //----- source alpha
1671
43
    aSrc = alpha;
1672
1673
    //----- special case for aSrc = 255
1674
43
    if (aSrc == 255) {
1675
43
      aResult = 255;
1676
43
      cResult0 = cSrc0;
1677
43
      cResult1 = cSrc1;
1678
43
      cResult2 = cSrc2;
1679
43
      cResult3 = cSrc3;
1680
43
    } else {
1681
1682
      //----- special case for aDest = 0
1683
0
      if (aDest == 0) {
1684
0
  aResult = aSrc;
1685
0
  cResult0 = cSrc0;
1686
0
  cResult1 = cSrc1;
1687
0
  cResult2 = cSrc2;
1688
0
  cResult3 = cSrc3;
1689
0
      } else {
1690
1691
  //----- result alpha and non-isolated group element correction
1692
0
  aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
1693
0
  alphaI = aResult;
1694
1695
  //----- result color
1696
0
  cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
1697
0
  cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
1698
0
  cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
1699
0
  cResult3 = (Guchar)(((alphaI - aSrc) * cDest3 + aSrc * cSrc3) / alphaI);
1700
0
      }
1701
0
    }
1702
1703
    //----- write destination pixel
1704
43
    destColorPtr[0] = cResult0;
1705
43
    destColorPtr[1] = cResult1;
1706
43
    destColorPtr[2] = cResult2;
1707
43
    destColorPtr[3] = cResult3;
1708
43
    destColorPtr += 4;
1709
43
    *destAlphaPtr++ = aResult;
1710
1711
43
    cSrcPtr += cSrcStride;
1712
43
    ++alphaPtr;
1713
43
  }
1714
1715
43
  updateModX(lastX);
1716
43
}
1717
#endif
1718
1719
1720
// special case:
1721
// !pipe->pattern && pipe->alphaOnly && !state->blendFunc &&
1722
// bitmap->mode == splashModeMono8 && !bitmap->alpha
1723
void Splash::pipeRunShapeNoAlphaMono8(SplashPipe *pipe, int x0, int x1, int y,
1724
                                      Guchar *shapePtr, Guchar *alphaPtr,
1725
2.23k
                                      SplashColorPtr cSrcPtr) {
1726
2.23k
  Guchar alpha, aSrc, cSrc0, cDest0, cResult0;
1727
2.23k
  SplashColorPtr destColorPtr;
1728
2.23k
  int cSrcStride, x, lastX;
1729
1730
2.23k
  if (cSrcPtr) {
1731
792
    cSrcStride = 1;
1732
1.43k
  } else {
1733
1.43k
    cSrcPtr = pipe->cSrcVal;
1734
1.43k
    cSrcStride = 0;
1735
1.43k
  }
1736
5.70k
  for (; x0 <= x1; ++x0) {
1737
4.86k
    if (*alphaPtr) {
1738
1.38k
      break;
1739
1.38k
    }
1740
3.47k
    cSrcPtr += cSrcStride;
1741
3.47k
    ++alphaPtr;
1742
3.47k
  }
1743
2.23k
  if (x0 > x1) {
1744
846
    return;
1745
846
  }
1746
1.38k
  updateModX(x0);
1747
1.38k
  updateModY(y);
1748
1.38k
  lastX = x0;
1749
1750
1.38k
  useBitmapRow(y);
1751
1752
1.38k
  destColorPtr = &bitmap->data[y * bitmap->rowSize + x0];
1753
1754
13.9k
  for (x = x0; x <= x1; ++x) {
1755
1756
    //----- alpha
1757
12.5k
    alpha = *alphaPtr;
1758
12.5k
    if (!alpha) {
1759
538
      ++destColorPtr;
1760
538
      cSrcPtr += cSrcStride;
1761
538
      ++alphaPtr;
1762
538
      continue;
1763
538
    }
1764
11.9k
    lastX = x;
1765
1766
    //----- source color
1767
11.9k
    cSrc0 = state->grayTransfer[cSrcPtr[0]];
1768
1769
    //----- source alpha
1770
11.9k
    aSrc = alpha;
1771
1772
    //----- special case for aSrc = 255
1773
11.9k
    if (aSrc == 255) {
1774
792
      cResult0 = cSrc0;
1775
11.2k
    } else {
1776
1777
      //----- read destination pixel
1778
11.2k
      cDest0 = *destColorPtr;
1779
1780
      //----- result color
1781
11.2k
      cResult0 = div255((255 - aSrc) * cDest0 + aSrc * cSrc0);
1782
11.2k
    }
1783
1784
    //----- write destination pixel
1785
11.9k
    *destColorPtr++ = cResult0;
1786
1787
11.9k
    cSrcPtr += cSrcStride;
1788
11.9k
    ++alphaPtr;
1789
11.9k
  }
1790
1791
1.38k
  updateModX(lastX);
1792
1.38k
}
1793
1794
// special case:
1795
// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
1796
// usesAlpha && !pipe->alpha0Ptr && !state->blendFunc &&
1797
// !pipe->nonIsolatedGroup &&
1798
// bitmap->mode == splashModeMono1 && !bitmap->alpha
1799
void Splash::pipeRunAAMono1(SplashPipe *pipe, int x0, int x1, int y,
1800
          Guchar *shapePtr, Guchar *alphaPtr,
1801
0
          SplashColorPtr cSrcPtr) {
1802
0
  Guchar alpha, aSrc, cSrc0, cDest0, cResult0;
1803
0
  SplashColorPtr destColorPtr;
1804
0
  Guchar destColorMask;
1805
0
  SplashScreenCursor screenCursor;
1806
0
  int cSrcStride, x, lastX;
1807
1808
0
  if (cSrcPtr) {
1809
0
    cSrcStride = 1;
1810
0
  } else {
1811
0
    cSrcPtr = pipe->cSrcVal;
1812
0
    cSrcStride = 0;
1813
0
  }
1814
0
  for (; x0 <= x1; ++x0) {
1815
0
    if (*alphaPtr) {
1816
0
      break;
1817
0
    }
1818
0
    cSrcPtr += cSrcStride;
1819
0
    ++alphaPtr;
1820
0
  }
1821
0
  if (x0 > x1) {
1822
0
    return;
1823
0
  }
1824
0
  updateModX(x0);
1825
0
  updateModY(y);
1826
0
  lastX = x0;
1827
1828
0
  useBitmapRow(y);
1829
1830
0
  destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)];
1831
0
  destColorMask = (Guchar)(0x80 >> (x0 & 7));
1832
1833
0
  screenCursor = state->screen->getTestCursor(y);
1834
1835
0
  for (x = x0; x <= x1; ++x) {
1836
1837
    //----- alpha
1838
0
    alpha = *alphaPtr;
1839
0
    if (!alpha) {
1840
0
      destColorPtr += destColorMask & 1;
1841
0
      destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1));
1842
0
      cSrcPtr += cSrcStride;
1843
0
      ++alphaPtr;
1844
0
      continue;
1845
0
    }
1846
0
    lastX = x;
1847
1848
    //----- read destination pixel
1849
0
    cDest0 = (*destColorPtr & destColorMask) ? 0xff : 0x00;
1850
1851
    //----- source color
1852
0
    cSrc0 = state->grayTransfer[cSrcPtr[0]];
1853
1854
    //----- source alpha
1855
0
    aSrc = div255(pipe->aInput * alpha);
1856
1857
    //----- result color
1858
    // note: aDest = alphaI = aResult = 0xff
1859
0
    cResult0 = (Guchar)div255((0xff - aSrc) * cDest0 + aSrc * cSrc0);
1860
1861
    //----- write destination pixel
1862
0
    if (state->screen->testWithCursor(screenCursor, x, cResult0)) {
1863
0
      *destColorPtr |= destColorMask;
1864
0
    } else {
1865
0
      *destColorPtr &= (Guchar)~destColorMask;
1866
0
    }
1867
0
    destColorPtr += destColorMask & 1;
1868
0
    destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1));
1869
1870
0
    cSrcPtr += cSrcStride;
1871
0
    ++alphaPtr;
1872
0
  }
1873
1874
0
  updateModX(lastX);
1875
0
}
1876
1877
// special case:
1878
// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
1879
// usesAlpha && !pipe->alpha0Ptr && !state->blendFunc &&
1880
// !pipe->nonIsolatedGroup &&
1881
// bitmap->mode == splashModeMono8 && bitmap->alpha
1882
void Splash::pipeRunAAMono8(SplashPipe *pipe, int x0, int x1, int y,
1883
          Guchar *shapePtr, Guchar *alphaPtr,
1884
0
          SplashColorPtr cSrcPtr) {
1885
0
  Guchar alpha, aSrc, aDest, alphaI, aResult, cSrc0, cDest0, cResult0;
1886
0
  SplashColorPtr destColorPtr;
1887
0
  Guchar *destAlphaPtr;
1888
0
  int cSrcStride, x, lastX;
1889
1890
0
  if (cSrcPtr) {
1891
0
    cSrcStride = 1;
1892
0
  } else {
1893
0
    cSrcPtr = pipe->cSrcVal;
1894
0
    cSrcStride = 0;
1895
0
  }
1896
0
  for (; x0 <= x1; ++x0) {
1897
0
    if (*alphaPtr) {
1898
0
      break;
1899
0
    }
1900
0
    cSrcPtr += cSrcStride;
1901
0
    ++alphaPtr;
1902
0
  }
1903
0
  if (x0 > x1) {
1904
0
    return;
1905
0
  }
1906
0
  updateModX(x0);
1907
0
  updateModY(y);
1908
0
  lastX = x0;
1909
1910
0
  useBitmapRow(y);
1911
1912
0
  destColorPtr = &bitmap->data[y * bitmap->rowSize + x0];
1913
0
  destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
1914
1915
0
  for (x = x0; x <= x1; ++x) {
1916
1917
    //----- alpha
1918
0
    alpha = *alphaPtr;
1919
0
    if (!alpha) {
1920
0
      ++destColorPtr;
1921
0
      ++destAlphaPtr;
1922
0
      cSrcPtr += cSrcStride;
1923
0
      ++alphaPtr;
1924
0
      continue;
1925
0
    }
1926
0
    lastX = x;
1927
1928
    //----- read destination pixel
1929
0
    cDest0 = *destColorPtr;
1930
0
    aDest = *destAlphaPtr;
1931
1932
    //----- source color
1933
0
    cSrc0 = state->grayTransfer[cSrcPtr[0]];
1934
1935
    //----- source alpha
1936
0
    aSrc = div255(pipe->aInput * alpha);
1937
1938
    //----- result alpha and non-isolated group element correction
1939
0
    aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
1940
0
    alphaI = aResult;
1941
1942
    //----- result color
1943
0
    if (alphaI == 0) {
1944
0
      cResult0 = 0;
1945
0
    } else {
1946
0
      cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
1947
0
    }
1948
1949
    //----- write destination pixel
1950
0
    *destColorPtr++ = cResult0;
1951
0
    *destAlphaPtr++ = aResult;
1952
1953
0
    cSrcPtr += cSrcStride;
1954
0
    ++alphaPtr;
1955
0
  }
1956
1957
0
  updateModX(lastX);
1958
0
}
1959
1960
// special case:
1961
// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
1962
// usesAlpha && !pipe->alpha0Ptr && !state->blendFunc &&
1963
// !pipe->nonIsolatedGroup &&
1964
// bitmap->mode == splashModeRGB8 && bitmap->alpha
1965
void Splash::pipeRunAARGB8(SplashPipe *pipe, int x0, int x1, int y,
1966
         Guchar *shapePtr, Guchar *alphaPtr,
1967
165
         SplashColorPtr cSrcPtr) {
1968
165
  Guchar alpha, aSrc, aDest, alphaI, aResult;
1969
165
  Guchar cSrc0, cSrc1, cSrc2;
1970
165
  Guchar cDest0, cDest1, cDest2;
1971
165
  Guchar cResult0, cResult1, cResult2;
1972
165
  SplashColorPtr destColorPtr;
1973
165
  Guchar *destAlphaPtr;
1974
165
  int cSrcStride, x, lastX;
1975
1976
165
  if (cSrcPtr) {
1977
13
    cSrcStride = 3;
1978
152
  } else {
1979
152
    cSrcPtr = pipe->cSrcVal;
1980
152
    cSrcStride = 0;
1981
152
  }
1982
168
  for (; x0 <= x1; ++x0) {
1983
165
    if (*alphaPtr) {
1984
162
      break;
1985
162
    }
1986
3
    cSrcPtr += cSrcStride;
1987
3
    ++alphaPtr;
1988
3
  }
1989
165
  if (x0 > x1) {
1990
3
    return;
1991
3
  }
1992
162
  updateModX(x0);
1993
162
  updateModY(y);
1994
162
  lastX = x0;
1995
1996
162
  useBitmapRow(y);
1997
1998
162
  destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
1999
162
  destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
2000
2001
324
  for (x = x0; x <= x1; ++x) {
2002
2003
    //----- alpha
2004
162
    alpha = *alphaPtr;
2005
162
    if (!alpha) {
2006
0
      destColorPtr += 3;
2007
0
      ++destAlphaPtr;
2008
0
      cSrcPtr += cSrcStride;
2009
0
      ++alphaPtr;
2010
0
      continue;
2011
0
    }
2012
162
    lastX = x;
2013
2014
    //----- read destination pixel
2015
162
    cDest0 = destColorPtr[0];
2016
162
    cDest1 = destColorPtr[1];
2017
162
    cDest2 = destColorPtr[2];
2018
162
    aDest = *destAlphaPtr;
2019
2020
    //----- source color
2021
162
    cSrc0 = state->rgbTransferR[cSrcPtr[0]];
2022
162
    cSrc1 = state->rgbTransferG[cSrcPtr[1]];
2023
162
    cSrc2 = state->rgbTransferB[cSrcPtr[2]];
2024
2025
    //----- source alpha
2026
162
    aSrc = div255(pipe->aInput * alpha);
2027
2028
    //----- result alpha and non-isolated group element correction
2029
162
    aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
2030
162
    alphaI = aResult;
2031
2032
    //----- result color
2033
162
    if (alphaI == 0) {
2034
65
      cResult0 = 0;
2035
65
      cResult1 = 0;
2036
65
      cResult2 = 0;
2037
97
    } else {
2038
97
      cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
2039
97
      cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
2040
97
      cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
2041
97
    }
2042
2043
    //----- write destination pixel
2044
162
    destColorPtr[0] = cResult0;
2045
162
    destColorPtr[1] = cResult1;
2046
162
    destColorPtr[2] = cResult2;
2047
162
    destColorPtr += 3;
2048
162
    *destAlphaPtr++ = aResult;
2049
2050
162
    cSrcPtr += cSrcStride;
2051
162
    ++alphaPtr;
2052
162
  }
2053
2054
162
  updateModX(lastX);
2055
162
}
2056
2057
// special case:
2058
// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
2059
// usesAlpha && !pipe->alpha0Ptr && !state->blendFunc &&
2060
// !pipe->nonIsolatedGroup &&
2061
// bitmap->mode == splashModeBGR8 && bitmap->alpha
2062
void Splash::pipeRunAABGR8(SplashPipe *pipe, int x0, int x1, int y,
2063
         Guchar *shapePtr, Guchar *alphaPtr,
2064
0
         SplashColorPtr cSrcPtr) {
2065
0
  Guchar alpha, aSrc, aDest, alphaI, aResult;
2066
0
  Guchar cSrc0, cSrc1, cSrc2;
2067
0
  Guchar cDest0, cDest1, cDest2;
2068
0
  Guchar cResult0, cResult1, cResult2;
2069
0
  SplashColorPtr destColorPtr;
2070
0
  Guchar *destAlphaPtr;
2071
0
  int cSrcStride, x, lastX;
2072
2073
0
  if (cSrcPtr) {
2074
0
    cSrcStride = 3;
2075
0
  } else {
2076
0
    cSrcPtr = pipe->cSrcVal;
2077
0
    cSrcStride = 0;
2078
0
  }
2079
0
  for (; x0 <= x1; ++x0) {
2080
0
    if (*alphaPtr) {
2081
0
      break;
2082
0
    }
2083
0
    cSrcPtr += cSrcStride;
2084
0
    ++alphaPtr;
2085
0
  }
2086
0
  if (x0 > x1) {
2087
0
    return;
2088
0
  }
2089
0
  updateModX(x0);
2090
0
  updateModY(y);
2091
0
  lastX = x0;
2092
2093
0
  useBitmapRow(y);
2094
2095
0
  destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
2096
0
  destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
2097
2098
0
  for (x = x0; x <= x1; ++x) {
2099
2100
    //----- alpha
2101
0
    alpha = *alphaPtr;
2102
0
    if (!alpha) {
2103
0
      destColorPtr += 3;
2104
0
      ++destAlphaPtr;
2105
0
      cSrcPtr += cSrcStride;
2106
0
      ++alphaPtr;
2107
0
      continue;
2108
0
    }
2109
0
    lastX = x;
2110
2111
    //----- read destination pixel
2112
0
    cDest0 = destColorPtr[2];
2113
0
    cDest1 = destColorPtr[1];
2114
0
    cDest2 = destColorPtr[0];
2115
0
    aDest = *destAlphaPtr;
2116
2117
    //----- source color
2118
0
    cSrc0 = state->rgbTransferR[cSrcPtr[0]];
2119
0
    cSrc1 = state->rgbTransferG[cSrcPtr[1]];
2120
0
    cSrc2 = state->rgbTransferB[cSrcPtr[2]];
2121
2122
    //----- source alpha
2123
0
    aSrc = div255(pipe->aInput * alpha);
2124
2125
    //----- result alpha and non-isolated group element correction
2126
0
    aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
2127
0
    alphaI = aResult;
2128
2129
    //----- result color
2130
0
    if (alphaI == 0) {
2131
0
      cResult0 = 0;
2132
0
      cResult1 = 0;
2133
0
      cResult2 = 0;
2134
0
    } else {
2135
0
      cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
2136
0
      cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
2137
0
      cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
2138
0
    }
2139
2140
    //----- write destination pixel
2141
0
    destColorPtr[0] = cResult2;
2142
0
    destColorPtr[1] = cResult1;
2143
0
    destColorPtr[2] = cResult0;
2144
0
    destColorPtr += 3;
2145
0
    *destAlphaPtr++ = aResult;
2146
2147
0
    cSrcPtr += cSrcStride;
2148
0
    ++alphaPtr;
2149
0
  }
2150
2151
0
  updateModX(lastX);
2152
0
}
2153
2154
#if SPLASH_CMYK
2155
// special case:
2156
// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
2157
// usesAlpha && !pipe->alpha0Ptr && !state->blendFunc &&
2158
// !pipe->nonIsolatedGroup &&
2159
// bitmap->mode == splashModeCMYK8 && bitmap->alpha
2160
void Splash::pipeRunAACMYK8(SplashPipe *pipe, int x0, int x1, int y,
2161
          Guchar *shapePtr, Guchar *alphaPtr,
2162
0
          SplashColorPtr cSrcPtr) {
2163
0
  Guchar alpha, aSrc, aDest, alphaI, aResult;
2164
0
  Guchar cSrc0, cSrc1, cSrc2, cSrc3;
2165
0
  Guchar cDest0, cDest1, cDest2, cDest3;
2166
0
  Guchar cResult0, cResult1, cResult2, cResult3;
2167
0
  SplashColorPtr destColorPtr;
2168
0
  Guchar *destAlphaPtr;
2169
0
  int cSrcStride, x, lastX;
2170
2171
0
  if (cSrcPtr) {
2172
0
    cSrcStride = 4;
2173
0
  } else {
2174
0
    cSrcPtr = pipe->cSrcVal;
2175
0
    cSrcStride = 0;
2176
0
  }
2177
0
  for (; x0 <= x1; ++x0) {
2178
0
    if (*alphaPtr) {
2179
0
      break;
2180
0
    }
2181
0
    cSrcPtr += cSrcStride;
2182
0
    ++alphaPtr;
2183
0
  }
2184
0
  if (x0 > x1) {
2185
0
    return;
2186
0
  }
2187
0
  updateModX(x0);
2188
0
  updateModY(y);
2189
0
  lastX = x0;
2190
2191
0
  useBitmapRow(y);
2192
2193
0
  destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0];
2194
0
  destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
2195
2196
0
  for (x = x0; x <= x1; ++x) {
2197
2198
    //----- alpha
2199
0
    alpha = *alphaPtr;
2200
0
    if (!alpha) {
2201
0
      destColorPtr += 4;
2202
0
      ++destAlphaPtr;
2203
0
      cSrcPtr += cSrcStride;
2204
0
      ++alphaPtr;
2205
0
      continue;
2206
0
    }
2207
0
    lastX = x;
2208
2209
    //----- read destination pixel
2210
0
    cDest0 = destColorPtr[0];
2211
0
    cDest1 = destColorPtr[1];
2212
0
    cDest2 = destColorPtr[2];
2213
0
    cDest3 = destColorPtr[3];
2214
0
    aDest = *destAlphaPtr;
2215
2216
    //----- overprint
2217
0
    if (state->overprintMask & 1) {
2218
0
      cSrc0 = state->cmykTransferC[cSrcPtr[0]];
2219
0
    } else {
2220
0
      cSrc0 = div255(aDest * cDest0);
2221
0
    }
2222
0
    if (state->overprintMask & 2) {
2223
0
      cSrc1 = state->cmykTransferM[cSrcPtr[1]];
2224
0
    } else {
2225
0
      cSrc1 = div255(aDest * cDest1);
2226
0
    }
2227
0
    if (state->overprintMask & 4) {
2228
0
      cSrc2 = state->cmykTransferY[cSrcPtr[2]];
2229
0
    } else {
2230
0
      cSrc2 = div255(aDest * cDest2);
2231
0
    }
2232
0
    if (state->overprintMask & 8) {
2233
0
      cSrc3 = state->cmykTransferK[cSrcPtr[3]];
2234
0
    } else {
2235
0
      cSrc3 = div255(aDest * cDest3);
2236
0
    }
2237
2238
    //----- source alpha
2239
0
    aSrc = div255(pipe->aInput * alpha);
2240
2241
    //----- result alpha and non-isolated group element correction
2242
0
    aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
2243
0
    alphaI = aResult;
2244
2245
    //----- result color
2246
0
    if (alphaI == 0) {
2247
0
      cResult0 = 0;
2248
0
      cResult1 = 0;
2249
0
      cResult2 = 0;
2250
0
      cResult3 = 0;
2251
0
    } else {
2252
0
      cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
2253
0
      cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
2254
0
      cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
2255
0
      cResult3 = (Guchar)(((alphaI - aSrc) * cDest3 + aSrc * cSrc3) / alphaI);
2256
0
    }
2257
2258
    //----- write destination pixel
2259
0
    destColorPtr[0] = cResult0;
2260
0
    destColorPtr[1] = cResult1;
2261
0
    destColorPtr[2] = cResult2;
2262
0
    destColorPtr[3] = cResult3;
2263
0
    destColorPtr += 4;
2264
0
    *destAlphaPtr++ = aResult;
2265
2266
0
    cSrcPtr += cSrcStride;
2267
0
    ++alphaPtr;
2268
0
  }
2269
2270
0
  updateModX(lastX);
2271
0
}
2272
#endif
2273
2274
2275
// special case:
2276
// !pipe->pattern && aInput == 255 && state->softMask && usesAlpha &&
2277
// !state->inNonIsolatedGroup && !state->inKnockoutGroup &&
2278
// !nonIsolatedGroup && state->overprintMask == 0xffffffff &&
2279
// !state->blendFunc &&
2280
// bitmap->mode == splashModeMono8 && bitmap->alpha
2281
void Splash::pipeRunSoftMaskMono8(SplashPipe *pipe, int x0, int x1, int y,
2282
          Guchar *shapePtr, Guchar *alphaPtr,
2283
132
          SplashColorPtr cSrcPtr) {
2284
132
  Guchar alpha, aSrc, aDest, alphaI, aResult;
2285
132
  Guchar cSrc0, cDest0, cResult0;
2286
132
  SplashColorPtr destColorPtr;
2287
132
  Guchar *destAlphaPtr;
2288
132
  SplashColorPtr softMaskPtr;
2289
132
  int cSrcStride, x, lastX;
2290
2291
132
  if (cSrcPtr) {
2292
0
    cSrcStride = 1;
2293
132
  } else {
2294
132
    cSrcPtr = pipe->cSrcVal;
2295
132
    cSrcStride = 0;
2296
132
  }
2297
164
  for (; x0 <= x1; ++x0) {
2298
132
    if (*alphaPtr) {
2299
100
      break;
2300
100
    }
2301
32
    cSrcPtr += cSrcStride;
2302
32
    ++alphaPtr;
2303
32
  }
2304
132
  if (x0 > x1) {
2305
32
    return;
2306
32
  }
2307
100
  updateModX(x0);
2308
100
  updateModY(y);
2309
100
  lastX = x0;
2310
2311
100
  useBitmapRow(y);
2312
2313
100
  destColorPtr = &bitmap->data[y * bitmap->rowSize + x0];
2314
100
  destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
2315
100
  softMaskPtr = &state->softMask->data[y * state->softMask->rowSize + x0];
2316
2317
200
  for (x = x0; x <= x1; ++x) {
2318
2319
    //----- alpha
2320
100
    alpha = *alphaPtr;
2321
100
    if (!alpha) {
2322
0
      ++destColorPtr;
2323
0
      ++destAlphaPtr;
2324
0
      ++softMaskPtr;
2325
0
      cSrcPtr += cSrcStride;
2326
0
      ++alphaPtr;
2327
0
      continue;
2328
0
    }
2329
100
    lastX = x;
2330
2331
    //----- read source color
2332
100
    cSrc0 = state->grayTransfer[cSrcPtr[0]];
2333
2334
    //----- source alpha
2335
100
    aSrc = div255(*softMaskPtr++ * alpha);
2336
2337
    //----- special case for aSrc = 255
2338
100
    if (aSrc == 255) {
2339
0
      aResult = 255;
2340
0
      cResult0 = cSrc0;
2341
100
    } else {
2342
2343
      //----- read destination alpha
2344
100
      aDest = *destAlphaPtr;
2345
2346
      //----- special case for aDest = 0
2347
100
      if (aDest == 0) {
2348
82
        aResult = aSrc;
2349
82
        cResult0 = cSrc0;
2350
82
      } else {
2351
2352
        //----- read destination pixel
2353
18
        cDest0 = destColorPtr[0];
2354
2355
        //----- result alpha and non-isolated group element correction
2356
18
        aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
2357
18
        alphaI = aResult;
2358
2359
        //----- result color
2360
18
        cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
2361
18
      }
2362
100
    }
2363
2364
    //----- write destination pixel
2365
100
    destColorPtr[0] = cResult0;
2366
100
    ++destColorPtr;
2367
100
    *destAlphaPtr++ = aResult;
2368
2369
100
    cSrcPtr += cSrcStride;
2370
100
    ++alphaPtr;
2371
100
  }
2372
2373
100
  updateModX(lastX);
2374
100
}
2375
2376
// special case:
2377
// !pipe->pattern && aInput == 255 && state->softMask && usesAlpha &&
2378
// !state->inNonIsolatedGroup && !state->inKnockoutGroup &&
2379
// !nonIsolatedGroup && state->overprintMask == 0xffffffff &&
2380
// !state->blendFunc &&
2381
// bitmap->mode == splashModeRGB8 && bitmap->alpha
2382
void Splash::pipeRunSoftMaskRGB8(SplashPipe *pipe, int x0, int x1, int y,
2383
                                 Guchar *shapePtr, Guchar *alphaPtr,
2384
300
         SplashColorPtr cSrcPtr) {
2385
300
  Guchar alpha, aSrc, aDest, alphaI, aResult;
2386
300
  Guchar cSrc0, cSrc1, cSrc2;
2387
300
  Guchar cDest0, cDest1, cDest2;
2388
300
  Guchar cResult0, cResult1, cResult2;
2389
300
  SplashColorPtr destColorPtr;
2390
300
  Guchar *destAlphaPtr;
2391
300
  SplashColorPtr softMaskPtr;
2392
300
  int cSrcStride, x, lastX;
2393
2394
300
  if (cSrcPtr) {
2395
167
    cSrcStride = 3;
2396
167
  } else {
2397
133
    cSrcPtr = pipe->cSrcVal;
2398
133
    cSrcStride = 0;
2399
133
  }
2400
338
  for (; x0 <= x1; ++x0) {
2401
300
    if (*alphaPtr) {
2402
262
      break;
2403
262
    }
2404
38
    cSrcPtr += cSrcStride;
2405
38
    ++alphaPtr;
2406
38
  }
2407
300
  if (x0 > x1) {
2408
38
    return;
2409
38
  }
2410
262
  updateModX(x0);
2411
262
  updateModY(y);
2412
262
  lastX = x0;
2413
2414
262
  useBitmapRow(y);
2415
2416
262
  destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * 3];
2417
262
  destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
2418
262
  softMaskPtr = &state->softMask->data[y * state->softMask->rowSize + x0];
2419
2420
524
  for (x = x0; x <= x1; ++x) {
2421
2422
    //----- alpha
2423
262
    alpha = *alphaPtr;
2424
262
    if (!alpha) {
2425
0
      destColorPtr += 3;
2426
0
      ++destAlphaPtr;
2427
0
      ++softMaskPtr;
2428
0
      cSrcPtr += cSrcStride;
2429
0
      ++alphaPtr;
2430
0
      continue;
2431
0
    }
2432
262
    lastX = x;
2433
2434
    //----- read source color
2435
262
    cSrc0 = state->rgbTransferR[cSrcPtr[0]];
2436
262
    cSrc1 = state->rgbTransferG[cSrcPtr[1]];
2437
262
    cSrc2 = state->rgbTransferB[cSrcPtr[2]];
2438
2439
    //----- source alpha
2440
262
    aSrc = div255(*softMaskPtr++ * alpha);
2441
2442
    //----- special case for aSrc = 255
2443
262
    if (aSrc == 255) {
2444
115
      aResult = 255;
2445
115
      cResult0 = cSrc0;
2446
115
      cResult1 = cSrc1;
2447
115
      cResult2 = cSrc2;
2448
147
    } else {
2449
2450
      //----- read destination alpha
2451
147
      aDest = *destAlphaPtr;
2452
2453
      //----- special case for aDest = 0
2454
147
      if (aDest == 0) {
2455
103
        aResult = aSrc;
2456
103
        cResult0 = cSrc0;
2457
103
        cResult1 = cSrc1;
2458
103
        cResult2 = cSrc2;
2459
103
      } else {
2460
2461
        //----- read destination pixel
2462
44
        cDest0 = destColorPtr[0];
2463
44
        cDest1 = destColorPtr[1];
2464
44
        cDest2 = destColorPtr[2];
2465
2466
        //----- result alpha and non-isolated group element correction
2467
44
        aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
2468
44
        alphaI = aResult;
2469
2470
        //----- result color
2471
44
        cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
2472
44
        cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
2473
44
        cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
2474
44
      }
2475
147
    }
2476
2477
    //----- write destination pixel
2478
262
    destColorPtr[0] = cResult0;
2479
262
    destColorPtr[1] = cResult1;
2480
262
    destColorPtr[2] = cResult2;
2481
262
    destColorPtr += 3;
2482
262
    *destAlphaPtr++ = aResult;
2483
2484
262
    cSrcPtr += cSrcStride;
2485
262
    ++alphaPtr;
2486
262
  }
2487
2488
262
  updateModX(lastX);
2489
262
}
2490
2491
// special case:
2492
// !pipe->pattern && aInput == 255 && state->softMask && usesAlpha &&
2493
// !state->inNonIsolatedGroup && !state->inKnockoutGroup &&
2494
// !nonIsolatedGroup && state->overprintMask == 0xffffffff &&
2495
// !state->blendFunc &&
2496
// bitmap->mode == splashModeBGR8 && bitmap->alpha
2497
void Splash::pipeRunSoftMaskBGR8(SplashPipe *pipe, int x0, int x1, int y,
2498
                                 Guchar *shapePtr, Guchar *alphaPtr,
2499
0
         SplashColorPtr cSrcPtr) {
2500
0
  Guchar alpha, aSrc, aDest, alphaI, aResult;
2501
0
  Guchar cSrc0, cSrc1, cSrc2;
2502
0
  Guchar cDest0, cDest1, cDest2;
2503
0
  Guchar cResult0, cResult1, cResult2;
2504
0
  SplashColorPtr destColorPtr;
2505
0
  Guchar *destAlphaPtr;
2506
0
  SplashColorPtr softMaskPtr;
2507
0
  int cSrcStride, x, lastX;
2508
2509
0
  if (cSrcPtr) {
2510
0
    cSrcStride = 3;
2511
0
  } else {
2512
0
    cSrcPtr = pipe->cSrcVal;
2513
0
    cSrcStride = 0;
2514
0
  }
2515
0
  for (; x0 <= x1; ++x0) {
2516
0
    if (*alphaPtr) {
2517
0
      break;
2518
0
    }
2519
0
    cSrcPtr += cSrcStride;
2520
0
    ++alphaPtr;
2521
0
  }
2522
0
  if (x0 > x1) {
2523
0
    return;
2524
0
  }
2525
0
  updateModX(x0);
2526
0
  updateModY(y);
2527
0
  lastX = x0;
2528
2529
0
  useBitmapRow(y);
2530
2531
0
  destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * 3];
2532
0
  destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
2533
0
  softMaskPtr = &state->softMask->data[y * state->softMask->rowSize + x0];
2534
2535
0
  for (x = x0; x <= x1; ++x) {
2536
2537
    //----- alpha
2538
0
    alpha = *alphaPtr;
2539
0
    if (!alpha) {
2540
0
      destColorPtr += 3;
2541
0
      ++destAlphaPtr;
2542
0
      ++softMaskPtr;
2543
0
      cSrcPtr += cSrcStride;
2544
0
      ++alphaPtr;
2545
0
      continue;
2546
0
    }
2547
0
    lastX = x;
2548
2549
    //----- read source color
2550
0
    cSrc0 = state->rgbTransferR[cSrcPtr[0]];
2551
0
    cSrc1 = state->rgbTransferG[cSrcPtr[1]];
2552
0
    cSrc2 = state->rgbTransferB[cSrcPtr[2]];
2553
2554
    //----- source alpha
2555
0
    aSrc = div255(*softMaskPtr++ * alpha);
2556
2557
    //----- special case for aSrc = 255
2558
0
    if (aSrc == 255) {
2559
0
      aResult = 255;
2560
0
      cResult0 = cSrc0;
2561
0
      cResult1 = cSrc1;
2562
0
      cResult2 = cSrc2;
2563
0
    } else {
2564
2565
      //----- read destination alpha
2566
0
      aDest = *destAlphaPtr;
2567
2568
      //----- special case for aDest = 0
2569
0
      if (aDest == 0) {
2570
0
        aResult = aSrc;
2571
0
        cResult0 = cSrc0;
2572
0
        cResult1 = cSrc1;
2573
0
        cResult2 = cSrc2;
2574
0
      } else {
2575
2576
        //----- read destination pixel
2577
0
        cDest0 = destColorPtr[2];
2578
0
        cDest1 = destColorPtr[1];
2579
0
        cDest2 = destColorPtr[0];
2580
2581
        //----- result alpha and non-isolated group element correction
2582
0
        aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
2583
0
        alphaI = aResult;
2584
2585
        //----- result color
2586
0
        cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
2587
0
        cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
2588
0
        cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
2589
0
      }
2590
0
    }
2591
2592
    //----- write destination pixel
2593
0
    destColorPtr[2] = cResult0;
2594
0
    destColorPtr[1] = cResult1;
2595
0
    destColorPtr[0] = cResult2;
2596
0
    destColorPtr += 3;
2597
0
    *destAlphaPtr++ = aResult;
2598
2599
0
    cSrcPtr += cSrcStride;
2600
0
    ++alphaPtr;
2601
0
  }
2602
2603
0
  updateModX(lastX);
2604
0
}
2605
2606
#if SPLASH_CMYK
2607
// special case:
2608
// !pipe->pattern && aInput == 255 && state->softMask && usesAlpha &&
2609
// !state->inNonIsolatedGroup && !state->inKnockoutGroup &&
2610
// !nonIsolatedGroup && state->overprintMask == 0xffffffff &&
2611
// !state->blendFunc &&
2612
// bitmap->mode == splashModeCMYK8 && bitmap->alpha
2613
void Splash::pipeRunSoftMaskCMYK8(SplashPipe *pipe, int x0, int x1, int y,
2614
          Guchar *shapePtr, Guchar *alphaPtr,
2615
0
          SplashColorPtr cSrcPtr) {
2616
0
  Guchar alpha, aSrc, aDest, alphaI, aResult;
2617
0
  Guchar cSrc0, cSrc1, cSrc2, cSrc3;
2618
0
  Guchar cDest0, cDest1, cDest2, cDest3;
2619
0
  Guchar cResult0, cResult1, cResult2, cResult3;
2620
0
  SplashColorPtr destColorPtr;
2621
0
  Guchar *destAlphaPtr;
2622
0
  SplashColorPtr softMaskPtr;
2623
0
  int cSrcStride, x, lastX;
2624
2625
0
  if (cSrcPtr) {
2626
0
    cSrcStride = 4;
2627
0
  } else {
2628
0
    cSrcPtr = pipe->cSrcVal;
2629
0
    cSrcStride = 0;
2630
0
  }
2631
0
  for (; x0 <= x1; ++x0) {
2632
0
    if (*alphaPtr) {
2633
0
      break;
2634
0
    }
2635
0
    cSrcPtr += cSrcStride;
2636
0
    ++alphaPtr;
2637
0
  }
2638
0
  if (x0 > x1) {
2639
0
    return;
2640
0
  }
2641
0
  updateModX(x0);
2642
0
  updateModY(y);
2643
0
  lastX = x0;
2644
2645
0
  useBitmapRow(y);
2646
2647
0
  destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * 4];
2648
0
  destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
2649
0
  softMaskPtr = &state->softMask->data[y * state->softMask->rowSize + x0];
2650
2651
0
  for (x = x0; x <= x1; ++x) {
2652
2653
    //----- alpha
2654
0
    alpha = *alphaPtr;
2655
0
    if (!alpha) {
2656
0
      destColorPtr += 4;
2657
0
      ++destAlphaPtr;
2658
0
      ++softMaskPtr;
2659
0
      cSrcPtr += cSrcStride;
2660
0
      ++alphaPtr;
2661
0
      continue;
2662
0
    }
2663
0
    lastX = x;
2664
2665
    //----- read destination pixel
2666
0
    cDest0 = destColorPtr[0];
2667
0
    cDest1 = destColorPtr[1];
2668
0
    cDest2 = destColorPtr[2];
2669
0
    cDest3 = destColorPtr[3];
2670
2671
    //----- read destination alpha
2672
0
    aDest = *destAlphaPtr;
2673
2674
    //----- overprint
2675
0
    cSrc0 = state->cmykTransferC[cSrcPtr[0]];
2676
0
    cSrc1 = state->cmykTransferM[cSrcPtr[1]];
2677
0
    cSrc2 = state->cmykTransferY[cSrcPtr[2]];
2678
0
    cSrc3 = state->cmykTransferK[cSrcPtr[3]];
2679
2680
    //----- source alpha
2681
0
    aSrc = div255(*softMaskPtr++ * alpha);
2682
2683
    //----- special case for aSrc = 255
2684
0
    if (aSrc == 255) {
2685
0
      aResult = 255;
2686
0
      cResult0 = cSrc0;
2687
0
      cResult1 = cSrc1;
2688
0
      cResult2 = cSrc2;
2689
0
      cResult3 = cSrc3;
2690
0
    } else {
2691
2692
      //----- special case for aDest = 0
2693
0
      if (aDest == 0) {
2694
0
        aResult = aSrc;
2695
0
        cResult0 = cSrc0;
2696
0
        cResult1 = cSrc1;
2697
0
        cResult2 = cSrc2;
2698
0
        cResult3 = cSrc3;
2699
0
      } else {
2700
2701
        //----- result alpha and non-isolated group element correction
2702
0
        aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
2703
0
        alphaI = aResult;
2704
2705
        //----- result color
2706
0
        cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
2707
0
        cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
2708
0
        cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
2709
0
        cResult3 = (Guchar)(((alphaI - aSrc) * cDest3 + aSrc * cSrc3) / alphaI);
2710
0
      }
2711
0
    }
2712
2713
    //----- write destination pixel
2714
0
    destColorPtr[0] = cResult0;
2715
0
    destColorPtr[1] = cResult1;
2716
0
    destColorPtr[2] = cResult2;
2717
0
    destColorPtr[3] = cResult3;
2718
0
    destColorPtr += 4;
2719
0
    *destAlphaPtr++ = aResult;
2720
2721
0
    cSrcPtr += cSrcStride;
2722
0
    ++alphaPtr;
2723
0
  }
2724
2725
0
  updateModX(lastX);
2726
0
}
2727
#endif
2728
2729
2730
void Splash::pipeRunNonIsoMono8(SplashPipe *pipe, int x0, int x1, int y,
2731
        Guchar *shapePtr, Guchar *alphaPtr,
2732
0
        SplashColorPtr cSrcPtr) {
2733
0
  Guchar alpha, aSrc, aDest, alphaI, alpha0, aResult;
2734
0
  Guchar cSrc0, cDest0, cResult0;
2735
0
  SplashColorPtr destColorPtr;
2736
0
  Guchar *destAlphaPtr;
2737
0
  Guchar *alpha0Ptr;
2738
0
  int cSrcStride, x, lastX;
2739
2740
0
  if (cSrcPtr) {
2741
0
    cSrcStride = 1;
2742
0
  } else {
2743
0
    cSrcPtr = pipe->cSrcVal;
2744
0
    cSrcStride = 0;
2745
0
  }
2746
0
  for (; x0 <= x1; ++x0) {
2747
0
    if (*alphaPtr) {
2748
0
      break;
2749
0
    }
2750
0
    cSrcPtr += cSrcStride;
2751
0
    ++alphaPtr;
2752
0
  }
2753
0
  if (x0 > x1) {
2754
0
    return;
2755
0
  }
2756
0
  updateModX(x0);
2757
0
  updateModY(y);
2758
0
  lastX = x0;
2759
2760
0
  useBitmapRow(y);
2761
2762
0
  destColorPtr = &bitmap->data[y * bitmap->rowSize + x0];
2763
0
  destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
2764
0
  if (alpha0Bitmap) {
2765
0
    alpha0Ptr = &alpha0Bitmap->alpha[y * alpha0Bitmap->alphaRowSize + x0];
2766
0
  } else {
2767
0
    alpha0Ptr = &parent->bitmap->alpha[
2768
0
        (parentOffsetY + y) * parent->bitmap->alphaRowSize
2769
0
        + (parentOffsetX + x0)];
2770
0
  }
2771
2772
0
  for (x = x0; x <= x1; ++x) {
2773
2774
    //----- alpha
2775
0
    alpha = *alphaPtr;
2776
0
    if (!alpha) {
2777
0
      destColorPtr += 1;
2778
0
      ++destAlphaPtr;
2779
0
      ++alpha0Ptr;
2780
0
      cSrcPtr += cSrcStride;
2781
0
      ++alphaPtr;
2782
0
      continue;
2783
0
    }
2784
0
    lastX = x;
2785
2786
    //----- read destination pixel
2787
0
    cDest0 = destColorPtr[0];
2788
0
    aDest = *destAlphaPtr;
2789
2790
    //----- source color
2791
0
    cSrc0 = state->grayTransfer[cSrcPtr[0]];
2792
2793
    //----- source alpha
2794
0
    aSrc = div255(pipe->aInput * alpha);
2795
2796
    //----- result alpha and non-isolated group element correction
2797
0
    aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
2798
0
    alpha0 = *alpha0Ptr++;
2799
0
    alphaI = (Guchar)(aResult + alpha0 - div255(aResult * alpha0));
2800
2801
    //----- result color
2802
0
    if (alphaI == 0) {
2803
0
      cResult0 = 0;
2804
0
    } else {
2805
0
      cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
2806
0
    }
2807
2808
    //----- write destination pixel
2809
0
    *destColorPtr++ = cResult0;
2810
0
    *destAlphaPtr++ = aResult;
2811
2812
0
    cSrcPtr += cSrcStride;
2813
0
    ++alphaPtr;
2814
0
  } // for (x ...)
2815
2816
0
  updateModX(lastX);
2817
0
}
2818
2819
void Splash::pipeRunNonIsoRGB8(SplashPipe *pipe, int x0, int x1, int y,
2820
             Guchar *shapePtr, Guchar *alphaPtr,
2821
16.7k
             SplashColorPtr cSrcPtr) {
2822
16.7k
  Guchar alpha, aSrc, aDest, alphaI, alpha0, aResult;
2823
16.7k
  Guchar cSrc0, cSrc1, cSrc2;
2824
16.7k
  Guchar cDest0, cDest1, cDest2;
2825
16.7k
  Guchar cResult0, cResult1, cResult2;
2826
16.7k
  SplashColorPtr destColorPtr;
2827
16.7k
  Guchar *destAlphaPtr;
2828
16.7k
  Guchar *alpha0Ptr;
2829
16.7k
  int cSrcStride, x, lastX;
2830
2831
16.7k
  if (cSrcPtr) {
2832
0
    cSrcStride = 3;
2833
16.7k
  } else {
2834
16.7k
    cSrcPtr = pipe->cSrcVal;
2835
16.7k
    cSrcStride = 0;
2836
16.7k
  }
2837
17.8k
  for (; x0 <= x1; ++x0) {
2838
16.7k
    if (*alphaPtr) {
2839
15.6k
      break;
2840
15.6k
    }
2841
1.14k
    cSrcPtr += cSrcStride;
2842
1.14k
    ++alphaPtr;
2843
1.14k
  }
2844
16.7k
  if (x0 > x1) {
2845
1.14k
    return;
2846
1.14k
  }
2847
15.6k
  updateModX(x0);
2848
15.6k
  updateModY(y);
2849
15.6k
  lastX = x0;
2850
2851
15.6k
  useBitmapRow(y);
2852
2853
15.6k
  destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * 3];
2854
15.6k
  destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
2855
15.6k
  if (alpha0Bitmap) {
2856
0
    alpha0Ptr = &alpha0Bitmap->alpha[y * alpha0Bitmap->alphaRowSize + x0];
2857
15.6k
  } else {
2858
15.6k
    alpha0Ptr = &parent->bitmap->alpha[
2859
15.6k
        (parentOffsetY + y) * parent->bitmap->alphaRowSize
2860
15.6k
        + (parentOffsetX + x0)];
2861
15.6k
  }
2862
2863
31.2k
  for (x = x0; x <= x1; ++x) {
2864
2865
    //----- alpha
2866
15.6k
    alpha = *alphaPtr;
2867
15.6k
    if (!alpha) {
2868
0
      destColorPtr += 3;
2869
0
      ++destAlphaPtr;
2870
0
      ++alpha0Ptr;
2871
0
      cSrcPtr += cSrcStride;
2872
0
      ++alphaPtr;
2873
0
      continue;
2874
0
    }
2875
15.6k
    lastX = x;
2876
2877
    //----- read destination pixel
2878
15.6k
    cDest0 = destColorPtr[0];
2879
15.6k
    cDest1 = destColorPtr[1];
2880
15.6k
    cDest2 = destColorPtr[2];
2881
15.6k
    aDest = *destAlphaPtr;
2882
2883
    //----- source color
2884
15.6k
    cSrc0 = state->rgbTransferR[cSrcPtr[0]];
2885
15.6k
    cSrc1 = state->rgbTransferG[cSrcPtr[1]];
2886
15.6k
    cSrc2 = state->rgbTransferB[cSrcPtr[2]];
2887
2888
    //----- source alpha
2889
15.6k
    aSrc = div255(pipe->aInput * alpha);
2890
2891
    //----- result alpha and non-isolated group element correction
2892
15.6k
    aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
2893
15.6k
    alpha0 = *alpha0Ptr++;
2894
15.6k
    alphaI = (Guchar)(aResult + alpha0 - div255(aResult * alpha0));
2895
2896
    //----- result color
2897
15.6k
    if (alphaI == 0) {
2898
11
      cResult0 = 0;
2899
11
      cResult1 = 0;
2900
11
      cResult2 = 0;
2901
15.6k
    } else {
2902
15.6k
      cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
2903
15.6k
      cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
2904
15.6k
      cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
2905
15.6k
    }
2906
2907
    //----- write destination pixel
2908
15.6k
    destColorPtr[0] = cResult0;
2909
15.6k
    destColorPtr[1] = cResult1;
2910
15.6k
    destColorPtr[2] = cResult2;
2911
15.6k
    destColorPtr += 3;
2912
15.6k
    *destAlphaPtr++ = aResult;
2913
2914
15.6k
    cSrcPtr += cSrcStride;
2915
15.6k
    ++alphaPtr;
2916
15.6k
  } // for (x ...)
2917
2918
15.6k
  updateModX(lastX);
2919
15.6k
}
2920
2921
void Splash::pipeRunNonIsoBGR8(SplashPipe *pipe, int x0, int x1, int y,
2922
             Guchar *shapePtr, Guchar *alphaPtr,
2923
0
             SplashColorPtr cSrcPtr) {
2924
0
  Guchar alpha, aSrc, aDest, alphaI, alpha0, aResult;
2925
0
  Guchar cSrc0, cSrc1, cSrc2;
2926
0
  Guchar cDest0, cDest1, cDest2;
2927
0
  Guchar cResult0, cResult1, cResult2;
2928
0
  SplashColorPtr destColorPtr;
2929
0
  Guchar *destAlphaPtr;
2930
0
  Guchar *alpha0Ptr;
2931
0
  int cSrcStride, x, lastX;
2932
2933
0
  if (cSrcPtr) {
2934
0
    cSrcStride = 3;
2935
0
  } else {
2936
0
    cSrcPtr = pipe->cSrcVal;
2937
0
    cSrcStride = 0;
2938
0
  }
2939
0
  for (; x0 <= x1; ++x0) {
2940
0
    if (*alphaPtr) {
2941
0
      break;
2942
0
    }
2943
0
    cSrcPtr += cSrcStride;
2944
0
    ++alphaPtr;
2945
0
  }
2946
0
  if (x0 > x1) {
2947
0
    return;
2948
0
  }
2949
0
  updateModX(x0);
2950
0
  updateModY(y);
2951
0
  lastX = x0;
2952
2953
0
  useBitmapRow(y);
2954
2955
0
  destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * 3];
2956
0
  destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
2957
0
  if (alpha0Bitmap) {
2958
0
    alpha0Ptr = &alpha0Bitmap->alpha[y * alpha0Bitmap->alphaRowSize + x0];
2959
0
  } else {
2960
0
    alpha0Ptr = &parent->bitmap->alpha[
2961
0
        (parentOffsetY + y) * parent->bitmap->alphaRowSize
2962
0
        + (parentOffsetX + x0)];
2963
0
  }
2964
2965
0
  for (x = x0; x <= x1; ++x) {
2966
2967
    //----- alpha
2968
0
    alpha = *alphaPtr;
2969
0
    if (!alpha) {
2970
0
      destColorPtr += 3;
2971
0
      ++destAlphaPtr;
2972
0
      ++alpha0Ptr;
2973
0
      cSrcPtr += cSrcStride;
2974
0
      ++alphaPtr;
2975
0
      continue;
2976
0
    }
2977
0
    lastX = x;
2978
2979
    //----- read destination pixel
2980
0
    cDest0 = destColorPtr[2];
2981
0
    cDest1 = destColorPtr[1];
2982
0
    cDest2 = destColorPtr[0];
2983
0
    aDest = *destAlphaPtr;
2984
2985
    //----- source color
2986
0
    cSrc0 = state->rgbTransferR[cSrcPtr[0]];
2987
0
    cSrc1 = state->rgbTransferG[cSrcPtr[1]];
2988
0
    cSrc2 = state->rgbTransferB[cSrcPtr[2]];
2989
2990
    //----- source alpha
2991
0
    aSrc = div255(pipe->aInput * alpha);
2992
2993
    //----- result alpha and non-isolated group element correction
2994
0
    aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
2995
0
    alpha0 = *alpha0Ptr++;
2996
0
    alphaI = (Guchar)(aResult + alpha0 - div255(aResult * alpha0));
2997
2998
    //----- result color
2999
0
    if (alphaI == 0) {
3000
0
      cResult0 = 0;
3001
0
      cResult1 = 0;
3002
0
      cResult2 = 0;
3003
0
    } else {
3004
0
      cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
3005
0
      cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
3006
0
      cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
3007
0
    }
3008
3009
    //----- write destination pixel
3010
0
    destColorPtr[0] = cResult2;
3011
0
    destColorPtr[1] = cResult1;
3012
0
    destColorPtr[2] = cResult0;
3013
0
    destColorPtr += 3;
3014
0
    *destAlphaPtr++ = aResult;
3015
3016
0
    cSrcPtr += cSrcStride;
3017
0
    ++alphaPtr;
3018
0
  } // for (x ...)
3019
3020
0
  updateModX(lastX);
3021
0
}
3022
3023
#if SPLASH_CMYK
3024
void Splash::pipeRunNonIsoCMYK8(SplashPipe *pipe, int x0, int x1, int y,
3025
        Guchar *shapePtr, Guchar *alphaPtr,
3026
0
        SplashColorPtr cSrcPtr) {
3027
0
  Guchar alpha, aSrc, aDest, alphaI, alpha0, aResult, aPrev;
3028
0
  Guchar cSrc0, cSrc1, cSrc2, cSrc3;
3029
0
  Guchar cDest0, cDest1, cDest2, cDest3;
3030
0
  Guchar cResult0, cResult1, cResult2, cResult3;
3031
0
  SplashColorPtr destColorPtr;
3032
0
  Guchar *destAlphaPtr;
3033
0
  Guchar *alpha0Ptr;
3034
0
  int cSrcStride, x, lastX;
3035
3036
0
  if (cSrcPtr) {
3037
0
    cSrcStride = 4;
3038
0
  } else {
3039
0
    cSrcPtr = pipe->cSrcVal;
3040
0
    cSrcStride = 0;
3041
0
  }
3042
0
  for (; x0 <= x1; ++x0) {
3043
0
    if (*alphaPtr) {
3044
0
      break;
3045
0
    }
3046
0
    cSrcPtr += cSrcStride;
3047
0
    ++alphaPtr;
3048
0
  }
3049
0
  if (x0 > x1) {
3050
0
    return;
3051
0
  }
3052
0
  updateModX(x0);
3053
0
  updateModY(y);
3054
0
  lastX = x0;
3055
3056
0
  useBitmapRow(y);
3057
3058
0
  destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * 4];
3059
0
  destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
3060
0
  if (alpha0Bitmap) {
3061
0
    alpha0Ptr = &alpha0Bitmap->alpha[y * alpha0Bitmap->alphaRowSize + x0];
3062
0
  } else {
3063
0
    alpha0Ptr = &parent->bitmap->alpha[
3064
0
        (parentOffsetY + y) * parent->bitmap->alphaRowSize
3065
0
        + (parentOffsetX + x0)];
3066
0
  }
3067
3068
0
  for (x = x0; x <= x1; ++x) {
3069
3070
    //----- alpha
3071
0
    alpha = *alphaPtr;
3072
0
    if (!alpha) {
3073
0
      destColorPtr += 4;
3074
0
      ++destAlphaPtr;
3075
0
      ++alpha0Ptr;
3076
0
      cSrcPtr += cSrcStride;
3077
0
      ++alphaPtr;
3078
0
      continue;
3079
0
    }
3080
0
    lastX = x;
3081
3082
    //----- read destination pixel
3083
0
    cDest0 = destColorPtr[0];
3084
0
    cDest1 = destColorPtr[1];
3085
0
    cDest2 = destColorPtr[2];
3086
0
    cDest3 = destColorPtr[3];
3087
0
    aDest = *destAlphaPtr;
3088
3089
    //----- overprint
3090
0
    aPrev = (Guchar)(*alpha0Ptr + aDest - div255(*alpha0Ptr * aDest));
3091
0
    if (state->overprintMask & 0x01) {
3092
0
      cSrc0 = state->cmykTransferC[cSrcPtr[0]];
3093
0
    } else {
3094
0
      cSrc0 = div255(aPrev * cDest0);
3095
0
    }
3096
0
    if (state->overprintMask & 0x02) {
3097
0
      cSrc1 = state->cmykTransferM[cSrcPtr[1]];
3098
0
    } else {
3099
0
      cSrc1 = div255(aPrev * cDest1);
3100
0
    }
3101
0
    if (state->overprintMask & 0x04) {
3102
0
      cSrc2 = state->cmykTransferY[cSrcPtr[2]];
3103
0
    } else {
3104
0
      cSrc2 = div255(aPrev * cDest2);
3105
0
    }
3106
0
    if (state->overprintMask & 0x08) {
3107
0
      cSrc3 = state->cmykTransferK[cSrcPtr[3]];
3108
0
    } else {
3109
0
      cSrc3 = div255(aPrev * cDest3);
3110
0
    }
3111
3112
    //----- source alpha
3113
0
    aSrc = div255(pipe->aInput * alpha);
3114
3115
    //----- result alpha and non-isolated group element correction
3116
0
    aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
3117
0
    alpha0 = *alpha0Ptr++;
3118
0
    alphaI = (Guchar)(aResult + alpha0 - div255(aResult * alpha0));
3119
3120
    //----- result color
3121
0
    if (alphaI == 0) {
3122
0
      cResult0 = 0;
3123
0
      cResult1 = 0;
3124
0
      cResult2 = 0;
3125
0
      cResult3 = 0;
3126
0
    } else {
3127
0
      cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
3128
0
      cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
3129
0
      cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
3130
0
      cResult3 = (Guchar)(((alphaI - aSrc) * cDest3 + aSrc * cSrc3) / alphaI);
3131
0
    }
3132
3133
    //----- write destination pixel
3134
0
    destColorPtr[0] = cResult0;
3135
0
    destColorPtr[1] = cResult1;
3136
0
    destColorPtr[2] = cResult2;
3137
0
    destColorPtr[3] = cResult3;
3138
0
    destColorPtr += 4;
3139
0
    *destAlphaPtr++ = aResult;
3140
3141
0
    cSrcPtr += cSrcStride;
3142
0
    ++alphaPtr;
3143
0
  } // for (x ...)
3144
3145
0
  updateModX(lastX);
3146
0
}
3147
#endif
3148
3149
3150
50.0k
void Splash::useBitmapRow(int y) {
3151
50.0k
  if (!deferredInit) {
3152
30.2k
    return;
3153
30.2k
  }
3154
3155
19.8k
  int y0, y1;
3156
19.8k
  if (deferredInitYMin > deferredInitYMax) {
3157
4.74k
    y0 = y1 = y;
3158
4.74k
    deferredInitYMin = deferredInitYMax = y;
3159
15.0k
  } else if (y < deferredInitYMin) {
3160
0
    y0 = y;
3161
0
    y1 = deferredInitYMin - 1;
3162
0
    deferredInitYMin = y;
3163
15.0k
  } else if (y > deferredInitYMax) {
3164
0
    y0 = deferredInitYMax + 1;
3165
0
    y1 = y;
3166
0
    deferredInitYMax = y;
3167
15.0k
  } else {
3168
15.0k
    return;
3169
15.0k
  }
3170
3171
4.74k
  parent->useBitmapRow(parentOffsetY + y);
3172
4.74k
  if (state->inNonIsolatedGroup) {
3173
6.53k
    for (int yy = y0; yy <= y1; ++yy) {
3174
3.26k
      copyParentRowColor(yy);
3175
3.26k
    }
3176
3.26k
  } else {
3177
2.95k
    for (int yy = y0; yy <= y1; ++yy) {
3178
1.47k
      zeroRowColor(yy);
3179
1.47k
    }
3180
1.47k
  }
3181
4.74k
  if (bitmap->alpha) {
3182
9.48k
    for (int yy = y0; yy <= y1; ++yy) {
3183
4.74k
      zeroRowAlpha(yy);
3184
4.74k
    }
3185
4.74k
  }
3186
4.74k
  if (alpha0Bitmap) {
3187
0
    for (int yy = y0; yy <= y1; ++yy) {
3188
0
      computeAlpha0Row(yy);
3189
0
    }
3190
0
  }
3191
4.74k
}
3192
3193
3.26k
void Splash::copyParentRowColor(int y) {
3194
3.26k
  if (parent->bitmap->mode != bitmap->mode) {
3195
0
    return;
3196
0
  }
3197
3.26k
  if (bitmap->mode == splashModeMono1) {
3198
0
    SplashColorPtr p = &bitmap->data[y * bitmap->rowSize];
3199
0
    Guchar mask = (Guchar)0x80;
3200
0
    SplashColorPtr q =
3201
0
        &parent->bitmap->data[(parentOffsetY + y) * parent->bitmap->rowSize
3202
0
            + (parentOffsetX >> 3)];
3203
0
    Guchar srcMask = (Guchar)(0x80 >> (parentOffsetX & 7));
3204
0
    for (int x = 0; x < bitmap->width; ++x) {
3205
0
      if (*q & srcMask) {
3206
0
  *p |= mask;
3207
0
      } else {
3208
0
  *p &= (Guchar)~mask;
3209
0
      }
3210
0
      if (!(mask = (Guchar)(mask >> 1))) {
3211
0
  mask = 0x80;
3212
0
  ++p;
3213
0
      }
3214
0
      if (!(srcMask = (Guchar)(srcMask >> 1))) {
3215
0
  srcMask = 0x80;
3216
0
  ++q;
3217
0
      }
3218
0
    }
3219
3.26k
  } else {
3220
3.26k
    SplashColorPtr p = &bitmap->data[y * bitmap->rowSize];
3221
3.26k
    SplashColorPtr q =
3222
3.26k
        &parent->bitmap->data[(parentOffsetY + y) * parent->bitmap->rowSize
3223
3.26k
            + bitmapComps * parentOffsetX];
3224
3.26k
    memcpy(p, q, bitmapComps * bitmap->width);
3225
3.26k
  }
3226
3.26k
}
3227
3228
1.47k
void Splash::zeroRowColor(int y) {
3229
1.47k
  memset(&bitmap->data[y * bitmap->rowSize], 0,
3230
1.47k
   bitmap->rowSize < 0 ? -bitmap->rowSize : bitmap->rowSize);
3231
1.47k
}
3232
3233
4.74k
void Splash::zeroRowAlpha(int y) {
3234
4.74k
  memset(&bitmap->alpha[y * bitmap->alphaRowSize], 0, bitmap->width);
3235
4.74k
}
3236
3237
0
void Splash::computeAlpha0Row(int y) {
3238
0
  Guchar *p = &alpha0Bitmap->alpha[y * alpha0Bitmap->alphaRowSize];
3239
0
  Guchar *q0;
3240
0
  if (parent->alpha0Bitmap) {
3241
0
    q0 = &parent->alpha0Bitmap->alpha[
3242
0
       (parentOffsetY + y) * parent->alpha0Bitmap->alphaRowSize
3243
0
       + parentOffsetX];
3244
0
  } else {
3245
0
    q0 = &parent->parent->bitmap->alpha[
3246
0
       (parentOffsetY + parent->parentOffsetY + y)
3247
0
         * parent->parent->bitmap->alphaRowSize
3248
0
       + parentOffsetX + parent->parentOffsetX];
3249
0
  }
3250
0
  Guchar *q1 = &parent->bitmap->alpha[
3251
0
       (parentOffsetY + y) * parent->bitmap->alphaRowSize
3252
0
       + parentOffsetX];
3253
0
  int w = alpha0Bitmap->width;
3254
0
  for (int x = 0; x < w; ++x) {
3255
0
    Guchar a0 = *q0++;
3256
0
    Guchar a1 = *q1++;
3257
0
    *p++ = (Guchar)(a0 + a1 - div255(a0 * a1));
3258
0
  }
3259
0
}
3260
3261
//------------------------------------------------------------------------
3262
3263
// Transform a point from user space to device space.
3264
inline void Splash::transform(SplashCoord *matrix,
3265
            SplashCoord xi, SplashCoord yi,
3266
68.5M
            SplashCoord *xo, SplashCoord *yo) {
3267
  //                          [ m[0] m[1] 0 ]
3268
  // [xo yo 1] = [xi yi 1] *  [ m[2] m[3] 0 ]
3269
  //                          [ m[4] m[5] 1 ]
3270
68.5M
  *xo = xi * matrix[0] + yi * matrix[2] + matrix[4];
3271
68.5M
  *yo = xi * matrix[1] + yi * matrix[3] + matrix[5];
3272
68.5M
}
3273
3274
//------------------------------------------------------------------------
3275
// SplashImageCache
3276
//------------------------------------------------------------------------
3277
3278
185k
SplashImageCache::SplashImageCache() {
3279
185k
  tag = NULL;
3280
185k
  width = 0;
3281
185k
  height = 0;
3282
185k
  mode = splashModeRGB8;
3283
185k
  alpha = gFalse;
3284
185k
  interpolate = gFalse;
3285
185k
  colorData = NULL;
3286
185k
  alphaData = NULL;
3287
185k
  refCount = 1;
3288
185k
}
3289
3290
184k
SplashImageCache::~SplashImageCache() {
3291
184k
  if (tag) {
3292
235
    delete tag;
3293
235
  }
3294
184k
  gfree(colorData);
3295
184k
  gfree(alphaData);
3296
184k
}
3297
3298
GBool SplashImageCache::match(GString *aTag, int aWidth, int aHeight,
3299
            SplashColorMode aMode, GBool aAlpha,
3300
2.68k
            GBool aInterpolate) {
3301
2.68k
  return aTag && tag && !aTag->cmp(tag) &&
3302
308
   aWidth == width && aHeight == height &&
3303
308
   aMode == mode && aAlpha == alpha &&
3304
308
   aInterpolate == interpolate;
3305
2.68k
}
3306
3307
void SplashImageCache::reset(GString *aTag, int aWidth, int aHeight,
3308
           SplashColorMode aMode, GBool aAlpha,
3309
2.37k
           GBool aInterpolate) {
3310
2.37k
  if (tag) {
3311
832
    delete tag;
3312
832
  }
3313
2.37k
  if (aTag) {
3314
1.06k
    tag = aTag->copy();
3315
1.31k
  } else {
3316
1.31k
    tag = NULL;
3317
1.31k
  }
3318
2.37k
  width = aWidth;
3319
2.37k
  height = aHeight;
3320
2.37k
  mode = aMode;
3321
2.37k
  alpha = aAlpha;
3322
2.37k
  interpolate = aInterpolate; 
3323
2.37k
  gfree(colorData);
3324
2.37k
  colorData = NULL;
3325
2.37k
  gfree(alphaData);
3326
2.37k
  alphaData = NULL;
3327
2.37k
}
3328
3329
9.62k
void SplashImageCache::incRefCount() {
3330
9.62k
  ++refCount;
3331
9.62k
}
3332
3333
194k
void SplashImageCache::decRefCount() {
3334
194k
  --refCount;
3335
194k
  if (refCount == 0) {
3336
184k
    delete this;
3337
184k
  }
3338
194k
}
3339
3340
//------------------------------------------------------------------------
3341
// ImageScaler
3342
//------------------------------------------------------------------------
3343
3344
// Abstract base class.
3345
class ImageScaler {
3346
public:
3347
3348
2.33k
  ImageScaler() {}
3349
2.33k
  virtual ~ImageScaler() {}
3350
3351
  // Compute the next line of the scaled image.  This can be called up
3352
  // to [scaledHeight] times.
3353
  virtual void nextLine() = 0;
3354
3355
  // Retrieve the color and alpha data generated by the most recent
3356
  // call to nextLine().
3357
  virtual Guchar *colorData() = 0;
3358
  virtual Guchar *alphaData() = 0;
3359
};
3360
3361
//------------------------------------------------------------------------
3362
// BasicImageScaler
3363
//------------------------------------------------------------------------
3364
3365
// The actual image scaler.
3366
class BasicImageScaler: public ImageScaler {
3367
public:
3368
3369
  BasicImageScaler(SplashImageSource aSrc, void *aSrcData,
3370
       int aSrcWidth, int aSrcHeight, int aNComps, GBool aHasAlpha,
3371
       int aScaledWidth, int aScaledHeight, GBool aInterpolate);
3372
  virtual ~BasicImageScaler();
3373
  virtual void nextLine();
3374
4.66k
  virtual Guchar *colorData() { return colorLine; }
3375
90
  virtual Guchar *alphaData() { return alphaLine; }
3376
3377
protected:
3378
3379
  void vertDownscaleHorizDownscale();
3380
  void vertDownscaleHorizUpscaleNoInterp();
3381
  void vertDownscaleHorizUpscaleInterp();
3382
  void vertUpscaleHorizDownscaleNoInterp();
3383
  void vertUpscaleHorizDownscaleInterp();
3384
  void vertUpscaleHorizUpscaleNoInterp();
3385
  void vertUpscaleHorizUpscaleInterp();
3386
3387
  // source image data function
3388
  SplashImageSource src;
3389
  void *srcData;
3390
3391
  // source image size
3392
  int srcWidth;
3393
  int srcHeight;
3394
3395
  // scaled image size
3396
  int scaledWidth;
3397
  int scaledHeight;
3398
3399
  // number of color and alpha components
3400
  int nComps;
3401
  GBool hasAlpha;
3402
3403
  // params/state for vertical scaling
3404
  int yp, yq;
3405
  int yt, yn;
3406
  int ySrcCur, yScaledCur;
3407
  SplashCoord yInvScale;
3408
3409
  // params for horizontal scaling
3410
  int xp, xq;
3411
  SplashCoord xInvScale;
3412
3413
  // scaling function
3414
  void (BasicImageScaler::*scalingFunc)();
3415
3416
  // temporary buffers for vertical scaling
3417
  Guchar *colorTmpBuf0;
3418
  Guchar *colorTmpBuf1;
3419
  Guchar *colorTmpBuf2;
3420
  Guchar *alphaTmpBuf0;
3421
  Guchar *alphaTmpBuf1;
3422
  Guchar *alphaTmpBuf2;
3423
  Guint *colorAccBuf;
3424
  Guint *alphaAccBuf;
3425
3426
  // output of horizontal scaling
3427
  Guchar *colorLine;
3428
  Guchar *alphaLine;
3429
};
3430
3431
BasicImageScaler::BasicImageScaler(SplashImageSource aSrc, void *aSrcData,
3432
           int aSrcWidth, int aSrcHeight,
3433
           int aNComps, GBool aHasAlpha,
3434
           int aScaledWidth, int aScaledHeight,
3435
2.33k
           GBool aInterpolate) {
3436
2.33k
  colorTmpBuf0 = NULL;
3437
2.33k
  colorTmpBuf1 = NULL;
3438
2.33k
  colorTmpBuf2 = NULL;
3439
2.33k
  alphaTmpBuf0 = NULL;
3440
2.33k
  alphaTmpBuf1 = NULL;
3441
2.33k
  alphaTmpBuf2 = NULL;
3442
2.33k
  colorAccBuf = NULL;
3443
2.33k
  alphaAccBuf = NULL;
3444
2.33k
  colorLine = NULL;
3445
2.33k
  alphaLine = NULL;
3446
3447
2.33k
  src = aSrc;
3448
2.33k
  srcData = aSrcData;
3449
2.33k
  srcWidth = aSrcWidth;
3450
2.33k
  srcHeight = aSrcHeight;
3451
2.33k
  scaledWidth = aScaledWidth;
3452
2.33k
  scaledHeight = aScaledHeight;
3453
2.33k
  nComps = aNComps;
3454
2.33k
  hasAlpha = aHasAlpha;
3455
3456
  // select scaling function; allocate buffers
3457
2.33k
  if (scaledHeight <= srcHeight) {
3458
    // vertical downscaling
3459
2.33k
    yp = srcHeight / scaledHeight;
3460
2.33k
    yq = srcHeight % scaledHeight;
3461
2.33k
    yt = 0;
3462
2.33k
    colorTmpBuf0 = (Guchar *)gmallocn(srcWidth, nComps);
3463
2.33k
    colorAccBuf = (Guint *)gmallocn(srcWidth, nComps * (int)sizeof(Guint));
3464
2.33k
    if (hasAlpha) {
3465
45
      alphaTmpBuf0 = (Guchar *)gmalloc(srcWidth);
3466
45
      alphaAccBuf = (Guint *)gmallocn(srcWidth, sizeof(Guint));
3467
45
    }
3468
2.33k
    if (scaledWidth <= srcWidth) {
3469
2.33k
      scalingFunc = &BasicImageScaler::vertDownscaleHorizDownscale;
3470
2.33k
    } else {
3471
0
      if (aInterpolate) {
3472
0
  scalingFunc = &BasicImageScaler::vertDownscaleHorizUpscaleInterp;
3473
0
      } else {
3474
0
  scalingFunc = &BasicImageScaler::vertDownscaleHorizUpscaleNoInterp;
3475
0
      }
3476
0
    }
3477
2.33k
  } else {
3478
    // vertical upscaling
3479
0
    yp = scaledHeight / srcHeight;
3480
0
    yq = scaledHeight % srcHeight;
3481
0
    yt = 0;
3482
0
    yn = 0;
3483
0
    if (aInterpolate) {
3484
0
      yInvScale = (SplashCoord)srcHeight / (SplashCoord)scaledHeight;
3485
0
      colorTmpBuf0 = (Guchar *)gmallocn(srcWidth, nComps);
3486
0
      colorTmpBuf1 = (Guchar *)gmallocn(srcWidth, nComps);
3487
0
      if (hasAlpha) {
3488
0
  alphaTmpBuf0 = (Guchar *)gmalloc(srcWidth);
3489
0
  alphaTmpBuf1 = (Guchar *)gmalloc(srcWidth);
3490
0
      }
3491
0
      ySrcCur = 0;
3492
0
      yScaledCur = 0;
3493
0
      if (scaledWidth <= srcWidth) {
3494
0
  scalingFunc = &BasicImageScaler::vertUpscaleHorizDownscaleInterp;
3495
0
      } else {
3496
0
  colorTmpBuf2 = (Guchar *)gmallocn(srcWidth, nComps);
3497
0
  if (hasAlpha) {
3498
0
    alphaTmpBuf2 = (Guchar *)gmalloc(srcWidth);
3499
0
  }
3500
0
  scalingFunc = &BasicImageScaler::vertUpscaleHorizUpscaleInterp;
3501
0
      }
3502
0
    } else {
3503
0
      colorTmpBuf0 = (Guchar *)gmallocn(srcWidth, nComps);
3504
0
      if (hasAlpha) {
3505
0
  alphaTmpBuf0 = (Guchar *)gmalloc(srcWidth);
3506
0
      }
3507
0
      if (scaledWidth <= srcWidth) {
3508
0
  scalingFunc = &BasicImageScaler::vertUpscaleHorizDownscaleNoInterp;
3509
0
      } else {
3510
0
  scalingFunc = &BasicImageScaler::vertUpscaleHorizUpscaleNoInterp;
3511
0
      }
3512
0
    }
3513
0
  }
3514
2.33k
  if (scaledWidth <= srcWidth) {
3515
2.33k
    xp = srcWidth / scaledWidth;
3516
2.33k
    xq = srcWidth % scaledWidth;
3517
2.33k
  } else {
3518
0
    xp = scaledWidth / srcWidth;
3519
0
    xq = scaledWidth % srcWidth;
3520
0
    if (aInterpolate) {
3521
0
      xInvScale = (SplashCoord)srcWidth / (SplashCoord)scaledWidth;
3522
0
    }
3523
0
  }
3524
2.33k
  colorLine = (Guchar *)gmallocn(scaledWidth, nComps);
3525
2.33k
  if (hasAlpha) {
3526
45
    alphaLine = (Guchar *)gmalloc(scaledWidth);
3527
45
  }
3528
2.33k
}
3529
3530
2.33k
BasicImageScaler::~BasicImageScaler() {
3531
2.33k
  gfree(colorTmpBuf0);
3532
2.33k
  gfree(colorTmpBuf1);
3533
2.33k
  gfree(colorTmpBuf2);
3534
2.33k
  gfree(alphaTmpBuf0);
3535
2.33k
  gfree(alphaTmpBuf1);
3536
2.33k
  gfree(alphaTmpBuf2);
3537
2.33k
  gfree(colorAccBuf);
3538
2.33k
  gfree(alphaAccBuf);
3539
2.33k
  gfree(colorLine);
3540
2.33k
  gfree(alphaLine);
3541
2.33k
}
3542
3543
2.33k
void BasicImageScaler::nextLine() {
3544
2.33k
  (this->*scalingFunc)();
3545
2.33k
}
3546
3547
2.33k
void BasicImageScaler::vertDownscaleHorizDownscale() {
3548
  //--- vert downscale
3549
2.33k
  int yStep = yp;
3550
2.33k
  yt += yq;
3551
2.33k
  if (yt >= scaledHeight) {
3552
0
    yt -= scaledHeight;
3553
0
    ++yStep;
3554
0
  }
3555
2.33k
  memset(colorAccBuf, 0, (srcWidth * nComps) * sizeof(Guint));
3556
2.33k
  if (hasAlpha) {
3557
45
    memset(alphaAccBuf, 0, srcWidth * sizeof(Guint));
3558
45
  }
3559
2.33k
  int nRowComps = srcWidth * nComps;
3560
222k
  for (int i = 0; i < yStep; ++i) {
3561
219k
    (*src)(srcData, colorTmpBuf0, alphaTmpBuf0);
3562
66.2M
    for (int j = 0; j < nRowComps; ++j) {
3563
65.9M
      colorAccBuf[j] += colorTmpBuf0[j];
3564
65.9M
    }
3565
219k
    if (hasAlpha) {
3566
1.06M
      for (int j = 0; j < srcWidth; ++j) {
3567
1.05M
  alphaAccBuf[j] += alphaTmpBuf0[j];
3568
1.05M
      }
3569
13.8k
    }
3570
219k
  }
3571
3572
  //--- horiz downscale
3573
2.33k
  int colorAcc[splashMaxColorComps];
3574
2.33k
  int xt = 0;
3575
2.33k
  int unscaledColorIdx = 0;
3576
2.33k
  int unscaledAlphaIdx = 0;
3577
2.33k
  int scaledColorIdx = 0;
3578
3579
4.66k
  for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
3580
2.33k
    int xStep = xp;
3581
2.33k
    xt += xq;
3582
2.33k
    if (xt >= scaledWidth) {
3583
0
      xt -= scaledWidth;
3584
0
      ++xStep;
3585
0
    }
3586
3587
7.35k
    for (int j = 0; j < nComps; ++j) {
3588
5.01k
      colorAcc[j] = 0;
3589
5.01k
    }
3590
245k
    for (int i = 0; i < xStep; ++i) {
3591
818k
      for (int j = 0; j < nComps; ++j) {
3592
575k
  colorAcc[j] += colorAccBuf[unscaledColorIdx + j];
3593
575k
      }
3594
243k
      unscaledColorIdx += nComps;
3595
243k
    }
3596
2.33k
    int nPixels = yStep * xStep;
3597
7.35k
    for (int j = 0; j < nComps; ++j) {
3598
5.01k
      colorLine[scaledColorIdx + j] = (Guchar)(colorAcc[j] / nPixels);
3599
5.01k
    }
3600
2.33k
    scaledColorIdx += nComps;
3601
3602
2.33k
    if (hasAlpha) {
3603
45
      int alphaAcc = 0;
3604
40.2k
      for (int i = 0; i < xStep; ++i) {
3605
40.1k
  alphaAcc += alphaAccBuf[unscaledAlphaIdx];
3606
40.1k
  ++unscaledAlphaIdx;
3607
40.1k
      }
3608
45
      alphaLine[scaledIdx] = (Guchar)(alphaAcc / nPixels);
3609
45
    }
3610
2.33k
  }
3611
2.33k
}
3612
3613
0
void BasicImageScaler::vertDownscaleHorizUpscaleNoInterp() {
3614
  //--- vert downscale
3615
0
  int yStep = yp;
3616
0
  yt += yq;
3617
0
  if (yt >= scaledHeight) {
3618
0
    yt -= scaledHeight;
3619
0
    ++yStep;
3620
0
  }
3621
0
  memset(colorAccBuf, 0, (srcWidth * nComps) * sizeof(Guint));
3622
0
  if (hasAlpha) {
3623
0
    memset(alphaAccBuf, 0, srcWidth * sizeof(Guint));
3624
0
  }
3625
0
  int nRowComps = srcWidth * nComps;
3626
0
  for (int i = 0; i < yStep; ++i) {
3627
0
    (*src)(srcData, colorTmpBuf0, alphaTmpBuf0);
3628
0
    for (int j = 0; j < nRowComps; ++j) {
3629
0
      colorAccBuf[j] += colorTmpBuf0[j];
3630
0
    }
3631
0
    if (hasAlpha) {
3632
0
      for (int j = 0; j < srcWidth; ++j) {
3633
0
  alphaAccBuf[j] += alphaTmpBuf0[j];
3634
0
      }
3635
0
    }
3636
0
  }
3637
3638
  //--- horiz upscale
3639
0
  Guchar colorBuf[splashMaxColorComps];
3640
0
  int xt = 0;
3641
0
  int scaledColorIdx = 0;
3642
0
  int srcColorIdx = 0;
3643
0
  int scaledAlphaIdx = 0;
3644
0
  for (int srcIdx = 0; srcIdx < srcWidth; ++srcIdx) {
3645
0
    int xStep = xp;
3646
0
    xt += xq;
3647
0
    if (xt >= srcWidth) {
3648
0
      xt -= srcWidth;
3649
0
      ++xStep;
3650
0
    }
3651
0
    for (int j = 0; j < nComps; ++j) {
3652
0
      colorBuf[j] = (Guchar)(colorAccBuf[srcColorIdx + j] / yStep);
3653
0
    }
3654
0
    srcColorIdx += nComps;
3655
0
    for (int i = 0; i < xStep; ++i) {
3656
0
      for (int j = 0; j < nComps; ++j) {
3657
0
  colorLine[scaledColorIdx + j] = colorBuf[j];
3658
0
      }
3659
0
      scaledColorIdx += nComps;
3660
0
    }
3661
0
    if (hasAlpha) {
3662
0
      Guchar alphaBuf = (Guchar)(alphaAccBuf[srcIdx] / yStep);
3663
0
      for (int i = 0; i < xStep; ++i) {
3664
0
  alphaLine[scaledAlphaIdx] = alphaBuf;
3665
0
  ++scaledAlphaIdx;
3666
0
      }
3667
0
    }
3668
0
  }
3669
0
}
3670
3671
0
void BasicImageScaler::vertDownscaleHorizUpscaleInterp() {
3672
  //--- vert downscale
3673
0
  int yStep = yp;
3674
0
  yt += yq;
3675
0
  if (yt >= scaledHeight) {
3676
0
    yt -= scaledHeight;
3677
0
    ++yStep;
3678
0
  }
3679
0
  memset(colorAccBuf, 0, (srcWidth * nComps) * sizeof(Guint));
3680
0
  if (hasAlpha) {
3681
0
    memset(alphaAccBuf, 0, srcWidth * sizeof(Guint));
3682
0
  }
3683
0
  int nRowComps = srcWidth * nComps;
3684
0
  for (int i = 0; i < yStep; ++i) {
3685
0
    (*src)(srcData, colorTmpBuf0, alphaTmpBuf0);
3686
0
    for (int j = 0; j < nRowComps; ++j) {
3687
0
      colorAccBuf[j] += colorTmpBuf0[j];
3688
0
    }
3689
0
    if (hasAlpha) {
3690
0
      for (int j = 0; j < srcWidth; ++j) {
3691
0
  alphaAccBuf[j] += alphaTmpBuf0[j];
3692
0
      }
3693
0
    }
3694
0
  }
3695
0
  for (int j = 0; j < srcWidth * nComps; ++j) {
3696
0
    colorAccBuf[j] /= yStep;
3697
0
  }
3698
0
  if (hasAlpha) {
3699
0
    for (int j = 0; j < srcWidth; ++j) {
3700
0
      alphaAccBuf[j] /= yStep;
3701
0
    }
3702
0
  }
3703
3704
  //--- horiz upscale
3705
0
  int scaledColorIdx = 0;
3706
0
  for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
3707
0
    SplashCoord xs = ((SplashCoord)scaledIdx + 0.5) * xInvScale;
3708
0
    int x0 = splashFloor(xs - 0.5);
3709
0
    int x1 = x0 + 1;
3710
0
    SplashCoord s0 = (SplashCoord)x1 + 0.5 - xs;
3711
0
    SplashCoord s1 = (SplashCoord)1 - s0;
3712
0
    if (x0 < 0) {
3713
0
      x0 = 0;
3714
0
    }
3715
0
    if (x1 >= srcWidth) {
3716
0
      x1 = srcWidth - 1;
3717
0
    }
3718
0
    for (int j = 0; j < nComps; ++j) {
3719
0
      colorLine[scaledColorIdx + j] =
3720
0
    (Guchar)(int)(s0 * colorAccBuf[x0 * nComps + j] +
3721
0
      s1 * colorAccBuf[x1 * nComps + j]);
3722
0
    }
3723
0
    scaledColorIdx += nComps;
3724
0
    if (hasAlpha) {
3725
0
      alphaLine[scaledIdx] = (Guchar)(int)(s0 * alphaAccBuf[x0] +
3726
0
             s1 * alphaAccBuf[x1]);
3727
0
    }
3728
0
  }
3729
0
}
3730
3731
0
void BasicImageScaler::vertUpscaleHorizDownscaleNoInterp() {
3732
  //--- vert upscale
3733
0
  if (yn == 0) {
3734
0
    yn = yp;
3735
0
    yt += yq;
3736
0
    if (yt >= srcHeight) {
3737
0
      yt -= srcHeight;
3738
0
      ++yn;
3739
0
    }
3740
0
    (*src)(srcData, colorTmpBuf0, alphaTmpBuf0);
3741
0
  }
3742
0
  --yn;
3743
3744
  //--- horiz downscale
3745
0
  int colorAcc[splashMaxColorComps];
3746
0
  int xt = 0;
3747
0
  int unscaledColorIdx = 0;
3748
0
  int unscaledAlphaIdx = 0;
3749
0
  int scaledColorIdx = 0;
3750
0
  for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
3751
0
    int xStep = xp;
3752
0
    xt += xq;
3753
0
    if (xt >= scaledWidth) {
3754
0
      xt -= scaledWidth;
3755
0
      ++xStep;
3756
0
    }
3757
3758
0
    for (int j = 0; j < nComps; ++j) {
3759
0
      colorAcc[j] = 0;
3760
0
    }
3761
0
    for (int i = 0; i < xStep; ++i) {
3762
0
      for (int j = 0; j < nComps; ++j) {
3763
0
  colorAcc[j] += colorTmpBuf0[unscaledColorIdx + j];
3764
0
      }
3765
0
      unscaledColorIdx += nComps;
3766
0
    }
3767
0
    for (int j = 0; j < nComps; ++j) {
3768
0
      colorLine[scaledColorIdx + j] = (Guchar)(colorAcc[j] / xStep);
3769
0
    }
3770
0
    scaledColorIdx += nComps;
3771
3772
0
    if (hasAlpha) {
3773
0
      int alphaAcc = 0;
3774
0
      for (int i = 0; i < xStep; ++i) {
3775
0
  alphaAcc += alphaTmpBuf0[unscaledAlphaIdx];
3776
0
  ++unscaledAlphaIdx;
3777
0
      }
3778
0
      alphaLine[scaledIdx] = (Guchar)(alphaAcc / xStep);
3779
0
    }
3780
0
  }
3781
0
}
3782
3783
0
void BasicImageScaler::vertUpscaleHorizDownscaleInterp() {
3784
  //--- vert upscale
3785
0
  if (ySrcCur == 0) {
3786
0
    (*src)(srcData, colorTmpBuf0, alphaTmpBuf0);
3787
0
    (*src)(srcData, colorTmpBuf1, alphaTmpBuf1);
3788
0
    ySrcCur = 1;
3789
0
  }
3790
0
  SplashCoord ys = ((SplashCoord)yScaledCur + 0.5) * yInvScale;
3791
0
  int y0 = splashFloor(ys - 0.5);
3792
0
  int y1 = y0 + 1;
3793
0
  SplashCoord vs0 = (SplashCoord)y1 + 0.5 - ys;
3794
0
  SplashCoord vs1 = (SplashCoord)1 - vs0;
3795
0
  if (y1 > ySrcCur && ySrcCur < srcHeight - 1) {
3796
0
    Guchar *t = colorTmpBuf0;
3797
0
    colorTmpBuf0 = colorTmpBuf1;
3798
0
    colorTmpBuf1 = t;
3799
0
    if (hasAlpha) {
3800
0
      t = alphaTmpBuf0;
3801
0
      alphaTmpBuf0 = alphaTmpBuf1;
3802
0
      alphaTmpBuf1 = t;
3803
0
    }
3804
0
    (*src)(srcData, colorTmpBuf1, alphaTmpBuf1);
3805
0
    ++ySrcCur;
3806
0
  }
3807
0
  Guchar *color0 = colorTmpBuf0;
3808
0
  Guchar *color1 = colorTmpBuf1;
3809
0
  Guchar *alpha0 = alphaTmpBuf0;
3810
0
  Guchar *alpha1 = alphaTmpBuf1;
3811
0
  if (y0 < 0) {
3812
0
    y0 = 0;
3813
0
    color1 = color0;
3814
0
    alpha1 = alpha0;
3815
0
  }
3816
0
  if (y1 >= srcHeight) {
3817
0
    y1 = srcHeight - 1;
3818
0
    color0 = color1;
3819
0
    alpha0 = alpha1;
3820
0
  }
3821
0
  ++yScaledCur;
3822
3823
  //--- horiz downscale
3824
0
  int colorAcc[splashMaxColorComps];
3825
0
  int xt = 0;
3826
0
  int unscaledColorIdx = 0;
3827
0
  int unscaledAlphaIdx = 0;
3828
0
  int scaledColorIdx = 0;
3829
0
  for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
3830
0
    int xStep = xp;
3831
0
    xt += xq;
3832
0
    if (xt >= scaledWidth) {
3833
0
      xt -= scaledWidth;
3834
0
      ++xStep;
3835
0
    }
3836
3837
0
    for (int j = 0; j < nComps; ++j) {
3838
0
      colorAcc[j] = 0;
3839
0
    }
3840
0
    for (int i = 0; i < xStep; ++i) {
3841
0
      for (int j = 0; j < nComps; ++j) {
3842
0
  colorAcc[j] += (int)(vs0 * (int)color0[unscaledColorIdx + j] +
3843
0
           vs1 * (int)color1[unscaledColorIdx + j]);
3844
0
      }
3845
0
      unscaledColorIdx += nComps;
3846
0
    }
3847
0
    for (int j = 0; j < nComps; ++j) {
3848
0
      colorLine[scaledColorIdx + j] = (Guchar)(colorAcc[j] / xStep);
3849
0
    }
3850
0
    scaledColorIdx += nComps;
3851
3852
0
    if (hasAlpha) {
3853
0
      int alphaAcc = 0;
3854
0
      for (int i = 0; i < xStep; ++i) {
3855
0
  alphaAcc += (int)(vs0 * (int)alpha0[unscaledAlphaIdx] +
3856
0
        vs1 * (int)alpha1[unscaledAlphaIdx]);
3857
0
  ++unscaledAlphaIdx;
3858
0
      }
3859
0
      alphaLine[scaledIdx] = (Guchar)(alphaAcc / xStep);
3860
0
    }
3861
0
  }
3862
0
}
3863
3864
0
void BasicImageScaler::vertUpscaleHorizUpscaleNoInterp() {
3865
  //--- vert upscale
3866
0
  if (yn == 0) {
3867
0
    yn = yp;
3868
0
    yt += yq;
3869
0
    if (yt >= srcHeight) {
3870
0
      yt -= srcHeight;
3871
0
      ++yn;
3872
0
    }
3873
0
    (*src)(srcData, colorTmpBuf0, alphaTmpBuf0);
3874
0
  }
3875
0
  --yn;
3876
3877
  //--- horiz upscale
3878
0
  int xt = 0;
3879
0
  int scaledColorIdx = 0;
3880
0
  int srcColorIdx = 0;
3881
0
  int scaledAlphaIdx = 0;
3882
0
  for (int srcIdx = 0; srcIdx < srcWidth; ++srcIdx) {
3883
0
    int xStep = xp;
3884
0
    xt += xq;
3885
0
    if (xt >= srcWidth) {
3886
0
      xt -= srcWidth;
3887
0
      ++xStep;
3888
0
    }
3889
0
    for (int i = 0; i < xStep; ++i) {
3890
0
      for (int j = 0; j < nComps; ++j) {
3891
0
  colorLine[scaledColorIdx + j] = colorTmpBuf0[srcColorIdx + j];
3892
0
      }
3893
0
      scaledColorIdx += nComps;
3894
0
    }
3895
0
    srcColorIdx += nComps;
3896
0
    if (hasAlpha) {
3897
0
      Guchar alphaBuf = alphaTmpBuf0[srcIdx];
3898
0
      for (int i = 0; i < xStep; ++i) {
3899
0
  alphaLine[scaledAlphaIdx] = alphaBuf;
3900
0
  ++scaledAlphaIdx;
3901
0
      }
3902
0
    }
3903
0
  }
3904
0
}
3905
3906
0
void BasicImageScaler::vertUpscaleHorizUpscaleInterp() {
3907
  //--- vert upscale
3908
0
  if (ySrcCur == 0) {
3909
0
    (*src)(srcData, colorTmpBuf0, alphaTmpBuf0);
3910
0
    (*src)(srcData, colorTmpBuf1, alphaTmpBuf1);
3911
0
    ySrcCur = 1;
3912
0
  }
3913
0
  SplashCoord ys = ((SplashCoord)yScaledCur + 0.5) * yInvScale;
3914
0
  int y0 = splashFloor(ys - 0.5);
3915
0
  int y1 = y0 + 1;
3916
0
  SplashCoord vs0 = (SplashCoord)y1 + 0.5 - ys;
3917
0
  SplashCoord vs1 = (SplashCoord)1 - vs0;
3918
0
  if (y1 > ySrcCur && ySrcCur < srcHeight - 1) {
3919
0
    Guchar *t = colorTmpBuf0;
3920
0
    colorTmpBuf0 = colorTmpBuf1;
3921
0
    colorTmpBuf1 = t;
3922
0
    if (hasAlpha) {
3923
0
      t = alphaTmpBuf0;
3924
0
      alphaTmpBuf0 = alphaTmpBuf1;
3925
0
      alphaTmpBuf1 = t;
3926
0
    }
3927
0
    (*src)(srcData, colorTmpBuf1, alphaTmpBuf1);
3928
0
    ++ySrcCur;
3929
0
  }
3930
0
  Guchar *color0 = colorTmpBuf0;
3931
0
  Guchar *color1 = colorTmpBuf1;
3932
0
  Guchar *alpha0 = alphaTmpBuf0;
3933
0
  Guchar *alpha1 = alphaTmpBuf1;
3934
0
  if (y0 < 0) {
3935
0
    y0 = 0;
3936
0
    color1 = color0;
3937
0
    alpha1 = alpha0;
3938
0
  }
3939
0
  if (y1 >= srcHeight) {
3940
0
    y1 = srcHeight - 1;
3941
0
    color0 = color1;
3942
0
    alpha0 = alpha1;
3943
0
  }
3944
0
  ++yScaledCur;
3945
0
  for (int srcIdx = 0; srcIdx < srcWidth * nComps; ++srcIdx) {
3946
0
    colorTmpBuf2[srcIdx] = (Guchar)(int)(vs0 * (int)color0[srcIdx] +
3947
0
           vs1 * (int)color1[srcIdx]);
3948
0
  }
3949
0
  if (hasAlpha) {
3950
0
    for (int srcIdx = 0; srcIdx < srcWidth; ++srcIdx) {
3951
0
      alphaTmpBuf2[srcIdx] = (Guchar)(int)(vs0 * (int)alpha0[srcIdx] +
3952
0
             vs1 * (int)alpha1[srcIdx]);
3953
0
    }
3954
0
  }
3955
3956
  //--- horiz upscale
3957
0
  int scaledColorIdx = 0;
3958
0
  for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
3959
0
    SplashCoord xs = ((SplashCoord)scaledIdx + 0.5) * xInvScale;
3960
0
    int x0 = splashFloor(xs - 0.5);
3961
0
    int x1 = x0 + 1;
3962
0
    SplashCoord hs0 = (SplashCoord)x1 + 0.5 - xs;
3963
0
    SplashCoord hs1 = (SplashCoord)1 - hs0;
3964
0
    if (x0 < 0) {
3965
0
      x0 = 0;
3966
0
    }
3967
0
    if (x1 >= srcWidth) {
3968
0
      x1 = srcWidth - 1;
3969
0
    }
3970
0
    for (int j = 0; j < nComps; ++j) {
3971
0
      colorLine[scaledColorIdx + j] =
3972
0
    (Guchar)(int)(hs0 * (int)colorTmpBuf2[x0 * nComps + j] +
3973
0
      hs1 * (int)colorTmpBuf2[x1 * nComps + j]);
3974
0
    }
3975
0
    scaledColorIdx += nComps;
3976
0
    if (hasAlpha) {
3977
0
      alphaLine[scaledIdx] = (Guchar)(int)(hs0 * (int)alphaTmpBuf2[x0] +
3978
0
             hs1 * (int)alphaTmpBuf2[x1]);
3979
0
    }
3980
0
  }
3981
0
}
3982
3983
//------------------------------------------------------------------------
3984
// SavingImageScaler
3985
//------------------------------------------------------------------------
3986
3987
// Wrapper around BasicImageScaler that saves the scaled image for use
3988
// by ReplayImageScaler.
3989
class SavingImageScaler: public BasicImageScaler {
3990
public:
3991
3992
  SavingImageScaler(SplashImageSource aSrc, void *aSrcData,
3993
        int aSrcWidth, int aSrcHeight, int aNComps, GBool aHasAlpha,
3994
        int aScaledWidth, int aScaledHeight, GBool aInterpolate,
3995
        Guchar *aColorCache, Guchar *aAlphaCache);
3996
  virtual void nextLine();
3997
3998
private:
3999
4000
  Guchar *colorPtr;
4001
  Guchar *alphaPtr;
4002
};
4003
4004
SavingImageScaler::SavingImageScaler(SplashImageSource aSrc, void *aSrcData,
4005
             int aSrcWidth, int aSrcHeight,
4006
             int aNComps, GBool aHasAlpha,
4007
             int aScaledWidth, int aScaledHeight,
4008
             GBool aInterpolate,
4009
             Guchar *aColorCache, Guchar *aAlphaCache):
4010
2.33k
  BasicImageScaler(aSrc, aSrcData, aSrcWidth, aSrcHeight, aNComps, aHasAlpha,
4011
2.33k
       aScaledWidth, aScaledHeight, aInterpolate)
4012
2.33k
{
4013
2.33k
  colorPtr = aColorCache;
4014
2.33k
  alphaPtr = aAlphaCache;
4015
2.33k
}
4016
4017
2.33k
void SavingImageScaler::nextLine() {
4018
2.33k
  BasicImageScaler::nextLine();
4019
2.33k
  memcpy(colorPtr, colorData(), scaledWidth * nComps);
4020
2.33k
  colorPtr += scaledWidth * nComps;
4021
2.33k
  if (hasAlpha) {
4022
45
    memcpy(alphaPtr, alphaData(), scaledWidth);
4023
45
    alphaPtr += scaledWidth;
4024
45
  }
4025
2.33k
}
4026
4027
//------------------------------------------------------------------------
4028
// ReplayImageScaler
4029
//------------------------------------------------------------------------
4030
4031
// "Replay" a scaled image saved by SavingImageScaler.
4032
class ReplayImageScaler: public ImageScaler {
4033
public:
4034
4035
  ReplayImageScaler(int aNComps, GBool aHasAlpha,
4036
        int aScaledWidth,
4037
        Guchar *aColorCache, Guchar *aAlphaCache);
4038
  virtual void nextLine();
4039
0
  virtual Guchar *colorData() { return colorLine; }
4040
0
  virtual Guchar *alphaData() { return alphaLine; }
4041
4042
private:
4043
4044
  int nComps;
4045
  GBool hasAlpha;
4046
  int scaledWidth;
4047
  Guchar *colorPtr;
4048
  Guchar *alphaPtr;
4049
  Guchar *colorLine;
4050
  Guchar *alphaLine;
4051
};
4052
4053
ReplayImageScaler::ReplayImageScaler(int aNComps, GBool aHasAlpha,
4054
             int aScaledWidth,
4055
0
             Guchar *aColorCache, Guchar *aAlphaCache) {
4056
0
  nComps = aNComps;
4057
0
  hasAlpha = aHasAlpha;
4058
0
  scaledWidth = aScaledWidth;
4059
0
  colorPtr = aColorCache;
4060
0
  alphaPtr = aAlphaCache;
4061
0
  colorLine = NULL;
4062
0
  alphaLine = NULL;
4063
0
}
4064
4065
0
void ReplayImageScaler::nextLine() {
4066
0
  colorLine = colorPtr;
4067
0
  alphaLine = alphaPtr;
4068
0
  colorPtr += scaledWidth * nComps;
4069
0
  if (hasAlpha) {
4070
0
    alphaPtr += scaledWidth;
4071
0
  }
4072
0
}
4073
4074
//------------------------------------------------------------------------
4075
// ImageMaskScaler
4076
//------------------------------------------------------------------------
4077
4078
class ImageMaskScaler {
4079
public:
4080
4081
  // Set up a MaskScaler to scale from [srcWidth]x[srcHeight] to
4082
  // [scaledWidth]x[scaledHeight].  The [interpolate] flag controls
4083
  // filtering on upsampling, and the [antialias] flag controls
4084
  // filtering on downsampling.
4085
  ImageMaskScaler(SplashImageMaskSource aSrc, void *aSrcData,
4086
      int aSrcWidth, int aSrcHeight,
4087
      int aScaledWidth, int aScaledHeight,
4088
      GBool aInterpolate, GBool aAntialias);
4089
4090
  ~ImageMaskScaler();
4091
4092
  // Compute the next line of the scaled image mask.  This can be
4093
  // called up to [scaledHeight] times.
4094
  void nextLine();
4095
4096
  // Retrieve the data generated by the most recent call to
4097
  // nextLine().
4098
11.0M
  Guchar *data() { return line; }
4099
4100
private:
4101
4102
  void vertDownscaleHorizDownscale();
4103
  void vertDownscaleHorizDownscaleThresh();
4104
  void vertDownscaleHorizUpscaleNoInterp();
4105
  void vertDownscaleHorizUpscaleInterp();
4106
  void vertDownscaleHorizUpscaleThresh();
4107
  void vertUpscaleHorizDownscaleNoInterp();
4108
  void vertUpscaleHorizDownscaleInterp();
4109
  void vertUpscaleHorizDownscaleThresh();
4110
  void vertUpscaleHorizUpscaleNoInterp();
4111
  void vertUpscaleHorizUpscaleInterp();
4112
4113
  // source image data function
4114
  SplashImageMaskSource src;
4115
  void *srcData;
4116
4117
  // source image size
4118
  int srcWidth;
4119
  int srcHeight;
4120
4121
  // scaled image size
4122
  int scaledWidth;
4123
  int scaledHeight;
4124
4125
  // params/state for vertical scaling
4126
  int yp, yq;
4127
  int yt, yn;
4128
  int ySrcCur, yScaledCur;
4129
  SplashCoord yInvScale;
4130
4131
  // params for horizontal scaling
4132
  int xp, xq;
4133
  SplashCoord xInvScale;
4134
4135
  // vertical and horizontal scaling functions
4136
  void (ImageMaskScaler::*scalingFunc)();
4137
4138
  // temporary buffers for vertical scaling
4139
  Guchar *tmpBuf0;
4140
  Guchar *tmpBuf1;
4141
  Guchar *tmpBuf2;
4142
  Guint *accBuf;
4143
4144
  // output of horizontal scaling
4145
  Guchar *line;
4146
};
4147
4148
ImageMaskScaler::ImageMaskScaler(SplashImageMaskSource aSrc, void *aSrcData,
4149
         int aSrcWidth, int aSrcHeight,
4150
         int aScaledWidth, int aScaledHeight,
4151
1.56k
         GBool aInterpolate, GBool aAntialias) {
4152
1.56k
  tmpBuf0 = NULL;
4153
1.56k
  tmpBuf1 = NULL;
4154
1.56k
  tmpBuf2 = NULL;
4155
1.56k
  accBuf = NULL;
4156
1.56k
  line = NULL;
4157
4158
1.56k
  src = aSrc;
4159
1.56k
  srcData = aSrcData;
4160
1.56k
  srcWidth = aSrcWidth;
4161
1.56k
  srcHeight = aSrcHeight;
4162
1.56k
  scaledWidth = aScaledWidth;
4163
1.56k
  scaledHeight = aScaledHeight;
4164
4165
  // select scaling function; allocate buffers
4166
1.56k
  if (scaledHeight <= srcHeight) {
4167
    // vertical downscaling
4168
1.33k
    yp = srcHeight / scaledHeight;
4169
1.33k
    yq = srcHeight % scaledHeight;
4170
1.33k
    yt = 0;
4171
1.33k
    tmpBuf0 = (Guchar *)gmalloc(srcWidth);
4172
1.33k
    accBuf = (Guint *)gmallocn(srcWidth, sizeof(Guint));
4173
1.33k
    if (scaledWidth <= srcWidth) {
4174
1.18k
      if (!aAntialias) {
4175
0
  scalingFunc = &ImageMaskScaler::vertDownscaleHorizDownscaleThresh;
4176
1.18k
      } else {
4177
1.18k
  scalingFunc = &ImageMaskScaler::vertDownscaleHorizDownscale;
4178
1.18k
      }
4179
1.18k
    } else {
4180
146
      if (!aAntialias) {
4181
0
  scalingFunc = &ImageMaskScaler::vertDownscaleHorizUpscaleThresh;
4182
146
      } else if (aInterpolate) {
4183
21
  scalingFunc = &ImageMaskScaler::vertDownscaleHorizUpscaleInterp;
4184
125
      } else {
4185
125
  scalingFunc = &ImageMaskScaler::vertDownscaleHorizUpscaleNoInterp;
4186
125
      }
4187
146
    }
4188
1.33k
  } else {
4189
    // vertical upscaling
4190
228
    yp = scaledHeight / srcHeight;
4191
228
    yq = scaledHeight % srcHeight;
4192
228
    yt = 0;
4193
228
    yn = 0;
4194
228
    if (!aAntialias) {
4195
0
      tmpBuf0 = (Guchar *)gmalloc(srcWidth);
4196
0
      if (scaledWidth <= srcWidth) {
4197
0
  scalingFunc = &ImageMaskScaler::vertUpscaleHorizDownscaleThresh;
4198
0
      } else {
4199
0
  scalingFunc = &ImageMaskScaler::vertUpscaleHorizUpscaleNoInterp;
4200
0
      }
4201
228
    } else if (aInterpolate) {
4202
23
      yInvScale = (SplashCoord)srcHeight / (SplashCoord)scaledHeight;
4203
23
      tmpBuf0 = (Guchar *)gmalloc(srcWidth);
4204
23
      tmpBuf1 = (Guchar *)gmalloc(srcWidth);
4205
23
      ySrcCur = 0;
4206
23
      yScaledCur = 0;
4207
23
      if (scaledWidth <= srcWidth) {
4208
8
  scalingFunc = &ImageMaskScaler::vertUpscaleHorizDownscaleInterp;
4209
15
      } else {
4210
15
  tmpBuf2 = (Guchar *)gmalloc(srcWidth);
4211
15
  scalingFunc = &ImageMaskScaler::vertUpscaleHorizUpscaleInterp;
4212
15
      }
4213
205
    } else {
4214
205
      tmpBuf0 = (Guchar *)gmalloc(srcWidth);
4215
205
      if (scaledWidth <= srcWidth) {
4216
69
  scalingFunc = &ImageMaskScaler::vertUpscaleHorizDownscaleNoInterp;
4217
136
      } else {
4218
136
  scalingFunc = &ImageMaskScaler::vertUpscaleHorizUpscaleNoInterp;
4219
136
      }
4220
205
    }
4221
228
  }
4222
1.56k
  if (scaledWidth <= srcWidth) {
4223
1.26k
    xp = srcWidth / scaledWidth;
4224
1.26k
    xq = srcWidth % scaledWidth;
4225
1.26k
  } else {
4226
297
    xp = scaledWidth / srcWidth;
4227
297
    xq = scaledWidth % srcWidth;
4228
297
    if (aInterpolate) {
4229
36
      xInvScale = (SplashCoord)srcWidth / (SplashCoord)scaledWidth;
4230
36
    }
4231
297
  }
4232
1.56k
  line = (Guchar *)gmalloc(scaledWidth);
4233
1.56k
}
4234
4235
1.56k
ImageMaskScaler::~ImageMaskScaler() {
4236
1.56k
  gfree(tmpBuf0);
4237
1.56k
  gfree(tmpBuf1);
4238
1.56k
  gfree(tmpBuf2);
4239
1.56k
  gfree(accBuf);
4240
1.56k
  gfree(line);
4241
1.56k
}
4242
4243
11.0M
void ImageMaskScaler::nextLine() {
4244
11.0M
  (this->*scalingFunc)();
4245
11.0M
}
4246
4247
15.6k
void ImageMaskScaler::vertDownscaleHorizDownscale() {
4248
  //--- vert downscale
4249
15.6k
  int yStep = yp;
4250
15.6k
  yt += yq;
4251
15.6k
  if (yt >= scaledHeight) {
4252
6.38k
    yt -= scaledHeight;
4253
6.38k
    ++yStep;
4254
6.38k
  }
4255
15.6k
  memset(accBuf, 0, srcWidth * sizeof(Guint));
4256
188k
  for (int i = 0; i < yStep; ++i) {
4257
172k
    (*src)(srcData, tmpBuf0);
4258
179M
    for (int j = 0; j < srcWidth; ++j) {
4259
179M
      accBuf[j] += tmpBuf0[j];
4260
179M
    }
4261
172k
  }
4262
4263
  //--- horiz downscale
4264
15.6k
  int acc;
4265
15.6k
  int xt = 0;
4266
15.6k
  int unscaledIdx = 0;
4267
900k
  for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
4268
885k
    int xStep = xp;
4269
885k
    xt += xq;
4270
885k
    if (xt >= scaledWidth) {
4271
1.57k
      xt -= scaledWidth;
4272
1.57k
      ++xStep;
4273
1.57k
    }
4274
4275
885k
    acc = 0;
4276
8.06M
    for (int i = 0; i < xStep; ++i) {
4277
7.18M
      acc += accBuf[unscaledIdx];
4278
7.18M
      ++unscaledIdx;
4279
7.18M
    }
4280
885k
    line[scaledIdx] = (Guchar)((255 * acc) / (xStep * yStep));
4281
885k
  }
4282
15.6k
}
4283
4284
0
void ImageMaskScaler::vertDownscaleHorizDownscaleThresh() {
4285
  //--- vert downscale
4286
0
  int yStep = yp;
4287
0
  yt += yq;
4288
0
  if (yt >= scaledHeight) {
4289
0
    yt -= scaledHeight;
4290
0
    ++yStep;
4291
0
  }
4292
0
  memset(accBuf, 0, srcWidth * sizeof(Guint));
4293
0
  for (int i = 0; i < yStep; ++i) {
4294
0
    (*src)(srcData, tmpBuf0);
4295
0
    for (int j = 0; j < srcWidth; ++j) {
4296
0
      accBuf[j] += tmpBuf0[j];
4297
0
    }
4298
0
  }
4299
4300
  //--- horiz downscale
4301
0
  int acc;
4302
0
  int xt = 0;
4303
0
  int unscaledIdx = 0;
4304
0
  for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
4305
0
    int xStep = xp;
4306
0
    xt += xq;
4307
0
    if (xt >= scaledWidth) {
4308
0
      xt -= scaledWidth;
4309
0
      ++xStep;
4310
0
    }
4311
4312
0
    acc = 0;
4313
0
    for (int i = 0; i < xStep; ++i) {
4314
0
      acc += accBuf[unscaledIdx];
4315
0
      ++unscaledIdx;
4316
0
    }
4317
0
    line[scaledIdx] = acc > ((xStep * yStep) >> 1) ? (Guchar)255 : (Guchar)0;
4318
0
  }
4319
0
}
4320
4321
7.63k
void ImageMaskScaler::vertDownscaleHorizUpscaleNoInterp() {
4322
  //--- vert downscale
4323
7.63k
  int yStep = yp;
4324
7.63k
  yt += yq;
4325
7.63k
  if (yt >= scaledHeight) {
4326
1.05k
    yt -= scaledHeight;
4327
1.05k
    ++yStep;
4328
1.05k
  }
4329
7.63k
  memset(accBuf, 0, srcWidth * sizeof(Guint));
4330
59.9k
  for (int i = 0; i < yStep; ++i) {
4331
52.3k
    (*src)(srcData, tmpBuf0);
4332
418k
    for (int j = 0; j < srcWidth; ++j) {
4333
366k
      accBuf[j] += tmpBuf0[j];
4334
366k
    }
4335
52.3k
  }
4336
4337
  //--- horiz upscale
4338
7.63k
  int xt = 0;
4339
7.63k
  int scaledIdx = 0;
4340
61.0k
  for (int srcIdx = 0; srcIdx < srcWidth; ++srcIdx) {
4341
53.4k
    int xStep = xp;
4342
53.4k
    xt += xq;
4343
53.4k
    if (xt >= srcWidth) {
4344
1.46k
      xt -= srcWidth;
4345
1.46k
      ++xStep;
4346
1.46k
    }
4347
53.4k
    Guchar buf = (Guchar)((255 * accBuf[srcIdx]) / yStep);
4348
103M
    for (int i = 0; i < xStep; ++i) {
4349
103M
      line[scaledIdx] = buf;
4350
103M
      ++scaledIdx;
4351
103M
    }
4352
53.4k
  }
4353
7.63k
}
4354
4355
579
void ImageMaskScaler::vertDownscaleHorizUpscaleInterp() {
4356
  //--- vert downscale
4357
579
  int yStep = yp;
4358
579
  yt += yq;
4359
579
  if (yt >= scaledHeight) {
4360
310
    yt -= scaledHeight;
4361
310
    ++yStep;
4362
310
  }
4363
579
  memset(accBuf, 0, srcWidth * sizeof(Guint));
4364
23.3k
  for (int i = 0; i < yStep; ++i) {
4365
22.7k
    (*src)(srcData, tmpBuf0);
4366
182k
    for (int j = 0; j < srcWidth; ++j) {
4367
159k
      accBuf[j] += tmpBuf0[j];
4368
159k
    }
4369
22.7k
  }
4370
4.63k
  for (int j = 0; j < srcWidth; ++j) {
4371
4.05k
    accBuf[j] = (255 * accBuf[j]) / yStep;
4372
4.05k
  }
4373
4374
  //--- horiz upscale
4375
52.0k
  for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
4376
51.4k
    SplashCoord xs = ((SplashCoord)scaledIdx + 0.5) * xInvScale;
4377
51.4k
    int x0 = splashFloor(xs - 0.5);
4378
51.4k
    int x1 = x0 + 1;
4379
51.4k
    SplashCoord s0 = (SplashCoord)x1 + 0.5 - xs;
4380
51.4k
    SplashCoord s1 = (SplashCoord)1 - s0;
4381
51.4k
    if (x0 < 0) {
4382
3.62k
      x0 = 0;
4383
3.62k
    }
4384
51.4k
    if (x1 >= srcWidth) {
4385
3.62k
      x1 = srcWidth - 1;
4386
3.62k
    }
4387
51.4k
    line[scaledIdx] = (Guchar)(int)(s0 * accBuf[x0] + s1 * accBuf[x1]);
4388
51.4k
  }
4389
579
}
4390
4391
0
void ImageMaskScaler::vertDownscaleHorizUpscaleThresh() {
4392
  //--- vert downscale
4393
0
  int yStep = yp;
4394
0
  yt += yq;
4395
0
  if (yt >= scaledHeight) {
4396
0
    yt -= scaledHeight;
4397
0
    ++yStep;
4398
0
  }
4399
0
  memset(accBuf, 0, srcWidth * sizeof(Guint));
4400
0
  for (int i = 0; i < yStep; ++i) {
4401
0
    (*src)(srcData, tmpBuf0);
4402
0
    for (int j = 0; j < srcWidth; ++j) {
4403
0
      accBuf[j] += tmpBuf0[j];
4404
0
    }
4405
0
  }
4406
4407
  //--- horiz upscale
4408
0
  int xt = 0;
4409
0
  int scaledIdx = 0;
4410
0
  for (int srcIdx = 0; srcIdx < srcWidth; ++srcIdx) {
4411
0
    int xStep = xp;
4412
0
    xt += xq;
4413
0
    if (xt >= srcWidth) {
4414
0
      xt -= srcWidth;
4415
0
      ++xStep;
4416
0
    }
4417
0
    Guchar buf = accBuf[srcIdx] > (Guint)(yStep >> 1) ? (Guchar)255 : (Guchar)0;
4418
0
    for (int i = 0; i < xStep; ++i) {
4419
0
      line[scaledIdx] = buf;
4420
0
      ++scaledIdx;
4421
0
    }
4422
0
  }
4423
0
}
4424
4425
9.61M
void ImageMaskScaler::vertUpscaleHorizDownscaleNoInterp() {
4426
  //--- vert upscale
4427
9.61M
  if (yn == 0) {
4428
2.25k
    yn = yp;
4429
2.25k
    yt += yq;
4430
2.25k
    if (yt >= srcHeight) {
4431
1.15k
      yt -= srcHeight;
4432
1.15k
      ++yn;
4433
1.15k
    }
4434
2.25k
    (*src)(srcData, tmpBuf0);
4435
2.25k
  }
4436
9.61M
  --yn;
4437
4438
  //--- horiz downscale
4439
9.61M
  int acc;
4440
9.61M
  int xt = 0;
4441
9.61M
  int unscaledIdx = 0;
4442
20.4M
  for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
4443
10.8M
    int xStep = xp;
4444
10.8M
    xt += xq;
4445
10.8M
    if (xt >= scaledWidth) {
4446
463k
      xt -= scaledWidth;
4447
463k
      ++xStep;
4448
463k
    }
4449
4450
10.8M
    acc = 0;
4451
85.6M
    for (int i = 0; i < xStep; ++i) {
4452
74.8M
      acc += tmpBuf0[unscaledIdx];
4453
74.8M
      ++unscaledIdx;
4454
74.8M
    }
4455
10.8M
    line[scaledIdx] = (Guchar)((255 * acc) / xStep);
4456
10.8M
  }
4457
9.61M
}
4458
4459
1.40M
void ImageMaskScaler::vertUpscaleHorizDownscaleInterp() {
4460
  //--- vert upscale
4461
1.40M
  if (ySrcCur == 0) {
4462
8
    (*src)(srcData, tmpBuf0);
4463
8
    (*src)(srcData, tmpBuf1);
4464
8
    ySrcCur = 1;
4465
8
  }
4466
1.40M
  SplashCoord ys = ((SplashCoord)yScaledCur + 0.5) * yInvScale;
4467
1.40M
  int y0 = splashFloor(ys - 0.5);
4468
1.40M
  int y1 = y0 + 1;
4469
1.40M
  SplashCoord vs0 = (SplashCoord)y1 + 0.5 - ys;
4470
1.40M
  SplashCoord vs1 = (SplashCoord)1 - vs0;
4471
1.40M
  if (y1 > ySrcCur && ySrcCur < srcHeight - 1) {
4472
369
    Guchar *t = tmpBuf0;
4473
369
    tmpBuf0 = tmpBuf1;
4474
369
    tmpBuf1 = t;
4475
369
    (*src)(srcData, tmpBuf1);
4476
369
    ++ySrcCur;
4477
369
  }
4478
1.40M
  Guchar *mask0 = tmpBuf0;
4479
1.40M
  Guchar *mask1 = tmpBuf1;
4480
1.40M
  if (y0 < 0) {
4481
16.8k
    y0 = 0;
4482
16.8k
    mask1 = mask0;
4483
16.8k
  }
4484
1.40M
  if (y1 >= srcHeight) {
4485
16.8k
    y1 = srcHeight - 1;
4486
16.8k
    mask0 = mask1;
4487
16.8k
  }
4488
1.40M
  ++yScaledCur;
4489
4490
  //--- horiz downscale
4491
1.40M
  int acc;
4492
1.40M
  int xt = 0;
4493
1.40M
  int unscaledIdx = 0;
4494
2.80M
  for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
4495
1.40M
    int xStep = xp;
4496
1.40M
    xt += xq;
4497
1.40M
    if (xt >= scaledWidth) {
4498
0
      xt -= scaledWidth;
4499
0
      ++xStep;
4500
0
    }
4501
4502
1.40M
    acc = 0;
4503
11.2M
    for (int i = 0; i < xStep; ++i) {
4504
9.80M
      acc += (int)(vs0 * (int)mask0[unscaledIdx] +
4505
9.80M
       vs1 * (int)mask1[unscaledIdx]);
4506
9.80M
      ++unscaledIdx;
4507
9.80M
    }
4508
1.40M
    line[scaledIdx] = (Guchar)((255 * acc) / xStep);
4509
1.40M
  }
4510
1.40M
}
4511
4512
0
void ImageMaskScaler::vertUpscaleHorizDownscaleThresh() {
4513
  //--- vert upscale
4514
0
  if (yn == 0) {
4515
0
    yn = yp;
4516
0
    yt += yq;
4517
0
    if (yt >= srcHeight) {
4518
0
      yt -= srcHeight;
4519
0
      ++yn;
4520
0
    }
4521
0
    (*src)(srcData, tmpBuf0);
4522
0
  }
4523
0
  --yn;
4524
4525
  //--- horiz downscale
4526
0
  int acc;
4527
0
  int xt = 0;
4528
0
  int unscaledIdx = 0;
4529
0
  for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
4530
0
    int xStep = xp;
4531
0
    xt += xq;
4532
0
    if (xt >= scaledWidth) {
4533
0
      xt -= scaledWidth;
4534
0
      ++xStep;
4535
0
    }
4536
4537
0
    acc = 0;
4538
0
    for (int i = 0; i < xStep; ++i) {
4539
0
      acc += tmpBuf0[unscaledIdx];
4540
0
      ++unscaledIdx;
4541
0
    }
4542
0
    line[scaledIdx] = acc > (xStep >> 1) ? (Guchar)255 : (Guchar)0;
4543
0
  }
4544
0
}
4545
4546
19.3k
void ImageMaskScaler::vertUpscaleHorizUpscaleNoInterp() {
4547
  //--- vert upscale
4548
19.3k
  if (yn == 0) {
4549
2.58k
    yn = yp;
4550
2.58k
    yt += yq;
4551
2.58k
    if (yt >= srcHeight) {
4552
965
      yt -= srcHeight;
4553
965
      ++yn;
4554
965
    }
4555
2.58k
    (*src)(srcData, tmpBuf0);
4556
2.58k
  }
4557
19.3k
  --yn;
4558
4559
  //--- horiz upscale
4560
19.3k
  int xt = 0;
4561
19.3k
  int scaledIdx = 0;
4562
155k
  for (int srcIdx = 0; srcIdx < srcWidth; ++srcIdx) {
4563
136k
    int xStep = xp;
4564
136k
    xt += xq;
4565
136k
    if (xt >= srcWidth) {
4566
108k
      xt -= srcWidth;
4567
108k
      ++xStep;
4568
108k
    }
4569
136k
    Guchar buf = (Guchar)(255 * tmpBuf0[srcIdx]);
4570
15.9M
    for (int i = 0; i < xStep; ++i) {
4571
15.7M
      line[scaledIdx] = buf;
4572
15.7M
      ++scaledIdx;
4573
15.7M
    }
4574
136k
  }
4575
19.3k
}
4576
4577
3.84k
void ImageMaskScaler::vertUpscaleHorizUpscaleInterp() {
4578
  //--- vert upscale
4579
3.84k
  if (ySrcCur == 0) {
4580
15
    (*src)(srcData, tmpBuf0);
4581
15
    (*src)(srcData, tmpBuf1);
4582
15
    ySrcCur = 1;
4583
15
  }
4584
3.84k
  SplashCoord ys = ((SplashCoord)yScaledCur + 0.5) * yInvScale;
4585
3.84k
  int y0 = splashFloor(ys - 0.5);
4586
3.84k
  int y1 = y0 + 1;
4587
3.84k
  SplashCoord vs0 = (SplashCoord)255 * ((SplashCoord)y1 + 0.5 - ys);
4588
3.84k
  SplashCoord vs1 = (SplashCoord)255 - vs0;
4589
3.84k
  if (y1 > ySrcCur && ySrcCur < srcHeight - 1) {
4590
296
    Guchar *t = tmpBuf0;
4591
296
    tmpBuf0 = tmpBuf1;
4592
296
    tmpBuf1 = t;
4593
296
    (*src)(srcData, tmpBuf1);
4594
296
    ++ySrcCur;
4595
296
  }
4596
3.84k
  Guchar *mask0 = tmpBuf0;
4597
3.84k
  Guchar *mask1 = tmpBuf1;
4598
3.84k
  if (y0 < 0) {
4599
605
    y0 = 0;
4600
605
    mask1 = mask0;
4601
605
  }
4602
3.84k
  if (y1 >= srcHeight) {
4603
605
    y1 = srcHeight - 1;
4604
605
    mask0 = mask1;
4605
605
  }
4606
3.84k
  ++yScaledCur;
4607
30.7k
  for (int srcIdx = 0; srcIdx < srcWidth; ++srcIdx) {
4608
26.9k
    tmpBuf2[srcIdx] = (Guchar)(int)(vs0 * (int)mask0[srcIdx] +
4609
26.9k
            vs1 * (int)mask1[srcIdx]);
4610
26.9k
  }
4611
4612
  //--- horiz upscale
4613
3.08M
  for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
4614
3.08M
    SplashCoord xs = ((SplashCoord)scaledIdx + 0.5) * xInvScale;
4615
3.08M
    int x0 = splashFloor(xs - 0.5);
4616
3.08M
    int x1 = x0 + 1;
4617
3.08M
    SplashCoord hs0 = (SplashCoord)x1 + 0.5 - xs;
4618
3.08M
    SplashCoord hs1 = (SplashCoord)1 - hs0;
4619
3.08M
    if (x0 < 0) {
4620
218k
      x0 = 0;
4621
218k
    }
4622
3.08M
    if (x1 >= srcWidth) {
4623
218k
      x1 = srcWidth - 1;
4624
218k
    }
4625
3.08M
    line[scaledIdx] = (Guchar)(int)(hs0 * (int)tmpBuf2[x0] +
4626
3.08M
            hs1 * (int)tmpBuf2[x1]);
4627
3.08M
  }
4628
3.84k
}
4629
4630
//------------------------------------------------------------------------
4631
// Splash
4632
//------------------------------------------------------------------------
4633
4634
Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,
4635
         SplashImageCache *imageCacheA,
4636
187k
         SplashScreenParams *screenParams) {
4637
187k
  bitmap = bitmapA;
4638
187k
  bitmapComps = splashColorModeNComps[bitmap->mode];
4639
187k
  vectorAntialias = vectorAntialiasA;
4640
187k
  state = new SplashState(bitmap->width, bitmap->height, vectorAntialias,
4641
187k
        screenParams);
4642
187k
  scanBuf = (Guchar *)gmalloc(bitmap->width);
4643
187k
  if (bitmap->mode == splashModeMono1) {
4644
162
    scanBuf2 = (Guchar *)gmalloc(bitmap->width);
4645
187k
  } else {
4646
187k
    scanBuf2 = NULL;
4647
187k
  }
4648
187k
  parent = NULL;
4649
187k
  deferredInit = gFalse;
4650
187k
  alpha0Bitmap = NULL;
4651
187k
  shapeBitmap = NULL;
4652
187k
  overprintMaskBitmap = NULL;
4653
187k
  minLineWidth = 0;
4654
187k
  clearModRegion();
4655
187k
  debugMode = gFalse;
4656
4657
187k
  if (imageCacheA) {
4658
2.40k
    imageCache = imageCacheA;
4659
2.40k
    imageCache->incRefCount();
4660
185k
  } else {
4661
185k
    imageCache = new SplashImageCache();
4662
185k
  }
4663
187k
}
4664
4665
Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,
4666
7.22k
         SplashImageCache *imageCacheA, SplashScreen *screenA) {
4667
7.22k
  bitmap = bitmapA;
4668
7.22k
  bitmapComps = splashColorModeNComps[bitmap->mode];
4669
7.22k
  vectorAntialias = vectorAntialiasA;
4670
7.22k
  state = new SplashState(bitmap->width, bitmap->height, vectorAntialias,
4671
7.22k
        screenA);
4672
7.22k
  scanBuf = (Guchar *)gmalloc(bitmap->width);
4673
7.22k
  if (bitmap->mode == splashModeMono1) {
4674
0
    scanBuf2 = (Guchar *)gmalloc(bitmap->width);
4675
7.22k
  } else {
4676
7.22k
    scanBuf2 = NULL;
4677
7.22k
  }
4678
7.22k
  parent = NULL;
4679
7.22k
  deferredInit = gFalse;
4680
7.22k
  alpha0Bitmap = NULL;
4681
7.22k
  shapeBitmap = NULL;
4682
7.22k
  overprintMaskBitmap = NULL;
4683
7.22k
  minLineWidth = 0;
4684
7.22k
  clearModRegion();
4685
7.22k
  debugMode = gFalse;
4686
4687
7.22k
  if (imageCacheA) {
4688
7.22k
    imageCache = imageCacheA;
4689
7.22k
    imageCache->incRefCount();
4690
7.22k
  } else {
4691
0
    imageCache = new SplashImageCache();
4692
0
  }
4693
7.22k
}
4694
4695
194k
Splash::~Splash() {
4696
194k
  imageCache->decRefCount();
4697
4698
194k
  while (state->next) {
4699
293
    restoreState();
4700
293
  }
4701
194k
  delete state;
4702
194k
  gfree(scanBuf);
4703
194k
  gfree(scanBuf2);
4704
194k
}
4705
4706
//------------------------------------------------------------------------
4707
// state read
4708
//------------------------------------------------------------------------
4709
4710
40.8k
SplashCoord *Splash::getMatrix() {
4711
40.8k
  return state->matrix;
4712
40.8k
}
4713
4714
5.58k
SplashPattern *Splash::getStrokePattern() {
4715
5.58k
  return state->strokePattern;
4716
5.58k
}
4717
4718
5.58k
SplashPattern *Splash::getFillPattern() {
4719
5.58k
  return state->fillPattern;
4720
5.58k
}
4721
4722
7.22k
SplashScreen *Splash::getScreen() {
4723
7.22k
  return state->screen;
4724
7.22k
}
4725
4726
0
SplashBlendFunc Splash::getBlendFunc() {
4727
0
  return state->blendFunc;
4728
0
}
4729
4730
0
SplashCoord Splash::getStrokeAlpha() {
4731
0
  return state->strokeAlpha;
4732
0
}
4733
4734
0
SplashCoord Splash::getFillAlpha() {
4735
0
  return state->fillAlpha;
4736
0
}
4737
4738
5.37k
SplashCoord Splash::getLineWidth() {
4739
5.37k
  return state->lineWidth;
4740
5.37k
}
4741
4742
5.37k
int Splash::getLineCap() {
4743
5.37k
  return state->lineCap;
4744
5.37k
}
4745
4746
5.37k
int Splash::getLineJoin() {
4747
5.37k
  return state->lineJoin;
4748
5.37k
}
4749
4750
0
SplashCoord Splash::getMiterLimit() {
4751
0
  return state->miterLimit;
4752
0
}
4753
4754
0
SplashCoord Splash::getFlatness() {
4755
0
  return state->flatness;
4756
0
}
4757
4758
5.37k
SplashCoord *Splash::getLineDash() {
4759
5.37k
  return state->lineDash;
4760
5.37k
}
4761
4762
5.37k
int Splash::getLineDashLength() {
4763
5.37k
  return state->lineDashLength;
4764
5.37k
}
4765
4766
5.37k
SplashCoord Splash::getLineDashPhase() {
4767
5.37k
  return state->lineDashPhase;
4768
5.37k
}
4769
4770
2.42M
SplashStrokeAdjustMode Splash::getStrokeAdjust() {
4771
2.42M
  return state->strokeAdjust;
4772
2.42M
}
4773
4774
29.5k
SplashClip *Splash::getClip() {
4775
29.5k
  return state->clip;
4776
29.5k
}
4777
4778
5.78k
SplashBitmap *Splash::getSoftMask() {
4779
5.78k
  return state->softMask;
4780
5.78k
}
4781
4782
9.38k
GBool Splash::getInNonIsolatedGroup() {
4783
9.38k
  return state->inNonIsolatedGroup;
4784
9.38k
}
4785
4786
5.78k
GBool Splash::getInKnockoutGroup() {
4787
5.78k
  return state->inKnockoutGroup;
4788
5.78k
}
4789
4790
//------------------------------------------------------------------------
4791
// state write
4792
//------------------------------------------------------------------------
4793
4794
2.92M
void Splash::setMatrix(SplashCoord *matrix) {
4795
2.92M
  memcpy(state->matrix, matrix, 6 * sizeof(SplashCoord));
4796
2.92M
}
4797
4798
512k
void Splash::setStrokePattern(SplashPattern *strokePattern) {
4799
512k
  state->setStrokePattern(strokePattern);
4800
512k
}
4801
4802
4.65M
void Splash::setFillPattern(SplashPattern *fillPattern) {
4803
4.65M
  state->setFillPattern(fillPattern);
4804
4.65M
}
4805
4806
0
void Splash::setScreen(SplashScreen *screen) {
4807
0
  state->setScreen(screen);
4808
0
}
4809
4810
4.77k
void Splash::setBlendFunc(SplashBlendFunc func) {
4811
4.77k
  state->blendFunc = func;
4812
4.77k
}
4813
4814
6.78k
void Splash::setStrokeAlpha(SplashCoord alpha) {
4815
6.78k
  state->strokeAlpha = alpha;
4816
6.78k
}
4817
4818
6.88k
void Splash::setFillAlpha(SplashCoord alpha) {
4819
6.88k
  state->fillAlpha = alpha;
4820
6.88k
}
4821
4822
320k
void Splash::setLineWidth(SplashCoord lineWidth) {
4823
320k
  state->lineWidth = lineWidth;
4824
320k
}
4825
4826
391k
void Splash::setLineCap(int lineCap) {
4827
391k
  if (lineCap >= 0 && lineCap <= 2) {
4828
391k
    state->lineCap = lineCap;
4829
391k
  } else {
4830
0
    state->lineCap = 0;
4831
0
  }
4832
391k
}
4833
4834
361k
void Splash::setLineJoin(int lineJoin) {
4835
361k
  if (lineJoin >= 0 && lineJoin <= 2) {
4836
361k
    state->lineJoin = lineJoin;
4837
361k
  } else {
4838
0
    state->lineJoin = 0;
4839
0
  }
4840
361k
}
4841
4842
362k
void Splash::setMiterLimit(SplashCoord miterLimit) {
4843
362k
  state->miterLimit = miterLimit;
4844
362k
}
4845
4846
169k
void Splash::setFlatness(SplashCoord flatness) {
4847
169k
  if (flatness < 1) {
4848
0
    state->flatness = 1;
4849
169k
  } else {
4850
169k
    state->flatness = flatness;
4851
169k
  }
4852
169k
}
4853
4854
void Splash::setLineDash(SplashCoord *lineDash, int lineDashLength,
4855
390k
       SplashCoord lineDashPhase) {
4856
390k
  state->setLineDash(lineDash, lineDashLength, lineDashPhase);
4857
390k
}
4858
4859
5.04M
void Splash::setStrokeAdjust(SplashStrokeAdjustMode strokeAdjust) {
4860
5.04M
  state->strokeAdjust = strokeAdjust;
4861
5.04M
}
4862
4863
3.97k
void Splash::setAlphaIsShape(GBool alphaIsShape) {
4864
3.97k
  state->alphaIsShape = alphaIsShape;
4865
3.97k
}
4866
4867
void Splash::clipResetToRect(SplashCoord x0, SplashCoord y0,
4868
0
           SplashCoord x1, SplashCoord y1) {
4869
0
  state->clipResetToRect(x0, y0, x1, y1);
4870
0
}
4871
4872
SplashError Splash::clipToRect(SplashCoord x0, SplashCoord y0,
4873
0
             SplashCoord x1, SplashCoord y1) {
4874
0
  return state->clipToRect(x0, y0, x1, y1);
4875
0
}
4876
4877
241k
SplashError Splash::clipToPath(SplashPath *path, GBool eo) {
4878
241k
  return state->clipToPath(path, eo);
4879
241k
}
4880
4881
11.9k
void Splash::setSoftMask(SplashBitmap *softMask, GBool deleteBitmap) {
4882
11.9k
  state->setSoftMask(softMask, deleteBitmap);
4883
11.9k
}
4884
4885
void Splash::setInTransparencyGroup(Splash *parentA,
4886
            int parentOffsetXA, int parentOffsetYA,
4887
            SplashAlphaBitmap *alpha0BitmapA,
4888
            SplashAlphaBitmap *shapeBitmapA,
4889
5.10k
            GBool nonIsolated, GBool knockout) {
4890
5.10k
  parent = parentA;
4891
5.10k
  parentOffsetX = parentOffsetXA;
4892
5.10k
  parentOffsetY = parentOffsetYA;
4893
5.10k
  deferredInit = gTrue;
4894
5.10k
  deferredInitYMin = 1;
4895
5.10k
  deferredInitYMax = 0;
4896
5.10k
  alpha0Bitmap = alpha0BitmapA;
4897
5.10k
  shapeBitmap = shapeBitmapA;
4898
5.10k
  state->inNonIsolatedGroup = nonIsolated;
4899
5.10k
  state->inKnockoutGroup = knockout;
4900
5.10k
}
4901
4902
1.36k
void Splash::forceDeferredInit(int y, int h) {
4903
1.36k
  useBitmapRow(y);
4904
1.36k
  useBitmapRow(y + h - 1);
4905
1.36k
}
4906
4907
// Check that alpha is 0 in the specified rectangle.
4908
// NB: This doesn't work correctly in a non-isolated group, because
4909
//     the rasterizer temporarily stores alpha_g instead of alpha.
4910
5.66k
GBool Splash::checkTransparentRect(int x, int y, int w, int h) {
4911
5.66k
  if (state->inNonIsolatedGroup) {
4912
0
    return gFalse;
4913
0
  }
4914
4915
4916
5.66k
  if (!bitmap->alpha) {
4917
0
    return gFalse;
4918
0
  }
4919
5.66k
  int yy0, yy1;
4920
5.66k
  if (deferredInit) {
4921
    // all transparency groups initialize alpha to zero, so anything
4922
    // outside of groupDestInit[YMin,YMax] will have alpha=0
4923
84
    yy0 = (y > deferredInitYMin) ? y : deferredInitYMin;
4924
84
    yy1 = (y + h - 1 < deferredInitYMax) ? y + h - 1 : deferredInitYMax;
4925
5.58k
  } else {
4926
5.58k
    yy0 = y;
4927
5.58k
    yy1 = y + h - 1;
4928
5.58k
  }
4929
5.66k
  Guchar *alphaP = &bitmap->alpha[yy0 * bitmap->alphaRowSize + x];
4930
7.89k
  for (int yy = yy0; yy <= yy1; ++yy) {
4931
7.84k
    for (int xx = 0; xx < w; ++xx) {
4932
5.61k
      if (alphaP[xx] != 0) {
4933
3.39k
  return gFalse;
4934
3.39k
      }
4935
5.61k
    }
4936
2.22k
    alphaP += bitmap->getAlphaRowSize();
4937
2.22k
  }
4938
2.27k
  return gTrue;
4939
5.66k
}
4940
4941
void Splash::setTransfer(Guchar *red, Guchar *green, Guchar *blue,
4942
741
       Guchar *gray) {
4943
741
  state->setTransfer(red, green, blue, gray);
4944
741
}
4945
4946
814k
void Splash::setOverprintMask(Guint overprintMask) {
4947
814k
  state->overprintMask = overprintMask;
4948
814k
}
4949
4950
4951
194k
void Splash::setEnablePathSimplification(GBool en) {
4952
194k
  state->enablePathSimplification = en;
4953
194k
}
4954
4955
//------------------------------------------------------------------------
4956
// state save/restore
4957
//------------------------------------------------------------------------
4958
4959
3.26M
void Splash::saveState() {
4960
3.26M
  SplashState *newState;
4961
4962
3.26M
  newState = state->copy();
4963
3.26M
  newState->next = state;
4964
3.26M
  state = newState;
4965
3.26M
}
4966
4967
3.38M
SplashError Splash::restoreState() {
4968
3.38M
  SplashState *oldState;
4969
4970
3.38M
  if (!state->next) {
4971
120k
    return splashErrNoSave;
4972
120k
  }
4973
3.26M
  oldState = state;
4974
3.26M
  state = state->next;
4975
3.26M
  delete oldState;
4976
3.26M
  return splashOk;
4977
3.38M
}
4978
4979
//------------------------------------------------------------------------
4980
// drawing operations
4981
//------------------------------------------------------------------------
4982
4983
186k
void Splash::clear(SplashColorPtr color, Guchar alpha) {
4984
186k
  SplashColorPtr row, p;
4985
186k
  Guchar mono;
4986
186k
  int x, y;
4987
4988
186k
  switch (bitmap->mode) {
4989
162
  case splashModeMono1:
4990
162
    mono = (color[0] & 0x80) ? 0xff : 0x00;
4991
162
    if (bitmap->rowSize < 0) {
4992
0
      memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
4993
0
       mono, -bitmap->rowSize * bitmap->height);
4994
162
    } else {
4995
162
      memset(bitmap->data, mono, bitmap->rowSize * bitmap->height);
4996
162
    }
4997
162
    break;
4998
271
  case splashModeMono8:
4999
271
    if (bitmap->rowSize < 0) {
5000
0
      memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
5001
0
       color[0], -bitmap->rowSize * bitmap->height);
5002
271
    } else {
5003
271
      memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
5004
271
    }
5005
271
    break;
5006
185k
  case splashModeRGB8:
5007
185k
    if (color[0] == color[1] && color[1] == color[2]) {
5008
185k
      if (bitmap->rowSize < 0) {
5009
0
  memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
5010
0
         color[0], -bitmap->rowSize * bitmap->height);
5011
185k
      } else {
5012
185k
  memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
5013
185k
      }
5014
185k
    } else {
5015
0
      row = bitmap->data;
5016
0
      for (y = 0; y < bitmap->height; ++y) {
5017
0
  p = row;
5018
0
  for (x = 0; x < bitmap->width; ++x) {
5019
0
    *p++ = color[0];
5020
0
    *p++ = color[1];
5021
0
    *p++ = color[2];
5022
0
  }
5023
0
  row += bitmap->rowSize;
5024
0
      }
5025
0
    }
5026
185k
    break;
5027
0
  case splashModeBGR8:
5028
0
    if (color[0] == color[1] && color[1] == color[2]) {
5029
0
      if (bitmap->rowSize < 0) {
5030
0
  memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
5031
0
         color[0], -bitmap->rowSize * bitmap->height);
5032
0
      } else {
5033
0
  memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
5034
0
      }
5035
0
    } else {
5036
0
      row = bitmap->data;
5037
0
      for (y = 0; y < bitmap->height; ++y) {
5038
0
  p = row;
5039
0
  for (x = 0; x < bitmap->width; ++x) {
5040
0
    *p++ = color[2];
5041
0
    *p++ = color[1];
5042
0
    *p++ = color[0];
5043
0
  }
5044
0
  row += bitmap->rowSize;
5045
0
      }
5046
0
    }
5047
0
    break;
5048
0
#if SPLASH_CMYK
5049
0
  case splashModeCMYK8:
5050
0
    if (color[0] == color[1] && color[1] == color[2] && color[2] == color[3]) {
5051
0
      if (bitmap->rowSize < 0) {
5052
0
  memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
5053
0
         color[0], -bitmap->rowSize * bitmap->height);
5054
0
      } else {
5055
0
  memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
5056
0
      }
5057
0
    } else {
5058
0
      row = bitmap->data;
5059
0
      for (y = 0; y < bitmap->height; ++y) {
5060
0
  p = row;
5061
0
  for (x = 0; x < bitmap->width; ++x) {
5062
0
    *p++ = color[0];
5063
0
    *p++ = color[1];
5064
0
    *p++ = color[2];
5065
0
    *p++ = color[3];
5066
0
  }
5067
0
  row += bitmap->rowSize;
5068
0
      }
5069
0
    }
5070
0
    break;
5071
186k
#endif
5072
186k
  }
5073
5074
186k
  if (bitmap->alpha) {
5075
185k
    memset(bitmap->alpha, alpha, bitmap->alphaRowSize * bitmap->height);
5076
185k
  }
5077
5078
186k
  updateModX(0);
5079
186k
  updateModY(0);
5080
186k
  updateModX(bitmap->width - 1);
5081
186k
  updateModY(bitmap->height - 1);
5082
186k
}
5083
5084
91.7k
SplashError Splash::stroke(SplashPath *path) {
5085
91.7k
  SplashPath *path2, *dPath;
5086
91.7k
  SplashCoord w, w2, lineDashMax, lineDashTotal;
5087
91.7k
  int lineCap, lineJoin, i;
5088
5089
91.7k
  if (debugMode) {
5090
0
    printf("stroke [dash:%d] [width:%.2f]:\n",
5091
0
     state->lineDashLength, (double)state->lineWidth);
5092
0
    dumpPath(path);
5093
0
  }
5094
91.7k
  opClipRes = splashClipAllOutside;
5095
91.7k
  if (path->length == 0) {
5096
1.95k
    return splashErrEmptyPath;
5097
1.95k
  }
5098
89.7k
  if (pathAllOutside(path, gTrue)) {
5099
59.5k
    return splashOk;
5100
59.5k
  }
5101
30.2k
  path2 = flattenPath(path, state->matrix, state->flatness);
5102
5103
  // Compute an approximation of the transformed line width.
5104
  // Given a CTM of [m0 m1],
5105
  //                [m2 m3]
5106
  // if |m0|*|m3| >= |m1|*|m2| then use min{|m0|,|m3|}, else
5107
  // use min{|m1|,|m2|}.
5108
  // This handles the common cases -- [s 0   ] and [0    s] --
5109
  //                                  [0 +/-s]     [+/-s 0]
5110
  // well, and still does something reasonable for the uncommon
5111
  // case transforms.
5112
30.2k
  double t0 = splashAbs(state->matrix[0]);
5113
30.2k
  double t1 = splashAbs(state->matrix[1]);
5114
30.2k
  double t2 = splashAbs(state->matrix[2]);
5115
30.2k
  double t3 = splashAbs(state->matrix[3]);
5116
30.2k
  double t01 = t0 * t0 + t1 * t1;
5117
30.2k
  double t23 = t2 * t2 + t3 * t3;
5118
30.2k
  w = sqrt((t01 > t23) ? t01 : t23);
5119
30.2k
  w2 = w * state->lineWidth;
5120
5121
  // construct the dashed path
5122
30.2k
  if (state->lineDashLength > 0) {
5123
5124
    // check the maximum transformed dash element length (using the
5125
    // same approximation as for line width) -- if it's less than 0.1
5126
    // pixel, don't apply the dash pattern; this avoids a huge
5127
    // performance/memory hit with PDF files that use absurd dash
5128
    // patterns like [0.0007 0.0003]
5129
    //
5130
    // we also skip the dash pattern for very long paths with short
5131
    // (but not necessarily sub-pixel) dash patterns -- Adobe appears
5132
    // to do something similar
5133
328
    lineDashTotal = 0;
5134
328
    lineDashMax = 0;
5135
3.47k
    for (i = 0; i < state->lineDashLength; ++i) {
5136
3.14k
      lineDashTotal += state->lineDash[i];
5137
3.14k
      if (state->lineDash[i] > lineDashMax) {
5138
459
  lineDashMax = state->lineDash[i];
5139
459
      }
5140
3.14k
    }
5141
    // Acrobat simply draws nothing if the dash array is [0]
5142
328
    if (lineDashTotal == 0) {
5143
33
      delete path2;
5144
33
      return splashOk;
5145
33
    }
5146
295
    if (w * lineDashMax > 0.1 &&
5147
249
  w * lineDashMax > 0.0001 * path->length) {
5148
249
      dPath = makeDashedPath(path2);
5149
249
      delete path2;
5150
249
      path2 = dPath;
5151
249
      if (path2->length == 0) {
5152
12
  delete path2;
5153
12
  return splashErrEmptyPath;
5154
12
      }
5155
249
    }
5156
295
  }
5157
5158
  // round caps on narrow lines look bad, and can't be
5159
  // stroke-adjusted, so use projecting caps instead (but we can't do
5160
  // this if there are zero-length dashes or segments, because those
5161
  // turn into round dots)
5162
30.2k
  lineCap = state->lineCap;
5163
30.2k
  lineJoin = state->lineJoin;
5164
30.2k
  if (state->strokeAdjust == splashStrokeAdjustCAD &&
5165
0
      w2 < 3.5) {
5166
0
    if (lineCap == splashLineCapRound &&
5167
0
  !state->lineDashContainsZeroLengthDashes() &&
5168
0
  !path->containsZeroLengthSubpaths()) {
5169
0
      lineCap = splashLineCapProjecting;
5170
0
    }
5171
0
    if (lineJoin == splashLineJoinRound) {
5172
0
      lineJoin = splashLineJoinBevel;
5173
0
    }
5174
0
  }
5175
5176
  // if there is a min line width set, and the transformed line width
5177
  // is smaller, use the min line width
5178
30.2k
  if (w > 0 && w2 < minLineWidth) {
5179
443
    strokeWide(path2, minLineWidth / w, splashLineCapButt, splashLineJoinBevel);
5180
29.7k
  } else if (bitmap->mode == splashModeMono1 || !vectorAntialias) {
5181
    // in monochrome mode or if antialiasing is disabled, use 0-width
5182
    // lines for any transformed line width <= 1 -- lines less than 1
5183
    // pixel wide look too fat without antialiasing
5184
0
    if (w2 < 1.001) {
5185
0
      strokeNarrow(path2);
5186
0
    } else {
5187
0
      strokeWide(path2, state->lineWidth, lineCap, lineJoin);
5188
0
    }
5189
29.7k
  } else {
5190
    // in gray and color modes, only use 0-width lines if the line
5191
    // width is explicitly set to 0
5192
29.7k
    if (state->lineWidth == 0) {
5193
1.39k
      strokeNarrow(path2);
5194
28.3k
    } else {
5195
28.3k
      strokeWide(path2, state->lineWidth, lineCap, lineJoin);
5196
28.3k
    }
5197
29.7k
  }
5198
5199
30.2k
  delete path2;
5200
30.2k
  return splashOk;
5201
30.2k
}
5202
5203
1.39k
void Splash::strokeNarrow(SplashPath *path) {
5204
1.39k
  SplashPipe pipe;
5205
1.39k
  SplashXPath *xPath;
5206
1.39k
  SplashXPathSeg *seg;
5207
1.39k
  int x0, x1, y0, y1, xa, xb, y;
5208
1.39k
  SplashCoord dxdy;
5209
1.39k
  SplashClipResult clipRes;
5210
1.39k
  int nClipRes[3];
5211
1.39k
  int i;
5212
5213
1.39k
  nClipRes[0] = nClipRes[1] = nClipRes[2] = 0;
5214
5215
1.39k
  xPath = new SplashXPath(path, state->matrix, state->flatness, gFalse,
5216
1.39k
        state->enablePathSimplification,
5217
1.39k
        state->strokeAdjust, state->clip);
5218
5219
1.39k
  pipeInit(&pipe, state->strokePattern,
5220
1.39k
     (Guchar)splashRound(state->strokeAlpha * 255),
5221
1.39k
     gFalse, gTrue, gFalse);
5222
5223
2.08M
  for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) {
5224
2.08M
    if (seg->y0 <= seg->y1) {
5225
2.05M
      y0 = splashFloor(seg->y0);
5226
2.05M
      y1 = splashFloor(seg->y1);
5227
2.05M
      x0 = splashFloor(seg->x0);
5228
2.05M
      x1 = splashFloor(seg->x1);
5229
2.05M
    } else {
5230
33.4k
      y0 = splashFloor(seg->y1);
5231
33.4k
      y1 = splashFloor(seg->y0);
5232
33.4k
      x0 = splashFloor(seg->x1);
5233
33.4k
      x1 = splashFloor(seg->x0);
5234
33.4k
    }
5235
5236
    // With CAD-mode stroke adjustment, and a simple rectangular clip
5237
    // region, horizontal and vertical stroke-adjusted edges that fall
5238
    // slightly outside the clip region are adjusted back inside the
5239
    // clip region.  This avoids problems with narrow lines in
5240
    // slightly mismatched clip rectangles, which appear to be
5241
    // generated somewhat commonly by buggy CAD software.  This is
5242
    // similar to the code in SplashXPath::strokeAdjust() -- narrow
5243
    // strokes aren't stroke-adjusted, so this tweak has to be done
5244
    // here.
5245
2.08M
    if (y0 == y1 &&
5246
2.08M
  seg->y0 == seg->y1 &&
5247
2.04M
  state->clip->getIsSimple() &&
5248
960k
  state->strokeAdjust == splashStrokeAdjustCAD) {
5249
0
      SplashCoord cy0 = state->clip->getYMin();
5250
0
      SplashCoord cy1 = state->clip->getYMax();
5251
0
      int cyi0 = state->clip->getYMinI(state->strokeAdjust);
5252
0
      int cyi1 = state->clip->getYMaxI(state->strokeAdjust);
5253
0
      if (y0 == cyi0 - 1 && cy0 - seg->y0 < 0.5) {
5254
0
  y0 = y1 = y0 + 1;
5255
0
      } else if (y0 == cyi1 + 1 && seg->y0 - cy1 < 0.5) {
5256
0
  y0 = y1 = y0 - 1;
5257
0
      }
5258
2.08M
    } else if (x0 == x1 &&
5259
2.08M
  seg->x0 == seg->x1 &&
5260
2.08M
  state->clip->getIsSimple() &&
5261
982k
  state->strokeAdjust == splashStrokeAdjustCAD) {
5262
0
      SplashCoord cx0 = state->clip->getXMin();
5263
0
      SplashCoord cx1 = state->clip->getXMax();
5264
0
      int cxi0 = state->clip->getXMinI(state->strokeAdjust);
5265
0
      int cxi1 = state->clip->getXMaxI(state->strokeAdjust);
5266
0
      if (x0 == cxi0 - 1 && cx0 - seg->x0 < 0.5) {
5267
0
  x0 = x1 = x0 + 1;
5268
0
      } else if (x0 == cxi1 + 1 && seg->x0 - cx1 < 0.5) {
5269
0
  x0 = x1 = x0 - 1;
5270
0
      }
5271
0
    }
5272
5273
2.08M
    if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0,
5274
2.08M
           x0 <= x1 ? x1 : x0, y1,
5275
2.08M
           state->strokeAdjust))
5276
2.08M
  != splashClipAllOutside) {
5277
2.55k
      if (y0 == y1) {
5278
1.10k
  if (x0 <= x1) {
5279
988
    drawStrokeSpan(&pipe, x0, x1, y0, clipRes == splashClipAllInside);
5280
988
  } else {
5281
120
    drawStrokeSpan(&pipe, x1, x0, y0, clipRes == splashClipAllInside);
5282
120
  }
5283
1.44k
      } else if (x0 == x1) {
5284
145
  y = state->clip->getYMinI(state->strokeAdjust);
5285
145
  if (y0 < y) {
5286
89
    y0 = y;
5287
89
  }
5288
145
  y = state->clip->getYMaxI(state->strokeAdjust);
5289
145
  if (y1 > y) {
5290
145
    y1 = y;
5291
145
  }
5292
290
  for (y = y0; y <= y1; ++y) {
5293
145
    drawStrokeSpan(&pipe, x0, x0, y, clipRes == splashClipAllInside);
5294
145
  }
5295
1.29k
      } else {
5296
1.29k
  dxdy = seg->dxdy;
5297
1.29k
  y = state->clip->getYMinI(state->strokeAdjust);
5298
1.29k
  if (y0 < y) {
5299
1.18k
    y0 = y;
5300
1.18k
    x0 = splashFloor(seg->x0 + ((SplashCoord)y0 - seg->y0) * dxdy);
5301
1.18k
  }
5302
1.29k
  y = state->clip->getYMaxI(state->strokeAdjust);
5303
1.29k
  if (y1 > y) {
5304
1.21k
    y1 = y;
5305
1.21k
    x1 = splashFloor(seg->x0 + ((SplashCoord)y1 + 1 - seg->y0) * dxdy);
5306
1.21k
  }
5307
1.29k
  if (x0 <= x1) {
5308
424
    xa = x0;
5309
848
    for (y = y0; y <= y1; ++y) {
5310
424
      if (y < y1) {
5311
0
        xb = splashFloor(seg->x0 +
5312
0
             ((SplashCoord)y + 1 - seg->y0) * dxdy);
5313
424
      } else {
5314
424
        xb = x1 + 1;
5315
424
      }
5316
424
      if (xa == xb) {
5317
0
        drawStrokeSpan(&pipe, xa, xa, y, clipRes == splashClipAllInside);
5318
424
      } else {
5319
424
        drawStrokeSpan(&pipe, xa, xb - 1, y,
5320
424
           clipRes == splashClipAllInside);
5321
424
      }
5322
424
      xa = xb;
5323
424
    }
5324
874
  } else {
5325
874
    xa = x0;
5326
1.74k
    for (y = y0; y <= y1; ++y) {
5327
874
      if (y < y1) {
5328
0
        xb = splashFloor(seg->x0 +
5329
0
             ((SplashCoord)y + 1 - seg->y0) * dxdy);
5330
874
      } else {
5331
874
        xb = x1 - 1;
5332
874
      }
5333
874
      if (xa == xb) {
5334
0
        drawStrokeSpan(&pipe, xa, xa, y, clipRes == splashClipAllInside);
5335
874
      } else {
5336
874
        drawStrokeSpan(&pipe, xb + 1, xa, y,
5337
874
           clipRes == splashClipAllInside);
5338
874
      }
5339
874
      xa = xb;
5340
874
    }
5341
874
  }
5342
1.29k
      }
5343
2.55k
    }
5344
2.08M
    ++nClipRes[clipRes];
5345
2.08M
  }
5346
5347
1.39k
  if (nClipRes[splashClipPartial] ||
5348
779
      (nClipRes[splashClipAllInside] && nClipRes[splashClipAllOutside])) {
5349
614
    opClipRes = splashClipPartial;
5350
776
  } else if (nClipRes[splashClipAllInside]) {
5351
213
    opClipRes = splashClipAllInside;
5352
563
  } else {
5353
563
    opClipRes = splashClipAllOutside;
5354
563
  }
5355
5356
1.39k
  delete xPath;
5357
1.39k
}
5358
5359
void Splash::drawStrokeSpan(SplashPipe *pipe, int x0, int x1, int y,
5360
2.55k
          GBool noClip) {
5361
2.55k
  int x;
5362
5363
2.55k
  x = state->clip->getXMinI(state->strokeAdjust);
5364
2.55k
  if (x > x0) {
5365
1.07k
    x0 = x;
5366
1.07k
  }
5367
2.55k
  x = state->clip->getXMaxI(state->strokeAdjust);
5368
2.55k
  if (x < x1) {
5369
561
    x1 = x;
5370
561
  }
5371
2.55k
  if (x0 > x1) {
5372
126
    return;
5373
126
  }
5374
4.85k
  for (x = x0; x <= x1; ++x) {
5375
2.42k
    scanBuf[x] = 0xff;
5376
2.42k
  }
5377
2.42k
  if (!noClip) {
5378
1.74k
    if (!state->clip->clipSpanBinary(scanBuf, y, x0, x1, state->strokeAdjust)) {
5379
189
      return;
5380
189
    }
5381
1.74k
  }
5382
2.23k
  (this->*pipe->run)(pipe, x0, x1, y, scanBuf + x0, scanBuf + x0, NULL);
5383
2.23k
}
5384
5385
void Splash::strokeWide(SplashPath *path, SplashCoord w,
5386
28.8k
      int lineCap, int lineJoin) {
5387
28.8k
  SplashPath *path2;
5388
5389
28.8k
  path2 = makeStrokePath(path, w, lineCap, lineJoin, gFalse);
5390
28.8k
  fillWithPattern(path2, gFalse, state->strokePattern, state->strokeAlpha);
5391
28.8k
  delete path2;
5392
28.8k
}
5393
5394
SplashPath *Splash::flattenPath(SplashPath *path, SplashCoord *matrix,
5395
30.4k
        SplashCoord flatness) {
5396
30.4k
  SplashPath *fPath;
5397
30.4k
  SplashCoord flatness2;
5398
30.4k
  Guchar flag;
5399
30.4k
  int i;
5400
5401
30.4k
  fPath = new SplashPath();
5402
#if USE_FIXEDPOINT
5403
  flatness2 = flatness;
5404
#else
5405
30.4k
  flatness2 = flatness * flatness;
5406
30.4k
#endif
5407
30.4k
  i = 0;
5408
192k
  while (i < path->length) {
5409
161k
    flag = path->flags[i];
5410
161k
    if (flag & splashPathFirst) {
5411
39.1k
      fPath->moveTo(path->pts[i].x, path->pts[i].y);
5412
39.1k
      ++i;
5413
122k
    } else {
5414
122k
      if (flag & splashPathCurve) {
5415
10.3k
  flattenCurve(path->pts[i-1].x, path->pts[i-1].y,
5416
10.3k
         path->pts[i  ].x, path->pts[i  ].y,
5417
10.3k
         path->pts[i+1].x, path->pts[i+1].y,
5418
10.3k
         path->pts[i+2].x, path->pts[i+2].y,
5419
10.3k
         matrix, flatness2, fPath);
5420
10.3k
  i += 3;
5421
112k
      } else {
5422
112k
  fPath->lineTo(path->pts[i].x, path->pts[i].y);
5423
112k
  ++i;
5424
112k
      }
5425
122k
      if (path->flags[i-1] & splashPathClosed) {
5426
24.4k
  fPath->close();
5427
24.4k
      }
5428
122k
    }
5429
161k
  }
5430
30.4k
  return fPath;
5431
30.4k
}
5432
5433
void Splash::flattenCurve(SplashCoord x0, SplashCoord y0,
5434
        SplashCoord x1, SplashCoord y1,
5435
        SplashCoord x2, SplashCoord y2,
5436
        SplashCoord x3, SplashCoord y3,
5437
        SplashCoord *matrix, SplashCoord flatness2,
5438
10.3k
        SplashPath *fPath) {
5439
10.3k
  SplashCoord cx[splashMaxCurveSplits + 1][3];
5440
10.3k
  SplashCoord cy[splashMaxCurveSplits + 1][3];
5441
10.3k
  int cNext[splashMaxCurveSplits + 1];
5442
10.3k
  SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh;
5443
10.3k
  SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh;
5444
10.3k
  SplashCoord dx, dy, mx, my, tx, ty, d1, d2;
5445
10.3k
  int p1, p2, p3;
5446
5447
  // initial segment
5448
10.3k
  p1 = 0;
5449
10.3k
  p2 = splashMaxCurveSplits;
5450
10.3k
  cx[p1][0] = x0;  cy[p1][0] = y0;
5451
10.3k
  cx[p1][1] = x1;  cy[p1][1] = y1;
5452
10.3k
  cx[p1][2] = x2;  cy[p1][2] = y2;
5453
10.3k
  cx[p2][0] = x3;  cy[p2][0] = y3;
5454
10.3k
  cNext[p1] = p2;
5455
5456
17.0M
  while (p1 < splashMaxCurveSplits) {
5457
5458
    // get the next segment
5459
17.0M
    xl0 = cx[p1][0];  yl0 = cy[p1][0];
5460
17.0M
    xx1 = cx[p1][1];  yy1 = cy[p1][1];
5461
17.0M
    xx2 = cx[p1][2];  yy2 = cy[p1][2];
5462
17.0M
    p2 = cNext[p1];
5463
17.0M
    xr3 = cx[p2][0];  yr3 = cy[p2][0];
5464
5465
    // compute the distances (in device space) from the control points
5466
    // to the midpoint of the straight line (this is a bit of a hack,
5467
    // but it's much faster than computing the actual distances to the
5468
    // line)
5469
17.0M
    transform(matrix, (xl0 + xr3) * 0.5, (yl0 + yr3) * 0.5, &mx, &my);
5470
17.0M
    transform(matrix, xx1, yy1, &tx, &ty);
5471
#if USE_FIXEDPOINT
5472
    d1 = splashDist(tx, ty, mx, my);
5473
#else
5474
17.0M
    dx = tx - mx;
5475
17.0M
    dy = ty - my;
5476
17.0M
    d1 = dx*dx + dy*dy;
5477
17.0M
#endif
5478
17.0M
    transform(matrix, xx2, yy2, &tx, &ty);
5479
#if USE_FIXEDPOINT
5480
    d2 = splashDist(tx, ty, mx, my);
5481
#else
5482
17.0M
    dx = tx - mx;
5483
17.0M
    dy = ty - my;
5484
17.0M
    d2 = dx*dx + dy*dy;
5485
17.0M
#endif
5486
5487
    // if the curve is flat enough, or no more subdivisions are
5488
    // allowed, add the straight line segment
5489
17.0M
    if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) {
5490
8.51M
      fPath->lineTo(xr3, yr3);
5491
8.51M
      p1 = p2;
5492
5493
    // otherwise, subdivide the curve
5494
8.51M
    } else {
5495
8.50M
      xl1 = splashAvg(xl0, xx1);
5496
8.50M
      yl1 = splashAvg(yl0, yy1);
5497
8.50M
      xh = splashAvg(xx1, xx2);
5498
8.50M
      yh = splashAvg(yy1, yy2);
5499
8.50M
      xl2 = splashAvg(xl1, xh);
5500
8.50M
      yl2 = splashAvg(yl1, yh);
5501
8.50M
      xr2 = splashAvg(xx2, xr3);
5502
8.50M
      yr2 = splashAvg(yy2, yr3);
5503
8.50M
      xr1 = splashAvg(xh, xr2);
5504
8.50M
      yr1 = splashAvg(yh, yr2);
5505
8.50M
      xr0 = splashAvg(xl2, xr1);
5506
8.50M
      yr0 = splashAvg(yl2, yr1);
5507
      // add the new subdivision points
5508
8.50M
      p3 = (p1 + p2) / 2;
5509
8.50M
      cx[p1][1] = xl1;  cy[p1][1] = yl1;
5510
8.50M
      cx[p1][2] = xl2;  cy[p1][2] = yl2;
5511
8.50M
      cNext[p1] = p3;
5512
8.50M
      cx[p3][0] = xr0;  cy[p3][0] = yr0;
5513
8.50M
      cx[p3][1] = xr1;  cy[p3][1] = yr1;
5514
8.50M
      cx[p3][2] = xr2;  cy[p3][2] = yr2;
5515
8.50M
      cNext[p3] = p2;
5516
8.50M
    }
5517
17.0M
  }
5518
10.3k
}
5519
5520
249
SplashPath *Splash::makeDashedPath(SplashPath *path) {
5521
249
  SplashPath *dPath;
5522
249
  SplashCoord lineDashTotal;
5523
249
  SplashCoord lineDashStartPhase, lineDashDist, segLen;
5524
249
  SplashCoord x0, y0, x1, y1, xa, ya;
5525
249
  GBool lineDashStartOn, lineDashEndOn, lineDashOn, newPath;
5526
249
  int lineDashStartIdx, lineDashIdx, subpathStart, nDashes;
5527
249
  int i, j, k;
5528
5529
249
  lineDashTotal = 0;
5530
2.98k
  for (i = 0; i < state->lineDashLength; ++i) {
5531
2.73k
    lineDashTotal += state->lineDash[i];
5532
2.73k
  }
5533
  // Acrobat simply draws nothing if the dash array is [0]
5534
249
  if (lineDashTotal == 0) {
5535
0
    return new SplashPath();
5536
0
  }
5537
249
  lineDashStartPhase = state->lineDashPhase;
5538
249
  if (lineDashStartPhase > 0) {
5539
57
    i = splashFloor(lineDashStartPhase / lineDashTotal);
5540
57
    lineDashStartPhase -= lineDashTotal * i;
5541
192
  } else {
5542
192
    i = splashCeil(-lineDashStartPhase / lineDashTotal);
5543
192
    lineDashStartPhase += lineDashTotal * i;
5544
192
  }
5545
249
  lineDashStartOn = !((state->lineDashLength & 1) && (i & 1));
5546
249
  lineDashStartIdx = 0;
5547
249
  if (lineDashStartPhase > 0) {
5548
235
    while (lineDashStartPhase >= state->lineDash[lineDashStartIdx]) {
5549
178
      lineDashStartOn = !lineDashStartOn;
5550
178
      lineDashStartPhase -= state->lineDash[lineDashStartIdx];
5551
178
      if (++lineDashStartIdx == state->lineDashLength) {
5552
0
  lineDashStartIdx = 0;
5553
0
      }
5554
178
    }
5555
57
  }
5556
5557
249
  dPath = new SplashPath();
5558
5559
  // process each subpath
5560
249
  i = 0;
5561
663
  while (i < path->length) {
5562
5563
    // find the end of the subpath
5564
414
    for (j = i;
5565
20.1k
   j < path->length - 1 && !(path->flags[j] & splashPathLast);
5566
19.6k
   ++j) ;
5567
5568
    // initialize the dash parameters
5569
414
    lineDashOn = lineDashStartOn;
5570
414
    lineDashEndOn = lineDashStartOn;
5571
414
    lineDashIdx = lineDashStartIdx;
5572
414
    lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase;
5573
414
    subpathStart = dPath->length;
5574
414
    nDashes = 0;
5575
5576
    // process each segment of the subpath
5577
414
    newPath = gTrue;
5578
20.1k
    for (k = i; k < j; ++k) {
5579
5580
      // grab the segment
5581
19.6k
      x0 = path->pts[k].x;
5582
19.6k
      y0 = path->pts[k].y;
5583
19.6k
      x1 = path->pts[k+1].x;
5584
19.6k
      y1 = path->pts[k+1].y;
5585
19.6k
      segLen = splashDist(x0, y0, x1, y1);
5586
5587
      // Special case for zero-length subpath: copy the zero-length
5588
      // segment into the dashed path so that the round line cap
5589
      // special case is handled.
5590
19.6k
      if (j == i+1 && segLen == 0) {
5591
22
  dPath->moveTo(x0, y0);
5592
22
  dPath->lineTo(x0, y0);
5593
22
      }
5594
5595
      // process the segment
5596
4.98M
      while (segLen > 0) {
5597
5598
  // Special case for zero-length dash segments: draw a very
5599
  // short -- but not zero-length -- segment.  This ensures that
5600
  // we get the correct behavior with butt and projecting line
5601
  // caps.  The PS/PDF specs imply that zero-length segments are
5602
  // not drawn unless the line cap is round, but Acrobat and
5603
  // Ghostscript both draw very short segments (for butt caps)
5604
  // and squares (for projecting caps).
5605
4.96M
  if (lineDashDist == 0) {
5606
850k
    if (lineDashOn) {
5607
390k
      if (newPath) {
5608
390k
        dPath->moveTo(x0, y0);
5609
390k
        newPath = gFalse;
5610
390k
        ++nDashes;
5611
390k
      }
5612
390k
      xa = x0 + ((SplashCoord)0.001 / segLen) * (x1 - x0);
5613
390k
      ya = y0 + ((SplashCoord)0.001 / segLen) * (y1 - y0);
5614
390k
      dPath->lineTo(xa, ya);
5615
390k
    }
5616
5617
4.11M
  } else if (lineDashDist >= segLen) {
5618
19.5k
    if (lineDashOn) {
5619
14.5k
      if (newPath) {
5620
827
        dPath->moveTo(x0, y0);
5621
827
        newPath = gFalse;
5622
827
        ++nDashes;
5623
827
      }
5624
14.5k
      dPath->lineTo(x1, y1);
5625
14.5k
    }
5626
19.5k
    lineDashDist -= segLen;
5627
19.5k
    segLen = 0;
5628
5629
4.09M
  } else {
5630
4.09M
    xa = x0 + (lineDashDist / segLen) * (x1 - x0);
5631
4.09M
    ya = y0 + (lineDashDist / segLen) * (y1 - y0);
5632
4.09M
    if (lineDashOn) {
5633
2.08M
      if (newPath) {
5634
2.08M
        dPath->moveTo(x0, y0);
5635
2.08M
        newPath = gFalse;
5636
2.08M
        ++nDashes;
5637
2.08M
      }
5638
2.08M
      dPath->lineTo(xa, ya);
5639
2.08M
    }
5640
4.09M
    x0 = xa;
5641
4.09M
    y0 = ya;
5642
4.09M
    segLen -= lineDashDist;
5643
4.09M
    lineDashDist = 0;
5644
4.09M
  }
5645
5646
4.96M
  lineDashEndOn = lineDashOn;
5647
5648
  // get the next entry in the dash array
5649
4.96M
  if (lineDashDist <= 0) {
5650
4.94M
    lineDashOn = !lineDashOn;
5651
4.94M
    if (++lineDashIdx == state->lineDashLength) {
5652
640k
      lineDashIdx = 0;
5653
640k
    }
5654
4.94M
    lineDashDist = state->lineDash[lineDashIdx];
5655
4.94M
    newPath = gTrue;
5656
4.94M
  }
5657
4.96M
      }
5658
19.6k
    }
5659
5660
    // in a closed subpath, where the dash pattern is "on" at both the
5661
    // start and end of the subpath, we need to merge the start and
5662
    // end to get a proper line join
5663
414
    if ((path->flags[j] & splashPathClosed) &&
5664
104
  lineDashStartOn &&
5665
103
  lineDashEndOn) {
5666
98
      if (nDashes == 1) {
5667
42
  dPath->close();
5668
56
      } else if (nDashes > 1) {
5669
34
  k = subpathStart;
5670
1.53k
  do {
5671
1.53k
    ++k;
5672
1.53k
    dPath->lineTo(dPath->pts[k].x, dPath->pts[k].y);
5673
1.53k
  } while (!(dPath->flags[k] & splashPathLast));
5674
34
  ++k;
5675
34
  memmove(&dPath->pts[subpathStart], &dPath->pts[k],
5676
34
    (dPath->length - k) * sizeof(SplashPathPoint));
5677
34
  memmove(&dPath->flags[subpathStart], &dPath->flags[k],
5678
34
    (dPath->length - k) * sizeof(Guchar));
5679
34
  dPath->length -= k - subpathStart;
5680
34
  dPath->curSubpath -= k - subpathStart;
5681
34
      }
5682
98
    }
5683
5684
414
    i = j + 1;
5685
414
  }
5686
5687
249
  return dPath;
5688
249
}
5689
5690
4.15M
SplashError Splash::fill(SplashPath *path, GBool eo) {
5691
4.15M
  if (debugMode) {
5692
0
    printf("fill [eo:%d]:\n", eo);
5693
0
    dumpPath(path);
5694
0
  }
5695
4.15M
  if (path->length == 0) {
5696
2.50k
    return splashErrEmptyPath;
5697
2.50k
  }
5698
4.15M
  if (pathAllOutside(path, gFalse)) {
5699
4.11M
    opClipRes = splashClipAllOutside;
5700
4.11M
    return splashOk;
5701
4.11M
  }
5702
39.8k
  return fillWithPattern(path, eo, state->fillPattern, state->fillAlpha);
5703
4.15M
}
5704
5705
SplashError Splash::fillWithPattern(SplashPath *path, GBool eo,
5706
            SplashPattern *pattern,
5707
68.7k
            SplashCoord alpha) {
5708
68.7k
  SplashPipe pipe;
5709
68.7k
  SplashPath *path2;
5710
68.7k
  SplashXPath *xPath;
5711
68.7k
  SplashXPathScanner *scanner;
5712
68.7k
  int xMin, yMin, xMax, xMin2, xMax2, yMax, y, t;
5713
68.7k
  SplashClipResult clipRes;
5714
5715
68.7k
  path2 = tweakFillPath(path);
5716
5717
68.7k
  xPath = new SplashXPath(path2, state->matrix, state->flatness, gTrue,
5718
68.7k
        state->enablePathSimplification,
5719
68.7k
        state->strokeAdjust, state->clip);
5720
68.7k
  if (path2 != path) {
5721
2.17k
    delete path2;
5722
2.17k
  }
5723
68.7k
  xMin = xPath->getXMin();
5724
68.7k
  yMin = xPath->getYMin();
5725
68.7k
  xMax = xPath->getXMax();
5726
68.7k
  yMax = xPath->getYMax();
5727
68.7k
  if (xMin > xMax || yMin > yMax) {
5728
67
    delete xPath;
5729
67
    return splashOk;
5730
67
  }
5731
68.6k
  scanner = new SplashXPathScanner(xPath, eo, yMin, yMax);
5732
5733
  // check clipping
5734
68.6k
  if ((clipRes = state->clip->testRect(xMin, yMin, xMax, yMax,
5735
68.6k
               state->strokeAdjust))
5736
68.6k
      != splashClipAllOutside) {
5737
5738
40.3k
    if ((t = state->clip->getXMinI(state->strokeAdjust)) > xMin) {
5739
17.7k
      xMin = t;
5740
17.7k
    }
5741
40.3k
    if ((t = state->clip->getXMaxI(state->strokeAdjust)) < xMax) {
5742
20.4k
      xMax = t;
5743
20.4k
    }
5744
40.3k
    if ((t = state->clip->getYMinI(state->strokeAdjust)) > yMin) {
5745
33.0k
      yMin = t;
5746
33.0k
    }
5747
40.3k
    if ((t = state->clip->getYMaxI(state->strokeAdjust)) < yMax) {
5748
32.9k
      yMax = t;
5749
32.9k
    }
5750
40.3k
    if (xMin > xMax || yMin > yMax) {
5751
0
      delete scanner;
5752
0
      delete xPath;
5753
0
      return splashOk;
5754
0
    }
5755
5756
40.3k
    pipeInit(&pipe, pattern, (Guchar)splashRound(alpha * 255),
5757
40.3k
       gFalse, gTrue, gFalse);
5758
5759
    // draw the spans
5760
40.3k
    if (vectorAntialias) {
5761
71.2k
      for (y = yMin; y <= yMax; ++y) {
5762
35.6k
  scanner->getSpan(scanBuf, y, xMin, xMax, &xMin2, &xMax2);
5763
35.6k
  if (xMin2 <= xMax2) {
5764
30.8k
    if (clipRes != splashClipAllInside) {
5765
28.2k
      state->clip->clipSpan(scanBuf, y, xMin2, xMax2,
5766
28.2k
          state->strokeAdjust);
5767
28.2k
    }
5768
30.8k
    (this->*pipe.run)(&pipe, xMin2, xMax2, y,
5769
30.8k
          NULL, scanBuf + xMin2, NULL);
5770
30.8k
  }
5771
35.6k
      }
5772
35.6k
    } else {
5773
9.49k
      for (y = yMin; y <= yMax; ++y) {
5774
4.74k
  scanner->getSpanBinary(scanBuf, y, xMin, xMax, &xMin2, &xMax2);
5775
4.74k
  if (xMin2 <= xMax2) {
5776
4.74k
    if (clipRes != splashClipAllInside) {
5777
4.72k
      state->clip->clipSpanBinary(scanBuf, y, xMin2, xMax2,
5778
4.72k
          state->strokeAdjust);
5779
4.72k
    }
5780
4.74k
    (this->*pipe.run)(&pipe, xMin2, xMax2, y,
5781
4.74k
          NULL, scanBuf + xMin2, NULL);
5782
4.74k
  }
5783
4.74k
      }
5784
4.74k
    }
5785
40.3k
  }
5786
68.6k
  opClipRes = clipRes;
5787
5788
68.6k
  delete scanner;
5789
68.6k
  delete xPath;
5790
68.6k
  return splashOk;
5791
68.6k
}
5792
5793
// Applies various tweaks to a fill path:
5794
// (1) add stroke adjust hints to a filled rectangle
5795
// (2) applies a minimum width to a zero-width filled rectangle (so
5796
//     stroke adjustment works correctly
5797
// (3) convert a degenerate fill ('moveto lineto fill' and 'moveto
5798
//     lineto closepath fill') to a minimum-width filled rectangle
5799
//
5800
// These tweaks only apply to paths with a single subpath.
5801
//
5802
// Returns either the unchanged input path or a new path (in which
5803
// case the returned path must be deleted by the caller).
5804
68.7k
SplashPath *Splash::tweakFillPath(SplashPath *path) {
5805
68.7k
  SplashPath *path2;
5806
68.7k
  SplashCoord xx0, yy0, xx1, yy1, dx, dy, d, wx, wy, w;
5807
68.7k
  int n;
5808
5809
68.7k
  if (state->strokeAdjust == splashStrokeAdjustOff || path->hints) {
5810
39.8k
    return path;
5811
39.8k
  }
5812
5813
28.8k
  n = path->getLength();
5814
28.8k
  if (!((n == 2) ||
5815
27.6k
  (n == 3 &&
5816
754
   path->flags[1] == 0) ||
5817
26.9k
  (n == 4 &&
5818
988
   path->flags[1] == 0 &&
5819
655
   path->flags[2] == 0) ||
5820
26.2k
  (n == 5 &&
5821
18.4k
   path->flags[1] == 0 &&
5822
18.2k
   path->flags[2] == 0 &&
5823
17.9k
   path->flags[3] == 0))) {
5824
8.32k
    return path;
5825
8.32k
  }
5826
5827
20.5k
  path2 = path;
5828
5829
  // degenerate fill (2 or 3 points) or rectangle of (nearly) zero
5830
  // width --> replace with a min-width rectangle and hint
5831
20.5k
  if (n == 2 ||
5832
19.3k
      (n == 3 && (path->flags[0] & splashPathClosed)) ||
5833
19.1k
      (n == 3 && (splashAbs(path->pts[0].x - path->pts[2].x) < 0.001 &&
5834
187
      splashAbs(path->pts[0].y - path->pts[2].y) < 0.001)) ||
5835
18.9k
      ((n == 4 ||
5836
18.3k
  (n == 5 && (path->flags[0] & splashPathClosed))) &&
5837
18.5k
       ((splashAbs(path->pts[0].x - path->pts[1].x) < 0.001 &&
5838
748
   splashAbs(path->pts[0].y - path->pts[1].y) < 0.001 &&
5839
714
   splashAbs(path->pts[2].x - path->pts[3].x) < 0.001 &&
5840
605
   splashAbs(path->pts[2].y - path->pts[3].y) < 0.001) ||
5841
17.9k
  (splashAbs(path->pts[0].x - path->pts[3].x) < 0.001 &&
5842
17.5k
   splashAbs(path->pts[0].y - path->pts[3].y) < 0.001 &&
5843
235
   splashAbs(path->pts[1].x - path->pts[2].x) < 0.001 &&
5844
2.17k
   splashAbs(path->pts[1].y - path->pts[2].y) < 0.001)))) {
5845
2.17k
    wx = state->matrix[0] + state->matrix[2];
5846
2.17k
    wy = state->matrix[1] + state->matrix[3];
5847
2.17k
    w = splashSqrt(wx*wx + wy*wy);
5848
2.17k
    if (w < 0.001) {
5849
269
      w = 0;
5850
1.90k
    } else {
5851
      // min width is 0.1 -- this constant is minWidth * sqrt(2)
5852
1.90k
      w = (SplashCoord)0.1414 / w;
5853
1.90k
    }
5854
2.17k
    xx0 = path->pts[0].x;
5855
2.17k
    yy0 = path->pts[0].y;
5856
2.17k
    if (n <= 3) {
5857
1.52k
      xx1 = path->pts[1].x;
5858
1.52k
      yy1 = path->pts[1].y;
5859
1.52k
    } else {
5860
652
      xx1 = path->pts[2].x;
5861
652
      yy1 = path->pts[2].y;
5862
652
    }
5863
2.17k
    dx = xx1 - xx0;
5864
2.17k
    dy = yy1 - yy0;
5865
2.17k
    d = splashSqrt(dx * dx + dy * dy);
5866
2.17k
    if (d < 0.001) {
5867
817
      d = 0;
5868
1.35k
    } else {
5869
1.35k
      d = w / d;
5870
1.35k
    }
5871
2.17k
    dx *= d;
5872
2.17k
    dy *= d;
5873
2.17k
    path2 = new SplashPath();
5874
2.17k
    path2->moveTo(xx0 + dy, yy0 - dx);
5875
2.17k
    path2->lineTo(xx1 + dy, yy1 - dx);
5876
2.17k
    path2->lineTo(xx1 - dy, yy1 + dx);
5877
2.17k
    path2->lineTo(xx0 - dy, yy0 + dx);
5878
2.17k
    path2->close(gTrue);
5879
2.17k
    path2->addStrokeAdjustHint(0, 2, 0, 4);
5880
2.17k
    path2->addStrokeAdjustHint(1, 3, 0, 4);
5881
5882
  // unclosed rectangle --> close and hint
5883
18.3k
  } else if (n == 4 && !(path->flags[0] & splashPathClosed)) {
5884
185
    path2->close(gTrue);
5885
185
    path2->addStrokeAdjustHint(0, 2, 0, 4);
5886
185
    path2->addStrokeAdjustHint(1, 3, 0, 4);
5887
5888
  // closed rectangle --> hint
5889
18.1k
  } else if (n == 5 && (path->flags[0] & splashPathClosed)) {
5890
17.5k
    path2->addStrokeAdjustHint(0, 2, 0, 4);
5891
17.5k
    path2->addStrokeAdjustHint(1, 3, 0, 4);
5892
17.5k
  }
5893
5894
20.5k
  return path2;
5895
28.8k
}
5896
5897
// Returns true if [path] is entirely outside the current clipping
5898
// path.  The path coordinates have not been stroke adjusted, so we
5899
// compare against the floating point clip rect.  If [stroke] is true,
5900
// allow for the stroke width and miter limit.
5901
4.24M
GBool Splash::pathAllOutside(SplashPath *path, GBool stroke) {
5902
4.24M
  SplashCoord xMin1, yMin1, xMax1, yMax1;
5903
4.24M
  SplashCoord xMin2, yMin2, xMax2, yMax2;
5904
4.24M
  SplashCoord x, y;
5905
4.24M
  int i;
5906
5907
  //--- compute the path's bbox in user space
5908
4.24M
  xMin1 = xMax1 = path->pts[0].x;
5909
4.24M
  yMin1 = yMax1 = path->pts[0].y;
5910
53.7M
  for (i = 1; i < path->length; ++i) {
5911
49.5M
    if (path->pts[i].x < xMin1) {
5912
8.85M
      xMin1 = path->pts[i].x;
5913
40.6M
    } else if (path->pts[i].x > xMax1) {
5914
9.86M
      xMax1 = path->pts[i].x;
5915
9.86M
    }
5916
49.5M
    if (path->pts[i].y < yMin1) {
5917
13.7M
      yMin1 = path->pts[i].y;
5918
35.7M
    } else if (path->pts[i].y > yMax1) {
5919
12.1M
      yMax1 = path->pts[i].y;
5920
12.1M
    }
5921
49.5M
  }
5922
5923
  //--- allow for stroke width and miter limit
5924
4.24M
  if (stroke && state->lineWidth > 0) {
5925
86.8k
    SplashCoord w = state->lineWidth * 0.5;
5926
86.8k
    if (state->lineJoin == splashLineJoinMiter) {
5927
84.2k
      w *= state->miterLimit;
5928
84.2k
    }
5929
86.8k
    xMin1 -= w;
5930
86.8k
    yMin1 -= w;
5931
86.8k
    xMax1 += w;
5932
86.8k
    yMax1 += w;
5933
86.8k
  }
5934
5935
  //--- convert path bbox to device space
5936
4.24M
  transform(state->matrix, xMin1, yMin1, &x, &y);
5937
4.24M
  xMin2 = xMax2 = x;
5938
4.24M
  yMin2 = yMax2 = y;
5939
4.24M
  transform(state->matrix, xMin1, yMax1, &x, &y);
5940
4.24M
  if (x < xMin2) {
5941
9.13k
    xMin2 = x;
5942
4.23M
  } else if (x > xMax2) {
5943
5.15k
    xMax2 = x;
5944
5.15k
  }
5945
4.24M
  if (y < yMin2) {
5946
39.2k
    yMin2 = y;
5947
4.20M
  } else if (y > yMax2) {
5948
3.67M
    yMax2 = y;
5949
3.67M
  }
5950
4.24M
  transform(state->matrix, xMax1, yMin1, &x, &y);
5951
4.24M
  if (x < xMin2) {
5952
100k
    xMin2 = x;
5953
4.14M
  } else if (x > xMax2) {
5954
2.86M
    xMax2 = x;
5955
2.86M
  }
5956
4.24M
  if (y < yMin2) {
5957
6.67k
    yMin2 = y;
5958
4.23M
  } else if (y > yMax2) {
5959
1.68k
    yMax2 = y;
5960
1.68k
  }
5961
4.24M
  transform(state->matrix, xMax1, yMax1, &x, &y);
5962
4.24M
  if (x < xMin2) {
5963
7.23k
    xMin2 = x;
5964
4.23M
  } else if (x > xMax2) {
5965
2.91k
    xMax2 = x;
5966
2.91k
  }
5967
4.24M
  if (y < yMin2) {
5968
6.94k
    yMin2 = y;
5969
4.23M
  } else if (y > yMax2) {
5970
2.23k
    yMax2 = y;
5971
2.23k
  }
5972
5973
  //--- handle zero-width strokes
5974
4.24M
  if (stroke && state->lineWidth == 0) {
5975
1.90k
    xMin1 -= 1;
5976
1.90k
    yMin1 -= 1;
5977
1.90k
    xMax1 += 1;
5978
1.90k
    yMax1 += 1;
5979
1.90k
  }
5980
5981
  //--- check against the clip rect
5982
4.24M
  return xMin2 > state->clip->getXMax() ||
5983
1.00M
         xMax2 < state->clip->getXMin() ||
5984
906k
         yMin2 > state->clip->getYMax() ||
5985
460k
         yMax2 < state->clip->getYMin();
5986
4.24M
}
5987
5988
SplashError Splash::fillChar(SplashCoord x, SplashCoord y,
5989
541k
           Guint c, SplashFont *font) {
5990
541k
  SplashGlyphBitmap glyph;
5991
541k
  SplashCoord xt, yt;
5992
541k
  int x0, y0, xFrac, yFrac;
5993
541k
  SplashError err;
5994
5995
541k
  if (debugMode) {
5996
0
    printf("fillChar: x=%.2f y=%.2f c=%3u=0x%02x='%c'\n",
5997
0
     (double)x, (double)y, c, c, c);
5998
0
  }
5999
541k
  transform(state->matrix, x, y, &xt, &yt);
6000
541k
  x0 = splashFloor(xt);
6001
541k
  xFrac = splashFloor((xt - x0) * splashFontFraction);
6002
541k
  y0 = splashFloor(yt);
6003
541k
  yFrac = splashFloor((yt - y0) * splashFontFraction);
6004
541k
  if (!font->getGlyph(c, xFrac, yFrac, &glyph)) {
6005
509k
    return splashErrNoGlyph;
6006
509k
  }
6007
32.4k
  err = fillGlyph2(x0, y0, &glyph);
6008
32.4k
  if (glyph.freeData) {
6009
25.8k
    gfree(glyph.data);
6010
25.8k
  }
6011
32.4k
  return err;
6012
541k
}
6013
6014
SplashError Splash::fillGlyph(SplashCoord x, SplashCoord y,
6015
2.82k
            SplashGlyphBitmap *glyph) {
6016
2.82k
  SplashCoord xt, yt;
6017
2.82k
  int x0, y0;
6018
6019
2.82k
  transform(state->matrix, x, y, &xt, &yt);
6020
2.82k
  x0 = splashFloor(xt);
6021
2.82k
  y0 = splashFloor(yt);
6022
2.82k
  return fillGlyph2(x0, y0, glyph);
6023
2.82k
}
6024
6025
35.3k
SplashError Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph) {
6026
35.3k
  SplashPipe pipe;
6027
35.3k
  SplashClipResult clipRes;
6028
35.3k
  Guchar alpha;
6029
35.3k
  Guchar *p;
6030
35.3k
  int xMin, yMin, xMax, yMax;
6031
35.3k
  int x, y, xg, yg, xx, t;
6032
6033
35.3k
  xg = x0 - glyph->x;
6034
35.3k
  yg = y0 - glyph->y;
6035
35.3k
  xMin = xg;
6036
35.3k
  xMax = xg + glyph->w - 1;
6037
35.3k
  yMin = yg;
6038
35.3k
  yMax = yg + glyph->h - 1;
6039
35.3k
  if ((clipRes = state->clip->testRect(xMin, yMin, xMax, yMax,
6040
35.3k
               state->strokeAdjust))
6041
35.3k
      != splashClipAllOutside) {
6042
489
    pipeInit(&pipe, state->fillPattern,
6043
489
       (Guchar)splashRound(state->fillAlpha * 255),
6044
489
       gFalse, gTrue, gFalse);
6045
489
    if (clipRes == splashClipAllInside) {
6046
324
      if (glyph->aa) {
6047
324
  p = glyph->data;
6048
1.62k
  for (y = yMin; y <= yMax; ++y) {
6049
1.29k
    (this->*pipe.run)(&pipe, xMin, xMax, y,
6050
1.29k
          NULL,
6051
1.29k
          glyph->data + (y - yMin) * glyph->w,
6052
1.29k
          NULL);
6053
1.29k
  }
6054
324
      } else {
6055
0
  p = glyph->data;
6056
0
  for (y = yMin; y <= yMax; ++y) {
6057
0
    for (x = xMin; x <= xMax; x += 8) {
6058
0
      alpha = *p++;
6059
0
      for (xx = 0; xx < 8 && x + xx <= xMax; ++xx) {
6060
0
        scanBuf[x + xx] = (alpha & 0x80) ? 0xff : 0x00;
6061
0
        alpha = (Guchar)(alpha << 1);
6062
0
      }
6063
0
    }
6064
0
    (this->*pipe.run)(&pipe, xMin, xMax, y,
6065
0
          NULL, scanBuf + xMin, NULL);
6066
0
  }
6067
0
      }
6068
324
    } else {
6069
165
      if ((t = state->clip->getXMinI(state->strokeAdjust)) > xMin) {
6070
165
  xMin = t;
6071
165
      }
6072
165
      if ((t = state->clip->getXMaxI(state->strokeAdjust)) < xMax) {
6073
165
  xMax = t;
6074
165
      }
6075
165
      if ((t = state->clip->getYMinI(state->strokeAdjust)) > yMin) {
6076
165
  yMin = t;
6077
165
      }
6078
165
      if ((t = state->clip->getYMaxI(state->strokeAdjust)) < yMax) {
6079
165
  yMax = t;
6080
165
      }
6081
165
      if (xMin <= xMax && yMin <= yMax) {
6082
165
  if (glyph->aa) {
6083
330
    for (y = yMin; y <= yMax; ++y) {
6084
165
      p = glyph->data + (y - yg) * glyph->w + (xMin - xg);
6085
165
      memcpy(scanBuf + xMin, p, xMax - xMin + 1);
6086
165
      state->clip->clipSpan(scanBuf, y, xMin, xMax,
6087
165
          state->strokeAdjust);
6088
165
      (this->*pipe.run)(&pipe, xMin, xMax, y,
6089
165
            NULL, scanBuf + xMin, NULL);
6090
165
    }
6091
165
  } else {
6092
0
    for (y = yMin; y <= yMax; ++y) {
6093
0
      p = glyph->data + (y - yg) * ((glyph->w + 7) >> 3)
6094
0
        + ((xMin - xg) >> 3);
6095
0
      alpha = *p++;
6096
0
      xx = (xMin - xg) & 7;
6097
0
      alpha = (Guchar)(alpha << xx);
6098
0
      for (x = xMin; xx < 8 && x <= xMax; ++x, ++xx) {
6099
0
        scanBuf[x] = (alpha & 0x80) ? 255 : 0;
6100
0
        alpha = (Guchar)(alpha << 1);
6101
0
      }
6102
0
      for (; x <= xMax; x += 8) {
6103
0
        alpha = *p++;
6104
0
        for (xx = 0; xx < 8 && x + xx <= xMax; ++xx) {
6105
0
    scanBuf[x + xx] = (alpha & 0x80) ? 255 : 0;
6106
0
    alpha = (Guchar)(alpha << 1);
6107
0
        }
6108
0
      }
6109
0
      state->clip->clipSpanBinary(scanBuf, y, xMin, xMax,
6110
0
          state->strokeAdjust);
6111
0
      (this->*pipe.run)(&pipe, xMin, xMax, y,
6112
0
            NULL, scanBuf + xMin, NULL);
6113
0
    }
6114
0
  }
6115
165
      }
6116
165
    }
6117
489
  }
6118
35.3k
  opClipRes = clipRes;
6119
6120
35.3k
  return splashOk;
6121
35.3k
}
6122
6123
void Splash::getImageBounds(SplashCoord xyMin, SplashCoord xyMax,
6124
104k
          int *xyMinI, int *xyMaxI) {
6125
104k
  if (state->strokeAdjust == splashStrokeAdjustOff) {
6126
    // make sure the coords fit in 32-bit ints
6127
#if USE_FIXEDPOINT
6128
    if (xyMin < -32767) {
6129
      xyMin = -32767;
6130
    } else if (xyMin > 32767) {
6131
      xyMin = 32767;
6132
    }
6133
    if (xyMax < -32767) {
6134
      xyMax = -32767;
6135
    } else if (xyMax > 32767) {
6136
      xyMax = 32767;
6137
    }
6138
#else
6139
21.8k
    if (xyMin < -1e9) {
6140
7.84k
      xyMin = -1e9;
6141
13.9k
    } else if (xyMin > 1e9) {
6142
10.6k
      xyMin = 1e9;
6143
10.6k
    }
6144
21.8k
    if (xyMax < -1e9) {
6145
6.08k
      xyMax = -1e9;
6146
15.7k
    } else if (xyMax > 1e9) {
6147
12.5k
      xyMax = 1e9;
6148
12.5k
    }
6149
21.8k
#endif
6150
21.8k
    *xyMinI = splashFloor(xyMin);
6151
21.8k
    *xyMaxI = splashFloor(xyMax);
6152
21.8k
    if (*xyMaxI <= *xyMinI) {
6153
18.0k
      *xyMaxI = *xyMinI + 1;
6154
18.0k
    }
6155
82.4k
  } else {
6156
82.4k
    splashStrokeAdjust(xyMin, xyMax, xyMinI, xyMaxI, state->strokeAdjust);
6157
82.4k
  }
6158
104k
}
6159
6160
struct SplashDrawImageMaskRowData {
6161
  SplashPipe pipe;
6162
};
6163
6164
// The glyphMode flag is not currently used, but may be useful if the
6165
// stroke adjustment behavior is changed.
6166
SplashError Splash::fillImageMask(GString *imageTag,
6167
          SplashImageMaskSource src, void *srcData,
6168
          int w, int h, SplashCoord *mat,
6169
          GBool glyphMode, GBool interpolate,
6170
39.5k
          GBool antialias) {
6171
39.5k
  if (debugMode) {
6172
0
    printf("fillImageMask: w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
6173
0
     w, h, (double)mat[0], (double)mat[1], (double)mat[2],
6174
0
     (double)mat[3], (double)mat[4], (double)mat[5]);
6175
0
  }
6176
6177
  //--- check for singular matrix
6178
39.5k
  if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) {
6179
947
    return splashErrSingularMatrix;
6180
947
  }
6181
6182
  //--- compute image bbox, check clipping
6183
38.6k
  GBool flipsOnly = splashAbs(mat[1]) <= 0.0001 && splashAbs(mat[2]) <= 0.0001;
6184
38.6k
  GBool rot90Only = splashAbs(mat[0]) <= 0.0001 && splashAbs(mat[3]) <= 0.0001;
6185
38.6k
  GBool horizFlip = gFalse;
6186
38.6k
  GBool vertFlip = gFalse;
6187
38.6k
  int xMin, yMin, xMax, yMax;
6188
38.6k
  if (flipsOnly) {
6189
14.4k
    horizFlip = mat[0] < 0;
6190
14.4k
    vertFlip = mat[3] < 0;
6191
14.4k
    if (horizFlip) {
6192
9.98k
      getImageBounds(mat[0] + mat[4], mat[4], &xMin, &xMax);
6193
9.98k
    } else {
6194
4.44k
      getImageBounds(mat[4], mat[0] + mat[4], &xMin, &xMax);
6195
4.44k
    }
6196
14.4k
    if (vertFlip) {
6197
8.26k
      getImageBounds(mat[3] + mat[5], mat[5], &yMin, &yMax);
6198
8.26k
    } else {
6199
6.16k
      getImageBounds(mat[5], mat[3] + mat[5], &yMin, &yMax);
6200
6.16k
    }
6201
24.2k
  } else if (rot90Only) {
6202
13.5k
    horizFlip = mat[2] < 0;
6203
13.5k
    vertFlip = mat[1] < 0;
6204
13.5k
    if (horizFlip) {
6205
1.82k
      getImageBounds(mat[2] + mat[4], mat[4], &xMin, &xMax);
6206
11.7k
    } else {
6207
11.7k
      getImageBounds(mat[4], mat[2] + mat[4], &xMin, &xMax);
6208
11.7k
    }
6209
13.5k
    if (vertFlip) {
6210
2.45k
      getImageBounds(mat[1] + mat[5], mat[5], &yMin, &yMax);
6211
11.1k
    } else {
6212
11.1k
      getImageBounds(mat[5], mat[1] + mat[5], &yMin, &yMax);
6213
11.1k
    }
6214
13.5k
  } else {
6215
10.6k
    int xx = splashRound(mat[4]);   // (0,0)
6216
10.6k
    int yy = splashRound(mat[5]);
6217
10.6k
    xMin = xMax = xx;
6218
10.6k
    yMin = yMax = yy;
6219
10.6k
    xx = splashRound(mat[0] + mat[4]);    // (1,0)
6220
10.6k
    yy = splashRound(mat[1] + mat[5]);
6221
10.6k
    if (xx < xMin) {
6222
322
      xMin = xx;
6223
10.3k
    } else if (xx > xMax) {
6224
809
      xMax = xx;
6225
809
    }
6226
10.6k
    if (yy < yMin) {
6227
335
      yMin = yy;
6228
10.3k
    } else if (yy > yMax) {
6229
309
      yMax = yy;
6230
309
    }
6231
10.6k
    xx = splashRound(mat[2] + mat[4]);    // (0,1)
6232
10.6k
    yy = splashRound(mat[3] + mat[5]);
6233
10.6k
    if (xx < xMin) {
6234
698
      xMin = xx;
6235
9.94k
    } else if (xx > xMax) {
6236
277
      xMax = xx;
6237
277
    }
6238
10.6k
    if (yy < yMin) {
6239
249
      yMin = yy;
6240
10.3k
    } else if (yy > yMax) {
6241
231
      yMax = yy;
6242
231
    }
6243
10.6k
    xx = splashRound(mat[0] + mat[2] + mat[4]); // (1,1)
6244
10.6k
    yy = splashRound(mat[1] + mat[3] + mat[5]);
6245
10.6k
    if (xx < xMin) {
6246
871
      xMin = xx;
6247
9.77k
    } else if (xx > xMax) {
6248
118
      xMax = xx;
6249
118
    }
6250
10.6k
    if (yy < yMin) {
6251
1.01k
      yMin = yy;
6252
9.62k
    } else if (yy > yMax) {
6253
262
      yMax = yy;
6254
262
    }
6255
10.6k
    if (xMax <= xMin) {
6256
8.43k
      xMax = xMin + 1;
6257
8.43k
    }
6258
10.6k
    if (yMax <= yMin) {
6259
8.66k
      yMax = yMin + 1;
6260
8.66k
    }
6261
10.6k
  }
6262
38.6k
  SplashClipResult clipRes =
6263
38.6k
      state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1,
6264
38.6k
          state->strokeAdjust);
6265
38.6k
  if (clipRes == splashClipAllOutside) {
6266
36.9k
    return splashOk;
6267
36.9k
  }
6268
  // If the scaled mask is much wider and/or taller than the clip
6269
  // region, we use the "arbitrary" scaling path, to avoid a
6270
  // potentially very slow loop in the flips-only path (which scans
6271
  // the full width and height of the scaled mask, regardless of the
6272
  // clip region).
6273
1.68k
  int clipW = state->clip->getXMaxI(state->strokeAdjust)
6274
1.68k
        - state->clip->getXMinI(state->strokeAdjust);
6275
1.68k
  int clipH = state->clip->getYMaxI(state->strokeAdjust)
6276
1.68k
        - state->clip->getYMinI(state->strokeAdjust);
6277
1.68k
  GBool veryLarge = ((xMax - xMin) / 8 > clipW && xMax - xMin > 1000) ||
6278
649
                    ((yMax - yMin) / 8 > clipH && yMax - yMin > 1000);
6279
6280
  //--- set up the SplashDrawImageMaskRowData object and the pipes
6281
1.68k
  SplashDrawImageMaskRowData dd;
6282
1.68k
  pipeInit(&dd.pipe, state->fillPattern,
6283
1.68k
     (Guchar)splashRound(state->fillAlpha * 255),
6284
1.68k
     gFalse, gTrue, gFalse);
6285
6286
  //--- choose the drawRow function
6287
1.68k
  SplashDrawImageMaskRowFunc drawRowFunc;
6288
1.68k
  if (clipRes == splashClipAllInside) {
6289
264
    drawRowFunc = &Splash::drawImageMaskRowNoClip;
6290
1.42k
  } else {
6291
1.42k
    if (antialias) {
6292
1.42k
      drawRowFunc = &Splash::drawImageMaskRowClipAA;
6293
1.42k
    } else {
6294
0
      drawRowFunc = &Splash::drawImageMaskRowClipNoAA;
6295
0
    }
6296
1.42k
  }
6297
6298
  //--- horizontal/vertical flips only
6299
1.68k
  if (flipsOnly && !veryLarge) {
6300
337
    int scaledWidth = xMax - xMin;
6301
337
    int scaledHeight = yMax - yMin;
6302
337
    ImageMaskScaler scaler(src, srcData, w, h,
6303
337
         scaledWidth, scaledHeight, interpolate, antialias);
6304
337
    Guchar *tmpLine = NULL;
6305
337
    if (horizFlip) {
6306
39
      tmpLine = (Guchar *)gmalloc(scaledWidth);
6307
39
    }
6308
337
    if (vertFlip) {
6309
162
      if (horizFlip) {    // bottom-up, mirrored
6310
54
  for (int y = 0; y < scaledHeight; ++y) {
6311
27
    scaler.nextLine();
6312
27
    mirrorImageMaskRow(scaler.data(), tmpLine, scaledWidth);
6313
27
    (this->*drawRowFunc)(&dd, tmpLine,
6314
27
             xMin, yMax - 1 - y, scaledWidth);
6315
27
  }
6316
135
      } else {            // bottom-up
6317
18.8k
  for (int y = 0; y < scaledHeight; ++y) {
6318
18.7k
    scaler.nextLine();
6319
18.7k
    (this->*drawRowFunc)(&dd, scaler.data(),
6320
18.7k
             xMin, yMax - 1 - y, scaledWidth);
6321
18.7k
  }
6322
135
      }
6323
175
    } else {
6324
175
      if (horizFlip) {    // top-down, mirrored
6325
24
  for (int y = 0; y < scaledHeight; ++y) {
6326
12
    scaler.nextLine();
6327
12
    mirrorImageMaskRow(scaler.data(), tmpLine, scaledWidth);
6328
12
    (this->*drawRowFunc)(&dd, tmpLine,
6329
12
             xMin, yMin + y, scaledWidth);
6330
12
  }
6331
163
      } else {            // top-down
6332
8.43k
  for (int y = 0; y < scaledHeight; ++y) {
6333
8.27k
    scaler.nextLine();
6334
8.27k
    (this->*drawRowFunc)(&dd, scaler.data(),
6335
8.27k
             xMin, yMin + y, scaledWidth);
6336
8.27k
  }
6337
163
      }
6338
175
    }
6339
337
    gfree(tmpLine);
6340
6341
  //--- 90/270 rotation
6342
1.35k
  } else if (rot90Only && !veryLarge) {
6343
6344
    // scale the mask
6345
89
    int scaledWidth = yMax - yMin;
6346
89
    int scaledHeight = xMax - xMin;
6347
89
    ImageMaskScaler scaler(src, srcData, w, h,
6348
89
         scaledWidth, scaledHeight, interpolate, antialias);
6349
89
    Guchar *scaledMask = (Guchar *)gmallocn64(scaledHeight, scaledWidth);
6350
89
    Guchar *ptr = scaledMask;
6351
1.73k
    for (int y = 0; y < scaledHeight; ++y) {
6352
1.64k
      scaler.nextLine();
6353
1.64k
      memcpy(ptr, scaler.data(), scaledWidth);
6354
1.64k
      ptr += scaledWidth;
6355
1.64k
    }
6356
6357
    // draw it
6358
89
    Guchar *tmpLine = (Guchar *)gmalloc(scaledHeight);
6359
1.56k
    for (int y = 0; y < scaledWidth; ++y) {
6360
1.47k
      if (vertFlip) {
6361
34
  ptr = scaledMask + (scaledWidth - 1 - y);
6362
1.44k
      } else {
6363
1.44k
  ptr = scaledMask + y;
6364
1.44k
      }
6365
1.47k
      if (horizFlip) {
6366
1.34k
  ptr += (scaledHeight - 1) * (SplashBitmapRowSize)scaledWidth;
6367
1.01M
  for (int x = 0; x < scaledHeight; ++x) {
6368
1.01M
    tmpLine[x] = *ptr;
6369
1.01M
    ptr -= scaledWidth;
6370
1.01M
  }
6371
1.34k
      } else {
6372
689
  for (int x = 0; x < scaledHeight; ++x) {
6373
552
    tmpLine[x] = *ptr;
6374
552
    ptr += scaledWidth;
6375
552
  }
6376
137
      }
6377
1.47k
      (this->*drawRowFunc)(&dd, tmpLine, xMin, yMin + y, scaledHeight);
6378
1.47k
    }
6379
6380
89
    gfree(tmpLine);
6381
89
    gfree(scaledMask);
6382
6383
  //--- arbitrary transform
6384
1.26k
  } else {
6385
    // estimate of size of scaled image
6386
1.26k
    int scaledWidth = splashRound(splashSqrt(mat[0] * mat[0]
6387
1.26k
               + mat[1] * mat[1]));
6388
1.26k
    int scaledHeight = splashRound(splashSqrt(mat[2] * mat[2]
6389
1.26k
                + mat[3] * mat[3]));
6390
1.26k
    if (scaledWidth < 1) {
6391
1.08k
      scaledWidth = 1;
6392
1.08k
    }
6393
1.26k
    if (scaledHeight < 1) {
6394
1.05k
      scaledHeight = 1;
6395
1.05k
    }
6396
1.26k
    GBool downscaling = gTrue;
6397
1.26k
    if (scaledWidth >= w && scaledHeight >= h) {
6398
99
      downscaling = gFalse;
6399
99
      scaledWidth = w;
6400
99
      scaledHeight = h;
6401
99
    }
6402
6403
    // compute mapping from device space to scaled image space
6404
1.26k
    SplashCoord mat1[6];
6405
1.26k
    mat1[0] = mat[0] / scaledWidth;
6406
1.26k
    mat1[1] = mat[1] / scaledWidth;
6407
1.26k
    mat1[2] = mat[2] / scaledHeight;
6408
1.26k
    mat1[3] = mat[3] / scaledHeight;
6409
1.26k
    mat1[4] = mat[4];
6410
1.26k
    mat1[5] = mat[5];
6411
1.26k
    SplashCoord det = mat1[0] * mat1[3] - mat1[1] * mat1[2];
6412
1.26k
    if (splashAbs(det) < 1e-6) {
6413
      // this should be caught by the singular matrix check in drawImage
6414
32
      return splashErrSingularMatrix;
6415
32
    }
6416
1.22k
    SplashCoord invMat[6];
6417
1.22k
    invMat[0] = mat1[3] / det;
6418
1.22k
    invMat[1] = -mat1[1] / det;
6419
1.22k
    invMat[2] = -mat1[2] / det;
6420
1.22k
    invMat[3] = mat1[0] / det;
6421
    // the extra "+ 0.5 * (...)" terms are here because the
6422
    // drawImageArbitrary(No)Interp functions multiply by pixel
6423
    // centers, (x + 0.5, y + 0.5)
6424
1.22k
    invMat[4] = (mat1[2] * mat1[5] - mat1[3] * mat1[4]) / det
6425
1.22k
                + (invMat[0] + invMat[2]) * 0.5;
6426
1.22k
    invMat[5] = (mat1[1] * mat1[4] - mat1[0] * mat1[5]) / det
6427
1.22k
                + (invMat[1] + invMat[3]) * 0.5;
6428
6429
    // if downscaling: store the downscaled image mask
6430
    // if upscaling: store the unscaled image mask
6431
1.22k
    Guchar *scaledMask = (Guchar *)gmallocn64(scaledHeight, scaledWidth);
6432
1.22k
    if (downscaling) {
6433
1.13k
      ImageMaskScaler scaler(src, srcData, w, h,
6434
1.13k
           scaledWidth, scaledHeight, interpolate, antialias);
6435
1.13k
      Guchar *ptr = scaledMask;
6436
11.0M
      for (int y = 0; y < scaledHeight; ++y) {
6437
11.0M
  scaler.nextLine();
6438
11.0M
  memcpy(ptr, scaler.data(), scaledWidth);
6439
11.0M
  ptr += scaledWidth;
6440
11.0M
      }
6441
1.13k
    } else {
6442
95
      Guchar *ptr = scaledMask;
6443
5.17k
      for (int y = 0; y < scaledHeight; ++y) {
6444
5.07k
  (*src)(srcData, ptr);
6445
38.7k
  for (int x = 0; x < scaledWidth; ++x) {
6446
33.6k
    *ptr = (Guchar)(*ptr * 255);
6447
33.6k
    ++ptr;
6448
33.6k
  }
6449
5.07k
      }
6450
95
    }
6451
6452
    // draw it
6453
1.22k
    if (interpolate && antialias) {
6454
37
      drawImageMaskArbitraryInterp(scaledMask,
6455
37
           &dd, drawRowFunc, invMat,
6456
37
           scaledWidth, scaledHeight,
6457
37
           xMin, yMin, xMax, yMax);
6458
1.19k
    } else {
6459
1.19k
      drawImageMaskArbitraryNoInterp(scaledMask,
6460
1.19k
             &dd, drawRowFunc, invMat,
6461
1.19k
             scaledWidth, scaledHeight,
6462
1.19k
             xMin, yMin, xMax, yMax);
6463
1.19k
    }
6464
6465
    // free the downscaled/unscaled image
6466
1.22k
    gfree(scaledMask);
6467
1.22k
  }
6468
6469
1.65k
  return splashOk;
6470
1.68k
}
6471
6472
void Splash::drawImageMaskArbitraryNoInterp(
6473
           Guchar *scaledMask,
6474
           SplashDrawImageMaskRowData *dd,
6475
           SplashDrawImageMaskRowFunc drawRowFunc,
6476
           SplashCoord *invMat,
6477
           int scaledWidth, int scaledHeight,
6478
1.19k
           int xMin, int yMin, int xMax, int yMax) {
6479
1.19k
  int tt = state->clip->getXMinI(state->strokeAdjust);
6480
1.19k
  if (tt > xMin) {
6481
177
    xMin = tt;
6482
177
  }
6483
1.19k
  tt = state->clip->getXMaxI(state->strokeAdjust) + 1;
6484
1.19k
  if (tt < xMax) {
6485
1.17k
    xMax = tt;
6486
1.17k
  }
6487
1.19k
  tt = state->clip->getYMinI(state->strokeAdjust);
6488
1.19k
  if (tt > yMin) {
6489
265
    yMin = tt;
6490
265
  }
6491
1.19k
  tt = state->clip->getYMaxI(state->strokeAdjust) + 1;
6492
1.19k
  if (tt < yMax) {
6493
1.16k
    yMax = tt;
6494
1.16k
  }
6495
1.19k
  if (xMax <= xMin || yMax <= yMin) {
6496
0
    return;
6497
0
  }
6498
6499
1.19k
  Guchar *buf = (Guchar *)gmalloc(xMax - xMin);
6500
6501
2.51k
  for (int y = yMin; y < yMax; ++y) {
6502
1.32k
    int rowMin = xMax;
6503
1.32k
    int rowMax = 0;
6504
12.5k
    for (int x = xMin; x < xMax; ++x) {
6505
      // note: invMat includes a "+0.5" factor so that this is really
6506
      // a multiply by (x+0.5, y+0.5)
6507
11.2k
      int xx = splashFloor((SplashCoord)x * invMat[0]
6508
11.2k
         + (SplashCoord)y * invMat[2] + invMat[4]);
6509
11.2k
      int yy = splashFloor((SplashCoord)x * invMat[1]
6510
11.2k
         + (SplashCoord)y * invMat[3] + invMat[5]);
6511
11.2k
      if (xx >= 0 && xx < scaledWidth &&
6512
10.8k
    yy >= 0 && yy < scaledHeight) {
6513
10.5k
  Guchar *p = scaledMask + (yy * (SplashBitmapRowSize)scaledWidth + xx);
6514
10.5k
  Guchar *q = buf + (x - xMin);
6515
10.5k
  *q = *p;
6516
10.5k
  if (x < rowMin) {
6517
804
    rowMin = x;
6518
804
  }
6519
10.5k
  rowMax = x + 1;
6520
10.5k
      }
6521
11.2k
    }
6522
1.32k
    if (rowMin < rowMax) {
6523
804
      (this->*drawRowFunc)(dd, buf + (rowMin - xMin),
6524
804
         rowMin, y, rowMax - rowMin);
6525
804
    }
6526
1.32k
  }
6527
6528
1.19k
  gfree(buf);
6529
1.19k
}
6530
6531
void Splash::drawImageMaskArbitraryInterp(
6532
           Guchar *scaledMask,
6533
           SplashDrawImageMaskRowData *dd,
6534
           SplashDrawImageMaskRowFunc drawRowFunc,
6535
           SplashCoord *invMat,
6536
           int scaledWidth, int scaledHeight,
6537
37
           int xMin, int yMin, int xMax, int yMax) {
6538
37
  int tt = state->clip->getXMinI(state->strokeAdjust);
6539
37
  if (tt > xMin) {
6540
9
    xMin = tt;
6541
9
  }
6542
37
  tt = state->clip->getXMaxI(state->strokeAdjust) + 1;
6543
37
  if (tt < xMax) {
6544
34
    xMax = tt;
6545
34
  }
6546
37
  tt = state->clip->getYMinI(state->strokeAdjust);
6547
37
  if (tt > yMin) {
6548
10
    yMin = tt;
6549
10
  }
6550
37
  tt = state->clip->getYMaxI(state->strokeAdjust) + 1;
6551
37
  if (tt < yMax) {
6552
33
    yMax = tt;
6553
33
  }
6554
37
  if (xMax <= xMin || yMax <= yMin) {
6555
0
    return;
6556
0
  }
6557
6558
37
  Guchar *buf = (Guchar *)gmalloc(xMax - xMin);
6559
6560
81
  for (int y = yMin; y < yMax; ++y) {
6561
44
    int rowMin = xMax;
6562
44
    int rowMax = 0;
6563
218
    for (int x = xMin; x < xMax; ++x) {
6564
      // note: invMat includes a "+0.5" factor so that this is really
6565
      // a multiply by (x+0.5, y+0.5)
6566
174
      SplashCoord xs = (SplashCoord)x * invMat[0]
6567
174
                 + (SplashCoord)y * invMat[2] + invMat[4];
6568
174
      SplashCoord ys = (SplashCoord)x * invMat[1]
6569
174
                 + (SplashCoord)y * invMat[3] + invMat[5];
6570
174
      int x0 = splashFloor(xs - 0.5);
6571
174
      int x1 = x0 + 1;
6572
174
      int y0 = splashFloor(ys - 0.5);
6573
174
      int y1 = y0 + 1;
6574
174
      if (x1 >= 0 && x0 < scaledWidth && y1 >= 0 && y0 < scaledHeight) {
6575
162
  SplashCoord sx0 = (SplashCoord)x1 + 0.5 - xs;
6576
162
  SplashCoord sx1 = (SplashCoord)1 - sx0;
6577
162
  SplashCoord sy0 = (SplashCoord)y1 + 0.5 - ys;
6578
162
  SplashCoord sy1 = (SplashCoord)1 - sy0;
6579
162
  if (x0 < 0) {
6580
147
    x0 = 0;
6581
147
  }
6582
162
  if (x1 >= scaledWidth) {
6583
15
    x1 = scaledWidth - 1;
6584
15
  }
6585
162
  if (y0 < 0) {
6586
3
    y0 = 0;
6587
3
  }
6588
162
  if (y1 >= scaledHeight) {
6589
159
    y1 = scaledHeight - 1;
6590
159
  }
6591
162
  Guchar *p00 = scaledMask + (y0 * (SplashBitmapRowSize)scaledWidth + x0);
6592
162
  Guchar *p10 = scaledMask + (y0 * (SplashBitmapRowSize)scaledWidth + x1);
6593
162
  Guchar *p01 = scaledMask + (y1 * (SplashBitmapRowSize)scaledWidth + x0);
6594
162
  Guchar *p11 = scaledMask + (y1 * (SplashBitmapRowSize)scaledWidth + x1);
6595
162
  Guchar *q = buf + (x - xMin);
6596
162
  *q = (Guchar)(int)(sx0 * (sy0 * (int)*p00 + sy1 * (int)*p01) +
6597
162
         sx1 * (sy0 * (int)*p10 + sy1 * (int)*p11));
6598
162
  if (x < rowMin) {
6599
32
    rowMin = x;
6600
32
  }
6601
162
  rowMax = x + 1;
6602
162
      }
6603
174
    }
6604
44
    if (rowMin < rowMax) {
6605
32
      (this->*drawRowFunc)(dd, buf + (rowMin - xMin),
6606
32
         rowMin, y, rowMax - rowMin);
6607
32
    }
6608
44
  }
6609
6610
37
  gfree(buf);
6611
37
}
6612
6613
39
void Splash::mirrorImageMaskRow(Guchar *maskIn, Guchar *maskOut, int width) {
6614
39
  Guchar *p, *q;
6615
6616
39
  p = maskIn;
6617
39
  q = maskOut + (width - 1);
6618
921
  for (int i = 0; i < width; ++i) {
6619
882
    *q = *p;
6620
882
    ++p;
6621
882
    --q;
6622
882
  }
6623
39
}
6624
6625
void Splash::drawImageMaskRowNoClip(SplashDrawImageMaskRowData *data,
6626
            Guchar *maskData,
6627
8.36k
            int x, int y, int width) {
6628
8.36k
  (this->*data->pipe.run)(&data->pipe, x, x + width - 1, y,
6629
8.36k
        NULL, maskData, NULL);
6630
8.36k
}
6631
6632
void Splash::drawImageMaskRowClipNoAA(SplashDrawImageMaskRowData *data,
6633
              Guchar *maskData,
6634
0
              int x, int y, int width) {
6635
0
  if (y < 0 || y >= bitmap->height) {
6636
0
    return;
6637
0
  }
6638
0
  if (x < 0) {
6639
0
    maskData -= x;
6640
0
    width += x;
6641
0
    x = 0;
6642
0
  }
6643
0
  if (x + width > bitmap->width) {
6644
0
    width = bitmap->width - x;
6645
0
  }
6646
0
  if (width <= 0) {
6647
0
    return;
6648
0
  }
6649
0
  memcpy(scanBuf + x, maskData, width);
6650
0
  state->clip->clipSpanBinary(scanBuf, y, x, x + width - 1,
6651
0
            state->strokeAdjust);
6652
0
  (this->*data->pipe.run)(&data->pipe, x, x + width - 1, y,
6653
0
        NULL, scanBuf + x, NULL);
6654
0
}
6655
6656
void Splash::drawImageMaskRowClipAA(SplashDrawImageMaskRowData *data,
6657
            Guchar *maskData,
6658
20.9k
            int x, int y, int width) {
6659
20.9k
  if (y < 0 || y >= bitmap->height) {
6660
19.9k
    return;
6661
19.9k
  }
6662
1.00k
  if (x < 0) {
6663
30
    maskData -= x;
6664
30
    width += x;
6665
30
    x = 0;
6666
30
  }
6667
1.00k
  if (x + width > bitmap->width) {
6668
123
    width = bitmap->width - x;
6669
123
  }
6670
1.00k
  if (width <= 0) {
6671
0
    return;
6672
0
  }
6673
1.00k
  memcpy(scanBuf + x, maskData, width);
6674
1.00k
  state->clip->clipSpan(scanBuf, y, x, x + width - 1, state->strokeAdjust);
6675
1.00k
  (this->*data->pipe.run)(&data->pipe, x, x + width - 1, y,
6676
1.00k
        NULL, scanBuf + x, NULL);
6677
1.00k
}
6678
6679
struct SplashDrawImageRowData {
6680
  int nComps;
6681
  GBool srcAlpha;
6682
  SplashPipe pipe;
6683
};
6684
6685
SplashError Splash::drawImage(GString *imageTag,
6686
            SplashImageSource src, void *srcData,
6687
            SplashColorMode srcMode, GBool srcAlpha,
6688
            int w, int h, SplashCoord *mat,
6689
28.3k
            GBool interpolate) {
6690
28.3k
  if (debugMode) {
6691
0
    printf("drawImage: srcMode=%d srcAlpha=%d w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
6692
0
     srcMode, srcAlpha, w, h, (double)mat[0], (double)mat[1], (double)mat[2],
6693
0
     (double)mat[3], (double)mat[4], (double)mat[5]);
6694
0
  }
6695
6696
  //--- check color modes
6697
28.3k
  GBool ok = gFalse;
6698
28.3k
  int nComps = 0;
6699
28.3k
  switch (bitmap->mode) {
6700
0
  case splashModeMono1:
6701
2.23k
  case splashModeMono8:
6702
2.23k
    ok = srcMode == splashModeMono8;
6703
2.23k
    nComps = 1;
6704
2.23k
    break;
6705
25.9k
  case splashModeRGB8:
6706
25.9k
  case splashModeBGR8:
6707
25.9k
    ok = srcMode == splashModeRGB8;
6708
25.9k
    nComps = 3;
6709
25.9k
    break;
6710
0
#if SPLASH_CMYK
6711
165
  case splashModeCMYK8:
6712
165
    ok = srcMode == splashModeCMYK8;
6713
165
    nComps = 4;
6714
165
    break;
6715
0
#endif
6716
0
  default:
6717
0
    ok = gFalse;
6718
0
    break;
6719
28.3k
  }
6720
28.3k
  if (!ok) {
6721
0
    return splashErrModeMismatch;
6722
0
  }
6723
6724
  //--- check for singular matrix
6725
28.3k
  if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) {
6726
607
    return splashErrSingularMatrix;
6727
607
  }
6728
6729
  //--- compute image bbox, check clipping
6730
27.7k
  GBool flipsOnly = splashAbs(mat[1]) <= 0.0001 && splashAbs(mat[2]) <= 0.0001;
6731
27.7k
  GBool rot90Only = splashAbs(mat[0]) <= 0.0001 && splashAbs(mat[3]) <= 0.0001;
6732
27.7k
  GBool horizFlip = gFalse;
6733
27.7k
  GBool vertFlip = gFalse;
6734
27.7k
  int xMin, yMin, xMax, yMax;
6735
27.7k
  if (flipsOnly) {
6736
16.7k
    horizFlip = mat[0] < 0;
6737
16.7k
    vertFlip = mat[3] < 0;
6738
16.7k
    if (horizFlip) {
6739
9.67k
      getImageBounds(mat[0] + mat[4], mat[4], &xMin, &xMax);
6740
9.67k
    } else {
6741
7.06k
      getImageBounds(mat[4], mat[0] + mat[4], &xMin, &xMax);
6742
7.06k
    }
6743
16.7k
    if (vertFlip) {
6744
10.9k
      getImageBounds(mat[3] + mat[5], mat[5], &yMin, &yMax);
6745
10.9k
    } else {
6746
5.83k
      getImageBounds(mat[5], mat[3] + mat[5], &yMin, &yMax);
6747
5.83k
    }
6748
16.7k
  } else if (rot90Only) {
6749
7.36k
    horizFlip = mat[2] < 0;
6750
7.36k
    vertFlip = mat[1] < 0;
6751
7.36k
    if (horizFlip) {
6752
2.47k
      getImageBounds(mat[2] + mat[4], mat[4], &xMin, &xMax);
6753
4.88k
    } else {
6754
4.88k
      getImageBounds(mat[4], mat[2] + mat[4], &xMin, &xMax);
6755
4.88k
    }
6756
7.36k
    if (vertFlip) {
6757
2.84k
      getImageBounds(mat[1] + mat[5], mat[5], &yMin, &yMax);
6758
4.52k
    } else {
6759
4.52k
      getImageBounds(mat[5], mat[1] + mat[5], &yMin, &yMax);
6760
4.52k
    }
6761
7.36k
  } else {
6762
3.68k
    int xx = splashRound(mat[4]);   // (0,0)
6763
3.68k
    int yy = splashRound(mat[5]);
6764
3.68k
    xMin = xMax = xx;
6765
3.68k
    yMin = yMax = yy;
6766
3.68k
    xx = splashRound(mat[0] + mat[4]);    // (1,0)
6767
3.68k
    yy = splashRound(mat[1] + mat[5]);
6768
3.68k
    if (xx < xMin) {
6769
132
      xMin = xx;
6770
3.54k
    } else if (xx > xMax) {
6771
598
      xMax = xx;
6772
598
    }
6773
3.68k
    if (yy < yMin) {
6774
510
      yMin = yy;
6775
3.17k
    } else if (yy > yMax) {
6776
260
      yMax = yy;
6777
260
    }
6778
3.68k
    xx = splashRound(mat[2] + mat[4]);    // (0,1)
6779
3.68k
    yy = splashRound(mat[3] + mat[5]);
6780
3.68k
    if (xx < xMin) {
6781
1.00k
      xMin = xx;
6782
2.68k
    } else if (xx > xMax) {
6783
156
      xMax = xx;
6784
156
    }
6785
3.68k
    if (yy < yMin) {
6786
405
      yMin = yy;
6787
3.27k
    } else if (yy > yMax) {
6788
451
      yMax = yy;
6789
451
    }
6790
3.68k
    xx = splashRound(mat[0] + mat[2] + mat[4]); // (1,1)
6791
3.68k
    yy = splashRound(mat[1] + mat[3] + mat[5]);
6792
3.68k
    if (xx < xMin) {
6793
440
      xMin = xx;
6794
3.24k
    } else if (xx > xMax) {
6795
57
      xMax = xx;
6796
57
    }
6797
3.68k
    if (yy < yMin) {
6798
449
      yMin = yy;
6799
3.23k
    } else if (yy > yMax) {
6800
72
      yMax = yy;
6801
72
    }
6802
3.68k
    if (xMax <= xMin) {
6803
1.99k
      xMax = xMin + 1;
6804
1.99k
    }
6805
3.68k
    if (yMax <= yMin) {
6806
2.17k
      yMax = yMin + 1;
6807
2.17k
    }
6808
3.68k
  }
6809
27.7k
  SplashClipResult clipRes =
6810
27.7k
      state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1,
6811
27.7k
          state->strokeAdjust);
6812
27.7k
  if (clipRes == splashClipAllOutside) {
6813
25.1k
    return splashOk;
6814
25.1k
  }
6815
  // If the scaled image is much wider and/or taller than the clip
6816
  // region, we use the arbitrary transform path, to avoid a
6817
  // potentially very slow loop in the flips-only path (which scans
6818
  // the full width and height of the scaled image, regardless of the
6819
  // clip region).
6820
2.68k
  int clipW = state->clip->getXMaxI(state->strokeAdjust)
6821
2.68k
        - state->clip->getXMinI(state->strokeAdjust);
6822
2.68k
  int clipH = state->clip->getYMaxI(state->strokeAdjust)
6823
2.68k
        - state->clip->getYMinI(state->strokeAdjust);
6824
2.68k
  GBool veryLarge = ((xMax - xMin) / 8 > clipW && xMax - xMin > 1000) ||
6825
244
                    ((yMax - yMin) / 8 > clipH && yMax - yMin > 1000);
6826
6827
  //--- set up the SplashDrawImageRowData object and the pipes
6828
2.68k
  SplashDrawImageRowData dd;
6829
2.68k
  dd.nComps = nComps;
6830
2.68k
  dd.srcAlpha = srcAlpha;
6831
2.68k
  pipeInit(&dd.pipe, NULL,
6832
2.68k
     (Guchar)splashRound(state->fillAlpha * 255),
6833
2.68k
     gFalse, clipRes != splashClipAllInside || srcAlpha,
6834
2.68k
     gFalse, gFalse, state->alphaIsShape);
6835
6836
  //--- choose the drawRow function
6837
2.68k
  SplashDrawImageRowFunc drawRowFunc;
6838
2.68k
  if (clipRes == splashClipAllInside) {
6839
0
    if (srcAlpha) {
6840
0
      drawRowFunc = &Splash::drawImageRowNoClipAlpha;
6841
0
    } else {
6842
0
      drawRowFunc = &Splash::drawImageRowNoClipNoAlpha;
6843
0
    }
6844
2.68k
  } else {
6845
2.68k
    if (srcAlpha) {
6846
107
      if (vectorAntialias) {
6847
107
  drawRowFunc = &Splash::drawImageRowClipAlphaAA;
6848
107
      } else {
6849
0
  drawRowFunc = &Splash::drawImageRowClipAlphaNoAA;
6850
0
      }
6851
2.58k
    } else {
6852
2.58k
      if (vectorAntialias) {
6853
2.58k
  drawRowFunc = &Splash::drawImageRowClipNoAlphaAA;
6854
2.58k
      } else {
6855
0
  drawRowFunc = &Splash::drawImageRowClipNoAlphaNoAA;
6856
0
      }
6857
2.58k
    }
6858
2.68k
  }
6859
6860
  //--- horizontal/vertical flips only
6861
2.68k
  if (flipsOnly && !veryLarge) {
6862
0
    int scaledWidth = xMax - xMin;
6863
0
    int scaledHeight = yMax - yMin;
6864
0
    ImageScaler *scaler = getImageScaler(imageTag, src, srcData,
6865
0
           w, h, nComps,
6866
0
           scaledWidth, scaledHeight,
6867
0
           srcMode, srcAlpha, interpolate);
6868
0
    Guchar *tmpLine = NULL;
6869
0
    Guchar *tmpAlphaLine = NULL;
6870
0
    if (horizFlip) {
6871
0
      tmpLine = (Guchar *)gmallocn(scaledWidth, nComps);
6872
0
      if (srcAlpha) {
6873
0
  tmpAlphaLine = (Guchar *)gmalloc(scaledWidth);
6874
0
      }
6875
0
    }
6876
0
    if (vertFlip) {
6877
0
      if (horizFlip) {    // bottom-up, mirrored
6878
0
  for (int y = 0; y < scaledHeight; ++y) {
6879
0
    scaler->nextLine();
6880
0
    mirrorImageRow(scaler->colorData(), scaler->alphaData(),
6881
0
       tmpLine, tmpAlphaLine,
6882
0
       scaledWidth, nComps, srcAlpha);
6883
0
    (this->*drawRowFunc)(&dd, tmpLine, tmpAlphaLine,
6884
0
             xMin, yMax - 1 - y, scaledWidth);
6885
0
  }
6886
0
      } else {            // bottom-up
6887
0
  for (int y = 0; y < scaledHeight; ++y) {
6888
0
    scaler->nextLine();
6889
0
    (this->*drawRowFunc)(&dd, scaler->colorData(), scaler->alphaData(),
6890
0
             xMin, yMax - 1 - y, scaledWidth);
6891
0
  }
6892
0
      }
6893
0
    } else {
6894
0
      if (horizFlip) {    // top-down, mirrored
6895
0
  for (int y = 0; y < scaledHeight; ++y) {
6896
0
    scaler->nextLine();
6897
0
    mirrorImageRow(scaler->colorData(), scaler->alphaData(),
6898
0
       tmpLine, tmpAlphaLine,
6899
0
       scaledWidth, nComps, srcAlpha);
6900
0
    (this->*drawRowFunc)(&dd, tmpLine, tmpAlphaLine,
6901
0
             xMin, yMin + y, scaledWidth);
6902
0
  }
6903
0
      } else {            // top-down
6904
0
  for (int y = 0; y < scaledHeight; ++y) {
6905
0
    scaler->nextLine();
6906
0
    (this->*drawRowFunc)(&dd, scaler->colorData(), scaler->alphaData(),
6907
0
             xMin, yMin + y, scaledWidth);
6908
0
  }
6909
0
      }
6910
0
    }
6911
0
    gfree(tmpLine);
6912
0
    gfree(tmpAlphaLine);
6913
0
    delete scaler;
6914
6915
  //--- 90/270 rotation
6916
2.68k
  } else if (rot90Only && !veryLarge) {
6917
6918
    // scale the image
6919
0
    int scaledWidth = yMax - yMin;
6920
0
    int scaledHeight = xMax - xMin;
6921
0
    Guchar *scaledColor, *scaledAlpha;
6922
0
    GBool freeScaledImage;
6923
0
    getScaledImage(imageTag, src, srcData, w, h, nComps,
6924
0
       scaledWidth, scaledHeight, srcMode, srcAlpha, interpolate,
6925
0
       &scaledColor, &scaledAlpha, &freeScaledImage);
6926
6927
    // draw it
6928
0
    Guchar *tmpLine = (Guchar *)gmallocn(scaledHeight, nComps);
6929
0
    Guchar *tmpAlphaLine = NULL;
6930
0
    if (srcAlpha) {
6931
0
      tmpAlphaLine = (Guchar *)gmalloc(scaledHeight);
6932
0
    }
6933
0
    for (int y = 0; y < scaledWidth; ++y) {
6934
0
      Guchar *ptr = NULL;
6935
0
      Guchar *alphaPtr = NULL;
6936
0
      if (vertFlip) {
6937
0
  ptr = scaledColor + ((SplashBitmapRowSize)scaledWidth - 1 - y) * nComps;
6938
0
  if (srcAlpha) {
6939
0
    alphaPtr = scaledAlpha + (scaledWidth - 1 - y);
6940
0
  }
6941
0
      } else {
6942
0
  ptr = scaledColor + y * nComps;
6943
0
  if (srcAlpha) {
6944
0
    alphaPtr = scaledAlpha + y;
6945
0
  }
6946
0
      }
6947
0
      if (horizFlip) {
6948
0
  ptr += (scaledHeight - 1) * (SplashBitmapRowSize)scaledWidth * nComps;
6949
0
  Guchar *q = tmpLine;
6950
0
  for (int x = 0; x < scaledHeight; ++x) {
6951
0
    for (int i = 0; i < nComps; ++i) {
6952
0
      *q++ = ptr[i];
6953
0
    }
6954
0
    ptr -= scaledWidth * nComps;
6955
0
  }
6956
0
  if (srcAlpha) {
6957
0
    alphaPtr += (scaledHeight - 1) * (SplashBitmapRowSize)scaledWidth;
6958
0
    q = tmpAlphaLine;
6959
0
    for (int x = 0; x < scaledHeight; ++x) {
6960
0
      *q++ = *alphaPtr;
6961
0
      alphaPtr -= scaledWidth;
6962
0
    }
6963
0
  }
6964
0
      } else {
6965
0
  Guchar *q = tmpLine;
6966
0
  for (int x = 0; x < scaledHeight; ++x) {
6967
0
    for (int i = 0; i < nComps; ++i) {
6968
0
      *q++ = ptr[i];
6969
0
    }
6970
0
    ptr += scaledWidth * nComps;
6971
0
  }
6972
0
  if (srcAlpha) {
6973
0
    q = tmpAlphaLine;
6974
0
    for (int x = 0; x < scaledHeight; ++x) {
6975
0
      *q++ = *alphaPtr;
6976
0
      alphaPtr += scaledWidth;
6977
0
    }
6978
0
  }
6979
0
      }
6980
0
      (this->*drawRowFunc)(&dd, tmpLine, tmpAlphaLine,
6981
0
         xMin, yMin + y, scaledHeight);
6982
0
    }
6983
6984
0
    gfree(tmpLine);
6985
0
    gfree(tmpAlphaLine);
6986
0
    if (freeScaledImage) {
6987
0
      gfree(scaledColor);
6988
0
      gfree(scaledAlpha);
6989
0
    }
6990
6991
  //--- arbitrary transform
6992
2.68k
  } else {
6993
    // estimate of size of scaled image
6994
2.68k
    int scaledWidth = splashRound(splashSqrt(mat[0] * mat[0]
6995
2.68k
               + mat[1] * mat[1]));
6996
2.68k
    int scaledHeight = splashRound(splashSqrt(mat[2] * mat[2]
6997
2.68k
                + mat[3] * mat[3]));
6998
2.68k
    if (scaledWidth < 1) {
6999
2.68k
      scaledWidth = 1;
7000
2.68k
    }
7001
2.68k
    if (scaledHeight < 1) {
7002
2.68k
      scaledHeight = 1;
7003
2.68k
    }
7004
2.68k
    if (scaledWidth >= w && scaledHeight >= h) {
7005
48
      scaledWidth = w;
7006
48
      scaledHeight = h;
7007
48
    }
7008
7009
    // compute mapping from device space to scaled image space
7010
2.68k
    SplashCoord mat1[6];
7011
2.68k
    mat1[0] = mat[0] / scaledWidth;
7012
2.68k
    mat1[1] = mat[1] / scaledWidth;
7013
2.68k
    mat1[2] = mat[2] / scaledHeight;
7014
2.68k
    mat1[3] = mat[3] / scaledHeight;
7015
2.68k
    mat1[4] = mat[4];
7016
2.68k
    mat1[5] = mat[5];
7017
2.68k
    SplashCoord det = mat1[0] * mat1[3] - mat1[1] * mat1[2];
7018
2.68k
    if (splashAbs(det) < 1e-6) {
7019
      // this should be caught by the singular matrix check in drawImage
7020
0
      return splashErrSingularMatrix;
7021
0
    }
7022
2.68k
    SplashCoord invMat[6];
7023
2.68k
    invMat[0] = mat1[3] / det;
7024
2.68k
    invMat[1] = -mat1[1] / det;
7025
2.68k
    invMat[2] = -mat1[2] / det;
7026
2.68k
    invMat[3] = mat1[0] / det;
7027
    // the extra "+ 0.5 * (...)" terms are here because the
7028
    // drawImageArbitrary(No)Interp functions multiply by pixel
7029
    // centers, (x + 0.5, y + 0.5)
7030
2.68k
    invMat[4] = (mat1[2] * mat1[5] - mat1[3] * mat1[4]) / det
7031
2.68k
                + (invMat[0] + invMat[2]) * 0.5;
7032
2.68k
    invMat[5] = (mat1[1] * mat1[4] - mat1[0] * mat1[5]) / det
7033
2.68k
                + (invMat[1] + invMat[3]) * 0.5;
7034
7035
2.68k
    Guchar *scaledColor, *scaledAlpha;
7036
2.68k
    GBool freeScaledImage;
7037
2.68k
    getScaledImage(imageTag, src, srcData, w, h, nComps,
7038
2.68k
       scaledWidth, scaledHeight, srcMode, srcAlpha, interpolate,
7039
2.68k
       &scaledColor, &scaledAlpha, &freeScaledImage);
7040
7041
    // draw it
7042
2.68k
    if (interpolate) {
7043
193
      drawImageArbitraryInterp(scaledColor, scaledAlpha,
7044
193
             &dd, drawRowFunc, invMat,
7045
193
             scaledWidth, scaledHeight,
7046
193
             xMin, yMin, xMax, yMax,
7047
193
             nComps, srcAlpha);
7048
2.49k
    } else {
7049
2.49k
      drawImageArbitraryNoInterp(scaledColor, scaledAlpha,
7050
2.49k
         &dd, drawRowFunc, invMat,
7051
2.49k
         scaledWidth, scaledHeight,
7052
2.49k
         xMin, yMin, xMax, yMax,
7053
2.49k
         nComps, srcAlpha);
7054
2.49k
    }
7055
7056
    // free the downscaled/unscaled image
7057
2.68k
    if (freeScaledImage) {
7058
0
      gfree(scaledColor);
7059
0
      gfree(scaledAlpha);
7060
0
    }
7061
2.68k
  }
7062
7063
2.68k
  return splashOk;
7064
2.68k
}
7065
7066
ImageScaler *Splash::getImageScaler(GString *imageTag,
7067
            SplashImageSource src, void *srcData,
7068
            int w, int h, int nComps,
7069
            int scaledWidth, int scaledHeight,
7070
            SplashColorMode srcMode,
7071
0
            GBool srcAlpha, GBool interpolate) {
7072
  // Notes:
7073
  //
7074
  // * If the scaled image is more than 8 Mpixels, we don't cache it.
7075
  //
7076
  // * Caching is done on the third consecutive use (second
7077
  //   consecutive reuse) of an image; this avoids overhead on the
7078
  //   common case of single-use images.
7079
7080
0
  if (scaledWidth < 8000000 / scaledHeight &&
7081
0
      imageCache->match(imageTag, scaledWidth, scaledHeight,
7082
0
      srcMode, srcAlpha, interpolate)) {
7083
0
    if (imageCache->colorData) {
7084
0
      return new ReplayImageScaler(nComps, srcAlpha, scaledWidth,
7085
0
           imageCache->colorData,
7086
0
           imageCache->alphaData);
7087
0
    } else {
7088
0
      int lineSize;
7089
0
      if (scaledWidth < INT_MAX / nComps) {
7090
0
  lineSize = scaledWidth * nComps;
7091
0
      } else {
7092
0
  lineSize = -1;
7093
0
      }
7094
0
      imageCache->colorData = (Guchar *)gmallocn64(scaledHeight, lineSize);
7095
0
      if (srcAlpha) {
7096
0
  imageCache->alphaData = (Guchar *)gmallocn64(scaledHeight, scaledWidth);
7097
0
      }
7098
0
      return new SavingImageScaler(src, srcData,
7099
0
           w, h, nComps, srcAlpha,
7100
0
           scaledWidth, scaledHeight,
7101
0
           interpolate,
7102
0
           imageCache->colorData,
7103
0
           imageCache->alphaData);
7104
0
    }
7105
0
  } else {
7106
0
    imageCache->reset(imageTag, scaledWidth, scaledHeight,
7107
0
          srcMode, srcAlpha, interpolate);
7108
0
    return new BasicImageScaler(src, srcData,
7109
0
        w, h, nComps, srcAlpha,
7110
0
        scaledWidth, scaledHeight,
7111
0
        interpolate);
7112
0
  }
7113
0
}
7114
7115
void Splash::getScaledImage(GString *imageTag,
7116
          SplashImageSource src, void *srcData,
7117
          int w, int h, int nComps,
7118
          int scaledWidth, int scaledHeight,
7119
          SplashColorMode srcMode,
7120
          GBool srcAlpha, GBool interpolate,
7121
          Guchar **scaledColor, Guchar **scaledAlpha,
7122
2.68k
          GBool *freeScaledImage) {
7123
  // Notes:
7124
  //
7125
  // * If the scaled image is more than 8 Mpixels, we don't cache it.
7126
  //
7127
  // * This buffers the whole image anyway, so there's no reason to
7128
  //   skip caching on the first reuse.
7129
7130
2.68k
  if (scaledWidth >= 8000000 / scaledHeight) {
7131
0
    int lineSize;
7132
0
    if (scaledWidth < INT_MAX / nComps) {
7133
0
      lineSize = scaledWidth * nComps;
7134
0
    } else {
7135
0
      lineSize = -1;
7136
0
    }
7137
0
    *scaledColor = (Guchar *)gmallocn64(scaledHeight, lineSize);
7138
0
    if (srcAlpha) {
7139
0
      *scaledAlpha = (Guchar *)gmallocn64(scaledHeight, scaledWidth);
7140
0
    } else {
7141
0
      *scaledAlpha = NULL;
7142
0
    }
7143
0
    *freeScaledImage = gTrue;
7144
0
    if (scaledWidth == w && scaledHeight == h) {
7145
0
      Guchar *colorPtr = *scaledColor;
7146
0
      Guchar *alphaPtr = *scaledAlpha;
7147
0
      for (int y = 0; y < scaledHeight; ++y) {
7148
0
  (*src)(srcData, colorPtr, alphaPtr);
7149
0
  colorPtr += scaledWidth * nComps;
7150
0
  if (srcAlpha) {
7151
0
    alphaPtr += scaledWidth;
7152
0
  }
7153
0
      }
7154
0
    } else {
7155
0
      BasicImageScaler scaler(src, srcData, w, h, nComps, srcAlpha,
7156
0
            scaledWidth, scaledHeight, interpolate);
7157
0
      Guchar *colorPtr = *scaledColor;
7158
0
      Guchar *alphaPtr = *scaledAlpha;
7159
0
      for (int y = 0; y < scaledHeight; ++y) {
7160
0
  scaler.nextLine();
7161
0
  memcpy(colorPtr, scaler.colorData(), scaledWidth * nComps);
7162
0
  colorPtr += scaledWidth * nComps;
7163
0
  if (srcAlpha) {
7164
0
    memcpy(alphaPtr, scaler.alphaData(), scaledWidth);
7165
0
    alphaPtr += scaledWidth;
7166
0
  }
7167
0
      }
7168
0
    }
7169
2.68k
  } else {
7170
2.68k
    if (!imageCache->match(imageTag, scaledWidth, scaledHeight,
7171
2.68k
        srcMode, srcAlpha, interpolate) ||
7172
2.37k
  !imageCache->colorData) {
7173
2.37k
      imageCache->reset(imageTag, scaledWidth, scaledHeight,
7174
2.37k
      srcMode, srcAlpha, interpolate);
7175
2.37k
      int lineSize;
7176
2.37k
      if (scaledWidth < INT_MAX / nComps) {
7177
2.37k
  lineSize = scaledWidth * nComps;
7178
2.37k
      } else {
7179
0
  lineSize = -1;
7180
0
      }
7181
2.37k
      imageCache->colorData = (Guchar *)gmallocn64(scaledHeight, lineSize);
7182
2.37k
      if (srcAlpha) {
7183
45
  imageCache->alphaData = (Guchar *)gmallocn64(scaledHeight, scaledWidth);
7184
45
      }
7185
2.37k
      if (scaledWidth == w && scaledHeight == h) {
7186
46
  Guchar *colorPtr = imageCache->colorData;
7187
46
  Guchar *alphaPtr = imageCache->alphaData;
7188
92
  for (int y = 0; y < scaledHeight; ++y) {
7189
46
    (*src)(srcData, colorPtr, alphaPtr);
7190
46
    colorPtr += scaledWidth * nComps;
7191
46
    if (srcAlpha) {
7192
0
      alphaPtr += scaledWidth;
7193
0
    }
7194
46
  }
7195
2.33k
      } else {
7196
2.33k
  SavingImageScaler scaler(src, srcData, w, h, nComps, srcAlpha,
7197
2.33k
         scaledWidth, scaledHeight, interpolate,
7198
2.33k
         imageCache->colorData, imageCache->alphaData);
7199
2.33k
  Guchar *colorPtr = imageCache->colorData;
7200
2.33k
  Guchar *alphaPtr = imageCache->alphaData;
7201
4.66k
  for (int y = 0; y < scaledHeight; ++y) {
7202
2.33k
    scaler.nextLine();
7203
2.33k
    memcpy(colorPtr, scaler.colorData(), scaledWidth * nComps);
7204
2.33k
    colorPtr += scaledWidth * nComps;
7205
2.33k
    if (srcAlpha) {
7206
45
      memcpy(alphaPtr, scaler.alphaData(), scaledWidth);
7207
45
      alphaPtr += scaledWidth;
7208
45
    }
7209
2.33k
  }
7210
2.33k
      }
7211
2.37k
    }
7212
2.68k
    *scaledColor = imageCache->colorData;
7213
2.68k
    *scaledAlpha = imageCache->alphaData;
7214
2.68k
    *freeScaledImage = gFalse;
7215
2.68k
  }
7216
2.68k
}
7217
7218
void Splash::drawImageArbitraryNoInterp(Guchar *scaledColor,
7219
          Guchar *scaledAlpha,
7220
          SplashDrawImageRowData *dd,
7221
          SplashDrawImageRowFunc drawRowFunc,
7222
          SplashCoord *invMat,
7223
          int scaledWidth, int scaledHeight,
7224
          int xMin, int yMin, int xMax, int yMax,
7225
2.49k
          int nComps, GBool srcAlpha) {
7226
2.49k
  int tt = state->clip->getXMinI(state->strokeAdjust);
7227
2.49k
  if (tt > xMin) {
7228
132
    xMin = tt;
7229
132
  }
7230
2.49k
  tt = state->clip->getXMaxI(state->strokeAdjust) + 1;
7231
2.49k
  if (tt < xMax) {
7232
2.48k
    xMax = tt;
7233
2.48k
  }
7234
2.49k
  tt = state->clip->getYMinI(state->strokeAdjust);
7235
2.49k
  if (tt > yMin) {
7236
521
    yMin = tt;
7237
521
  }
7238
2.49k
  tt = state->clip->getYMaxI(state->strokeAdjust) + 1;
7239
2.49k
  if (tt < yMax) {
7240
2.49k
    yMax = tt;
7241
2.49k
  }
7242
2.49k
  if (xMax <= xMin || yMax <= yMin) {
7243
0
    return;
7244
0
  }
7245
7246
2.49k
  Guchar *colorBuf = (Guchar *)gmallocn(xMax - xMin, nComps);
7247
2.49k
  Guchar *alphaBuf = NULL;
7248
2.49k
  if (srcAlpha) {
7249
107
    alphaBuf = (Guchar *)gmalloc(xMax - xMin);
7250
107
  }
7251
7252
4.98k
  for (int y = yMin; y < yMax; ++y) {
7253
2.49k
    int rowMin = xMax;
7254
2.49k
    int rowMax = 0;
7255
4.98k
    for (int x = xMin; x < xMax; ++x) {
7256
      // note: invMat includes a "+0.5" factor so that this is really
7257
      // a multiply by (x+0.5, y+0.5)
7258
2.49k
      int xx = splashFloor((SplashCoord)x * invMat[0]
7259
2.49k
         + (SplashCoord)y * invMat[2] + invMat[4]);
7260
2.49k
      int yy = splashFloor((SplashCoord)x * invMat[1]
7261
2.49k
         + (SplashCoord)y * invMat[3] + invMat[5]);
7262
2.49k
      if (xx >= 0 && xx < scaledWidth &&
7263
2.11k
    yy >= 0 && yy < scaledHeight) {
7264
1.84k
  Guchar *p = scaledColor +
7265
1.84k
                (yy * (SplashBitmapRowSize)scaledWidth + xx) * nComps;
7266
1.84k
  Guchar *q = colorBuf + (x - xMin) * nComps;
7267
5.83k
  for (int i = 0; i < nComps; ++i) {
7268
3.99k
    *q++ = *p++;
7269
3.99k
  }
7270
1.84k
  if (srcAlpha) {
7271
40
    alphaBuf[x - xMin] =
7272
40
        scaledAlpha[yy * (SplashBitmapRowSize)scaledWidth + xx];
7273
40
  }
7274
1.84k
  if (x < rowMin) {
7275
1.84k
    rowMin = x;
7276
1.84k
  }
7277
1.84k
  rowMax = x + 1;
7278
1.84k
      }
7279
2.49k
    }
7280
2.49k
    if (rowMin < rowMax) {
7281
1.84k
      (this->*drawRowFunc)(dd,
7282
1.84k
         colorBuf + (rowMin - xMin) * nComps,
7283
1.84k
         alphaBuf + (rowMin - xMin),
7284
1.84k
         rowMin, y, rowMax - rowMin);
7285
1.84k
    }
7286
2.49k
  }
7287
7288
2.49k
  gfree(colorBuf);
7289
2.49k
  gfree(alphaBuf);
7290
2.49k
}
7291
7292
void Splash::drawImageArbitraryInterp(Guchar *scaledColor, Guchar *scaledAlpha,
7293
              SplashDrawImageRowData *dd,
7294
              SplashDrawImageRowFunc drawRowFunc,
7295
              SplashCoord *invMat,
7296
              int scaledWidth, int scaledHeight,
7297
              int xMin, int yMin, int xMax, int yMax,
7298
193
              int nComps, GBool srcAlpha) {
7299
193
  int tt = state->clip->getXMinI(state->strokeAdjust);
7300
193
  if (tt > xMin) {
7301
98
    xMin = tt;
7302
98
  }
7303
193
  tt = state->clip->getXMaxI(state->strokeAdjust) + 1;
7304
193
  if (tt < xMax) {
7305
193
    xMax = tt;
7306
193
  }
7307
193
  tt = state->clip->getYMinI(state->strokeAdjust);
7308
193
  if (tt > yMin) {
7309
71
    yMin = tt;
7310
71
  }
7311
193
  tt = state->clip->getYMaxI(state->strokeAdjust) + 1;
7312
193
  if (tt < yMax) {
7313
193
    yMax = tt;
7314
193
  }
7315
193
  if (xMax <= xMin || yMax <= yMin) {
7316
0
    return;
7317
0
  }
7318
7319
193
  Guchar *colorBuf = (Guchar *)gmallocn(xMax - xMin, nComps);
7320
193
  Guchar *alphaBuf = NULL;
7321
193
  if (srcAlpha) {
7322
0
    alphaBuf = (Guchar *)gmalloc(xMax - xMin);
7323
0
  }
7324
7325
386
  for (int y = yMin; y < yMax; ++y) {
7326
193
    int rowMin = xMax;
7327
193
    int rowMax = 0;
7328
386
    for (int x = xMin; x < xMax; ++x) {
7329
      // note: invMat includes a "+0.5" factor so that this is really
7330
      // a multiply by (x+0.5, y+0.5)
7331
193
      SplashCoord xs = (SplashCoord)x * invMat[0]
7332
193
                 + (SplashCoord)y * invMat[2] + invMat[4];
7333
193
      SplashCoord ys = (SplashCoord)x * invMat[1]
7334
193
                 + (SplashCoord)y * invMat[3] + invMat[5];
7335
193
      int x0 = splashFloor(xs - 0.5);
7336
193
      int x1 = x0 + 1;
7337
193
      int y0 = splashFloor(ys - 0.5);
7338
193
      int y1 = y0 + 1;
7339
193
      if (x1 >= 0 && x0 < scaledWidth && y1 >= 0 && y0 < scaledHeight) {
7340
193
  SplashCoord sx0 = (SplashCoord)x1 + 0.5 - xs;
7341
193
  SplashCoord sx1 = (SplashCoord)1 - sx0;
7342
193
  SplashCoord sy0 = (SplashCoord)y1 + 0.5 - ys;
7343
193
  SplashCoord sy1 = (SplashCoord)1 - sy0;
7344
193
  if (x0 < 0) {
7345
0
    x0 = 0;
7346
0
  }
7347
193
  if (x1 >= scaledWidth) {
7348
193
    x1 = scaledWidth - 1;
7349
193
  }
7350
193
  if (y0 < 0) {
7351
21
    y0 = 0;
7352
21
  }
7353
193
  if (y1 >= scaledHeight) {
7354
172
    y1 = scaledHeight - 1;
7355
172
  }
7356
193
  Guchar *p00 = scaledColor +
7357
193
                  (y0 * (SplashBitmapRowSize)scaledWidth + x0) * nComps;
7358
193
  Guchar *p10 = scaledColor +
7359
193
                  (y0 * (SplashBitmapRowSize)scaledWidth + x1) * nComps;
7360
193
  Guchar *p01 = scaledColor +
7361
193
                  (y1 * (SplashBitmapRowSize)scaledWidth + x0) * nComps;
7362
193
  Guchar *p11 = scaledColor +
7363
193
                  (y1 * (SplashBitmapRowSize)scaledWidth + x1) * nComps;
7364
193
  Guchar *q = colorBuf + (x - xMin) * nComps;
7365
772
  for (int i = 0; i < nComps; ++i) {
7366
579
    *q++ = (Guchar)(int)(sx0 * (sy0 * (int)*p00++ + sy1 * (int)*p01++) +
7367
579
             sx1 * (sy0 * (int)*p10++ + sy1 * (int)*p11++));
7368
579
  }
7369
193
  if (srcAlpha) {
7370
0
    p00 = scaledAlpha + (y0 * (SplashBitmapRowSize)scaledWidth + x0);
7371
0
    p10 = scaledAlpha + (y0 * (SplashBitmapRowSize)scaledWidth + x1);
7372
0
    p01 = scaledAlpha + (y1 * (SplashBitmapRowSize)scaledWidth + x0);
7373
0
    p11 = scaledAlpha + (y1 * (SplashBitmapRowSize)scaledWidth + x1);
7374
0
    q = alphaBuf + (x - xMin);
7375
0
    *q = (Guchar)(int)(sx0 * (sy0 * (int)*p00 + sy1 * (int)*p01) +
7376
0
           sx1 * (sy0 * (int)*p10 + sy1 * (int)*p11));
7377
0
  }
7378
193
  if (x < rowMin) {
7379
193
    rowMin = x;
7380
193
  }
7381
193
  rowMax = x + 1;
7382
193
      }
7383
193
    }
7384
193
    if (rowMin < rowMax) {
7385
193
      (this->*drawRowFunc)(dd,
7386
193
         colorBuf + (rowMin - xMin) * nComps,
7387
193
         alphaBuf + (rowMin - xMin),
7388
193
         rowMin, y, rowMax - rowMin);
7389
193
    }
7390
193
  }
7391
7392
193
  gfree(colorBuf);
7393
193
  gfree(alphaBuf);
7394
193
}
7395
7396
void Splash::mirrorImageRow(Guchar *colorIn, Guchar *alphaIn,
7397
          Guchar *colorOut, Guchar *alphaOut,
7398
0
          int width, int nComps, GBool srcAlpha) {
7399
0
  Guchar *p, *q;
7400
7401
0
  p = colorIn;
7402
0
  q = colorOut + (width - 1) * nComps;
7403
0
  for (int i = 0; i < width; ++i) {
7404
0
    for (int j = 0; j < nComps; ++j) {
7405
0
      q[j] = p[j];
7406
0
    }
7407
0
    p += nComps;
7408
0
    q -= nComps;
7409
0
  }
7410
7411
0
  if (srcAlpha) {
7412
0
    p = alphaIn;
7413
0
    q = alphaOut + (width - 1);
7414
0
    for (int i = 0; i < width; ++i) {
7415
0
      *q = *p;
7416
0
      ++p;
7417
0
      --q;
7418
0
    }
7419
0
  }
7420
0
}
7421
7422
void Splash::drawImageRowNoClipNoAlpha(SplashDrawImageRowData *data,
7423
               Guchar *colorData, Guchar *alphaData,
7424
0
               int x, int y, int width) {
7425
0
  (this->*data->pipe.run)(&data->pipe, x, x + width - 1, y,
7426
0
        NULL, NULL, colorData);
7427
0
}
7428
7429
void Splash::drawImageRowNoClipAlpha(SplashDrawImageRowData *data,
7430
             Guchar *colorData, Guchar *alphaData,
7431
0
             int x, int y, int width) {
7432
0
  (this->*data->pipe.run)(&data->pipe, x, x + width - 1, y,
7433
0
        NULL, alphaData, colorData);
7434
0
}
7435
7436
void Splash::drawImageRowClipNoAlphaNoAA(SplashDrawImageRowData *data,
7437
           Guchar *colorData,
7438
           Guchar *alphaData,
7439
0
           int x, int y, int width) {
7440
0
  if (y < 0 || y >= bitmap->height) {
7441
0
    return;
7442
0
  }
7443
0
  if (x < 0) {
7444
0
    colorData -= x * data->nComps;
7445
0
    width += x;
7446
0
    x = 0;
7447
0
  }
7448
0
  if (x + width > bitmap->width) {
7449
0
    width = bitmap->width - x;
7450
0
  }
7451
0
  if (width <= 0) {
7452
0
    return;
7453
0
  }
7454
0
  memset(scanBuf + x, 0xff, width);
7455
0
  state->clip->clipSpanBinary(scanBuf, y, x, x + width - 1,
7456
0
            state->strokeAdjust);
7457
0
  (this->*data->pipe.run)(&data->pipe, x, x + width - 1, y,
7458
0
        NULL, scanBuf + x, colorData);
7459
0
}
7460
7461
void Splash::drawImageRowClipNoAlphaAA(SplashDrawImageRowData *data,
7462
               Guchar *colorData,
7463
               Guchar *alphaData,
7464
1.99k
               int x, int y, int width) {
7465
1.99k
  if (y < 0 || y >= bitmap->height) {
7466
0
    return;
7467
0
  }
7468
1.99k
  if (x < 0) {
7469
0
    colorData -= x * data->nComps;
7470
0
    width += x;
7471
0
    x = 0;
7472
0
  }
7473
1.99k
  if (x + width > bitmap->width) {
7474
0
    width = bitmap->width - x;
7475
0
  }
7476
1.99k
  if (width <= 0) {
7477
0
    return;
7478
0
  }
7479
1.99k
  memset(scanBuf + x, 0xff, width);
7480
1.99k
  state->clip->clipSpan(scanBuf, y, x, x + width - 1, state->strokeAdjust);
7481
1.99k
  (this->*data->pipe.run)(&data->pipe, x, x + width - 1, y,
7482
1.99k
        NULL, scanBuf + x, colorData);
7483
1.99k
}
7484
7485
void Splash::drawImageRowClipAlphaNoAA(SplashDrawImageRowData *data,
7486
               Guchar *colorData,
7487
               Guchar *alphaData,
7488
0
               int x, int y, int width) {
7489
0
  if (y < 0 || y >= bitmap->height) {
7490
0
    return;
7491
0
  }
7492
0
  if (x < 0) {
7493
0
    colorData -= x * data->nComps;
7494
0
    alphaData -= x;
7495
0
    width += x;
7496
0
    x = 0;
7497
0
  }
7498
0
  if (x + width > bitmap->width) {
7499
0
    width = bitmap->width - x;
7500
0
  }
7501
0
  if (width <= 0) {
7502
0
    return;
7503
0
  }
7504
0
  memcpy(scanBuf + x, alphaData, width);
7505
0
  state->clip->clipSpanBinary(scanBuf, y, x, x + width - 1,
7506
0
            state->strokeAdjust);
7507
0
  (this->*data->pipe.run)(&data->pipe, x, x + width - 1, y,
7508
0
        NULL, scanBuf + x, colorData);
7509
0
}
7510
7511
void Splash::drawImageRowClipAlphaAA(SplashDrawImageRowData *data,
7512
             Guchar *colorData,
7513
             Guchar *alphaData,
7514
40
             int x, int y, int width) {
7515
40
  if (y < 0 || y >= bitmap->height) {
7516
0
    return;
7517
0
  }
7518
40
  if (x < 0) {
7519
0
    colorData -= x * data->nComps;
7520
0
    alphaData -= x;
7521
0
    width += x;
7522
0
    x = 0;
7523
0
  }
7524
40
  if (x + width > bitmap->width) {
7525
0
    width = bitmap->width - x;
7526
0
  }
7527
40
  if (width <= 0) {
7528
0
    return;
7529
0
  }
7530
40
  memcpy(scanBuf + x, alphaData, width);
7531
40
  state->clip->clipSpan(scanBuf, y, x, x + width - 1, state->strokeAdjust);
7532
40
  (this->*data->pipe.run)(&data->pipe, x, x + width - 1, y,
7533
40
        NULL, scanBuf + x, colorData);
7534
40
}
7535
7536
SplashError Splash::composite(SplashBitmap *src, SplashAlphaBitmap *srcShape,
7537
            int xSrc, int ySrc, int xDest, int yDest,
7538
3.97k
            int w, int h, GBool noClip, GBool nonIsolated) {
7539
3.97k
  SplashPipe pipe;
7540
3.97k
  Guchar *mono1Ptr, *lineBuf, *linePtr;
7541
3.97k
  Guchar mono1Mask, b;
7542
3.97k
  int x0, x1, x, y0, y1, y, t;
7543
7544
3.97k
  if (!(src->mode == bitmap->mode ||
7545
0
  (src->mode == splashModeMono8 && bitmap->mode == splashModeMono1) ||
7546
0
  (src->mode == splashModeRGB8 && bitmap->mode == splashModeBGR8))) {
7547
0
    return splashErrModeMismatch;
7548
0
  }
7549
7550
3.97k
  GBool usesShape = srcShape && shapeBitmap && src->alpha;
7551
3.97k
  GBool usesAlpha = !noClip || src->alpha;
7552
3.97k
  pipeInit(&pipe, NULL,
7553
3.97k
     (Guchar)splashRound(state->fillAlpha * 255),
7554
3.97k
     usesShape, usesAlpha, nonIsolated);
7555
3.97k
  if (src->mode == splashModeMono1) {
7556
    // in mono1 mode, pipeRun expects the source to be in mono8
7557
    // format, so we need to extract the source color values into
7558
    // scanBuf, expanding them from mono1 to mono8
7559
0
    if (noClip) {
7560
0
      if (usesShape) {
7561
0
  for (y = 0; y < h; ++y) {
7562
0
    mono1Ptr = src->data + (ySrc + y) * src->rowSize + (xSrc >> 3);
7563
0
    mono1Mask = (Guchar)(0x80 >> (xSrc & 7));
7564
0
    for (x = 0; x < w; ++x) {
7565
0
      scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00;
7566
0
      mono1Ptr += mono1Mask & 1;
7567
0
      mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1));
7568
0
    }
7569
0
    (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
7570
0
          srcShape->alpha +
7571
0
            (ySrc + y) * srcShape->alphaRowSize + xSrc,
7572
0
          src->alpha +
7573
0
            (ySrc + y) * src->alphaRowSize + xSrc,
7574
0
          scanBuf);
7575
0
  }
7576
0
      } else  if (usesAlpha) {
7577
0
  for (y = 0; y < h; ++y) {
7578
0
    mono1Ptr = src->data + (ySrc + y) * src->rowSize + (xSrc >> 3);
7579
0
    mono1Mask = (Guchar)(0x80 >> (xSrc & 7));
7580
0
    for (x = 0; x < w; ++x) {
7581
0
      scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00;
7582
0
      mono1Ptr += mono1Mask & 1;
7583
0
      mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1));
7584
0
    }
7585
    // this uses shape instead of alpha, which isn't technically
7586
    // correct, but works out the same
7587
0
    (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
7588
0
          NULL,
7589
0
          src->alpha +
7590
0
            (ySrc + y) * src->alphaRowSize + xSrc,
7591
0
          scanBuf);
7592
0
  }
7593
0
      } else {
7594
0
  for (y = 0; y < h; ++y) {
7595
0
    mono1Ptr = src->data + (ySrc + y) * src->rowSize + (xSrc >> 3);
7596
0
    mono1Mask = (Guchar)(0x80 >> (xSrc & 7));
7597
0
    for (x = 0; x < w; ++x) {
7598
0
      scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00;
7599
0
      mono1Ptr += mono1Mask & 1;
7600
0
      mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1));
7601
0
    }
7602
0
    (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
7603
0
          NULL, NULL, scanBuf);
7604
0
  }
7605
0
      }
7606
0
    } else {
7607
0
      x0 = xDest;
7608
0
      if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) {
7609
0
  x0 = t;
7610
0
      }
7611
0
      x1 = xDest + w;
7612
0
      if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) {
7613
0
  x1 = t;
7614
0
      }
7615
0
      y0 = yDest;
7616
0
      if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) {
7617
0
  y0 = t;
7618
0
      }
7619
0
      y1 = yDest + h;
7620
0
      if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) {
7621
0
  y1 = t;
7622
0
      }
7623
0
      if (x0 < x1 && y0 < y1) {
7624
0
  if (usesShape) {
7625
0
    for (y = y0; y < y1; ++y) {
7626
0
      mono1Ptr = src->data
7627
0
                 + (ySrc + y - yDest) * src->rowSize
7628
0
                 + ((xSrc + x0 - xDest) >> 3);
7629
0
      mono1Mask = (Guchar)(0x80 >> ((xSrc + x0 - xDest) & 7));
7630
0
      for (x = x0; x < x1; ++x) {
7631
0
        scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00;
7632
0
        mono1Ptr += mono1Mask & 1;
7633
0
        mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1));
7634
0
      }
7635
0
      memcpy(scanBuf2 + x0,
7636
0
       srcShape->alpha +
7637
0
         (ySrc + y - yDest) * srcShape->alphaRowSize + 
7638
0
         (xSrc + x0 - xDest),
7639
0
       x1 - x0);
7640
0
      if (!state->clip->clipSpanBinary(scanBuf2, y, x0, x1 - 1,
7641
0
               state->strokeAdjust)) {
7642
0
        continue;
7643
0
      }
7644
0
      (this->*pipe.run)(&pipe, x0, x1 - 1, y,
7645
0
            scanBuf2 + x0,
7646
0
            srcShape->alpha +
7647
0
              (ySrc + y - yDest) * srcShape->alphaRowSize + 
7648
0
              (xSrc + x0 - xDest),
7649
0
            scanBuf + x0);
7650
0
    }
7651
0
  } else if (usesAlpha) {
7652
0
    for (y = y0; y < y1; ++y) {
7653
0
      mono1Ptr = src->data
7654
0
                 + (ySrc + y - yDest) * src->rowSize
7655
0
                 + ((xSrc + x0 - xDest) >> 3);
7656
0
      mono1Mask = (Guchar)(0x80 >> ((xSrc + x0 - xDest) & 7));
7657
0
      for (x = x0; x < x1; ++x) {
7658
0
        scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00;
7659
0
        mono1Ptr += mono1Mask & 1;
7660
0
        mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1));
7661
0
      }
7662
0
      memcpy(scanBuf2 + x0,
7663
0
       src->alpha + (ySrc + y - yDest) * src->alphaRowSize + 
7664
0
         (xSrc + x0 - xDest),
7665
0
       x1 - x0);
7666
0
      if (!state->clip->clipSpanBinary(scanBuf2, y, x0, x1 - 1,
7667
0
               state->strokeAdjust)) {
7668
0
        continue;
7669
0
      }
7670
0
      (this->*pipe.run)(&pipe, x0, x1 - 1, y,
7671
0
            NULL, scanBuf2 + x0, scanBuf + x0);
7672
0
    }
7673
0
  } else {
7674
0
    for (y = y0; y < y1; ++y) {
7675
0
      mono1Ptr = src->data
7676
0
                 + (ySrc + y - yDest) * src->rowSize
7677
0
                 + ((xSrc + x0 - xDest) >> 3);
7678
0
      mono1Mask = (Guchar)(0x80 >> ((xSrc + x0 - xDest) & 7));
7679
0
      for (x = x0; x < x1; ++x) {
7680
0
        scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00;
7681
0
        mono1Ptr += mono1Mask & 1;
7682
0
        mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1));
7683
0
      }
7684
0
      memset(scanBuf2 + x0, 0xff, x1 - x0);
7685
0
      if (!state->clip->clipSpanBinary(scanBuf2, y, x0, x1 - 1,
7686
0
               state->strokeAdjust)) {
7687
0
        continue;
7688
0
      }
7689
0
      (this->*pipe.run)(&pipe, x0, x1 - 1, y,
7690
0
            NULL, scanBuf2 + x0, scanBuf + x0);
7691
0
    }
7692
0
  }
7693
0
      }
7694
0
    }
7695
7696
3.97k
  } else if (src->mode == splashModeBGR8) {
7697
    // in BGR8 mode, pipeRun expects the source to be in RGB8 format,
7698
    // so we need to swap bytes
7699
0
    lineBuf = (Guchar *)gmallocn(w, 3);
7700
0
    if (noClip) {
7701
0
      if (usesShape) {
7702
0
  for (y = 0; y < h; ++y) {
7703
0
    memcpy(lineBuf,
7704
0
     src->data + (ySrc + y) * src->rowSize + xSrc * 3,
7705
0
     w * 3);
7706
0
    for (x = 0, linePtr = lineBuf; x < w; ++x, linePtr += 3) {
7707
0
      b = linePtr[0];
7708
0
      linePtr[0] = linePtr[2];
7709
0
      linePtr[2] = b;
7710
0
    }
7711
0
    (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
7712
0
          srcShape->alpha +
7713
0
            (ySrc + y) * srcShape->alphaRowSize + xSrc,
7714
0
          src->alpha +
7715
0
            (ySrc + y) * src->alphaRowSize + xSrc,
7716
0
          lineBuf);
7717
0
  }
7718
0
      } else if (usesAlpha) {
7719
0
  for (y = 0; y < h; ++y) {
7720
0
    memcpy(lineBuf,
7721
0
     src->data + (ySrc + y) * src->rowSize + xSrc * 3,
7722
0
     w * 3);
7723
0
    for (x = 0, linePtr = lineBuf; x < w; ++x, linePtr += 3) {
7724
0
      b = linePtr[0];
7725
0
      linePtr[0] = linePtr[2];
7726
0
      linePtr[2] = b;
7727
0
    }
7728
0
    (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
7729
0
          NULL,
7730
0
          src->alpha +
7731
0
            (ySrc + y) * src->alphaRowSize + xSrc,
7732
0
          lineBuf);
7733
0
  }
7734
0
      } else {
7735
0
  for (y = 0; y < h; ++y) {
7736
0
    memcpy(lineBuf,
7737
0
     src->data + (ySrc + y) * src->rowSize + xSrc * 3,
7738
0
     w * 3);
7739
0
    for (x = 0, linePtr = lineBuf; x < w; ++x, linePtr += 3) {
7740
0
      b = linePtr[0];
7741
0
      linePtr[0] = linePtr[2];
7742
0
      linePtr[2] = b;
7743
0
    }
7744
0
    (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
7745
0
          NULL, NULL, lineBuf);
7746
0
  }
7747
0
      }
7748
0
    } else {
7749
0
      x0 = xDest;
7750
0
      if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) {
7751
0
  x0 = t;
7752
0
      }
7753
0
      x1 = xDest + w;
7754
0
      if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) {
7755
0
  x1 = t;
7756
0
      }
7757
0
      y0 = yDest;
7758
0
      if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) {
7759
0
  y0 = t;
7760
0
      }
7761
0
      y1 = yDest + h;
7762
0
      if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) {
7763
0
  y1 = t;
7764
0
      }
7765
0
      if (x0 < x1 && y0 < y1) {
7766
0
  if (usesShape) {
7767
0
    for (y = y0; y < y1; ++y) {
7768
0
      memcpy(scanBuf + x0,
7769
0
       srcShape->alpha +
7770
0
         (ySrc + y - yDest) * srcShape->alphaRowSize + 
7771
0
         (xSrc + x0 - xDest),
7772
0
       x1 - x0);
7773
0
      state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust);
7774
0
      memcpy(lineBuf,
7775
0
       src->data +
7776
0
         (ySrc + y - yDest) * src->rowSize +
7777
0
         (xSrc + x0 - xDest) * 3,
7778
0
       (x1 - x0) * 3);
7779
0
      for (x = 0, linePtr = lineBuf; x < x1 - x0; ++x, linePtr += 3) {
7780
0
        b = linePtr[0];
7781
0
        linePtr[0] = linePtr[2];
7782
0
        linePtr[2] = b;
7783
0
      }
7784
      // this uses shape instead of alpha, which isn't technically
7785
      // correct, but works out the same
7786
0
      (this->*pipe.run)(&pipe, x0, x1 - 1, y,
7787
0
            scanBuf + x0,
7788
0
            src->alpha +
7789
0
              (ySrc + y - yDest) * src->alphaRowSize + 
7790
0
              (xSrc + x0 - xDest),
7791
0
            lineBuf);
7792
0
    }
7793
0
  } else if (usesAlpha) {
7794
0
    for (y = y0; y < y1; ++y) {
7795
0
      memcpy(scanBuf + x0,
7796
0
       src->alpha + (ySrc + y - yDest) * src->alphaRowSize + 
7797
0
         (xSrc + x0 - xDest),
7798
0
       x1 - x0);
7799
0
      state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust);
7800
0
      memcpy(lineBuf,
7801
0
       src->data +
7802
0
         (ySrc + y - yDest) * src->rowSize +
7803
0
         (xSrc + x0 - xDest) * 3,
7804
0
       (x1 - x0) * 3);
7805
0
      for (x = 0, linePtr = lineBuf; x < x1 - x0; ++x, linePtr += 3) {
7806
0
        b = linePtr[0];
7807
0
        linePtr[0] = linePtr[2];
7808
0
        linePtr[2] = b;
7809
0
      }
7810
      // this uses shape instead of alpha, which isn't technically
7811
      // correct, but works out the same
7812
0
      (this->*pipe.run)(&pipe, x0, x1 - 1, y,
7813
0
            NULL, scanBuf + x0, lineBuf);
7814
0
    }
7815
0
  } else {
7816
0
    for (y = y0; y < y1; ++y) {
7817
0
      memset(scanBuf + x0, 0xff, x1 - x0);
7818
0
      state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust);
7819
0
      memcpy(lineBuf,
7820
0
       src->data +
7821
0
         (ySrc + y - yDest) * src->rowSize +
7822
0
         (xSrc + x0 - xDest) * 3,
7823
0
       (x1 - x0) * 3);
7824
0
      for (x = 0, linePtr = lineBuf; x < x1 - x0; ++x, linePtr += 3) {
7825
0
        b = linePtr[0];
7826
0
        linePtr[0] = linePtr[2];
7827
0
        linePtr[2] = b;
7828
0
      }
7829
0
      (this->*pipe.run)(&pipe, x0, x1 - 1, yDest + y,
7830
0
            NULL,
7831
0
            scanBuf + x0,
7832
0
            src->data +
7833
0
              (ySrc + y - yDest) * src->rowSize +
7834
0
              (xSrc + x0 - xDest) * bitmapComps);
7835
0
    }
7836
0
  }
7837
0
      }
7838
0
    }
7839
0
    gfree(lineBuf);
7840
7841
3.97k
  } else { // src->mode not mono1 or BGR8
7842
3.97k
    if (noClip) {
7843
78
      if (usesShape) {
7844
0
  for (y = 0; y < h; ++y) {
7845
0
    (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
7846
0
          srcShape->alpha +
7847
0
            (ySrc + y) * srcShape->alphaRowSize + xSrc,
7848
0
          src->alpha +
7849
0
            (ySrc + y) * src->alphaRowSize + xSrc,
7850
0
          src->data + (ySrc + y) * src->rowSize +
7851
0
          xSrc * bitmapComps);
7852
0
  }
7853
78
      } else if (usesAlpha) {
7854
156
  for (y = 0; y < h; ++y) {
7855
78
    (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
7856
78
          NULL,
7857
78
          src->alpha +
7858
78
            (ySrc + y) * src->alphaRowSize + xSrc,
7859
78
          src->data + (ySrc + y) * src->rowSize +
7860
78
          xSrc * bitmapComps);
7861
78
  }
7862
78
      } else {
7863
0
  for (y = 0; y < h; ++y) {
7864
0
    (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
7865
0
          NULL, NULL,
7866
0
          src->data + (ySrc + y) * src->rowSize +
7867
0
            xSrc * bitmapComps);
7868
0
  }
7869
0
      }
7870
3.89k
    } else {
7871
3.89k
      x0 = xDest;
7872
3.89k
      if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) {
7873
988
  x0 = t;
7874
988
      }
7875
3.89k
      x1 = xDest + w;
7876
3.89k
      if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) {
7877
1.19k
  x1 = t;
7878
1.19k
      }
7879
3.89k
      y0 = yDest;
7880
3.89k
      if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) {
7881
1.06k
  y0 = t;
7882
1.06k
      }
7883
3.89k
      y1 = yDest + h;
7884
3.89k
      if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) {
7885
1.19k
  y1 = t;
7886
1.19k
      }
7887
3.89k
      if (x0 < x1 && y0 < y1) {
7888
2.07k
  if (usesShape) {
7889
0
    for (y = y0; y < y1; ++y) {
7890
0
      memcpy(scanBuf + x0,
7891
0
       srcShape->alpha +
7892
0
         (ySrc + y - yDest) * srcShape->alphaRowSize + 
7893
0
         (xSrc + x0 - xDest),
7894
0
       x1 - x0);
7895
0
      state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust);
7896
0
      (this->*pipe.run)(&pipe, x0, x1 - 1, y,
7897
0
            scanBuf + x0,
7898
0
            src->alpha +
7899
0
              (ySrc + y - yDest) * src->alphaRowSize + 
7900
0
              (xSrc + x0 - xDest),
7901
0
            src->data +
7902
0
              (ySrc + y - yDest) * src->rowSize +
7903
0
              (xSrc + x0 - xDest) * bitmapComps);
7904
0
    }
7905
2.07k
  } else if (usesAlpha) {
7906
4.14k
    for (y = y0; y < y1; ++y) {
7907
2.07k
      memcpy(scanBuf + x0,
7908
2.07k
       src->alpha + (ySrc + y - yDest) * src->alphaRowSize + 
7909
2.07k
         (xSrc + x0 - xDest),
7910
2.07k
       x1 - x0);
7911
2.07k
      state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust);
7912
2.07k
      (this->*pipe.run)(&pipe, x0, x1 - 1, y,
7913
2.07k
            NULL,
7914
2.07k
            scanBuf + x0,
7915
2.07k
            src->data +
7916
2.07k
              (ySrc + y - yDest) * src->rowSize +
7917
2.07k
              (xSrc + x0 - xDest) * bitmapComps);
7918
2.07k
    }
7919
2.07k
  } else {
7920
0
    for (y = y0; y < y1; ++y) {
7921
0
      memset(scanBuf + x0, 0xff, x1 - x0);
7922
0
      state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust);
7923
0
      (this->*pipe.run)(&pipe, x0, x1 - 1, yDest + y,
7924
0
            NULL,
7925
0
            scanBuf + x0,
7926
0
            src->data +
7927
0
              (ySrc + y - yDest) * src->rowSize +
7928
0
              (xSrc + x0 - xDest) * bitmapComps);
7929
0
    }
7930
0
  }
7931
2.07k
      }
7932
3.89k
    }
7933
3.97k
  }
7934
7935
3.97k
  return splashOk;
7936
3.97k
}
7937
7938
SplashError Splash::compositeWithOverprint(SplashBitmap *src,
7939
             SplashAlphaBitmap *srcShape,
7940
             Guint *srcOverprintMaskBitmap,
7941
             int xSrc, int ySrc,
7942
             int xDest, int yDest, int w, int h,
7943
0
             GBool noClip, GBool nonIsolated) {
7944
0
  SplashPipe pipe;
7945
0
  int x0, x1, y0, y1, y, t;
7946
7947
0
  if (!(src->mode == bitmap->mode ||
7948
0
  (src->mode == splashModeMono8 && bitmap->mode == splashModeMono1) ||
7949
0
  (src->mode == splashModeRGB8 && bitmap->mode == splashModeBGR8))) {
7950
0
    return splashErrModeMismatch;
7951
0
  }
7952
7953
0
  GBool usesShape = srcShape && shapeBitmap && src->alpha;
7954
0
  GBool usesAlpha = !noClip || src->alpha;
7955
0
  pipeInit(&pipe, NULL,
7956
0
     (Guchar)splashRound(state->fillAlpha * 255),
7957
0
     usesShape, usesAlpha, nonIsolated, gTrue);
7958
7959
0
  if (noClip) {
7960
0
    if (usesShape) {
7961
0
      for (y = 0; y < h; ++y) {
7962
0
  pipe.srcOverprintMaskPtr = srcOverprintMaskBitmap + y * w + xSrc;
7963
0
  (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
7964
0
        srcShape->alpha +
7965
0
          (ySrc + y) * srcShape->alphaRowSize + xSrc,
7966
0
        src->alpha +
7967
0
          (ySrc + y) * src->alphaRowSize + xSrc,
7968
0
        src->data + (ySrc + y) * src->rowSize +
7969
0
          xSrc * bitmapComps);
7970
0
      }
7971
0
    } else if (usesAlpha) {
7972
0
      for (y = 0; y < h; ++y) {
7973
0
  pipe.srcOverprintMaskPtr = srcOverprintMaskBitmap + y * w + xSrc;
7974
0
  (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
7975
0
        NULL,
7976
0
        src->alpha +
7977
0
          (ySrc + y) * src->alphaRowSize + xSrc,
7978
0
        src->data + (ySrc + y) * src->rowSize +
7979
0
          xSrc * bitmapComps);
7980
0
      }
7981
0
    } else {
7982
0
      for (y = 0; y < h; ++y) {
7983
0
  pipe.srcOverprintMaskPtr = srcOverprintMaskBitmap + y * w + xSrc;
7984
0
  (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
7985
0
        NULL, NULL,
7986
0
        src->data + (ySrc + y) * src->rowSize +
7987
0
          xSrc * bitmapComps);
7988
0
      }
7989
0
    }
7990
0
  } else {
7991
0
    x0 = xDest;
7992
0
    if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) {
7993
0
      x0 = t;
7994
0
    }
7995
0
    x1 = xDest + w;
7996
0
    if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) {
7997
0
      x1 = t;
7998
0
    }
7999
0
    y0 = yDest;
8000
0
    if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) {
8001
0
      y0 = t;
8002
0
    }
8003
0
    y1 = yDest + h;
8004
0
    if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) {
8005
0
      y1 = t;
8006
0
    }
8007
0
    if (x0 < x1 && y0 < y1) {
8008
0
      if (usesShape) {
8009
0
  for (y = y0; y < y1; ++y) {
8010
0
    memcpy(scanBuf + x0,
8011
0
     srcShape->alpha +
8012
0
       (ySrc + y - yDest) * srcShape->alphaRowSize + 
8013
0
       (xSrc + x0 - xDest),
8014
0
     x1 - x0);
8015
0
    state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust);
8016
0
    pipe.srcOverprintMaskPtr = srcOverprintMaskBitmap
8017
0
                               + (ySrc + y - yDest) * w
8018
0
                               + (xSrc + x0 - xDest);
8019
0
    (this->*pipe.run)(&pipe, x0, x1 - 1, y,
8020
0
          scanBuf + x0,
8021
0
          src->alpha +
8022
0
            (ySrc + y - yDest) * src->alphaRowSize + 
8023
0
            (xSrc + x0 - xDest),
8024
0
          src->data +
8025
0
            (ySrc + y - yDest) * src->rowSize +
8026
0
            (xSrc + x0 - xDest) * bitmapComps);
8027
0
  }
8028
0
      } else if (usesAlpha) {
8029
0
  for (y = y0; y < y1; ++y) {
8030
0
    memcpy(scanBuf + x0,
8031
0
     src->alpha + (ySrc + y - yDest) * src->alphaRowSize + 
8032
0
       (xSrc + x0 - xDest),
8033
0
     x1 - x0);
8034
0
    state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust);
8035
0
    pipe.srcOverprintMaskPtr = srcOverprintMaskBitmap
8036
0
                               + (ySrc + y - yDest) * w
8037
0
                               + (xSrc + x0 - xDest);
8038
0
    (this->*pipe.run)(&pipe, x0, x1 - 1, y,
8039
0
          NULL,
8040
0
          scanBuf + x0,
8041
0
          src->data +
8042
0
            (ySrc + y - yDest) * src->rowSize +
8043
0
            (xSrc + x0 - xDest) * bitmapComps);
8044
0
  }
8045
0
      } else {
8046
0
  for (y = y0; y < y1; ++y) {
8047
0
    memset(scanBuf + x0, 0xff, x1 - x0);
8048
0
    state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust);
8049
0
    pipe.srcOverprintMaskPtr = srcOverprintMaskBitmap
8050
0
                               + (ySrc + y - yDest) * w
8051
0
                               + (xSrc + x0 - xDest);
8052
0
    (this->*pipe.run)(&pipe, x0, x1 - 1, y,
8053
0
          NULL,
8054
0
          scanBuf + x0,
8055
0
          src->data +
8056
0
            (ySrc + y - yDest) * src->rowSize +
8057
0
            (xSrc + x0 - xDest) * bitmapComps);
8058
0
  }
8059
0
      }
8060
0
    }
8061
0
  }
8062
8063
0
  return splashOk;
8064
0
}
8065
8066
1.11k
void Splash::compositeBackground(SplashColorPtr color) {
8067
1.11k
  SplashColorPtr p;
8068
1.11k
  Guchar *q;
8069
1.11k
  Guchar alpha, alpha1, c, color0, color1, color2, mask;
8070
1.11k
#if SPLASH_CMYK
8071
1.11k
  Guchar color3;
8072
1.11k
#endif
8073
1.11k
  int x, y;
8074
8075
1.11k
  switch (bitmap->mode) {
8076
0
  case splashModeMono1:
8077
0
    color0 = color[0];
8078
0
    for (y = 0; y < bitmap->height; ++y) {
8079
0
      p = &bitmap->data[y * bitmap->rowSize];
8080
0
      q = &bitmap->alpha[y * bitmap->alphaRowSize];
8081
0
      mask = 0x80;
8082
0
      for (x = 0; x < bitmap->width; ++x) {
8083
0
  alpha = *q++;
8084
0
  if (alpha == 0) {
8085
0
    if (color0 & 0x80) {
8086
0
      *p |= mask;
8087
0
    } else {
8088
0
      *p &= (Guchar)~mask;
8089
0
    }
8090
0
  } else if (alpha != 255) {
8091
0
    alpha1 = (Guchar)(255 - alpha);
8092
0
    c = (*p & mask) ? 0xff : 0x00;
8093
0
    c = div255(alpha1 * color0 + alpha * c);
8094
0
    if (c & 0x80) {
8095
0
      *p |= mask;
8096
0
    } else {
8097
0
      *p &= (Guchar)~mask;
8098
0
    }
8099
0
  }
8100
0
  if (!(mask = (Guchar)(mask >> 1))) {
8101
0
    mask = 0x80;
8102
0
    ++p;
8103
0
  }
8104
0
      }
8105
0
    }
8106
0
    break;
8107
865
  case splashModeMono8:
8108
865
    color0 = color[0];
8109
1.73k
    for (y = 0; y < bitmap->height; ++y) {
8110
865
      p = &bitmap->data[y * bitmap->rowSize];
8111
865
      q = &bitmap->alpha[y * bitmap->alphaRowSize];
8112
1.73k
      for (x = 0; x < bitmap->width; ++x) {
8113
865
  alpha = *q++;
8114
865
  if (alpha == 0) {
8115
546
    p[0] = color0;
8116
546
  } else if (alpha != 255) {
8117
124
    alpha1 = (Guchar)(255 - alpha);
8118
124
    p[0] = div255(alpha1 * color0 + alpha * p[0]);
8119
124
  }
8120
865
  ++p;
8121
865
      }
8122
865
    }
8123
865
    break;
8124
46
  case splashModeRGB8:
8125
46
  case splashModeBGR8:
8126
46
    color0 = color[0];
8127
46
    color1 = color[1];
8128
46
    color2 = color[2];
8129
92
    for (y = 0; y < bitmap->height; ++y) {
8130
46
      p = &bitmap->data[y * bitmap->rowSize];
8131
46
      q = &bitmap->alpha[y * bitmap->alphaRowSize];
8132
92
      for (x = 0; x < bitmap->width; ++x) {
8133
46
  alpha = *q++;
8134
46
  if (alpha == 0) {
8135
46
    p[0] = color0;
8136
46
    p[1] = color1;
8137
46
    p[2] = color2;
8138
46
  } else if (alpha != 255) {
8139
0
    alpha1 = (Guchar)(255 - alpha);
8140
0
    p[0] = div255(alpha1 * color0 + alpha * p[0]);
8141
0
    p[1] = div255(alpha1 * color1 + alpha * p[1]);
8142
0
    p[2] = div255(alpha1 * color2 + alpha * p[2]);
8143
0
  }
8144
46
  p += 3;
8145
46
      }
8146
46
    }
8147
46
    break;
8148
0
#if SPLASH_CMYK
8149
208
  case splashModeCMYK8:
8150
208
    color0 = color[0];
8151
208
    color1 = color[1];
8152
208
    color2 = color[2];
8153
208
    color3 = color[3];
8154
416
    for (y = 0; y < bitmap->height; ++y) {
8155
208
      p = &bitmap->data[y * bitmap->rowSize];
8156
208
      q = &bitmap->alpha[y * bitmap->alphaRowSize];
8157
416
      for (x = 0; x < bitmap->width; ++x) {
8158
208
  alpha = *q++;
8159
208
  if (alpha == 0) {
8160
165
    p[0] = color0;
8161
165
    p[1] = color1;
8162
165
    p[2] = color2;
8163
165
    p[3] = color3;
8164
165
  } else if (alpha != 255) {
8165
0
    alpha1 = (Guchar)(255 - alpha);
8166
0
    p[0] = div255(alpha1 * color0 + alpha * p[0]);
8167
0
    p[1] = div255(alpha1 * color1 + alpha * p[1]);
8168
0
    p[2] = div255(alpha1 * color2 + alpha * p[2]);
8169
0
    p[3] = div255(alpha1 * color3 + alpha * p[3]);
8170
0
  }
8171
208
  p += 4;
8172
208
      }
8173
208
    }
8174
208
    break;
8175
1.11k
#endif
8176
1.11k
  }
8177
1.11k
  memset(bitmap->alpha, 255, bitmap->alphaRowSize * bitmap->height);
8178
1.11k
}
8179
8180
SplashPath *Splash::makeStrokePath(SplashPath *path, SplashCoord w,
8181
           int lineCap, int lineJoin,
8182
29.0k
           GBool flatten) {
8183
29.0k
  SplashPath *pathIn, *dashPath, *pathOut;
8184
29.0k
  SplashCoord d, dx, dy, wdx, wdy, dxNext, dyNext, wdxNext, wdyNext;
8185
29.0k
  SplashCoord crossprod, dotprod, miter, m;
8186
29.0k
  SplashCoord angle, angleNext, dAngle, xc, yc;
8187
29.0k
  SplashCoord dxJoin, dyJoin, dJoin, kappa;
8188
29.0k
  SplashCoord cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4;
8189
29.0k
  GBool first, last, closed;
8190
29.0k
  int subpathStart0, subpathStart1, seg, i0, i1, j0, j1, k0, k1;
8191
29.0k
  int left0, left1, left2, right0, right1, right2, join0, join1, join2;
8192
29.0k
  int leftFirst, rightFirst, firstPt;
8193
8194
29.0k
  pathOut = new SplashPath();
8195
8196
29.0k
  if (path->length == 0) {
8197
0
    return pathOut;
8198
0
  }
8199
8200
29.0k
  if (flatten) {
8201
210
    pathIn = flattenPath(path, state->matrix, state->flatness);
8202
210
    if (state->lineDashLength > 0) {
8203
0
      dashPath = makeDashedPath(pathIn);
8204
0
      delete pathIn;
8205
0
      pathIn = dashPath;
8206
0
      if (pathIn->length == 0) {
8207
0
  delete pathIn;
8208
0
  return pathOut;
8209
0
      }
8210
0
    }
8211
28.8k
  } else {
8212
28.8k
    pathIn = path;
8213
28.8k
  }
8214
8215
29.0k
  subpathStart0 = subpathStart1 = 0; // make gcc happy
8216
29.0k
  seg = 0; // make gcc happy
8217
29.0k
  closed = gFalse; // make gcc happy
8218
29.0k
  left0 = left1 = right0 = right1 = join0 = join1 = 0; // make gcc happy
8219
29.0k
  leftFirst = rightFirst = firstPt = 0; // make gcc happy
8220
8221
29.0k
  i0 = 0;
8222
29.0k
  for (i1 = i0;
8223
35.2k
       !(pathIn->flags[i1] & splashPathLast) &&
8224
32.1k
   i1 + 1 < pathIn->length &&
8225
32.1k
   pathIn->pts[i1+1].x == pathIn->pts[i1].x &&
8226
7.84k
   pathIn->pts[i1+1].y == pathIn->pts[i1].y;
8227
29.0k
       ++i1) ;
8228
8229
10.4M
  while (i1 < pathIn->length) {
8230
10.3M
    if ((first = pathIn->flags[i0] & splashPathFirst)) {
8231
2.47M
      subpathStart0 = i0;
8232
2.47M
      subpathStart1 = i1;
8233
2.47M
      seg = 0;
8234
2.47M
      closed = pathIn->flags[i0] & splashPathClosed;
8235
2.47M
    }
8236
10.3M
    j0 = i1 + 1;
8237
10.3M
    if (j0 < pathIn->length) {
8238
10.3M
      for (j1 = j0;
8239
11.4M
     !(pathIn->flags[j1] & splashPathLast) &&
8240
8.97M
       j1 + 1 < pathIn->length &&
8241
8.97M
       pathIn->pts[j1+1].x == pathIn->pts[j1].x &&
8242
1.25M
       pathIn->pts[j1+1].y == pathIn->pts[j1].y;
8243
10.3M
     ++j1) ;
8244
10.3M
    } else {
8245
29.0k
      j1 = j0;
8246
29.0k
    }
8247
10.3M
    if (pathIn->flags[i1] & splashPathLast) {
8248
2.47M
      if (first && lineCap == splashLineCapRound) {
8249
  // special case: zero-length subpath with round line caps -->
8250
  // draw a circle
8251
1.04k
  pathOut->moveTo(pathIn->pts[i0].x + (SplashCoord)0.5 * w,
8252
1.04k
      pathIn->pts[i0].y);
8253
1.04k
  pathOut->curveTo(pathIn->pts[i0].x + (SplashCoord)0.5 * w,
8254
1.04k
       pathIn->pts[i0].y + bezierCircle2 * w,
8255
1.04k
       pathIn->pts[i0].x + bezierCircle2 * w,
8256
1.04k
       pathIn->pts[i0].y + (SplashCoord)0.5 * w,
8257
1.04k
       pathIn->pts[i0].x,
8258
1.04k
       pathIn->pts[i0].y + (SplashCoord)0.5 * w);
8259
1.04k
  pathOut->curveTo(pathIn->pts[i0].x - bezierCircle2 * w,
8260
1.04k
       pathIn->pts[i0].y + (SplashCoord)0.5 * w,
8261
1.04k
       pathIn->pts[i0].x - (SplashCoord)0.5 * w,
8262
1.04k
       pathIn->pts[i0].y + bezierCircle2 * w,
8263
1.04k
       pathIn->pts[i0].x - (SplashCoord)0.5 * w,
8264
1.04k
       pathIn->pts[i0].y);
8265
1.04k
  pathOut->curveTo(pathIn->pts[i0].x - (SplashCoord)0.5 * w,
8266
1.04k
       pathIn->pts[i0].y - bezierCircle2 * w,
8267
1.04k
       pathIn->pts[i0].x - bezierCircle2 * w,
8268
1.04k
       pathIn->pts[i0].y - (SplashCoord)0.5 * w,
8269
1.04k
       pathIn->pts[i0].x,
8270
1.04k
       pathIn->pts[i0].y - (SplashCoord)0.5 * w);
8271
1.04k
  pathOut->curveTo(pathIn->pts[i0].x + bezierCircle2 * w,
8272
1.04k
       pathIn->pts[i0].y - (SplashCoord)0.5 * w,
8273
1.04k
       pathIn->pts[i0].x + (SplashCoord)0.5 * w,
8274
1.04k
       pathIn->pts[i0].y - bezierCircle2 * w,
8275
1.04k
       pathIn->pts[i0].x + (SplashCoord)0.5 * w,
8276
1.04k
       pathIn->pts[i0].y);
8277
1.04k
  pathOut->close();
8278
1.04k
      }
8279
2.47M
      i0 = j0;
8280
2.47M
      i1 = j1;
8281
2.47M
      continue;
8282
2.47M
    }
8283
7.89M
    last = pathIn->flags[j1] & splashPathLast;
8284
7.89M
    if (last) {
8285
1.40M
      k0 = subpathStart1 + 1;
8286
6.49M
    } else {
8287
6.49M
      k0 = j1 + 1;
8288
6.49M
    }
8289
7.89M
    for (k1 = k0;
8290
7.92M
   !(pathIn->flags[k1] & splashPathLast) &&
8291
6.52M
     k1 + 1 < pathIn->length &&
8292
6.52M
     pathIn->pts[k1+1].x == pathIn->pts[k1].x &&
8293
164k
     pathIn->pts[k1+1].y == pathIn->pts[k1].y;
8294
7.89M
   ++k1) ;
8295
8296
    // compute the deltas for segment (i1, j0)
8297
#if USE_FIXEDPOINT
8298
    // the 1/d value can be small, which introduces significant
8299
    // inaccuracies in fixed point mode
8300
    d = splashDist(pathIn->pts[i1].x, pathIn->pts[i1].y,
8301
       pathIn->pts[j0].x, pathIn->pts[j0].y);
8302
    dx = (pathIn->pts[j0].x - pathIn->pts[i1].x) / d;
8303
    dy = (pathIn->pts[j0].y - pathIn->pts[i1].y) / d;
8304
#else
8305
7.89M
    d = (SplashCoord)1 / splashDist(pathIn->pts[i1].x, pathIn->pts[i1].y,
8306
7.89M
            pathIn->pts[j0].x, pathIn->pts[j0].y);
8307
7.89M
    dx = d * (pathIn->pts[j0].x - pathIn->pts[i1].x);
8308
7.89M
    dy = d * (pathIn->pts[j0].y - pathIn->pts[i1].y);
8309
7.89M
#endif
8310
7.89M
    wdx = (SplashCoord)0.5 * w * dx;
8311
7.89M
    wdy = (SplashCoord)0.5 * w * dy;
8312
8313
    // draw the start cap
8314
7.89M
    if (i0 == subpathStart0) {
8315
1.40M
      firstPt = pathOut->length;
8316
1.40M
    }
8317
7.89M
    if (first && !closed) {
8318
1.38M
      switch (lineCap) {
8319
1.37M
      case splashLineCapButt:
8320
1.37M
  pathOut->moveTo(pathIn->pts[i0].x - wdy, pathIn->pts[i0].y + wdx);
8321
1.37M
  pathOut->lineTo(pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx);
8322
1.37M
  break;
8323
7.77k
      case splashLineCapRound:
8324
7.77k
  pathOut->moveTo(pathIn->pts[i0].x - wdy, pathIn->pts[i0].y + wdx);
8325
7.77k
  pathOut->curveTo(pathIn->pts[i0].x - wdy - bezierCircle * wdx,
8326
7.77k
       pathIn->pts[i0].y + wdx - bezierCircle * wdy,
8327
7.77k
       pathIn->pts[i0].x - wdx - bezierCircle * wdy,
8328
7.77k
       pathIn->pts[i0].y - wdy + bezierCircle * wdx,
8329
7.77k
       pathIn->pts[i0].x - wdx,
8330
7.77k
       pathIn->pts[i0].y - wdy);
8331
7.77k
  pathOut->curveTo(pathIn->pts[i0].x - wdx + bezierCircle * wdy,
8332
7.77k
       pathIn->pts[i0].y - wdy - bezierCircle * wdx,
8333
7.77k
       pathIn->pts[i0].x + wdy - bezierCircle * wdx,
8334
7.77k
       pathIn->pts[i0].y - wdx - bezierCircle * wdy,
8335
7.77k
       pathIn->pts[i0].x + wdy,
8336
7.77k
       pathIn->pts[i0].y - wdx);
8337
7.77k
  break;
8338
1.78k
      case splashLineCapProjecting:
8339
1.78k
  pathOut->moveTo(pathIn->pts[i0].x - wdx - wdy,
8340
1.78k
      pathIn->pts[i0].y + wdx - wdy);
8341
1.78k
  pathOut->lineTo(pathIn->pts[i0].x - wdx + wdy,
8342
1.78k
      pathIn->pts[i0].y - wdx - wdy);
8343
1.78k
  break;
8344
1.38M
      }
8345
6.51M
    } else {
8346
6.51M
      pathOut->moveTo(pathIn->pts[i0].x - wdy, pathIn->pts[i0].y + wdx);
8347
6.51M
      pathOut->lineTo(pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx);
8348
6.51M
    }
8349
8350
    // draw the left side of the segment rectangle and the end cap
8351
7.89M
    left2 = pathOut->length - 1;
8352
7.89M
    if (last && !closed) {
8353
1.38M
      switch (lineCap) {
8354
1.37M
      case splashLineCapButt:
8355
1.37M
  pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx);
8356
1.37M
  pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx);
8357
1.37M
  break;
8358
7.77k
      case splashLineCapRound:
8359
7.77k
  pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx);
8360
7.77k
  pathOut->curveTo(pathIn->pts[j0].x + wdy + bezierCircle * wdx,
8361
7.77k
       pathIn->pts[j0].y - wdx + bezierCircle * wdy,
8362
7.77k
       pathIn->pts[j0].x + wdx + bezierCircle * wdy,
8363
7.77k
       pathIn->pts[j0].y + wdy - bezierCircle * wdx,
8364
7.77k
       pathIn->pts[j0].x + wdx,
8365
7.77k
       pathIn->pts[j0].y + wdy);
8366
7.77k
  pathOut->curveTo(pathIn->pts[j0].x + wdx - bezierCircle * wdy,
8367
7.77k
       pathIn->pts[j0].y + wdy + bezierCircle * wdx,
8368
7.77k
       pathIn->pts[j0].x - wdy + bezierCircle * wdx,
8369
7.77k
       pathIn->pts[j0].y + wdx + bezierCircle * wdy,
8370
7.77k
       pathIn->pts[j0].x - wdy,
8371
7.77k
       pathIn->pts[j0].y + wdx);
8372
7.77k
  break;
8373
1.78k
      case splashLineCapProjecting:
8374
1.78k
  pathOut->lineTo(pathIn->pts[j0].x + wdy + wdx,
8375
1.78k
      pathIn->pts[j0].y - wdx + wdy);
8376
1.78k
  pathOut->lineTo(pathIn->pts[j0].x - wdy + wdx,
8377
1.78k
      pathIn->pts[j0].y + wdx + wdy);
8378
1.78k
  break;
8379
1.38M
      }
8380
6.51M
    } else {
8381
6.51M
      pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx);
8382
6.51M
      pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx);
8383
6.51M
    }
8384
8385
    // draw the right side of the segment rectangle
8386
    // (NB: if stroke adjustment is enabled, the closepath operation MUST
8387
    // add a segment because this segment is used for a hint)
8388
7.89M
    right2 = pathOut->length - 1;
8389
7.89M
    pathOut->close(state->strokeAdjust != splashStrokeAdjustOff);
8390
8391
    // draw the join
8392
7.89M
    join2 = pathOut->length;
8393
7.89M
    if (!last || closed) {
8394
8395
      // compute the deltas for segment (j1, k0)
8396
#if USE_FIXEDPOINT
8397
      // the 1/d value can be small, which introduces significant
8398
      // inaccuracies in fixed point mode
8399
      d = splashDist(pathIn->pts[j1].x, pathIn->pts[j1].y,
8400
         pathIn->pts[k0].x, pathIn->pts[k0].y);
8401
      dxNext = (pathIn->pts[k0].x - pathIn->pts[j1].x) / d;
8402
      dyNext = (pathIn->pts[k0].y - pathIn->pts[j1].y) / d;
8403
#else
8404
6.51M
      d = (SplashCoord)1 / splashDist(pathIn->pts[j1].x, pathIn->pts[j1].y,
8405
6.51M
              pathIn->pts[k0].x, pathIn->pts[k0].y);
8406
6.51M
      dxNext = d * (pathIn->pts[k0].x - pathIn->pts[j1].x);
8407
6.51M
      dyNext = d * (pathIn->pts[k0].y - pathIn->pts[j1].y);
8408
6.51M
#endif
8409
6.51M
      wdxNext = (SplashCoord)0.5 * w * dxNext;
8410
6.51M
      wdyNext = (SplashCoord)0.5 * w * dyNext;
8411
8412
      // compute the join parameters
8413
6.51M
      crossprod = dx * dyNext - dy * dxNext;
8414
6.51M
      dotprod = -(dx * dxNext + dy * dyNext);
8415
6.51M
      if (dotprod > 0.9999) {
8416
  // avoid a divide-by-zero -- set miter to something arbitrary
8417
  // such that sqrt(miter) will exceed miterLimit (and m is never
8418
  // used in that situation)
8419
  // (note: the comparison value (0.9999) has to be less than
8420
  // 1-epsilon, where epsilon is the smallest value
8421
  // representable in the fixed point format)
8422
8.45k
  miter = (state->miterLimit + 1) * (state->miterLimit + 1);
8423
8.45k
  m = 0;
8424
6.50M
      } else {
8425
6.50M
  miter = (SplashCoord)2 / ((SplashCoord)1 - dotprod);
8426
6.50M
  if (miter < 1) {
8427
    // this can happen because of floating point inaccuracies
8428
958
    miter = 1;
8429
958
  }
8430
6.50M
  m = splashSqrt(miter - 1);
8431
6.50M
      }
8432
8433
      // round join
8434
6.51M
      if (lineJoin == splashLineJoinRound) {
8435
  // join angle < 180
8436
1.30M
  if (crossprod < 0) {
8437
554k
    angle = atan2((double)dx, (double)-dy);
8438
554k
    angleNext = atan2((double)dxNext, (double)-dyNext);
8439
554k
    if (angle < angleNext) {
8440
1.04k
      angle += 2 * M_PI;
8441
1.04k
    }
8442
554k
    dAngle = (angle  - angleNext) / M_PI;
8443
554k
    if (dAngle < 0.501) {
8444
      // span angle is <= 90 degrees -> draw a single arc
8445
554k
      kappa = dAngle * bezierCircle * w;
8446
554k
      cx1 = pathIn->pts[j0].x - wdy + kappa * dx;
8447
554k
      cy1 = pathIn->pts[j0].y + wdx + kappa * dy;
8448
554k
      cx2 = pathIn->pts[j0].x - wdyNext - kappa * dxNext;
8449
554k
      cy2 = pathIn->pts[j0].y + wdxNext - kappa * dyNext;
8450
554k
      pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y);
8451
554k
      pathOut->lineTo(pathIn->pts[j0].x - wdyNext,
8452
554k
          pathIn->pts[j0].y + wdxNext);
8453
554k
      pathOut->curveTo(cx2, cy2, cx1, cy1,
8454
554k
           pathIn->pts[j0].x - wdy,
8455
554k
           pathIn->pts[j0].y + wdx);
8456
554k
    } else {
8457
      // span angle is > 90 degrees -> split into two arcs
8458
632
      dJoin = splashDist(-wdy, wdx, -wdyNext, wdxNext);
8459
632
      if (dJoin > 0) {
8460
632
        dxJoin = (-wdyNext + wdy) / dJoin;
8461
632
        dyJoin = (wdxNext - wdx) / dJoin;
8462
632
        xc = pathIn->pts[j0].x
8463
632
       + (SplashCoord)0.5 * w
8464
632
         * cos((double)((SplashCoord)0.5 * (angle + angleNext)));
8465
632
        yc = pathIn->pts[j0].y
8466
632
       + (SplashCoord)0.5 * w
8467
632
         * sin((double)((SplashCoord)0.5 * (angle + angleNext)));
8468
632
        kappa = dAngle * bezierCircle2 * w;
8469
632
        cx1 = pathIn->pts[j0].x - wdy + kappa * dx;
8470
632
        cy1 = pathIn->pts[j0].y + wdx + kappa * dy;
8471
632
        cx2 = xc - kappa * dxJoin;
8472
632
        cy2 = yc - kappa * dyJoin;
8473
632
        cx3 = xc + kappa * dxJoin;
8474
632
        cy3 = yc + kappa * dyJoin;
8475
632
        cx4 = pathIn->pts[j0].x - wdyNext - kappa * dxNext;
8476
632
        cy4 = pathIn->pts[j0].y + wdxNext - kappa * dyNext;
8477
632
        pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y);
8478
632
        pathOut->lineTo(pathIn->pts[j0].x - wdyNext,
8479
632
            pathIn->pts[j0].y + wdxNext);
8480
632
        pathOut->curveTo(cx4, cy4, cx3, cy3, xc, yc);
8481
632
        pathOut->curveTo(cx2, cy2, cx1, cy1,
8482
632
             pathIn->pts[j0].x - wdy,
8483
632
             pathIn->pts[j0].y + wdx);
8484
632
      }
8485
632
    }
8486
8487
  // join angle >= 180
8488
753k
  } else {
8489
753k
    angle = atan2((double)-dx, (double)dy);
8490
753k
    angleNext = atan2((double)-dxNext, (double)dyNext);
8491
753k
    if (angleNext < angle) {
8492
2.01k
      angleNext += 2 * M_PI;
8493
2.01k
    }
8494
753k
    dAngle = (angleNext - angle) / M_PI;
8495
753k
    if (dAngle < 0.501) {
8496
      // span angle is <= 90 degrees -> draw a single arc
8497
751k
      kappa = dAngle * bezierCircle * w;
8498
751k
        cx1 = pathIn->pts[j0].x + wdy + kappa * dx;
8499
751k
        cy1 = pathIn->pts[j0].y - wdx + kappa * dy;
8500
751k
        cx2 = pathIn->pts[j0].x + wdyNext - kappa * dxNext;
8501
751k
        cy2 = pathIn->pts[j0].y - wdxNext - kappa * dyNext;
8502
751k
        pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y);
8503
751k
        pathOut->lineTo(pathIn->pts[j0].x + wdy,
8504
751k
            pathIn->pts[j0].y - wdx);
8505
751k
        pathOut->curveTo(cx1, cy1, cx2, cy2,
8506
751k
             pathIn->pts[j0].x + wdyNext,
8507
751k
             pathIn->pts[j0].y - wdxNext);
8508
751k
    } else {
8509
      // span angle is > 90 degrees -> split into two arcs
8510
2.36k
      dJoin = splashDist(wdy, -wdx, wdyNext, -wdxNext);
8511
2.36k
      if (dJoin > 0) {
8512
1.84k
        dxJoin = (wdyNext - wdy) / dJoin;
8513
1.84k
        dyJoin = (-wdxNext + wdx) / dJoin;
8514
1.84k
        xc = pathIn->pts[j0].x
8515
1.84k
       + (SplashCoord)0.5 * w
8516
1.84k
         * cos((double)((SplashCoord)0.5 * (angle + angleNext)));
8517
1.84k
        yc = pathIn->pts[j0].y
8518
1.84k
       + (SplashCoord)0.5 * w
8519
1.84k
         * sin((double)((SplashCoord)0.5 * (angle + angleNext)));
8520
1.84k
        kappa = dAngle * bezierCircle2 * w;
8521
1.84k
        cx1 = pathIn->pts[j0].x + wdy + kappa * dx;
8522
1.84k
        cy1 = pathIn->pts[j0].y - wdx + kappa * dy;
8523
1.84k
        cx2 = xc - kappa * dxJoin;
8524
1.84k
        cy2 = yc - kappa * dyJoin;
8525
1.84k
        cx3 = xc + kappa * dxJoin;
8526
1.84k
        cy3 = yc + kappa * dyJoin;
8527
1.84k
        cx4 = pathIn->pts[j0].x + wdyNext - kappa * dxNext;
8528
1.84k
        cy4 = pathIn->pts[j0].y - wdxNext - kappa * dyNext;
8529
1.84k
        pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y);
8530
1.84k
        pathOut->lineTo(pathIn->pts[j0].x + wdy,
8531
1.84k
            pathIn->pts[j0].y - wdx);
8532
1.84k
        pathOut->curveTo(cx1, cy1, cx2, cy2, xc, yc);
8533
1.84k
        pathOut->curveTo(cx3, cy3, cx4, cy4,
8534
1.84k
             pathIn->pts[j0].x + wdyNext,
8535
1.84k
             pathIn->pts[j0].y - wdxNext);
8536
1.84k
      }
8537
2.36k
    }
8538
753k
  }
8539
8540
5.20M
      } else {
8541
5.20M
  pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y);
8542
8543
  // join angle < 180
8544
5.20M
  if (crossprod < 0) {
8545
1.80M
    pathOut->lineTo(pathIn->pts[j0].x - wdyNext,
8546
1.80M
        pathIn->pts[j0].y + wdxNext);
8547
    // miter join inside limit
8548
1.80M
    if (lineJoin == splashLineJoinMiter &&
8549
1.60M
        splashSqrt(miter) <= state->miterLimit) {
8550
1.38M
      pathOut->lineTo(pathIn->pts[j0].x - wdy + wdx * m,
8551
1.38M
          pathIn->pts[j0].y + wdx + wdy * m);
8552
1.38M
      pathOut->lineTo(pathIn->pts[j0].x - wdy,
8553
1.38M
          pathIn->pts[j0].y + wdx);
8554
    // bevel join or miter join outside limit
8555
1.38M
    } else {
8556
422k
      pathOut->lineTo(pathIn->pts[j0].x - wdy,
8557
422k
          pathIn->pts[j0].y + wdx);
8558
422k
    }
8559
8560
  // join angle >= 180
8561
3.40M
  } else {
8562
3.40M
    pathOut->lineTo(pathIn->pts[j0].x + wdy,
8563
3.40M
        pathIn->pts[j0].y - wdx);
8564
    // miter join inside limit
8565
3.40M
    if (lineJoin == splashLineJoinMiter &&
8566
3.13M
        splashSqrt(miter) <= state->miterLimit) {
8567
2.93M
      pathOut->lineTo(pathIn->pts[j0].x + wdy + wdx * m,
8568
2.93M
          pathIn->pts[j0].y - wdx + wdy * m);
8569
2.93M
      pathOut->lineTo(pathIn->pts[j0].x + wdyNext,
8570
2.93M
          pathIn->pts[j0].y - wdxNext);
8571
    // bevel join or miter join outside limit
8572
2.93M
    } else {
8573
463k
      pathOut->lineTo(pathIn->pts[j0].x + wdyNext,
8574
463k
          pathIn->pts[j0].y - wdxNext);
8575
463k
    }
8576
3.40M
  }
8577
5.20M
      }
8578
8579
6.51M
      pathOut->close();
8580
6.51M
    }
8581
8582
    // add stroke adjustment hints
8583
7.89M
    if (state->strokeAdjust != splashStrokeAdjustOff) {
8584
8585
      // subpath with one segment
8586
7.82M
      if (seg == 0 && last) {
8587
1.37M
  switch (lineCap) {
8588
1.36M
  case splashLineCapButt:
8589
1.36M
    pathOut->addStrokeAdjustHint(firstPt, left2 + 1,
8590
1.36M
               firstPt, pathOut->length - 1);
8591
1.36M
    break;
8592
660
  case splashLineCapProjecting:
8593
660
    pathOut->addStrokeAdjustHint(firstPt, left2 + 1,
8594
660
               firstPt, pathOut->length - 1, gTrue);
8595
660
    break;
8596
7.55k
  case splashLineCapRound:
8597
7.55k
    break;
8598
1.37M
  }
8599
1.37M
  pathOut->addStrokeAdjustHint(left2, right2,
8600
1.37M
             firstPt, pathOut->length - 1);
8601
6.45M
      } else {
8602
8603
  // start of subpath
8604
6.45M
  if (seg == 1) {
8605
8606
    // start cap
8607
17.7k
    if (!closed) {
8608
6.00k
      switch (lineCap) {
8609
4.81k
      case splashLineCapButt:
8610
4.81k
        pathOut->addStrokeAdjustHint(firstPt, left1 + 1,
8611
4.81k
             firstPt, firstPt + 1);
8612
4.81k
        pathOut->addStrokeAdjustHint(firstPt, left1 + 1,
8613
4.81k
             right1 + 1, right1 + 1);
8614
4.81k
        break;
8615
1.01k
      case splashLineCapProjecting:
8616
1.01k
        pathOut->addStrokeAdjustHint(firstPt, left1 + 1,
8617
1.01k
             firstPt, firstPt + 1, gTrue);
8618
1.01k
        pathOut->addStrokeAdjustHint(firstPt, left1 + 1,
8619
1.01k
             right1 + 1, right1 + 1, gTrue);
8620
1.01k
        break;
8621
167
      case splashLineCapRound:
8622
167
        break;
8623
6.00k
      }
8624
6.00k
    }
8625
8626
    // first segment
8627
17.7k
    pathOut->addStrokeAdjustHint(left1, right1, firstPt, left2);
8628
17.7k
    pathOut->addStrokeAdjustHint(left1, right1, right2 + 1, right2 + 1);
8629
17.7k
  }
8630
8631
  // middle of subpath
8632
6.45M
  if (seg > 1) {
8633
6.41M
    pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0);
8634
6.41M
    pathOut->addStrokeAdjustHint(left1, right1, join0, left2);
8635
6.41M
    pathOut->addStrokeAdjustHint(left1, right1, right2 + 1, right2 + 1);
8636
6.41M
  }
8637
8638
  // end of subpath
8639
6.45M
  if (last) {
8640
8641
17.7k
    if (closed) {
8642
      // first segment
8643
11.7k
      pathOut->addStrokeAdjustHint(leftFirst, rightFirst,
8644
11.7k
           left2 + 1, right2);
8645
11.7k
      pathOut->addStrokeAdjustHint(leftFirst, rightFirst,
8646
11.7k
           join2, pathOut->length - 1);
8647
8648
      // last segment
8649
11.7k
      pathOut->addStrokeAdjustHint(left2, right2,
8650
11.7k
           left1 + 1, right1);
8651
11.7k
      pathOut->addStrokeAdjustHint(left2, right2,
8652
11.7k
           join1, pathOut->length - 1);
8653
11.7k
      pathOut->addStrokeAdjustHint(left2, right2,
8654
11.7k
           leftFirst - 1, leftFirst);
8655
11.7k
      pathOut->addStrokeAdjustHint(left2, right2,
8656
11.7k
           rightFirst + 1, rightFirst + 1);
8657
8658
11.7k
    } else {
8659
8660
      // last segment
8661
6.00k
      pathOut->addStrokeAdjustHint(left2, right2,
8662
6.00k
           left1 + 1, right1);
8663
6.00k
      pathOut->addStrokeAdjustHint(left2, right2,
8664
6.00k
           join1, pathOut->length - 1);
8665
8666
      // end cap
8667
6.00k
      switch (lineCap) {
8668
4.81k
      case splashLineCapButt:
8669
4.81k
        pathOut->addStrokeAdjustHint(left2 - 1, left2 + 1,
8670
4.81k
             left2 + 1, left2 + 2);
8671
4.81k
        break;
8672
1.01k
      case splashLineCapProjecting:
8673
1.01k
        pathOut->addStrokeAdjustHint(left2 - 1, left2 + 1,
8674
1.01k
             left2 + 1, left2 + 2, gTrue);
8675
1.01k
        break;
8676
167
      case splashLineCapRound:
8677
167
        break;
8678
6.00k
      }
8679
6.00k
    }
8680
17.7k
  }
8681
6.45M
      }
8682
8683
7.82M
      left0 = left1;
8684
7.82M
      left1 = left2;
8685
7.82M
      right0 = right1;
8686
7.82M
      right1 = right2;
8687
7.82M
      join0 = join1;
8688
7.82M
      join1 = join2;
8689
7.82M
      if (seg == 0) {
8690
1.39M
  leftFirst = left2;
8691
1.39M
  rightFirst = right2;
8692
1.39M
      }
8693
7.82M
    }
8694
8695
7.89M
    i0 = j0;
8696
7.89M
    i1 = j1;
8697
7.89M
    ++seg;
8698
7.89M
  }
8699
8700
29.0k
  if (pathIn != path) {
8701
210
    delete pathIn;
8702
210
  }
8703
8704
29.0k
  return pathOut;
8705
29.0k
}
8706
8707
SplashClipResult Splash::limitRectToClipRect(int *xMin, int *yMin,
8708
1.46k
               int *xMax, int *yMax) {
8709
1.46k
  int t;
8710
8711
1.46k
  if ((t = state->clip->getXMinI(state->strokeAdjust)) > *xMin) {
8712
1.18k
    *xMin = t;
8713
1.18k
  }
8714
1.46k
  if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < *xMax) {
8715
563
    *xMax = t;
8716
563
  }
8717
1.46k
  if ((t = state->clip->getYMinI(state->strokeAdjust)) > *yMin) {
8718
1.25k
    *yMin = t;
8719
1.25k
  }
8720
1.46k
  if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < *yMax) {
8721
391
    *yMax = t;
8722
391
  }
8723
1.46k
  if (*xMin >= *xMax || *yMin >= *yMax) {
8724
1.37k
    return splashClipAllOutside;
8725
1.37k
  }
8726
87
  return state->clip->testRect(*xMin, *yMin, *xMax - 1, *yMax - 1,
8727
87
             state->strokeAdjust);
8728
1.46k
}
8729
8730
0
void Splash::dumpPath(SplashPath *path) {
8731
0
  int i;
8732
8733
0
  for (i = 0; i < path->length; ++i) {
8734
0
    printf("  %3d: x=%8.2f y=%8.2f%s%s%s%s\n",
8735
0
     i, (double)path->pts[i].x, (double)path->pts[i].y,
8736
0
     (path->flags[i] & splashPathFirst) ? " first" : "",
8737
0
     (path->flags[i] & splashPathLast) ? " last" : "",
8738
0
     (path->flags[i] & splashPathClosed) ? " closed" : "",
8739
0
     (path->flags[i] & splashPathCurve) ? " curve" : "");
8740
0
  }
8741
0
  if (path->hintsLength == 0) {
8742
0
    printf("  no hints\n");
8743
0
  } else {
8744
0
    for (i = 0; i < path->hintsLength; ++i) {
8745
0
      printf("  hint %3d: ctrl0=%d ctrl1=%d pts=%d..%d\n",
8746
0
       i, path->hints[i].ctrl0, path->hints[i].ctrl1,
8747
0
       path->hints[i].firstPt, path->hints[i].lastPt);
8748
0
    }
8749
0
  }
8750
0
}
8751
8752
0
void Splash::dumpXPath(SplashXPath *path) {
8753
0
  int i;
8754
8755
0
  for (i = 0; i < path->length; ++i) {
8756
0
    printf("  %4d: x0=%8.2f y0=%8.2f x1=%8.2f y1=%8.2f count=%d\n",
8757
0
     i, (double)path->segs[i].x0, (double)path->segs[i].y0,
8758
0
     (double)path->segs[i].x1, (double)path->segs[i].y1,
8759
0
     path->segs[i].count);
8760
0
  }
8761
0
}
8762