Coverage Report

Created: 2025-07-11 07:47

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