w32tex
About: TeX Live provides a comprehensive TeX system including all the major TeX-related programs, macro packages, and fonts that are free software. Windows sources.
  Fossies Dox: w32tex-src.tar.xz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

Splash.cc
Go to the documentation of this file.
1 //========================================================================
2 //
3 // Splash.cc
4 //
5 //========================================================================
6 
7 //========================================================================
8 //
9 // Modified under the Poppler project - http://poppler.freedesktop.org
10 //
11 // All changes made under the Poppler project to this file are licensed
12 // under GPL version 2 or later
13 //
14 // Copyright (C) 2005-2021 Albert Astals Cid <aacid@kde.org>
15 // Copyright (C) 2005 Marco Pesenti Gritti <mpg@redhat.com>
16 // Copyright (C) 2010-2016 Thomas Freitag <Thomas.Freitag@alfa.de>
17 // Copyright (C) 2010 Christian Feuersänger <cfeuersaenger@googlemail.com>
18 // Copyright (C) 2011-2013, 2015 William Bader <williambader@hotmail.com>
19 // Copyright (C) 2012 Markus Trippelsdorf <markus@trippelsdorf.de>
20 // Copyright (C) 2012, 2017 Adrian Johnson <ajohnson@redneon.com>
21 // Copyright (C) 2012 Matthias Kramm <kramm@quiss.org>
22 // Copyright (C) 2018, 2019 Stefan Brüns <stefan.bruens@rwth-aachen.de>
23 // Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de>
24 // Copyright (C) 2019, 2020 Oliver Sander <oliver.sander@tu-dresden.de>
25 // Copyright (C) 2019 Marek Kasik <mkasik@redhat.com>
26 // Copyright (C) 2020 Tobias Deiminger <haxtibal@posteo.de>
27 //
28 // To see a description of the changes please see the Changelog file that
29 // came with your tarball or type make ChangeLog if you are building from git
30 //
31 //========================================================================
32 
33 #include <config.h>
34 
35 #include <cstdlib>
36 #include <cstring>
37 #include <climits>
38 #include <cassert>
39 #include <cmath>
40 #include "goo/gmem.h"
41 #include "goo/GooLikely.h"
42 #include "poppler/Error.h"
43 #include "SplashErrorCodes.h"
44 #include "SplashMath.h"
45 #include "SplashBitmap.h"
46 #include "SplashState.h"
47 #include "SplashPath.h"
48 #include "SplashXPath.h"
49 #include "SplashXPathScanner.h"
50 #include "SplashPattern.h"
51 #include "SplashScreen.h"
52 #include "SplashFont.h"
53 #include "SplashGlyphBitmap.h"
54 #include "Splash.h"
55 #include <algorithm>
56 
57 //------------------------------------------------------------------------
58 
59 #define splashAAGamma 1.5
60 
61 // distance of Bezier control point from center for circle approximation
62 // = (4 * (sqrt(2) - 1) / 3) * r
63 #define bezierCircle ((SplashCoord)0.55228475)
64 #define bezierCircle2 ((SplashCoord)(0.5 * 0.55228475))
65 
66 // Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result.
67 static inline unsigned char div255(int x)
68 {
69  return (unsigned char)((x + (x >> 8) + 0x80) >> 8);
70 }
71 
72 // Clip x to lie in [0, 255].
73 static inline unsigned char clip255(int x)
74 {
75  return x < 0 ? 0 : x > 255 ? 255 : x;
76 }
77 
78 template<typename T>
79 inline void Guswap(T &a, T &b)
80 {
81  T tmp = a;
82  a = b;
83  b = tmp;
84 }
85 
86 // The PDF spec says that all pixels whose *centers* lie within the
87 // image target region get painted, so we want to round n+0.5 down to
88 // n. But this causes problems, e.g., with PDF files that fill a
89 // rectangle with black and then draw an image to the exact same
90 // rectangle, so we instead use the fill scan conversion rule.
91 // However, the correct rule works better for glyphs, so we also
92 // provide that option in fillImageMask.
93 #if 0
94 static inline int imgCoordMungeLower(SplashCoord x) {
95  return splashCeil(x + 0.5) - 1;
96 }
97 static inline int imgCoordMungeUpper(SplashCoord x) {
98  return splashCeil(x + 0.5) - 1;
99 }
100 #else
101 static inline int imgCoordMungeLower(SplashCoord x)
102 {
103  return splashFloor(x);
104 }
105 static inline int imgCoordMungeUpper(SplashCoord x)
106 {
107  return splashFloor(x) + 1;
108 }
109 static inline int imgCoordMungeLowerC(SplashCoord x, bool glyphMode)
110 {
111  return glyphMode ? (splashCeil(x + 0.5) - 1) : splashFloor(x);
112 }
113 static inline int imgCoordMungeUpperC(SplashCoord x, bool glyphMode)
114 {
115  return glyphMode ? (splashCeil(x + 0.5) - 1) : (splashFloor(x) + 1);
116 }
117 #endif
118 
119 // Used by drawImage and fillImageMask to divide the target
120 // quadrilateral into sections.
121 struct ImageSection
122 {
123  int y0, y1; // actual y range
124  int ia0, ia1; // vertex indices for edge A
125  int ib0, ib1; // vertex indices for edge A
126  SplashCoord xa0, ya0, xa1, ya1; // edge A
127  SplashCoord dxdya; // slope of edge A
128  SplashCoord xb0, yb0, xb1, yb1; // edge B
129  SplashCoord dxdyb; // slope of edge B
130 };
131 
132 //------------------------------------------------------------------------
133 // SplashPipe
134 //------------------------------------------------------------------------
135 
136 #define splashPipeMaxStages 9
137 
138 struct SplashPipe
139 {
140  // pixel coordinates
141  int x, y;
142 
143  // source pattern
145 
146  // source alpha and color
147  unsigned char aInput;
148  bool usesShape;
150  SplashColor cSrcVal = {};
151 
152  // non-isolated group alpha0
153  unsigned char *alpha0Ptr;
154 
155  // knockout groups
156  bool knockout;
157  unsigned char knockoutOpacity;
158 
159  // soft mask
161 
162  // destination alpha and color
165  unsigned char *destAlphaPtr;
166 
167  // shape
168  unsigned char shape;
169 
170  // result alpha and color
173 
174  // non-isolated group correction
176 
177  // the "run" function
178  void (Splash::*run)(SplashPipe *pipe);
179 };
180 
183 
186 
189 
190 //------------------------------------------------------------------------
191 
193 {
194  int i;
195 
196  for (i = 0; i < splashColorModeNComps[cm]; ++i) {
197  blend[i] = src[i] ^ dest[i];
198  }
199 }
200 
201 //------------------------------------------------------------------------
202 // pipeline
203 //------------------------------------------------------------------------
204 
205 inline void Splash::pipeInit(SplashPipe *pipe, int x, int y, SplashPattern *pattern, SplashColorPtr cSrc, unsigned char aInput, bool usesShape, bool nonIsolatedGroup, bool knockout, unsigned char knockoutOpacity)
206 {
207  pipeSetXY(pipe, x, y);
208  pipe->pattern = nullptr;
209 
210  // source color
211  if (pattern) {
212  if (pattern->isStatic()) {
213  pattern->getColor(x, y, pipe->cSrcVal);
214  } else {
215  pipe->pattern = pattern;
216  }
217  pipe->cSrc = pipe->cSrcVal;
218  } else {
219  pipe->cSrc = cSrc;
220  }
221 
222  // source alpha
223  pipe->aInput = aInput;
224  pipe->usesShape = usesShape;
225  pipe->shape = 0;
226 
227  // knockout
228  pipe->knockout = knockout;
229  pipe->knockoutOpacity = knockoutOpacity;
230 
231  // result alpha
232  if (aInput == 255 && !state->softMask && !usesShape && !state->inNonIsolatedGroup && !nonIsolatedGroup) {
233  pipe->noTransparency = true;
234  } else {
235  pipe->noTransparency = false;
236  }
237 
238  // result color
239  if (pipe->noTransparency) {
240  // the !state->blendFunc case is handled separately in pipeRun
241  pipe->resultColorCtrl = pipeResultColorNoAlphaBlend[bitmap->mode];
242  } else if (!state->blendFunc) {
243  pipe->resultColorCtrl = pipeResultColorAlphaNoBlend[bitmap->mode];
244  } else {
245  pipe->resultColorCtrl = pipeResultColorAlphaBlend[bitmap->mode];
246  }
247 
248  // non-isolated group correction
249  pipe->nonIsolatedGroup = nonIsolatedGroup;
250 
251  // select the 'run' function
252  pipe->run = &Splash::pipeRun;
253  if (!pipe->pattern && pipe->noTransparency && !state->blendFunc) {
254  if (bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr) {
256  } else if (bitmap->mode == splashModeMono8 && pipe->destAlphaPtr) {
258  } else if (bitmap->mode == splashModeRGB8 && pipe->destAlphaPtr) {
260  } else if (bitmap->mode == splashModeXBGR8 && pipe->destAlphaPtr) {
262  } else if (bitmap->mode == splashModeBGR8 && pipe->destAlphaPtr) {
264  } else if (bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr) {
266  } else if (bitmap->mode == splashModeDeviceN8 && pipe->destAlphaPtr) {
268  }
269  } else if (!pipe->pattern && !pipe->noTransparency && !state->softMask && pipe->usesShape && !(state->inNonIsolatedGroup && alpha0Bitmap->alpha) && !state->blendFunc && !pipe->nonIsolatedGroup) {
270  if (bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr) {
272  } else if (bitmap->mode == splashModeMono8 && pipe->destAlphaPtr) {
274  } else if (bitmap->mode == splashModeRGB8 && pipe->destAlphaPtr) {
275  pipe->run = &Splash::pipeRunAARGB8;
276  } else if (bitmap->mode == splashModeXBGR8 && pipe->destAlphaPtr) {
278  } else if (bitmap->mode == splashModeBGR8 && pipe->destAlphaPtr) {
279  pipe->run = &Splash::pipeRunAABGR8;
280  } else if (bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr) {
282  } else if (bitmap->mode == splashModeDeviceN8 && pipe->destAlphaPtr) {
284  }
285  }
286 }
287 
288 // general case
290 {
291  unsigned char aSrc, aDest, alphaI, alphaIm1, alpha0, aResult;
292  SplashColor cSrcNonIso, cDest, cBlend;
293  SplashColorPtr cSrc;
294  unsigned char cResult0, cResult1, cResult2, cResult3;
295  int t;
296  int cp, mask;
297  unsigned char cResult[SPOT_NCOMPS + 4];
298 
299  //----- source color
300 
301  // static pattern: handled in pipeInit
302  // fixed color: handled in pipeInit
303 
304  // dynamic pattern
305  if (pipe->pattern) {
306  if (!pipe->pattern->getColor(pipe->x, pipe->y, pipe->cSrcVal)) {
307  pipeIncX(pipe);
308  return;
309  }
310  if (bitmap->mode == splashModeCMYK8 || bitmap->mode == splashModeDeviceN8) {
311  if (state->fillOverprint && state->overprintMode && pipe->pattern->isCMYK()) {
312  unsigned int overprintMask = 15;
313  if (pipe->cSrcVal[0] == 0) {
314  overprintMask &= ~1;
315  }
316  if (pipe->cSrcVal[1] == 0) {
317  overprintMask &= ~2;
318  }
319  if (pipe->cSrcVal[2] == 0) {
320  overprintMask &= ~4;
321  }
322  if (pipe->cSrcVal[3] == 0) {
323  overprintMask &= ~8;
324  }
325  state->overprintMask = overprintMask;
326  }
327  }
328  }
329 
330  if (pipe->noTransparency && !state->blendFunc) {
331 
332  //----- write destination pixel
333 
334  switch (bitmap->mode) {
335  case splashModeMono1:
336  cResult0 = state->grayTransfer[pipe->cSrc[0]];
337  if (state->screen->test(pipe->x, pipe->y, cResult0)) {
338  *pipe->destColorPtr |= pipe->destColorMask;
339  } else {
340  *pipe->destColorPtr &= ~~pipe->destColorMask;
341  }
342  if (!(pipe->destColorMask >>= 1)) {
343  pipe->destColorMask = 0x80;
344  ++pipe->destColorPtr;
345  }
346  break;
347  case splashModeMono8:
348  *pipe->destColorPtr++ = state->grayTransfer[pipe->cSrc[0]];
349  break;
350  case splashModeRGB8:
351  *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]];
352  *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]];
353  *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]];
354  break;
355  case splashModeXBGR8:
356  *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]];
357  *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]];
358  *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]];
359  *pipe->destColorPtr++ = 255;
360  break;
361  case splashModeBGR8:
362  *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]];
363  *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]];
364  *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]];
365  break;
366  case splashModeCMYK8:
367  if (state->overprintMask & 1) {
368  pipe->destColorPtr[0] = (state->overprintAdditive) ? std::min<int>(pipe->destColorPtr[0] + state->cmykTransferC[pipe->cSrc[0]], 255) : state->cmykTransferC[pipe->cSrc[0]];
369  }
370  if (state->overprintMask & 2) {
371  pipe->destColorPtr[1] = (state->overprintAdditive) ? std::min<int>(pipe->destColorPtr[1] + state->cmykTransferM[pipe->cSrc[1]], 255) : state->cmykTransferM[pipe->cSrc[1]];
372  }
373  if (state->overprintMask & 4) {
374  pipe->destColorPtr[2] = (state->overprintAdditive) ? std::min<int>(pipe->destColorPtr[2] + state->cmykTransferY[pipe->cSrc[2]], 255) : state->cmykTransferY[pipe->cSrc[2]];
375  }
376  if (state->overprintMask & 8) {
377  pipe->destColorPtr[3] = (state->overprintAdditive) ? std::min<int>(pipe->destColorPtr[3] + state->cmykTransferK[pipe->cSrc[3]], 255) : state->cmykTransferK[pipe->cSrc[3]];
378  }
379  pipe->destColorPtr += 4;
380  break;
381  case splashModeDeviceN8:
382  mask = 1;
383  for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) {
384  if (state->overprintMask & mask) {
385  pipe->destColorPtr[cp] = state->deviceNTransfer[cp][pipe->cSrc[cp]];
386  }
387  mask <<= 1;
388  }
389  pipe->destColorPtr += (SPOT_NCOMPS + 4);
390  break;
391  }
392  if (pipe->destAlphaPtr) {
393  *pipe->destAlphaPtr++ = 255;
394  }
395 
396  } else {
397 
398  //----- read destination pixel
399 
400  unsigned char *destColorPtr;
401  if (pipe->shape && state->blendFunc && pipe->knockout && alpha0Bitmap != nullptr) {
402  destColorPtr = alpha0Bitmap->data + (alpha0Y + pipe->y) * alpha0Bitmap->rowSize;
403  switch (bitmap->mode) {
404  case splashModeMono1:
405  destColorPtr += (alpha0X + pipe->x) / 8;
406  break;
407  case splashModeMono8:
408  destColorPtr += (alpha0X + pipe->x);
409  break;
410  case splashModeRGB8:
411  case splashModeBGR8:
412  destColorPtr += (alpha0X + pipe->x) * 3;
413  break;
414  case splashModeXBGR8:
415  case splashModeCMYK8:
416  destColorPtr += (alpha0X + pipe->x) * 4;
417  break;
418  case splashModeDeviceN8:
419  destColorPtr += (alpha0X + pipe->x) * (SPOT_NCOMPS + 4);
420  break;
421  }
422  } else {
423  destColorPtr = pipe->destColorPtr;
424  }
425  switch (bitmap->mode) {
426  case splashModeMono1:
427  cDest[0] = (*destColorPtr & pipe->destColorMask) ? 0xff : 0x00;
428  break;
429  case splashModeMono8:
430  cDest[0] = *destColorPtr;
431  break;
432  case splashModeRGB8:
433  cDest[0] = destColorPtr[0];
434  cDest[1] = destColorPtr[1];
435  cDest[2] = destColorPtr[2];
436  break;
437  case splashModeXBGR8:
438  cDest[0] = destColorPtr[2];
439  cDest[1] = destColorPtr[1];
440  cDest[2] = destColorPtr[0];
441  cDest[3] = 255;
442  break;
443  case splashModeBGR8:
444  cDest[0] = destColorPtr[2];
445  cDest[1] = destColorPtr[1];
446  cDest[2] = destColorPtr[0];
447  break;
448  case splashModeCMYK8:
449  cDest[0] = destColorPtr[0];
450  cDest[1] = destColorPtr[1];
451  cDest[2] = destColorPtr[2];
452  cDest[3] = destColorPtr[3];
453  break;
454  case splashModeDeviceN8:
455  for (cp = 0; cp < SPOT_NCOMPS + 4; cp++)
456  cDest[cp] = destColorPtr[cp];
457  break;
458  }
459  if (pipe->destAlphaPtr) {
460  aDest = *pipe->destAlphaPtr;
461  } else {
462  aDest = 0xff;
463  }
464 
465  //----- source alpha
466 
467  if (state->softMask) {
468  if (pipe->usesShape) {
469  aSrc = div255(div255(pipe->aInput * *pipe->softMaskPtr++) * pipe->shape);
470  } else {
471  aSrc = div255(pipe->aInput * *pipe->softMaskPtr++);
472  }
473  } else if (pipe->usesShape) {
474  aSrc = div255(pipe->aInput * pipe->shape);
475  } else {
476  aSrc = pipe->aInput;
477  }
478 
479  //----- non-isolated group correction
480 
481  if (pipe->nonIsolatedGroup) {
482  // This path is only used when Splash::composite() is called to
483  // composite a non-isolated group onto the backdrop. In this
484  // case, pipe->shape is the source (group) alpha.
485  if (pipe->shape == 0) {
486  // this value will be multiplied by zero later, so it doesn't
487  // matter what we use
488  cSrc = pipe->cSrc;
489  } else {
490  t = (aDest * 255) / pipe->shape - aDest;
491  switch (bitmap->mode) {
492  case splashModeDeviceN8:
493  for (cp = 0; cp < SPOT_NCOMPS + 4; cp++)
494  cSrcNonIso[cp] = clip255(pipe->cSrc[cp] + ((pipe->cSrc[cp] - cDest[cp]) * t) / 255);
495  break;
496  case splashModeCMYK8:
497  for (cp = 0; cp < 4; cp++)
498  cSrcNonIso[cp] = clip255(pipe->cSrc[cp] + ((pipe->cSrc[cp] - cDest[cp]) * t) / 255);
499  break;
500  case splashModeXBGR8:
501  cSrcNonIso[3] = 255;
502  // fallthrough
503  case splashModeRGB8:
504  case splashModeBGR8:
505  cSrcNonIso[2] = clip255(pipe->cSrc[2] + ((pipe->cSrc[2] - cDest[2]) * t) / 255);
506  cSrcNonIso[1] = clip255(pipe->cSrc[1] + ((pipe->cSrc[1] - cDest[1]) * t) / 255);
507  // fallthrough
508  case splashModeMono1:
509  case splashModeMono8:
510  cSrcNonIso[0] = clip255(pipe->cSrc[0] + ((pipe->cSrc[0] - cDest[0]) * t) / 255);
511  break;
512  }
513  cSrc = cSrcNonIso;
514  // knockout: remove backdrop color
515  if (pipe->knockout && pipe->shape >= pipe->knockoutOpacity) {
516  aDest = 0;
517  }
518  }
519  } else {
520  cSrc = pipe->cSrc;
521  }
522 
523  //----- blend function
524 
525  if (state->blendFunc) {
526  if (bitmap->mode == splashModeDeviceN8) {
527  for (int k = 4; k < 4 + SPOT_NCOMPS; k++) {
528  cBlend[k] = 0;
529  }
530  }
531  (*state->blendFunc)(cSrc, cDest, cBlend, bitmap->mode);
532  }
533 
534  //----- result alpha and non-isolated group element correction
535 
536  if (pipe->noTransparency) {
537  alphaI = alphaIm1 = aResult = 255;
538  } else {
539  aResult = aSrc + aDest - div255(aSrc * aDest);
540 
541  // alphaI = alpha_i
542  // alphaIm1 = alpha_(i-1)
543  if (pipe->alpha0Ptr) {
544  alpha0 = *pipe->alpha0Ptr++;
545  alphaI = aResult + alpha0 - div255(aResult * alpha0);
546  alphaIm1 = alpha0 + aDest - div255(alpha0 * aDest);
547  } else {
548  alphaI = aResult;
549  alphaIm1 = aDest;
550  }
551  }
552 
553  //----- result color
554 
555  cResult0 = cResult1 = cResult2 = cResult3 = 0; // make gcc happy
556 
557  switch (pipe->resultColorCtrl) {
558 
560  cResult0 = state->grayTransfer[div255((255 - aDest) * cSrc[0] + aDest * cBlend[0])];
561  break;
563  cResult0 = state->rgbTransferR[div255((255 - aDest) * cSrc[0] + aDest * cBlend[0])];
564  cResult1 = state->rgbTransferG[div255((255 - aDest) * cSrc[1] + aDest * cBlend[1])];
565  cResult2 = state->rgbTransferB[div255((255 - aDest) * cSrc[2] + aDest * cBlend[2])];
566  break;
568  cResult0 = state->cmykTransferC[div255((255 - aDest) * cSrc[0] + aDest * cBlend[0])];
569  cResult1 = state->cmykTransferM[div255((255 - aDest) * cSrc[1] + aDest * cBlend[1])];
570  cResult2 = state->cmykTransferY[div255((255 - aDest) * cSrc[2] + aDest * cBlend[2])];
571  cResult3 = state->cmykTransferK[div255((255 - aDest) * cSrc[3] + aDest * cBlend[3])];
572  break;
574  for (cp = 0; cp < SPOT_NCOMPS + 4; cp++)
575  cResult[cp] = state->deviceNTransfer[cp][div255((255 - aDest) * cSrc[cp] + aDest * cBlend[cp])];
576  break;
577 
579  if (alphaI == 0) {
580  cResult0 = 0;
581  } else {
582  cResult0 = state->grayTransfer[((alphaI - aSrc) * cDest[0] + aSrc * cSrc[0]) / alphaI];
583  }
584  break;
586  if (alphaI == 0) {
587  cResult0 = 0;
588  cResult1 = 0;
589  cResult2 = 0;
590  } else {
591  cResult0 = state->rgbTransferR[((alphaI - aSrc) * cDest[0] + aSrc * cSrc[0]) / alphaI];
592  cResult1 = state->rgbTransferG[((alphaI - aSrc) * cDest[1] + aSrc * cSrc[1]) / alphaI];
593  cResult2 = state->rgbTransferB[((alphaI - aSrc) * cDest[2] + aSrc * cSrc[2]) / alphaI];
594  }
595  break;
597  if (alphaI == 0) {
598  cResult0 = 0;
599  cResult1 = 0;
600  cResult2 = 0;
601  cResult3 = 0;
602  } else {
603  cResult0 = state->cmykTransferC[((alphaI - aSrc) * cDest[0] + aSrc * cSrc[0]) / alphaI];
604  cResult1 = state->cmykTransferM[((alphaI - aSrc) * cDest[1] + aSrc * cSrc[1]) / alphaI];
605  cResult2 = state->cmykTransferY[((alphaI - aSrc) * cDest[2] + aSrc * cSrc[2]) / alphaI];
606  cResult3 = state->cmykTransferK[((alphaI - aSrc) * cDest[3] + aSrc * cSrc[3]) / alphaI];
607  }
608  break;
610  if (alphaI == 0) {
611  for (cp = 0; cp < SPOT_NCOMPS + 4; cp++)
612  cResult[cp] = 0;
613  } else {
614  for (cp = 0; cp < SPOT_NCOMPS + 4; cp++)
615  cResult[cp] = state->deviceNTransfer[cp][((alphaI - aSrc) * cDest[cp] + aSrc * cSrc[cp]) / alphaI];
616  }
617  break;
618 
620  if (alphaI == 0) {
621  cResult0 = 0;
622  } else {
623  cResult0 = state->grayTransfer[((alphaI - aSrc) * cDest[0] + aSrc * ((255 - alphaIm1) * cSrc[0] + alphaIm1 * cBlend[0]) / 255) / alphaI];
624  }
625  break;
627  if (alphaI == 0) {
628  cResult0 = 0;
629  cResult1 = 0;
630  cResult2 = 0;
631  } else {
632  cResult0 = state->rgbTransferR[((alphaI - aSrc) * cDest[0] + aSrc * ((255 - alphaIm1) * cSrc[0] + alphaIm1 * cBlend[0]) / 255) / alphaI];
633  cResult1 = state->rgbTransferG[((alphaI - aSrc) * cDest[1] + aSrc * ((255 - alphaIm1) * cSrc[1] + alphaIm1 * cBlend[1]) / 255) / alphaI];
634  cResult2 = state->rgbTransferB[((alphaI - aSrc) * cDest[2] + aSrc * ((255 - alphaIm1) * cSrc[2] + alphaIm1 * cBlend[2]) / 255) / alphaI];
635  }
636  break;
638  if (alphaI == 0) {
639  cResult0 = 0;
640  cResult1 = 0;
641  cResult2 = 0;
642  cResult3 = 0;
643  } else {
644  cResult0 = state->cmykTransferC[((alphaI - aSrc) * cDest[0] + aSrc * ((255 - alphaIm1) * cSrc[0] + alphaIm1 * cBlend[0]) / 255) / alphaI];
645  cResult1 = state->cmykTransferM[((alphaI - aSrc) * cDest[1] + aSrc * ((255 - alphaIm1) * cSrc[1] + alphaIm1 * cBlend[1]) / 255) / alphaI];
646  cResult2 = state->cmykTransferY[((alphaI - aSrc) * cDest[2] + aSrc * ((255 - alphaIm1) * cSrc[2] + alphaIm1 * cBlend[2]) / 255) / alphaI];
647  cResult3 = state->cmykTransferK[((alphaI - aSrc) * cDest[3] + aSrc * ((255 - alphaIm1) * cSrc[3] + alphaIm1 * cBlend[3]) / 255) / alphaI];
648  }
649  break;
651  if (alphaI == 0) {
652  for (cp = 0; cp < SPOT_NCOMPS + 4; cp++)
653  cResult[cp] = 0;
654  } else {
655  for (cp = 0; cp < SPOT_NCOMPS + 4; cp++)
656  cResult[cp] = state->deviceNTransfer[cp][((alphaI - aSrc) * cDest[cp] + aSrc * ((255 - alphaIm1) * cSrc[cp] + alphaIm1 * cBlend[cp]) / 255) / alphaI];
657  }
658  break;
659  }
660 
661  //----- write destination pixel
662 
663  switch (bitmap->mode) {
664  case splashModeMono1:
665  if (state->screen->test(pipe->x, pipe->y, cResult0)) {
666  *pipe->destColorPtr |= pipe->destColorMask;
667  } else {
668  *pipe->destColorPtr &= ~~pipe->destColorMask;
669  }
670  if (!(pipe->destColorMask >>= 1)) {
671  pipe->destColorMask = 0x80;
672  ++pipe->destColorPtr;
673  }
674  break;
675  case splashModeMono8:
676  *pipe->destColorPtr++ = cResult0;
677  break;
678  case splashModeRGB8:
679  *pipe->destColorPtr++ = cResult0;
680  *pipe->destColorPtr++ = cResult1;
681  *pipe->destColorPtr++ = cResult2;
682  break;
683  case splashModeXBGR8:
684  *pipe->destColorPtr++ = cResult2;
685  *pipe->destColorPtr++ = cResult1;
686  *pipe->destColorPtr++ = cResult0;
687  *pipe->destColorPtr++ = 255;
688  break;
689  case splashModeBGR8:
690  *pipe->destColorPtr++ = cResult2;
691  *pipe->destColorPtr++ = cResult1;
692  *pipe->destColorPtr++ = cResult0;
693  break;
694  case splashModeCMYK8:
695  if (state->overprintMask & 1) {
696  pipe->destColorPtr[0] = (state->overprintAdditive) ? std::min<int>(pipe->destColorPtr[0] + cResult0, 255) : cResult0;
697  }
698  if (state->overprintMask & 2) {
699  pipe->destColorPtr[1] = (state->overprintAdditive) ? std::min<int>(pipe->destColorPtr[1] + cResult1, 255) : cResult1;
700  }
701  if (state->overprintMask & 4) {
702  pipe->destColorPtr[2] = (state->overprintAdditive) ? std::min<int>(pipe->destColorPtr[2] + cResult2, 255) : cResult2;
703  }
704  if (state->overprintMask & 8) {
705  pipe->destColorPtr[3] = (state->overprintAdditive) ? std::min<int>(pipe->destColorPtr[3] + cResult3, 255) : cResult3;
706  }
707  pipe->destColorPtr += 4;
708  break;
709  case splashModeDeviceN8:
710  mask = 1;
711  for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) {
712  if (state->overprintMask & mask) {
713  pipe->destColorPtr[cp] = cResult[cp];
714  }
715  mask <<= 1;
716  }
717  pipe->destColorPtr += (SPOT_NCOMPS + 4);
718  break;
719  }
720  if (pipe->destAlphaPtr) {
721  *pipe->destAlphaPtr++ = aResult;
722  }
723  }
724 
725  ++pipe->x;
726 }
727 
728 // special case:
729 // !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
730 // bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr) {
732 {
733  unsigned char cResult0;
734 
735  //----- write destination pixel
736  cResult0 = state->grayTransfer[pipe->cSrc[0]];
737  if (state->screen->test(pipe->x, pipe->y, cResult0)) {
738  *pipe->destColorPtr |= pipe->destColorMask;
739  } else {
740  *pipe->destColorPtr &= ~~pipe->destColorMask;
741  }
742  if (!(pipe->destColorMask >>= 1)) {
743  pipe->destColorMask = 0x80;
744  ++pipe->destColorPtr;
745  }
746 
747  ++pipe->x;
748 }
749 
750 // special case:
751 // !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
752 // bitmap->mode == splashModeMono8 && pipe->destAlphaPtr) {
754 {
755  //----- write destination pixel
756  *pipe->destColorPtr++ = state->grayTransfer[pipe->cSrc[0]];
757  *pipe->destAlphaPtr++ = 255;
758 
759  ++pipe->x;
760 }
761 
762 // special case:
763 // !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
764 // bitmap->mode == splashModeRGB8 && pipe->destAlphaPtr) {
766 {
767  //----- write destination pixel
768  *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]];
769  *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]];
770  *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]];
771  *pipe->destAlphaPtr++ = 255;
772 
773  ++pipe->x;
774 }
775 
776 // special case:
777 // !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
778 // bitmap->mode == splashModeXBGR8 && pipe->destAlphaPtr) {
780 {
781  //----- write destination pixel
782  *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]];
783  *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]];
784  *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]];
785  *pipe->destColorPtr++ = 255;
786  *pipe->destAlphaPtr++ = 255;
787 
788  ++pipe->x;
789 }
790 
791 // special case:
792 // !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
793 // bitmap->mode == splashModeBGR8 && pipe->destAlphaPtr) {
795 {
796  //----- write destination pixel
797  *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]];
798  *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]];
799  *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]];
800  *pipe->destAlphaPtr++ = 255;
801 
802  ++pipe->x;
803 }
804 
805 // special case:
806 // !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
807 // bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr) {
809 {
810  //----- write destination pixel
811  if (state->overprintMask & 1) {
812  pipe->destColorPtr[0] = (state->overprintAdditive) ? std::min<int>(pipe->destColorPtr[0] + state->cmykTransferC[pipe->cSrc[0]], 255) : state->cmykTransferC[pipe->cSrc[0]];
813  }
814  if (state->overprintMask & 2) {
815  pipe->destColorPtr[1] = (state->overprintAdditive) ? std::min<int>(pipe->destColorPtr[1] + state->cmykTransferM[pipe->cSrc[1]], 255) : state->cmykTransferM[pipe->cSrc[1]];
816  }
817  if (state->overprintMask & 4) {
818  pipe->destColorPtr[2] = (state->overprintAdditive) ? std::min<int>(pipe->destColorPtr[2] + state->cmykTransferY[pipe->cSrc[2]], 255) : state->cmykTransferY[pipe->cSrc[2]];
819  }
820  if (state->overprintMask & 8) {
821  pipe->destColorPtr[3] = (state->overprintAdditive) ? std::min<int>(pipe->destColorPtr[3] + state->cmykTransferK[pipe->cSrc[3]], 255) : state->cmykTransferK[pipe->cSrc[3]];
822  }
823  pipe->destColorPtr += 4;
824  *pipe->destAlphaPtr++ = 255;
825 
826  ++pipe->x;
827 }
828 
829 // special case:
830 // !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
831 // bitmap->mode == splashModeDeviceN8 && pipe->destAlphaPtr) {
833 {
834  //----- write destination pixel
835  int mask = 1;
836  for (int cp = 0; cp < SPOT_NCOMPS + 4; cp++) {
837  if (state->overprintMask & mask) {
838  pipe->destColorPtr[cp] = state->deviceNTransfer[cp][pipe->cSrc[cp]];
839  }
840  mask <<= 1;
841  }
842  pipe->destColorPtr += (SPOT_NCOMPS + 4);
843  *pipe->destAlphaPtr++ = 255;
844 
845  ++pipe->x;
846 }
847 
848 // special case:
849 // !pipe->pattern && !pipe->noTransparency && !state->softMask &&
850 // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
851 // !pipe->nonIsolatedGroup &&
852 // bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr
854 {
855  unsigned char aSrc;
856  SplashColor cDest;
857  unsigned char cResult0;
858 
859  //----- read destination pixel
860  cDest[0] = (*pipe->destColorPtr & pipe->destColorMask) ? 0xff : 0x00;
861 
862  //----- source alpha
863  aSrc = div255(pipe->aInput * pipe->shape);
864 
865  //----- result color
866  // note: aDest = alpha2 = aResult = 0xff
867  cResult0 = state->grayTransfer[(unsigned char)div255((0xff - aSrc) * cDest[0] + aSrc * pipe->cSrc[0])];
868 
869  //----- write destination pixel
870  if (state->screen->test(pipe->x, pipe->y, cResult0)) {
871  *pipe->destColorPtr |= pipe->destColorMask;
872  } else {
873  *pipe->destColorPtr &= ~~pipe->destColorMask;
874  }
875  if (!(pipe->destColorMask >>= 1)) {
876  pipe->destColorMask = 0x80;
877  ++pipe->destColorPtr;
878  }
879 
880  ++pipe->x;
881 }
882 
883 // special case:
884 // !pipe->pattern && !pipe->noTransparency && !state->softMask &&
885 // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
886 // !pipe->nonIsolatedGroup &&
887 // bitmap->mode == splashModeMono8 && pipe->destAlphaPtr
889 {
890  unsigned char aSrc, aDest, alpha2, aResult;
891  SplashColor cDest;
892  unsigned char cResult0;
893 
894  //----- read destination pixel
895  cDest[0] = *pipe->destColorPtr;
896  aDest = *pipe->destAlphaPtr;
897 
898  //----- source alpha
899  aSrc = div255(pipe->aInput * pipe->shape);
900 
901  //----- result alpha and non-isolated group element correction
902  aResult = aSrc + aDest - div255(aSrc * aDest);
903  alpha2 = aResult;
904 
905  //----- result color
906  if (alpha2 == 0) {
907  cResult0 = 0;
908  } else {
909  cResult0 = state->grayTransfer[(unsigned char)(((alpha2 - aSrc) * cDest[0] + aSrc * pipe->cSrc[0]) / alpha2)];
910  }
911 
912  //----- write destination pixel
913  *pipe->destColorPtr++ = cResult0;
914  *pipe->destAlphaPtr++ = aResult;
915 
916  ++pipe->x;
917 }
918 
919 // special case:
920 // !pipe->pattern && !pipe->noTransparency && !state->softMask &&
921 // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
922 // !pipe->nonIsolatedGroup &&
923 // bitmap->mode == splashModeRGB8 && pipe->destAlphaPtr
925 {
926  unsigned char aSrc, aDest, alpha2, aResult;
927  SplashColor cDest;
928  unsigned char cResult0, cResult1, cResult2;
929 
930  //----- read destination alpha
931  aDest = *pipe->destAlphaPtr;
932 
933  //----- source alpha
934  aSrc = div255(pipe->aInput * pipe->shape);
935 
936  //----- result color
937  if (aSrc == 255) {
938  cResult0 = state->rgbTransferR[pipe->cSrc[0]];
939  cResult1 = state->rgbTransferG[pipe->cSrc[1]];
940  cResult2 = state->rgbTransferB[pipe->cSrc[2]];
941  aResult = 255;
942 
943  } else if (aSrc == 0 && aDest == 0) {
944  cResult0 = 0;
945  cResult1 = 0;
946  cResult2 = 0;
947  aResult = 0;
948 
949  } else {
950  //----- read destination pixel
951  cDest[0] = pipe->destColorPtr[0];
952  cDest[1] = pipe->destColorPtr[1];
953  cDest[2] = pipe->destColorPtr[2];
954 
955  //----- result alpha and non-isolated group element correction
956  aResult = aSrc + aDest - div255(aSrc * aDest);
957  alpha2 = aResult;
958 
959  cResult0 = state->rgbTransferR[(unsigned char)(((alpha2 - aSrc) * cDest[0] + aSrc * pipe->cSrc[0]) / alpha2)];
960  cResult1 = state->rgbTransferG[(unsigned char)(((alpha2 - aSrc) * cDest[1] + aSrc * pipe->cSrc[1]) / alpha2)];
961  cResult2 = state->rgbTransferB[(unsigned char)(((alpha2 - aSrc) * cDest[2] + aSrc * pipe->cSrc[2]) / alpha2)];
962  }
963 
964  //----- write destination pixel
965  *pipe->destColorPtr++ = cResult0;
966  *pipe->destColorPtr++ = cResult1;
967  *pipe->destColorPtr++ = cResult2;
968  *pipe->destAlphaPtr++ = aResult;
969 
970  ++pipe->x;
971 }
972 
973 // special case:
974 // !pipe->pattern && !pipe->noTransparency && !state->softMask &&
975 // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
976 // !pipe->nonIsolatedGroup &&
977 // bitmap->mode == splashModeXBGR8 && pipe->destAlphaPtr
979 {
980  unsigned char aSrc, aDest, alpha2, aResult;
981  SplashColor cDest;
982  unsigned char cResult0, cResult1, cResult2;
983 
984  //----- read destination alpha
985  aDest = *pipe->destAlphaPtr;
986 
987  //----- source alpha
988  aSrc = div255(pipe->aInput * pipe->shape);
989 
990  //----- result color
991  if (aSrc == 255) {
992  cResult0 = state->rgbTransferR[pipe->cSrc[0]];
993  cResult1 = state->rgbTransferG[pipe->cSrc[1]];
994  cResult2 = state->rgbTransferB[pipe->cSrc[2]];
995  aResult = 255;
996 
997  } else if (aSrc == 0 && aDest == 0) {
998  cResult0 = 0;
999  cResult1 = 0;
1000  cResult2 = 0;
1001  aResult = 0;
1002 
1003  } else {
1004  //----- read destination color
1005  cDest[0] = pipe->destColorPtr[2];
1006  cDest[1] = pipe->destColorPtr[1];
1007  cDest[2] = pipe->destColorPtr[0];
1008 
1009  //----- result alpha and non-isolated group element correction
1010  aResult = aSrc + aDest - div255(aSrc * aDest);
1011  alpha2 = aResult;
1012 
1013  cResult0 = state->rgbTransferR[(unsigned char)(((alpha2 - aSrc) * cDest[0] + aSrc * pipe->cSrc[0]) / alpha2)];
1014  cResult1 = state->rgbTransferG[(unsigned char)(((alpha2 - aSrc) * cDest[1] + aSrc * pipe->cSrc[1]) / alpha2)];
1015  cResult2 = state->rgbTransferB[(unsigned char)(((alpha2 - aSrc) * cDest[2] + aSrc * pipe->cSrc[2]) / alpha2)];
1016  }
1017 
1018  //----- write destination pixel
1019  *pipe->destColorPtr++ = cResult2;
1020  *pipe->destColorPtr++ = cResult1;
1021  *pipe->destColorPtr++ = cResult0;
1022  *pipe->destColorPtr++ = 255;
1023  *pipe->destAlphaPtr++ = aResult;
1024 
1025  ++pipe->x;
1026 }
1027 
1028 // special case:
1029 // !pipe->pattern && !pipe->noTransparency && !state->softMask &&
1030 // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
1031 // !pipe->nonIsolatedGroup &&
1032 // bitmap->mode == splashModeBGR8 && pipe->destAlphaPtr
1034 {
1035  unsigned char aSrc, aDest, alpha2, aResult;
1036  SplashColor cDest;
1037  unsigned char cResult0, cResult1, cResult2;
1038 
1039  //----- read destination alpha
1040  aDest = *pipe->destAlphaPtr;
1041 
1042  //----- source alpha
1043  aSrc = div255(pipe->aInput * pipe->shape);
1044 
1045  //----- result color
1046  if (aSrc == 255) {
1047  cResult0 = state->rgbTransferR[pipe->cSrc[0]];
1048  cResult1 = state->rgbTransferG[pipe->cSrc[1]];
1049  cResult2 = state->rgbTransferB[pipe->cSrc[2]];
1050  aResult = 255;
1051 
1052  } else if (aSrc == 0 && aDest == 0) {
1053  cResult0 = 0;
1054  cResult1 = 0;
1055  cResult2 = 0;
1056  aResult = 0;
1057 
1058  } else {
1059  //----- read destination color
1060  cDest[0] = pipe->destColorPtr[2];
1061  cDest[1] = pipe->destColorPtr[1];
1062  cDest[2] = pipe->destColorPtr[0];
1063 
1064  //----- result alpha and non-isolated group element correction
1065  aResult = aSrc + aDest - div255(aSrc * aDest);
1066  alpha2 = aResult;
1067 
1068  cResult0 = state->rgbTransferR[(unsigned char)(((alpha2 - aSrc) * cDest[0] + aSrc * pipe->cSrc[0]) / alpha2)];
1069  cResult1 = state->rgbTransferG[(unsigned char)(((alpha2 - aSrc) * cDest[1] + aSrc * pipe->cSrc[1]) / alpha2)];
1070  cResult2 = state->rgbTransferB[(unsigned char)(((alpha2 - aSrc) * cDest[2] + aSrc * pipe->cSrc[2]) / alpha2)];
1071  }
1072 
1073  //----- write destination pixel
1074  *pipe->destColorPtr++ = cResult2;
1075  *pipe->destColorPtr++ = cResult1;
1076  *pipe->destColorPtr++ = cResult0;
1077  *pipe->destAlphaPtr++ = aResult;
1078 
1079  ++pipe->x;
1080 }
1081 
1082 // special case:
1083 // !pipe->pattern && !pipe->noTransparency && !state->softMask &&
1084 // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
1085 // !pipe->nonIsolatedGroup &&
1086 // bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr
1088 {
1089  unsigned char aSrc, aDest, alpha2, aResult;
1090  SplashColor cDest;
1091  unsigned char cResult0, cResult1, cResult2, cResult3;
1092 
1093  //----- read destination pixel
1094  cDest[0] = pipe->destColorPtr[0];
1095  cDest[1] = pipe->destColorPtr[1];
1096  cDest[2] = pipe->destColorPtr[2];
1097  cDest[3] = pipe->destColorPtr[3];
1098  aDest = *pipe->destAlphaPtr;
1099 
1100  //----- source alpha
1101  aSrc = div255(pipe->aInput * pipe->shape);
1102 
1103  //----- result alpha and non-isolated group element correction
1104  aResult = aSrc + aDest - div255(aSrc * aDest);
1105  alpha2 = aResult;
1106 
1107  //----- result color
1108  if (alpha2 == 0) {
1109  cResult0 = 0;
1110  cResult1 = 0;
1111  cResult2 = 0;
1112  cResult3 = 0;
1113  } else {
1114  cResult0 = state->cmykTransferC[(unsigned char)(((alpha2 - aSrc) * cDest[0] + aSrc * pipe->cSrc[0]) / alpha2)];
1115  cResult1 = state->cmykTransferM[(unsigned char)(((alpha2 - aSrc) * cDest[1] + aSrc * pipe->cSrc[1]) / alpha2)];
1116  cResult2 = state->cmykTransferY[(unsigned char)(((alpha2 - aSrc) * cDest[2] + aSrc * pipe->cSrc[2]) / alpha2)];
1117  cResult3 = state->cmykTransferK[(unsigned char)(((alpha2 - aSrc) * cDest[3] + aSrc * pipe->cSrc[3]) / alpha2)];
1118  }
1119 
1120  //----- write destination pixel
1121  if (state->overprintMask & 1) {
1122  pipe->destColorPtr[0] = (state->overprintAdditive && pipe->shape != 0) ? std::min<int>(pipe->destColorPtr[0] + cResult0, 255) : cResult0;
1123  }
1124  if (state->overprintMask & 2) {
1125  pipe->destColorPtr[1] = (state->overprintAdditive && pipe->shape != 0) ? std::min<int>(pipe->destColorPtr[1] + cResult1, 255) : cResult1;
1126  }
1127  if (state->overprintMask & 4) {
1128  pipe->destColorPtr[2] = (state->overprintAdditive && pipe->shape != 0) ? std::min<int>(pipe->destColorPtr[2] + cResult2, 255) : cResult2;
1129  }
1130  if (state->overprintMask & 8) {
1131  pipe->destColorPtr[3] = (state->overprintAdditive && pipe->shape != 0) ? std::min<int>(pipe->destColorPtr[3] + cResult3, 255) : cResult3;
1132  }
1133  pipe->destColorPtr += 4;
1134  *pipe->destAlphaPtr++ = aResult;
1135 
1136  ++pipe->x;
1137 }
1138 
1139 // special case:
1140 // !pipe->pattern && !pipe->noTransparency && !state->softMask &&
1141 // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
1142 // !pipe->nonIsolatedGroup &&
1143 // bitmap->mode == splashModeDeviceN8 && pipe->destAlphaPtr
1145 {
1146  unsigned char aSrc, aDest, alpha2, aResult;
1147  SplashColor cDest;
1148  unsigned char cResult[SPOT_NCOMPS + 4];
1149  int cp, mask;
1150 
1151  //----- read destination pixel
1152  for (cp = 0; cp < SPOT_NCOMPS + 4; cp++)
1153  cDest[cp] = pipe->destColorPtr[cp];
1154  aDest = *pipe->destAlphaPtr;
1155 
1156  //----- source alpha
1157  aSrc = div255(pipe->aInput * pipe->shape);
1158 
1159  //----- result alpha and non-isolated group element correction
1160  aResult = aSrc + aDest - div255(aSrc * aDest);
1161  alpha2 = aResult;
1162 
1163  //----- result color
1164  if (alpha2 == 0) {
1165  for (cp = 0; cp < SPOT_NCOMPS + 4; cp++)
1166  cResult[cp] = 0;
1167  } else {
1168  for (cp = 0; cp < SPOT_NCOMPS + 4; cp++)
1169  cResult[cp] = state->deviceNTransfer[cp][(unsigned char)(((alpha2 - aSrc) * cDest[cp] + aSrc * pipe->cSrc[cp]) / alpha2)];
1170  }
1171 
1172  //----- write destination pixel
1173  mask = 1;
1174  for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) {
1175  if (state->overprintMask & mask) {
1176  pipe->destColorPtr[cp] = cResult[cp];
1177  }
1178  mask <<= 1;
1179  }
1180  pipe->destColorPtr += (SPOT_NCOMPS + 4);
1181  *pipe->destAlphaPtr++ = aResult;
1182 
1183  ++pipe->x;
1184 }
1185 
1186 inline void Splash::pipeSetXY(SplashPipe *pipe, int x, int y)
1187 {
1188  pipe->x = x;
1189  pipe->y = y;
1190  if (state->softMask) {
1191  pipe->softMaskPtr = &state->softMask->data[y * state->softMask->rowSize + x];
1192  }
1193  switch (bitmap->mode) {
1194  case splashModeMono1:
1195  pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + (x >> 3)];
1196  pipe->destColorMask = 0x80 >> (x & 7);
1197  break;
1198  case splashModeMono8:
1199  pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + x];
1200  break;
1201  case splashModeRGB8:
1202  case splashModeBGR8:
1203  pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x];
1204  break;
1205  case splashModeXBGR8:
1206  pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x];
1207  break;
1208  case splashModeCMYK8:
1209  pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x];
1210  break;
1211  case splashModeDeviceN8:
1212  pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + (SPOT_NCOMPS + 4) * x];
1213  break;
1214  }
1215  if (bitmap->alpha) {
1216  pipe->destAlphaPtr = &bitmap->alpha[y * bitmap->width + x];
1217  } else {
1218  pipe->destAlphaPtr = nullptr;
1219  }
1220  if (state->inNonIsolatedGroup && alpha0Bitmap->alpha) {
1221  pipe->alpha0Ptr = &alpha0Bitmap->alpha[(alpha0Y + y) * alpha0Bitmap->width + (alpha0X + x)];
1222  } else {
1223  pipe->alpha0Ptr = nullptr;
1224  }
1225 }
1226 
1228 {
1229  ++pipe->x;
1230  if (state->softMask) {
1231  ++pipe->softMaskPtr;
1232  }
1233  switch (bitmap->mode) {
1234  case splashModeMono1:
1235  if (!(pipe->destColorMask >>= 1)) {
1236  pipe->destColorMask = 0x80;
1237  ++pipe->destColorPtr;
1238  }
1239  break;
1240  case splashModeMono8:
1241  ++pipe->destColorPtr;
1242  break;
1243  case splashModeRGB8:
1244  case splashModeBGR8:
1245  pipe->destColorPtr += 3;
1246  break;
1247  case splashModeXBGR8:
1248  pipe->destColorPtr += 4;
1249  break;
1250  case splashModeCMYK8:
1251  pipe->destColorPtr += 4;
1252  break;
1253  case splashModeDeviceN8:
1254  pipe->destColorPtr += (SPOT_NCOMPS + 4);
1255  break;
1256  }
1257  if (pipe->destAlphaPtr) {
1258  ++pipe->destAlphaPtr;
1259  }
1260  if (pipe->alpha0Ptr) {
1261  ++pipe->alpha0Ptr;
1262  }
1263 }
1264 
1265 inline void Splash::drawPixel(SplashPipe *pipe, int x, int y, bool noClip)
1266 {
1267  if (unlikely(y < 0))
1268  return;
1269 
1270  if (noClip || state->clip->test(x, y)) {
1271  pipeSetXY(pipe, x, y);
1272  (this->*pipe->run)(pipe);
1273  }
1274 }
1275 
1277 {
1278  aaBufY = -1;
1279 }
1280 
1281 inline void Splash::drawAAPixel(SplashPipe *pipe, int x, int y)
1282 {
1283 #if splashAASize == 4
1284  static const int bitCount4[16] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 };
1285  int w;
1286 #else
1287  int xx, yy;
1288 #endif
1289  SplashColorPtr p;
1290  int x0, x1, t;
1291 
1292  if (x < 0 || x >= bitmap->width || y < state->clip->getYMinI() || y > state->clip->getYMaxI()) {
1293  return;
1294  }
1295 
1296  // update aaBuf
1297  if (y != aaBufY) {
1298  memset(aaBuf->getDataPtr(), 0xff, aaBuf->getRowSize() * aaBuf->getHeight());
1299  x0 = 0;
1300  x1 = bitmap->width - 1;
1301  state->clip->clipAALine(aaBuf, &x0, &x1, y);
1302  aaBufY = y;
1303  }
1304 
1305  // compute the shape value
1306 #if splashAASize == 4
1307  p = aaBuf->getDataPtr() + (x >> 1);
1308  w = aaBuf->getRowSize();
1309  if (x & 1) {
1310  t = bitCount4[*p & 0x0f] + bitCount4[p[w] & 0x0f] + bitCount4[p[2 * w] & 0x0f] + bitCount4[p[3 * w] & 0x0f];
1311  } else {
1312  t = bitCount4[*p >> 4] + bitCount4[p[w] >> 4] + bitCount4[p[2 * w] >> 4] + bitCount4[p[3 * w] >> 4];
1313  }
1314 #else
1315  t = 0;
1316  for (yy = 0; yy < splashAASize; ++yy) {
1317  for (xx = 0; xx < splashAASize; ++xx) {
1318  p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + ((x * splashAASize + xx) >> 3);
1319  t += (*p >> (7 - ((x * splashAASize + xx) & 7))) & 1;
1320  }
1321  }
1322 #endif
1323 
1324  // draw the pixel
1325  if (t != 0) {
1326  pipeSetXY(pipe, x, y);
1327  pipe->shape = div255(aaGamma[t] * pipe->shape);
1328  (this->*pipe->run)(pipe);
1329  }
1330 }
1331 
1332 inline void Splash::drawSpan(SplashPipe *pipe, int x0, int x1, int y, bool noClip)
1333 {
1334  int x;
1335 
1336  if (noClip) {
1337  pipeSetXY(pipe, x0, y);
1338  for (x = x0; x <= x1; ++x) {
1339  (this->*pipe->run)(pipe);
1340  }
1341  } else {
1342  if (x0 < state->clip->getXMinI()) {
1343  x0 = state->clip->getXMinI();
1344  }
1345  if (x1 > state->clip->getXMaxI()) {
1346  x1 = state->clip->getXMaxI();
1347  }
1348  pipeSetXY(pipe, x0, y);
1349  for (x = x0; x <= x1; ++x) {
1350  if (state->clip->test(x, y)) {
1351  (this->*pipe->run)(pipe);
1352  } else {
1353  pipeIncX(pipe);
1354  }
1355  }
1356  }
1357 }
1358 
1359 inline void Splash::drawAALine(SplashPipe *pipe, int x0, int x1, int y, bool adjustLine, unsigned char lineOpacity)
1360 {
1361 #if splashAASize == 4
1362  static const int bitCount4[16] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 };
1363  SplashColorPtr p0, p1, p2, p3;
1364  int t;
1365 #else
1366  SplashColorPtr p;
1367  int xx, yy, t;
1368 #endif
1369  int x;
1370 
1371 #if splashAASize == 4
1372  p0 = aaBuf->getDataPtr() + (x0 >> 1);
1373  p1 = p0 + aaBuf->getRowSize();
1374  p2 = p1 + aaBuf->getRowSize();
1375  p3 = p2 + aaBuf->getRowSize();
1376 #endif
1377  pipeSetXY(pipe, x0, y);
1378  for (x = x0; x <= x1; ++x) {
1379 
1380  // compute the shape value
1381 #if splashAASize == 4
1382  if (x & 1) {
1383  t = bitCount4[*p0 & 0x0f] + bitCount4[*p1 & 0x0f] + bitCount4[*p2 & 0x0f] + bitCount4[*p3 & 0x0f];
1384  ++p0;
1385  ++p1;
1386  ++p2;
1387  ++p3;
1388  } else {
1389  t = bitCount4[*p0 >> 4] + bitCount4[*p1 >> 4] + bitCount4[*p2 >> 4] + bitCount4[*p3 >> 4];
1390  }
1391 #else
1392  t = 0;
1393  for (yy = 0; yy < splashAASize; ++yy) {
1394  for (xx = 0; xx < splashAASize; ++xx) {
1395  p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + ((x * splashAASize + xx) >> 3);
1396  t += (*p >> (7 - ((x * splashAASize + xx) & 7))) & 1;
1397  }
1398  }
1399 #endif
1400 
1401  if (t != 0) {
1402  pipe->shape = (adjustLine) ? div255((int)lineOpacity * (double)aaGamma[t]) : (double)aaGamma[t];
1403  (this->*pipe->run)(pipe);
1404  } else {
1405  pipeIncX(pipe);
1406  }
1407  }
1408 }
1409 
1410 //------------------------------------------------------------------------
1411 
1412 // Transform a point from user space to device space.
1414 {
1415  // [ m[0] m[1] 0 ]
1416  // [xo yo 1] = [xi yi 1] * [ m[2] m[3] 0 ]
1417  // [ m[4] m[5] 1 ]
1418  *xo = xi * matrix[0] + yi * matrix[2] + matrix[4];
1419  *yo = xi * matrix[1] + yi * matrix[3] + matrix[5];
1420 }
1421 
1422 //------------------------------------------------------------------------
1423 // Splash
1424 //------------------------------------------------------------------------
1425 
1426 Splash::Splash(SplashBitmap *bitmapA, bool vectorAntialiasA, SplashScreenParams *screenParams)
1427 {
1428  int i;
1429 
1430  bitmap = bitmapA;
1431  vectorAntialias = vectorAntialiasA;
1432  inShading = false;
1433  state = new SplashState(bitmap->width, bitmap->height, vectorAntialias, screenParams);
1434  if (vectorAntialias) {
1435  aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize, 1, splashModeMono1, false);
1436  for (i = 0; i <= splashAASize * splashAASize; ++i) {
1438  }
1439  } else {
1440  aaBuf = nullptr;
1441  }
1442  minLineWidth = 0;
1444  debugMode = false;
1445  alpha0Bitmap = nullptr;
1446 }
1447 
1448 Splash::Splash(SplashBitmap *bitmapA, bool vectorAntialiasA, SplashScreen *screenA)
1449 {
1450  int i;
1451 
1452  bitmap = bitmapA;
1453  inShading = false;
1454  vectorAntialias = vectorAntialiasA;
1455  state = new SplashState(bitmap->width, bitmap->height, vectorAntialias, screenA);
1456  if (vectorAntialias) {
1457  aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize, 1, splashModeMono1, false);
1458  for (i = 0; i <= splashAASize * splashAASize; ++i) {
1460  }
1461  } else {
1462  aaBuf = nullptr;
1463  }
1464  minLineWidth = 0;
1466  debugMode = false;
1467  alpha0Bitmap = nullptr;
1468 }
1469 
1471 {
1472  while (state->next) {
1473  restoreState();
1474  }
1475  delete state;
1476  delete aaBuf;
1477 }
1478 
1479 //------------------------------------------------------------------------
1480 // state read
1481 //------------------------------------------------------------------------
1482 
1484 {
1485  return state->matrix;
1486 }
1487 
1489 {
1490  return state->strokePattern;
1491 }
1492 
1494 {
1495  return state->fillPattern;
1496 }
1497 
1499 {
1500  return state->screen;
1501 }
1502 
1504 {
1505  return state->blendFunc;
1506 }
1507 
1509 {
1510  return state->strokeAlpha;
1511 }
1512 
1514 {
1515  return state->fillAlpha;
1516 }
1517 
1519 {
1520  return state->lineWidth;
1521 }
1522 
1523 int Splash::getLineCap()
1524 {
1525  return state->lineCap;
1526 }
1527 
1528 int Splash::getLineJoin()
1529 {
1530  return state->lineJoin;
1531 }
1532 
1534 {
1535  return state->miterLimit;
1536 }
1537 
1539 {
1540  return state->flatness;
1541 }
1542 
1544 {
1545  return state->lineDash;
1546 }
1547 
1549 {
1550  return state->lineDashLength;
1551 }
1552 
1554 {
1555  return state->lineDashPhase;
1556 }
1557 
1559 {
1560  return state->strokeAdjust;
1561 }
1562 
1564 {
1565  return state->clip;
1566 }
1567 
1569 {
1570  return state->softMask;
1571 }
1572 
1574 {
1575  return state->inNonIsolatedGroup;
1576 }
1577 
1578 //------------------------------------------------------------------------
1579 // state write
1580 //------------------------------------------------------------------------
1581 
1583 {
1584  memcpy(state->matrix, matrix, 6 * sizeof(SplashCoord));
1585 }
1586 
1587 void Splash::setStrokePattern(SplashPattern *strokePattern)
1588 {
1589  state->setStrokePattern(strokePattern);
1590 }
1591 
1592 void Splash::setFillPattern(SplashPattern *fillPattern)
1593 {
1594  state->setFillPattern(fillPattern);
1595 }
1596 
1598 {
1599  state->setScreen(screen);
1600 }
1601 
1603 {
1604  state->blendFunc = func;
1605 }
1606 
1608 {
1609  state->strokeAlpha = (state->multiplyPatternAlpha) ? alpha * state->patternStrokeAlpha : alpha;
1610 }
1611 
1613 {
1614  state->fillAlpha = (state->multiplyPatternAlpha) ? alpha * state->patternFillAlpha : alpha;
1615 }
1616 
1618 {
1619  state->patternStrokeAlpha = strokeAlpha;
1620  state->patternFillAlpha = fillAlpha;
1621  state->multiplyPatternAlpha = true;
1622 }
1623 
1625 {
1626  state->patternStrokeAlpha = 1;
1627  state->patternFillAlpha = 1;
1628  state->multiplyPatternAlpha = false;
1629 }
1630 
1632 {
1633  state->fillOverprint = fop;
1634 }
1635 
1637 {
1638  state->strokeOverprint = sop;
1639 }
1640 
1642 {
1643  state->overprintMode = opm;
1644 }
1645 
1646 void Splash::setLineWidth(SplashCoord lineWidth)
1647 {
1648  state->lineWidth = lineWidth;
1649 }
1650 
1651 void Splash::setLineCap(int lineCap)
1652 {
1653  state->lineCap = lineCap;
1654 }
1655 
1656 void Splash::setLineJoin(int lineJoin)
1657 {
1658  state->lineJoin = lineJoin;
1659 }
1660 
1661 void Splash::setMiterLimit(SplashCoord miterLimit)
1662 {
1663  state->miterLimit = miterLimit;
1664 }
1665 
1667 {
1668  if (flatness < 1) {
1669  state->flatness = 1;
1670  } else {
1671  state->flatness = flatness;
1672  }
1673 }
1674 
1675 void Splash::setLineDash(SplashCoord *lineDash, int lineDashLength, SplashCoord lineDashPhase)
1676 {
1677  state->setLineDash(lineDash, lineDashLength, lineDashPhase);
1678 }
1679 
1680 void Splash::setStrokeAdjust(bool strokeAdjust)
1681 {
1682  state->strokeAdjust = strokeAdjust;
1683 }
1684 
1686 {
1687  state->clip->resetToRect(x0, y0, x1, y1);
1688 }
1689 
1691 {
1692  return state->clip->clipToRect(x0, y0, x1, y1);
1693 }
1694 
1696 {
1697  return state->clip->clipToPath(path, state->matrix, state->flatness, eo);
1698 }
1699 
1700 void Splash::setSoftMask(SplashBitmap *softMask)
1701 {
1702  state->setSoftMask(softMask);
1703 }
1704 
1705 void Splash::setInNonIsolatedGroup(SplashBitmap *alpha0BitmapA, int alpha0XA, int alpha0YA)
1706 {
1707  alpha0Bitmap = alpha0BitmapA;
1708  alpha0X = alpha0XA;
1709  alpha0Y = alpha0YA;
1710  state->inNonIsolatedGroup = true;
1711 }
1712 
1713 void Splash::setTransfer(unsigned char *red, unsigned char *green, unsigned char *blue, unsigned char *gray)
1714 {
1715  state->setTransfer(red, green, blue, gray);
1716 }
1717 
1718 void Splash::setOverprintMask(unsigned int overprintMask, bool additive)
1719 {
1720  state->overprintMask = overprintMask;
1721  state->overprintAdditive = additive;
1722 }
1723 
1724 //------------------------------------------------------------------------
1725 // state save/restore
1726 //------------------------------------------------------------------------
1727 
1728 void Splash::saveState()
1729 {
1730  SplashState *newState;
1731 
1732  newState = state->copy();
1733  newState->next = state;
1734  state = newState;
1735 }
1736 
1738 {
1739  SplashState *oldState;
1740 
1741  if (!state->next) {
1742  return splashErrNoSave;
1743  }
1744  oldState = state;
1745  state = state->next;
1746  delete oldState;
1747  return splashOk;
1748 }
1749 
1750 //------------------------------------------------------------------------
1751 // drawing operations
1752 //------------------------------------------------------------------------
1753 
1754 void Splash::clear(SplashColorPtr color, unsigned char alpha)
1755 {
1756  SplashColorPtr row, p;
1757  unsigned char mono;
1758  int x, y;
1759 
1760  switch (bitmap->mode) {
1761  case splashModeMono1:
1762  mono = (color[0] & 0x80) ? 0xff : 0x00;
1763  if (bitmap->rowSize < 0) {
1764  memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), mono, -bitmap->rowSize * bitmap->height);
1765  } else {
1766  memset(bitmap->data, mono, bitmap->rowSize * bitmap->height);
1767  }
1768  break;
1769  case splashModeMono8:
1770  if (bitmap->rowSize < 0) {
1771  memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), color[0], -bitmap->rowSize * bitmap->height);
1772  } else {
1773  memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1774  }
1775  break;
1776  case splashModeRGB8:
1777  if (color[0] == color[1] && color[1] == color[2]) {
1778  if (bitmap->rowSize < 0) {
1779  memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), color[0], -bitmap->rowSize * bitmap->height);
1780  } else {
1781  memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1782  }
1783  } else {
1784  row = bitmap->data;
1785  for (y = 0; y < bitmap->height; ++y) {
1786  p = row;
1787  for (x = 0; x < bitmap->width; ++x) {
1788  *p++ = color[2];
1789  *p++ = color[1];
1790  *p++ = color[0];
1791  }
1792  row += bitmap->rowSize;
1793  }
1794  }
1795  break;
1796  case splashModeXBGR8:
1797  if (color[0] == color[1] && color[1] == color[2]) {
1798  if (bitmap->rowSize < 0) {
1799  memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), color[0], -bitmap->rowSize * bitmap->height);
1800  } else {
1801  memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1802  }
1803  } else {
1804  row = bitmap->data;
1805  for (y = 0; y < bitmap->height; ++y) {
1806  p = row;
1807  for (x = 0; x < bitmap->width; ++x) {
1808  *p++ = color[0];
1809  *p++ = color[1];
1810  *p++ = color[2];
1811  *p++ = 255;
1812  }
1813  row += bitmap->rowSize;
1814  }
1815  }
1816  break;
1817  case splashModeBGR8:
1818  if (color[0] == color[1] && color[1] == color[2]) {
1819  if (bitmap->rowSize < 0) {
1820  memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), color[0], -bitmap->rowSize * bitmap->height);
1821  } else {
1822  memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1823  }
1824  } else {
1825  row = bitmap->data;
1826  for (y = 0; y < bitmap->height; ++y) {
1827  p = row;
1828  for (x = 0; x < bitmap->width; ++x) {
1829  *p++ = color[0];
1830  *p++ = color[1];
1831  *p++ = color[2];
1832  }
1833  row += bitmap->rowSize;
1834  }
1835  }
1836  break;
1837  case splashModeCMYK8:
1838  if (color[0] == color[1] && color[1] == color[2] && color[2] == color[3]) {
1839  if (bitmap->rowSize < 0) {
1840  memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), color[0], -bitmap->rowSize * bitmap->height);
1841  } else {
1842  memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1843  }
1844  } else {
1845  row = bitmap->data;
1846  for (y = 0; y < bitmap->height; ++y) {
1847  p = row;
1848  for (x = 0; x < bitmap->width; ++x) {
1849  *p++ = color[0];
1850  *p++ = color[1];
1851  *p++ = color[2];
1852  *p++ = color[3];
1853  }
1854  row += bitmap->rowSize;
1855  }
1856  }
1857  break;
1858  case splashModeDeviceN8:
1859  row = bitmap->data;
1860  for (y = 0; y < bitmap->height; ++y) {
1861  p = row;
1862  for (x = 0; x < bitmap->width; ++x) {
1863  for (int cp = 0; cp < SPOT_NCOMPS + 4; cp++)
1864  *p++ = color[cp];
1865  }
1866  row += bitmap->rowSize;
1867  }
1868  break;
1869  }
1870 
1871  if (bitmap->alpha) {
1872  memset(bitmap->alpha, alpha, bitmap->width * bitmap->height);
1873  }
1874 }
1875 
1877 {
1878  SplashPath *path2, *dPath;
1879  SplashCoord d1, d2, t1, t2, w;
1880 
1881  if (debugMode) {
1882  printf("stroke [dash:%d] [width:%.2f]:\n", state->lineDashLength, (double)state->lineWidth);
1883  dumpPath(path);
1884  }
1886  if (path->length == 0) {
1887  return splashErrEmptyPath;
1888  }
1889  path2 = flattenPath(path, state->matrix, state->flatness);
1890  if (state->lineDashLength > 0) {
1891  dPath = makeDashedPath(path2);
1892  delete path2;
1893  path2 = dPath;
1894  if (path2->length == 0) {
1895  delete path2;
1896  return splashErrEmptyPath;
1897  }
1898  }
1899 
1900  // transform a unit square, and take the half the max of the two
1901  // diagonals; the product of this number and the line width is the
1902  // (approximate) transformed line width
1903  t1 = state->matrix[0] + state->matrix[2];
1904  t2 = state->matrix[1] + state->matrix[3];
1905  d1 = t1 * t1 + t2 * t2;
1906  t1 = state->matrix[0] - state->matrix[2];
1907  t2 = state->matrix[1] - state->matrix[3];
1908  d2 = t1 * t1 + t2 * t2;
1909  if (d2 > d1) {
1910  d1 = d2;
1911  }
1912  d1 *= 0.5;
1913  if (d1 > 0 && d1 * state->lineWidth * state->lineWidth < minLineWidth * minLineWidth) {
1914  w = minLineWidth / splashSqrt(d1);
1915  strokeWide(path2, w);
1916  } else if (bitmap->mode == splashModeMono1) {
1917  // this gets close to Adobe's behavior in mono mode
1918  if (d1 * state->lineWidth <= 2) {
1919  strokeNarrow(path2);
1920  } else {
1921  strokeWide(path2, state->lineWidth);
1922  }
1923  } else {
1924  if (state->lineWidth == 0) {
1925  strokeNarrow(path2);
1926  } else {
1927  strokeWide(path2, state->lineWidth);
1928  }
1929  }
1930 
1931  delete path2;
1932  return splashOk;
1933 }
1934 
1936 {
1937  SplashPipe pipe;
1939  int x0, x1, y0, y1, xa, xb, y;
1940  SplashCoord dxdy;
1941  SplashClipResult clipRes;
1942  int nClipRes[3];
1943  int i;
1944 
1945  nClipRes[0] = nClipRes[1] = nClipRes[2] = 0;
1946 
1947  SplashXPath xPath(path, state->matrix, state->flatness, false);
1948 
1949  pipeInit(&pipe, 0, 0, state->strokePattern, nullptr, (unsigned char)splashRound(state->strokeAlpha * 255), false, false);
1950 
1951  for (i = 0, seg = xPath.segs; i < xPath.length; ++i, ++seg) {
1952  if (seg->y0 <= seg->y1) {
1953  y0 = splashFloor(seg->y0);
1954  y1 = splashFloor(seg->y1);
1955  x0 = splashFloor(seg->x0);
1956  x1 = splashFloor(seg->x1);
1957  } else {
1958  y0 = splashFloor(seg->y1);
1959  y1 = splashFloor(seg->y0);
1960  x0 = splashFloor(seg->x1);
1961  x1 = splashFloor(seg->x0);
1962  }
1963  if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0, x0 <= x1 ? x1 : x0, y1)) != splashClipAllOutside) {
1964  if (y0 == y1) {
1965  if (x0 <= x1) {
1966  drawSpan(&pipe, x0, x1, y0, clipRes == splashClipAllInside);
1967  } else {
1968  drawSpan(&pipe, x1, x0, y0, clipRes == splashClipAllInside);
1969  }
1970  } else {
1971  dxdy = seg->dxdy;
1972  if (y0 < state->clip->getYMinI()) {
1973  y0 = state->clip->getYMinI();
1974  x0 = splashFloor(seg->x0 + (state->clip->getYMin() - seg->y0) * dxdy);
1975  }
1976  if (y1 > state->clip->getYMaxI()) {
1977  y1 = state->clip->getYMaxI();
1978  x1 = splashFloor(seg->x0 + (state->clip->getYMax() - seg->y0) * dxdy);
1979  }
1980  if (x0 <= x1) {
1981  xa = x0;
1982  for (y = y0; y <= y1; ++y) {
1983  if (y < y1) {
1984  xb = splashFloor(seg->x0 + ((SplashCoord)y + 1 - seg->y0) * dxdy);
1985  } else {
1986  xb = x1 + 1;
1987  }
1988  if (xa == xb) {
1989  drawPixel(&pipe, xa, y, clipRes == splashClipAllInside);
1990  } else {
1991  drawSpan(&pipe, xa, xb - 1, y, clipRes == splashClipAllInside);
1992  }
1993  xa = xb;
1994  }
1995  } else {
1996  xa = x0;
1997  for (y = y0; y <= y1; ++y) {
1998  if (y < y1) {
1999  xb = splashFloor(seg->x0 + ((SplashCoord)y + 1 - seg->y0) * dxdy);
2000  } else {
2001  xb = x1 - 1;
2002  }
2003  if (xa == xb) {
2004  drawPixel(&pipe, xa, y, clipRes == splashClipAllInside);
2005  } else {
2006  drawSpan(&pipe, xb + 1, xa, y, clipRes == splashClipAllInside);
2007  }
2008  xa = xb;
2009  }
2010  }
2011  }
2012  }
2013  ++nClipRes[clipRes];
2014  }
2015  if (nClipRes[splashClipPartial] || (nClipRes[splashClipAllInside] && nClipRes[splashClipAllOutside])) {
2017  } else if (nClipRes[splashClipAllInside]) {
2019  } else {
2021  }
2022 }
2023 
2025 {
2026  SplashPath *path2;
2027 
2028  path2 = makeStrokePath(path, w, false);
2029  fillWithPattern(path2, false, state->strokePattern, state->strokeAlpha);
2030  delete path2;
2031 }
2032 
2034 {
2035  SplashPath *fPath;
2036  SplashCoord flatness2;
2037  unsigned char flag;
2038  int i;
2039 
2040  fPath = new SplashPath();
2041  flatness2 = flatness * flatness;
2042  i = 0;
2043  while (i < path->length) {
2044  flag = path->flags[i];
2045  if (flag & splashPathFirst) {
2046  fPath->moveTo(path->pts[i].x, path->pts[i].y);
2047  ++i;
2048  } else {
2049  if (flag & splashPathCurve) {
2050  flattenCurve(path->pts[i - 1].x, path->pts[i - 1].y, path->pts[i].x, path->pts[i].y, path->pts[i + 1].x, path->pts[i + 1].y, path->pts[i + 2].x, path->pts[i + 2].y, matrix, flatness2, fPath);
2051  i += 3;
2052  } else {
2053  fPath->lineTo(path->pts[i].x, path->pts[i].y);
2054  ++i;
2055  }
2056  if (path->flags[i - 1] & splashPathClosed) {
2057  fPath->close();
2058  }
2059  }
2060  }
2061  return fPath;
2062 }
2063 
2065 {
2068  int cNext[splashMaxCurveSplits + 1];
2069  SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh;
2070  SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh;
2071  SplashCoord dx, dy, mx, my, tx, ty, d1, d2;
2072  int p1, p2, p3;
2073 
2074  // initial segment
2075  p1 = 0;
2077  cx[p1][0] = x0;
2078  cy[p1][0] = y0;
2079  cx[p1][1] = x1;
2080  cy[p1][1] = y1;
2081  cx[p1][2] = x2;
2082  cy[p1][2] = y2;
2083  cx[p2][0] = x3;
2084  cy[p2][0] = y3;
2085  cNext[p1] = p2;
2086 
2087  while (p1 < splashMaxCurveSplits) {
2088 
2089  // get the next segment
2090  xl0 = cx[p1][0];
2091  yl0 = cy[p1][0];
2092  xx1 = cx[p1][1];
2093  yy1 = cy[p1][1];
2094  xx2 = cx[p1][2];
2095  yy2 = cy[p1][2];
2096  p2 = cNext[p1];
2097  xr3 = cx[p2][0];
2098  yr3 = cy[p2][0];
2099 
2100  // compute the distances (in device space) from the control points
2101  // to the midpoint of the straight line (this is a bit of a hack,
2102  // but it's much faster than computing the actual distances to the
2103  // line)
2104  transform(matrix, (xl0 + xr3) * 0.5, (yl0 + yr3) * 0.5, &mx, &my);
2105  transform(matrix, xx1, yy1, &tx, &ty);
2106  dx = tx - mx;
2107  dy = ty - my;
2108  d1 = dx * dx + dy * dy;
2109  transform(matrix, xx2, yy2, &tx, &ty);
2110  dx = tx - mx;
2111  dy = ty - my;
2112  d2 = dx * dx + dy * dy;
2113 
2114  // if the curve is flat enough, or no more subdivisions are
2115  // allowed, add the straight line segment
2116  if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) {
2117  fPath->lineTo(xr3, yr3);
2118  p1 = p2;
2119 
2120  // otherwise, subdivide the curve
2121  } else {
2122  xl1 = splashAvg(xl0, xx1);
2123  yl1 = splashAvg(yl0, yy1);
2124  xh = splashAvg(xx1, xx2);
2125  yh = splashAvg(yy1, yy2);
2126  xl2 = splashAvg(xl1, xh);
2127  yl2 = splashAvg(yl1, yh);
2128  xr2 = splashAvg(xx2, xr3);
2129  yr2 = splashAvg(yy2, yr3);
2130  xr1 = splashAvg(xh, xr2);
2131  yr1 = splashAvg(yh, yr2);
2132  xr0 = splashAvg(xl2, xr1);
2133  yr0 = splashAvg(yl2, yr1);
2134  // add the new subdivision points
2135  p3 = (p1 + p2) / 2;
2136  cx[p1][1] = xl1;
2137  cy[p1][1] = yl1;
2138  cx[p1][2] = xl2;
2139  cy[p1][2] = yl2;
2140  cNext[p1] = p3;
2141  cx[p3][0] = xr0;
2142  cy[p3][0] = yr0;
2143  cx[p3][1] = xr1;
2144  cy[p3][1] = yr1;
2145  cx[p3][2] = xr2;
2146  cy[p3][2] = yr2;
2147  cNext[p3] = p2;
2148  }
2149  }
2150 }
2151 
2153 {
2154  SplashPath *dPath;
2155  SplashCoord lineDashTotal;
2156  SplashCoord lineDashStartPhase, lineDashDist, segLen;
2157  SplashCoord x0, y0, x1, y1, xa, ya;
2158  bool lineDashStartOn, lineDashOn, newPath;
2159  int lineDashStartIdx, lineDashIdx;
2160  int i, j, k;
2161 
2162  lineDashTotal = 0;
2163  for (i = 0; i < state->lineDashLength; ++i) {
2164  lineDashTotal += state->lineDash[i];
2165  }
2166  // Acrobat simply draws nothing if the dash array is [0]
2167  if (lineDashTotal == 0) {
2168  return new SplashPath();
2169  }
2170  lineDashStartPhase = state->lineDashPhase;
2171  i = splashFloor(lineDashStartPhase / lineDashTotal);
2172  lineDashStartPhase -= (SplashCoord)i * lineDashTotal;
2173  lineDashStartOn = true;
2174  lineDashStartIdx = 0;
2175  if (lineDashStartPhase > 0) {
2176  while (lineDashStartIdx < state->lineDashLength && lineDashStartPhase >= state->lineDash[lineDashStartIdx]) {
2177  lineDashStartOn = !lineDashStartOn;
2178  lineDashStartPhase -= state->lineDash[lineDashStartIdx];
2179  ++lineDashStartIdx;
2180  }
2181  if (unlikely(lineDashStartIdx == state->lineDashLength)) {
2182  return new SplashPath();
2183  }
2184  }
2185 
2186  dPath = new SplashPath();
2187 
2188  // process each subpath
2189  i = 0;
2190  while (i < path->length) {
2191 
2192  // find the end of the subpath
2193  for (j = i; j < path->length - 1 && !(path->flags[j] & splashPathLast); ++j)
2194  ;
2195 
2196  // initialize the dash parameters
2197  lineDashOn = lineDashStartOn;
2198  lineDashIdx = lineDashStartIdx;
2199  lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase;
2200 
2201  // process each segment of the subpath
2202  newPath = true;
2203  for (k = i; k < j; ++k) {
2204 
2205  // grab the segment
2206  x0 = path->pts[k].x;
2207  y0 = path->pts[k].y;
2208  x1 = path->pts[k + 1].x;
2209  y1 = path->pts[k + 1].y;
2210  segLen = splashDist(x0, y0, x1, y1);
2211 
2212  // process the segment
2213  while (segLen > 0) {
2214 
2215  if (lineDashDist >= segLen) {
2216  if (lineDashOn) {
2217  if (newPath) {
2218  dPath->moveTo(x0, y0);
2219  newPath = false;
2220  }
2221  dPath->lineTo(x1, y1);
2222  }
2223  lineDashDist -= segLen;
2224  segLen = 0;
2225 
2226  } else {
2227  xa = x0 + (lineDashDist / segLen) * (x1 - x0);
2228  ya = y0 + (lineDashDist / segLen) * (y1 - y0);
2229  if (lineDashOn) {
2230  if (newPath) {
2231  dPath->moveTo(x0, y0);
2232  newPath = false;
2233  }
2234  dPath->lineTo(xa, ya);
2235  }
2236  x0 = xa;
2237  y0 = ya;
2238  segLen -= lineDashDist;
2239  lineDashDist = 0;
2240  }
2241 
2242  // get the next entry in the dash array
2243  if (lineDashDist <= 0) {
2244  lineDashOn = !lineDashOn;
2245  if (++lineDashIdx == state->lineDashLength) {
2246  lineDashIdx = 0;
2247  }
2248  lineDashDist = state->lineDash[lineDashIdx];
2249  newPath = true;
2250  }
2251  }
2252  }
2253  i = j + 1;
2254  }
2255 
2256  if (dPath->length == 0) {
2257  bool allSame = true;
2258  for (i = 0; allSame && i < path->length - 1; ++i) {
2259  allSame = path->pts[i].x == path->pts[i + 1].x && path->pts[i].y == path->pts[i + 1].y;
2260  }
2261  if (allSame) {
2262  x0 = path->pts[0].x;
2263  y0 = path->pts[0].y;
2264  dPath->moveTo(x0, y0);
2265  dPath->lineTo(x0, y0);
2266  }
2267  }
2268 
2269  return dPath;
2270 }
2271 
2273 {
2274  if (debugMode) {
2275  printf("fill [eo:%d]:\n", eo);
2276  dumpPath(path);
2277  }
2278  return fillWithPattern(path, eo, state->fillPattern, state->fillAlpha);
2279 }
2280 
2281 inline void Splash::getBBoxFP(SplashPath *path, SplashCoord *xMinA, SplashCoord *yMinA, SplashCoord *xMaxA, SplashCoord *yMaxA)
2282 {
2283  SplashCoord xMinFP, yMinFP, xMaxFP, yMaxFP, tx, ty;
2284 
2285  // make compiler happy:
2286  xMinFP = xMaxFP = yMinFP = yMaxFP = 0;
2287  for (int i = 0; i < path->length; ++i) {
2288  transform(state->matrix, path->pts[i].x, path->pts[i].y, &tx, &ty);
2289  if (i == 0) {
2290  xMinFP = xMaxFP = tx;
2291  yMinFP = yMaxFP = ty;
2292  } else {
2293  if (tx < xMinFP)
2294  xMinFP = tx;
2295  if (tx > xMaxFP)
2296  xMaxFP = tx;
2297  if (ty < yMinFP)
2298  yMinFP = ty;
2299  if (ty > yMaxFP)
2300  yMaxFP = ty;
2301  }
2302  }
2303 
2304  *xMinA = xMinFP;
2305  *yMinA = yMinFP;
2306  *xMaxA = xMaxFP;
2307  *yMaxA = yMaxFP;
2308 }
2309 
2311 {
2312  SplashPipe pipe = {};
2313  int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
2314  SplashClipResult clipRes, clipRes2;
2315  bool adjustLine = false;
2316  int linePosI = 0;
2317 
2318  if (path->length == 0) {
2319  return splashErrEmptyPath;
2320  }
2321  if (pathAllOutside(path)) {
2323  return splashOk;
2324  }
2325 
2326  // add stroke adjustment hints for filled rectangles -- this only
2327  // applies to paths that consist of a single subpath
2328  // (this appears to match Acrobat's behavior)
2329  if (state->strokeAdjust && !path->hints) {
2330  int n;
2331  n = path->getLength();
2332  if (n == 4 && !(path->flags[0] & splashPathClosed) && !(path->flags[1] & splashPathLast) && !(path->flags[2] & splashPathLast)) {
2333  path->close(true);
2334  path->addStrokeAdjustHint(0, 2, 0, 4);
2335  path->addStrokeAdjustHint(1, 3, 0, 4);
2336  } else if (n == 5 && (path->flags[0] & splashPathClosed) && !(path->flags[1] & splashPathLast) && !(path->flags[2] & splashPathLast) && !(path->flags[3] & splashPathLast)) {
2337  path->addStrokeAdjustHint(0, 2, 0, 4);
2338  path->addStrokeAdjustHint(1, 3, 0, 4);
2339  }
2340  }
2341 
2343  if (state->clip->getXMinI() == state->clip->getXMaxI()) {
2344  linePosI = state->clip->getXMinI();
2345  adjustLine = true;
2346  } else if (state->clip->getXMinI() == state->clip->getXMaxI() - 1) {
2347  adjustLine = true;
2348  linePosI = splashFloor(state->clip->getXMin() + state->lineWidth);
2349  } else if (state->clip->getYMinI() == state->clip->getYMaxI()) {
2350  linePosI = state->clip->getYMinI();
2351  adjustLine = true;
2352  } else if (state->clip->getYMinI() == state->clip->getYMaxI() - 1) {
2353  adjustLine = true;
2354  linePosI = splashFloor(state->clip->getYMin() + state->lineWidth);
2355  }
2356  }
2357 
2358  SplashXPath xPath(path, state->matrix, state->flatness, true, adjustLine, linePosI);
2359  if (vectorAntialias && !inShading) {
2360  xPath.aaScale();
2361  }
2362  xPath.sort();
2363  yMinI = state->clip->getYMinI();
2364  yMaxI = state->clip->getYMaxI();
2365  if (vectorAntialias && !inShading) {
2366  yMinI = yMinI * splashAASize;
2367  yMaxI = (yMaxI + 1) * splashAASize - 1;
2368  }
2369  SplashXPathScanner scanner(&xPath, eo, yMinI, yMaxI);
2370 
2371  // get the min and max x and y values
2372  if (vectorAntialias && !inShading) {
2373  scanner.getBBoxAA(&xMinI, &yMinI, &xMaxI, &yMaxI);
2374  } else {
2375  scanner.getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
2376  }
2377 
2378  if (eo && (yMinI == yMaxI || xMinI == xMaxI) && thinLineMode != splashThinLineDefault) {
2379  SplashCoord delta, xMinFP, yMinFP, xMaxFP, yMaxFP;
2380  getBBoxFP(path, &xMinFP, &yMinFP, &xMaxFP, &yMaxFP);
2381  delta = (yMinI == yMaxI) ? yMaxFP - yMinFP : xMaxFP - xMinFP;
2382  if (delta < 0.2) {
2384  return splashOk;
2385  }
2386  }
2387 
2388  // check clipping
2389  if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI)) != splashClipAllOutside) {
2390  if (scanner.hasPartialClip()) {
2391  clipRes = splashClipPartial;
2392  }
2393 
2394  pipeInit(&pipe, 0, yMinI, pattern, nullptr, (unsigned char)splashRound(alpha * 255), vectorAntialias && !inShading, false);
2395 
2396  // draw the spans
2397  if (vectorAntialias && !inShading) {
2398  for (y = yMinI; y <= yMaxI; ++y) {
2399  scanner.renderAALine(aaBuf, &x0, &x1, y, thinLineMode != splashThinLineDefault && xMinI == xMaxI);
2400  if (clipRes != splashClipAllInside) {
2401  state->clip->clipAALine(aaBuf, &x0, &x1, y, thinLineMode != splashThinLineDefault && xMinI == xMaxI);
2402  }
2403  unsigned char lineShape = 255;
2404  bool doAdjustLine = false;
2405  if (thinLineMode == splashThinLineShape && (xMinI == xMaxI || yMinI == yMaxI)) {
2406  // compute line shape for thin lines:
2407  SplashCoord mx, my, delta;
2408  transform(state->matrix, 0, 0, &mx, &my);
2409  transform(state->matrix, state->lineWidth, 0, &delta, &my);
2410  doAdjustLine = true;
2411  lineShape = clip255((delta - mx) * 255);
2412  }
2413  drawAALine(&pipe, x0, x1, y, doAdjustLine, lineShape);
2414  }
2415  } else {
2416  for (y = yMinI; y <= yMaxI; ++y) {
2417  SplashXPathScanIterator iterator(scanner, y);
2418  while (iterator.getNextSpan(&x0, &x1)) {
2419  if (clipRes == splashClipAllInside) {
2420  drawSpan(&pipe, x0, x1, y, true);
2421  } else {
2422  // limit the x range
2423  if (x0 < state->clip->getXMinI()) {
2424  x0 = state->clip->getXMinI();
2425  }
2426  if (x1 > state->clip->getXMaxI()) {
2427  x1 = state->clip->getXMaxI();
2428  }
2429  clipRes2 = state->clip->testSpan(x0, x1, y);
2430  drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside);
2431  }
2432  }
2433  }
2434  }
2435  }
2436  opClipRes = clipRes;
2437 
2438  return splashOk;
2439 }
2440 
2442 {
2443  SplashCoord xMin1, yMin1, xMax1, yMax1;
2444  SplashCoord xMin2, yMin2, xMax2, yMax2;
2445  SplashCoord x, y;
2446  int xMinI, yMinI, xMaxI, yMaxI;
2447  int i;
2448 
2449  xMin1 = xMax1 = path->pts[0].x;
2450  yMin1 = yMax1 = path->pts[0].y;
2451  for (i = 1; i < path->length; ++i) {
2452  if (path->pts[i].x < xMin1) {
2453  xMin1 = path->pts[i].x;
2454  } else if (path->pts[i].x > xMax1) {
2455  xMax1 = path->pts[i].x;
2456  }
2457  if (path->pts[i].y < yMin1) {
2458  yMin1 = path->pts[i].y;
2459  } else if (path->pts[i].y > yMax1) {
2460  yMax1 = path->pts[i].y;
2461  }
2462  }
2463 
2464  transform(state->matrix, xMin1, yMin1, &x, &y);
2465  xMin2 = xMax2 = x;
2466  yMin2 = yMax2 = y;
2467  transform(state->matrix, xMin1, yMax1, &x, &y);
2468  if (x < xMin2) {
2469  xMin2 = x;
2470  } else if (x > xMax2) {
2471  xMax2 = x;
2472  }
2473  if (y < yMin2) {
2474  yMin2 = y;
2475  } else if (y > yMax2) {
2476  yMax2 = y;
2477  }
2478  transform(state->matrix, xMax1, yMin1, &x, &y);
2479  if (x < xMin2) {
2480  xMin2 = x;
2481  } else if (x > xMax2) {
2482  xMax2 = x;
2483  }
2484  if (y < yMin2) {
2485  yMin2 = y;
2486  } else if (y > yMax2) {
2487  yMax2 = y;
2488  }
2489  transform(state->matrix, xMax1, yMax1, &x, &y);
2490  if (x < xMin2) {
2491  xMin2 = x;
2492  } else if (x > xMax2) {
2493  xMax2 = x;
2494  }
2495  if (y < yMin2) {
2496  yMin2 = y;
2497  } else if (y > yMax2) {
2498  yMax2 = y;
2499  }
2500  xMinI = splashFloor(xMin2);
2501  yMinI = splashFloor(yMin2);
2502  xMaxI = splashFloor(xMax2);
2503  yMaxI = splashFloor(yMax2);
2504 
2505  return state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI) == splashClipAllOutside;
2506 }
2507 
2509 {
2510  SplashPipe pipe;
2511  int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
2512  SplashClipResult clipRes, clipRes2;
2513  SplashBlendFunc origBlendFunc;
2514 
2515  if (path->length == 0) {
2516  return splashErrEmptyPath;
2517  }
2518  SplashXPath xPath(path, state->matrix, state->flatness, true);
2519  xPath.sort();
2520  SplashXPathScanner scanner(&xPath, eo, state->clip->getYMinI(), state->clip->getYMaxI());
2521 
2522  // get the min and max x and y values
2523  scanner.getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
2524 
2525  // check clipping
2526  if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI)) != splashClipAllOutside) {
2527  if (scanner.hasPartialClip()) {
2528  clipRes = splashClipPartial;
2529  }
2530 
2531  origBlendFunc = state->blendFunc;
2532  state->blendFunc = &blendXor;
2533  pipeInit(&pipe, 0, yMinI, state->fillPattern, nullptr, 255, false, false);
2534 
2535  // draw the spans
2536  for (y = yMinI; y <= yMaxI; ++y) {
2537  SplashXPathScanIterator iterator(scanner, y);
2538  while (iterator.getNextSpan(&x0, &x1)) {
2539  if (clipRes == splashClipAllInside) {
2540  drawSpan(&pipe, x0, x1, y, true);
2541  } else {
2542  // limit the x range
2543  if (x0 < state->clip->getXMinI()) {
2544  x0 = state->clip->getXMinI();
2545  }
2546  if (x1 > state->clip->getXMaxI()) {
2547  x1 = state->clip->getXMaxI();
2548  }
2549  clipRes2 = state->clip->testSpan(x0, x1, y);
2550  drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside);
2551  }
2552  }
2553  }
2554  state->blendFunc = origBlendFunc;
2555  }
2556  opClipRes = clipRes;
2557 
2558  return splashOk;
2559 }
2560 
2562 {
2564  SplashCoord xt, yt;
2565  int x0, y0, xFrac, yFrac;
2566  SplashClipResult clipRes;
2567 
2568  if (debugMode) {
2569  printf("fillChar: x=%.2f y=%.2f c=%3d=0x%02x='%c'\n", (double)x, (double)y, c, c, c);
2570  }
2571  transform(state->matrix, x, y, &xt, &yt);
2572  x0 = splashFloor(xt);
2573  xFrac = splashFloor((xt - x0) * splashFontFraction);
2574  y0 = splashFloor(yt);
2575  yFrac = splashFloor((yt - y0) * splashFontFraction);
2576  if (!font->getGlyph(c, xFrac, yFrac, &glyph, x0, y0, state->clip, &clipRes)) {
2577  return splashErrNoGlyph;
2578  }
2579  if (clipRes != splashClipAllOutside) {
2580  fillGlyph2(x0, y0, &glyph, clipRes == splashClipAllInside);
2581  }
2582  opClipRes = clipRes;
2583  if (glyph.freeData) {
2584  gfree(glyph.data);
2585  }
2586  return splashOk;
2587 }
2588 
2590 {
2591  SplashCoord xt, yt;
2592  int x0, y0;
2593 
2594  transform(state->matrix, x, y, &xt, &yt);
2595  x0 = splashFloor(xt);
2596  y0 = splashFloor(yt);
2597  SplashClipResult clipRes = state->clip->testRect(x0 - glyph->x, y0 - glyph->y, x0 - glyph->x + glyph->w - 1, y0 - glyph->y + glyph->h - 1);
2598  if (clipRes != splashClipAllOutside) {
2599  fillGlyph2(x0, y0, glyph, clipRes == splashClipAllInside);
2600  }
2601  opClipRes = clipRes;
2602 }
2603 
2604 void Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph, bool noClip)
2605 {
2606  SplashPipe pipe;
2607  int alpha0;
2608  unsigned char alpha;
2609  unsigned char *p;
2610  int x1, y1, xx, xx1, yy;
2611 
2612  p = glyph->data;
2613  int xStart = x0 - glyph->x;
2614  int yStart = y0 - glyph->y;
2615  int xxLimit = glyph->w;
2616  int yyLimit = glyph->h;
2617  int xShift = 0;
2618 
2619  if (yStart < 0) {
2620  p += (glyph->aa ? glyph->w : splashCeil(glyph->w / 8.0)) * -yStart; // move p to the beginning of the first painted row
2621  yyLimit += yStart;
2622  yStart = 0;
2623  }
2624 
2625  if (xStart < 0) {
2626  if (glyph->aa) {
2627  p += -xStart;
2628  } else {
2629  p += (-xStart) / 8;
2630  xShift = (-xStart) % 8;
2631  }
2632  xxLimit += xStart;
2633  xStart = 0;
2634  }
2635 
2636  if (xxLimit + xStart >= bitmap->width)
2637  xxLimit = bitmap->width - xStart;
2638  if (yyLimit + yStart >= bitmap->height)
2639  yyLimit = bitmap->height - yStart;
2640 
2641  if (noClip) {
2642  if (glyph->aa) {
2643  pipeInit(&pipe, xStart, yStart, state->fillPattern, nullptr, (unsigned char)splashRound(state->fillAlpha * 255), true, false);
2644  for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) {
2645  pipeSetXY(&pipe, xStart, y1);
2646  for (xx = 0, x1 = xStart; xx < xxLimit; ++xx, ++x1) {
2647  alpha = p[xx];
2648  if (alpha != 0) {
2649  pipe.shape = alpha;
2650  (this->*pipe.run)(&pipe);
2651  } else {
2652  pipeIncX(&pipe);
2653  }
2654  }
2655  p += glyph->w;
2656  }
2657  } else {
2658  const int widthEight = splashCeil(glyph->w / 8.0);
2659 
2660  pipeInit(&pipe, xStart, yStart, state->fillPattern, nullptr, (unsigned char)splashRound(state->fillAlpha * 255), false, false);
2661  for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) {
2662  pipeSetXY(&pipe, xStart, y1);
2663  for (xx = 0, x1 = xStart; xx < xxLimit; xx += 8) {
2664  alpha0 = (xShift > 0 && xx < xxLimit - 8 ? (p[xx / 8] << xShift) | (p[xx / 8 + 1] >> (8 - xShift)) : p[xx / 8]);
2665  for (xx1 = 0; xx1 < 8 && xx + xx1 < xxLimit; ++xx1, ++x1) {
2666  if (alpha0 & 0x80) {
2667  (this->*pipe.run)(&pipe);
2668  } else {
2669  pipeIncX(&pipe);
2670  }
2671  alpha0 <<= 1;
2672  }
2673  }
2674  p += widthEight;
2675  }
2676  }
2677  } else {
2678  if (glyph->aa) {
2679  pipeInit(&pipe, xStart, yStart, state->fillPattern, nullptr, (unsigned char)splashRound(state->fillAlpha * 255), true, false);
2680  for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) {
2681  pipeSetXY(&pipe, xStart, y1);
2682  for (xx = 0, x1 = xStart; xx < xxLimit; ++xx, ++x1) {
2683  if (state->clip->test(x1, y1)) {
2684  alpha = p[xx];
2685  if (alpha != 0) {
2686  pipe.shape = alpha;
2687  (this->*pipe.run)(&pipe);
2688  } else {
2689  pipeIncX(&pipe);
2690  }
2691  } else {
2692  pipeIncX(&pipe);
2693  }
2694  }
2695  p += glyph->w;
2696  }
2697  } else {
2698  const int widthEight = splashCeil(glyph->w / 8.0);
2699 
2700  pipeInit(&pipe, xStart, yStart, state->fillPattern, nullptr, (unsigned char)splashRound(state->fillAlpha * 255), false, false);
2701  for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) {
2702  pipeSetXY(&pipe, xStart, y1);
2703  for (xx = 0, x1 = xStart; xx < xxLimit; xx += 8) {
2704  alpha0 = (xShift > 0 && xx < xxLimit - 8 ? (p[xx / 8] << xShift) | (p[xx / 8 + 1] >> (8 - xShift)) : p[xx / 8]);
2705  for (xx1 = 0; xx1 < 8 && xx + xx1 < xxLimit; ++xx1, ++x1) {
2706  if (state->clip->test(x1, y1)) {
2707  if (alpha0 & 0x80) {
2708  (this->*pipe.run)(&pipe);
2709  } else {
2710  pipeIncX(&pipe);
2711  }
2712  } else {
2713  pipeIncX(&pipe);
2714  }
2715  alpha0 <<= 1;
2716  }
2717  }
2718  p += widthEight;
2719  }
2720  }
2721  }
2722 }
2723 
2724 SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData, int w, int h, SplashCoord *mat, bool glyphMode)
2725 {
2726  SplashBitmap *scaledMask;
2727  SplashClipResult clipRes;
2728  bool minorAxisZero;
2729  int x0, y0, x1, y1, scaledWidth, scaledHeight;
2730  int yp;
2731 
2732  if (debugMode) {
2733  printf("fillImageMask: w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n", w, h, (double)mat[0], (double)mat[1], (double)mat[2], (double)mat[3], (double)mat[4], (double)mat[5]);
2734  }
2735 
2736  if (w == 0 && h == 0)
2737  return splashErrZeroImage;
2738 
2739  // check for singular matrix
2740  if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) {
2741  return splashErrSingularMatrix;
2742  }
2743 
2744  minorAxisZero = mat[1] == 0 && mat[2] == 0;
2745 
2746  // scaling only
2747  if (mat[0] > 0 && minorAxisZero && mat[3] > 0) {
2748  x0 = imgCoordMungeLowerC(mat[4], glyphMode);
2749  y0 = imgCoordMungeLowerC(mat[5], glyphMode);
2750  x1 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode);
2751  y1 = imgCoordMungeUpperC(mat[3] + mat[5], glyphMode);
2752  // make sure narrow images cover at least one pixel
2753  if (x0 == x1) {
2754  ++x1;
2755  }
2756  if (y0 == y1) {
2757  ++y1;
2758  }
2759  clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1);
2760  opClipRes = clipRes;
2761  if (clipRes != splashClipAllOutside) {
2762  scaledWidth = x1 - x0;
2763  scaledHeight = y1 - y0;
2764  yp = h / scaledHeight;
2765  if (yp < 0 || yp > INT_MAX - 1) {
2766  return splashErrBadArg;
2767  }
2768  scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight);
2769  blitMask(scaledMask, x0, y0, clipRes);
2770  delete scaledMask;
2771  }
2772 
2773  // scaling plus vertical flip
2774  } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) {
2775  x0 = imgCoordMungeLowerC(mat[4], glyphMode);
2776  y0 = imgCoordMungeLowerC(mat[3] + mat[5], glyphMode);
2777  x1 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode);
2778  y1 = imgCoordMungeUpperC(mat[5], glyphMode);
2779  // make sure narrow images cover at least one pixel
2780  if (x0 == x1) {
2781  ++x1;
2782  }
2783  if (y0 == y1) {
2784  ++y1;
2785  }
2786  clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1);
2787  opClipRes = clipRes;
2788  if (clipRes != splashClipAllOutside) {
2789  scaledWidth = x1 - x0;
2790  scaledHeight = y1 - y0;
2791  yp = h / scaledHeight;
2792  if (yp < 0 || yp > INT_MAX - 1) {
2793  return splashErrBadArg;
2794  }
2795  scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight);
2796  vertFlipImage(scaledMask, scaledWidth, scaledHeight, 1);
2797  blitMask(scaledMask, x0, y0, clipRes);
2798  delete scaledMask;
2799  }
2800 
2801  // all other cases
2802  } else {
2803  arbitraryTransformMask(src, srcData, w, h, mat, glyphMode);
2804  }
2805 
2806  return splashOk;
2807 }
2808 
2809 void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, SplashCoord *mat, bool glyphMode)
2810 {
2811  SplashBitmap *scaledMask;
2812  SplashClipResult clipRes, clipRes2;
2813  SplashPipe pipe;
2814  int scaledWidth, scaledHeight, t0, t1;
2815  SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11;
2816  SplashCoord vx[4], vy[4];
2817  int xMin, yMin, xMax, yMax;
2818  ImageSection section[3];
2819  int nSections;
2820  int y, xa, xb, x, i, xx, yy;
2821 
2822  // compute the four vertices of the target quadrilateral
2823  vx[0] = mat[4];
2824  vy[0] = mat[5];
2825  vx[1] = mat[2] + mat[4];
2826  vy[1] = mat[3] + mat[5];
2827  vx[2] = mat[0] + mat[2] + mat[4];
2828  vy[2] = mat[1] + mat[3] + mat[5];
2829  vx[3] = mat[0] + mat[4];
2830  vy[3] = mat[1] + mat[5];
2831 
2832  // make sure vx/vy fit in integers since we're transforming them to in the next lines
2833  for (i = 0; i < 4; ++i) {
2834  if (unlikely(vx[i] < INT_MIN || vx[i] > INT_MAX || vy[i] < INT_MIN || vy[i] > INT_MAX)) {
2835  error(errInternal, -1, "arbitraryTransformMask vertices values don't fit in an integer");
2836  return;
2837  }
2838  }
2839 
2840  // clipping
2841  xMin = imgCoordMungeLowerC(vx[0], glyphMode);
2842  xMax = imgCoordMungeUpperC(vx[0], glyphMode);
2843  yMin = imgCoordMungeLowerC(vy[0], glyphMode);
2844  yMax = imgCoordMungeUpperC(vy[0], glyphMode);
2845  for (i = 1; i < 4; ++i) {
2846  t0 = imgCoordMungeLowerC(vx[i], glyphMode);
2847  if (t0 < xMin) {
2848  xMin = t0;
2849  }
2850  t0 = imgCoordMungeUpperC(vx[i], glyphMode);
2851  if (t0 > xMax) {
2852  xMax = t0;
2853  }
2854  t1 = imgCoordMungeLowerC(vy[i], glyphMode);
2855  if (t1 < yMin) {
2856  yMin = t1;
2857  }
2858  t1 = imgCoordMungeUpperC(vy[i], glyphMode);
2859  if (t1 > yMax) {
2860  yMax = t1;
2861  }
2862  }
2863  clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1);
2864  opClipRes = clipRes;
2865  if (clipRes == splashClipAllOutside) {
2866  return;
2867  }
2868 
2869  // compute the scale factors
2870  if (mat[0] >= 0) {
2871  t0 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode) - imgCoordMungeLowerC(mat[4], glyphMode);
2872  } else {
2873  t0 = imgCoordMungeUpperC(mat[4], glyphMode) - imgCoordMungeLowerC(mat[0] + mat[4], glyphMode);
2874  }
2875  if (mat[1] >= 0) {
2876  t1 = imgCoordMungeUpperC(mat[1] + mat[5], glyphMode) - imgCoordMungeLowerC(mat[5], glyphMode);
2877  } else {
2878  t1 = imgCoordMungeUpperC(mat[5], glyphMode) - imgCoordMungeLowerC(mat[1] + mat[5], glyphMode);
2879  }
2880  scaledWidth = t0 > t1 ? t0 : t1;
2881  if (mat[2] >= 0) {
2882  t0 = imgCoordMungeUpperC(mat[2] + mat[4], glyphMode) - imgCoordMungeLowerC(mat[4], glyphMode);
2883  } else {
2884  t0 = imgCoordMungeUpperC(mat[4], glyphMode) - imgCoordMungeLowerC(mat[2] + mat[4], glyphMode);
2885  }
2886  if (mat[3] >= 0) {
2887  t1 = imgCoordMungeUpperC(mat[3] + mat[5], glyphMode) - imgCoordMungeLowerC(mat[5], glyphMode);
2888  } else {
2889  t1 = imgCoordMungeUpperC(mat[5], glyphMode) - imgCoordMungeLowerC(mat[3] + mat[5], glyphMode);
2890  }
2891  scaledHeight = t0 > t1 ? t0 : t1;
2892  if (scaledWidth == 0) {
2893  scaledWidth = 1;
2894  }
2895  if (scaledHeight == 0) {
2896  scaledHeight = 1;
2897  }
2898 
2899  // compute the inverse transform (after scaling) matrix
2900  r00 = mat[0] / scaledWidth;
2901  r01 = mat[1] / scaledWidth;
2902  r10 = mat[2] / scaledHeight;
2903  r11 = mat[3] / scaledHeight;
2904  det = r00 * r11 - r01 * r10;
2905  if (splashAbs(det) < 1e-6) {
2906  // this should be caught by the singular matrix check in fillImageMask
2907  return;
2908  }
2909  ir00 = r11 / det;
2910  ir01 = -r01 / det;
2911  ir10 = -r10 / det;
2912  ir11 = r00 / det;
2913 
2914  // scale the input image
2915  scaledMask = scaleMask(src, srcData, srcWidth, srcHeight, scaledWidth, scaledHeight);
2916  if (scaledMask->data == nullptr) {
2917  error(errInternal, -1, "scaledMask->data is NULL in Splash::arbitraryTransformMask");
2918  delete scaledMask;
2919  return;
2920  }
2921 
2922  // construct the three sections
2923  i = (vy[2] <= vy[3]) ? 2 : 3;
2924  if (vy[1] <= vy[i]) {
2925  i = 1;
2926  }
2927  if (vy[0] < vy[i] || (i != 3 && vy[0] == vy[i])) {
2928  i = 0;
2929  }
2930  if (vy[i] == vy[(i + 1) & 3]) {
2931  section[0].y0 = imgCoordMungeLowerC(vy[i], glyphMode);
2932  section[0].y1 = imgCoordMungeUpperC(vy[(i + 2) & 3], glyphMode) - 1;
2933  if (vx[i] < vx[(i + 1) & 3]) {
2934  section[0].ia0 = i;
2935  section[0].ia1 = (i + 3) & 3;
2936  section[0].ib0 = (i + 1) & 3;
2937  section[0].ib1 = (i + 2) & 3;
2938  } else {
2939  section[0].ia0 = (i + 1) & 3;
2940  section[0].ia1 = (i + 2) & 3;
2941  section[0].ib0 = i;
2942  section[0].ib1 = (i + 3) & 3;
2943  }
2944  nSections = 1;
2945  } else {
2946  section[0].y0 = imgCoordMungeLowerC(vy[i], glyphMode);
2947  section[2].y1 = imgCoordMungeUpperC(vy[(i + 2) & 3], glyphMode) - 1;
2948  section[0].ia0 = section[0].ib0 = i;
2949  section[2].ia1 = section[2].ib1 = (i + 2) & 3;
2950  if (vx[(i + 1) & 3] < vx[(i + 3) & 3]) {
2951  section[0].ia1 = section[2].ia0 = (i + 1) & 3;
2952  section[0].ib1 = section[2].ib0 = (i + 3) & 3;
2953  } else {
2954  section[0].ia1 = section[2].ia0 = (i + 3) & 3;
2955  section[0].ib1 = section[2].ib0 = (i + 1) & 3;
2956  }
2957  if (vy[(i + 1) & 3] < vy[(i + 3) & 3]) {
2958  section[1].y0 = imgCoordMungeLowerC(vy[(i + 1) & 3], glyphMode);
2959  section[2].y0 = imgCoordMungeUpperC(vy[(i + 3) & 3], glyphMode);
2960  if (vx[(i + 1) & 3] < vx[(i + 3) & 3]) {
2961  section[1].ia0 = (i + 1) & 3;
2962  section[1].ia1 = (i + 2) & 3;
2963  section[1].ib0 = i;
2964  section[1].ib1 = (i + 3) & 3;
2965  } else {
2966  section[1].ia0 = i;
2967  section[1].ia1 = (i + 3) & 3;
2968  section[1].ib0 = (i + 1) & 3;
2969  section[1].ib1 = (i + 2) & 3;
2970  }
2971  } else {
2972  section[1].y0 = imgCoordMungeLowerC(vy[(i + 3) & 3], glyphMode);
2973  section[2].y0 = imgCoordMungeUpperC(vy[(i + 1) & 3], glyphMode);
2974  if (vx[(i + 1) & 3] < vx[(i + 3) & 3]) {
2975  section[1].ia0 = i;
2976  section[1].ia1 = (i + 1) & 3;
2977  section[1].ib0 = (i + 3) & 3;
2978  section[1].ib1 = (i + 2) & 3;
2979  } else {
2980  section[1].ia0 = (i + 3) & 3;
2981  section[1].ia1 = (i + 2) & 3;
2982  section[1].ib0 = i;
2983  section[1].ib1 = (i + 1) & 3;
2984  }
2985  }
2986  section[0].y1 = section[1].y0 - 1;
2987  section[1].y1 = section[2].y0 - 1;
2988  nSections = 3;
2989  }
2990  for (i = 0; i < nSections; ++i) {
2991  section[i].xa0 = vx[section[i].ia0];
2992  section[i].ya0 = vy[section[i].ia0];
2993  section[i].xa1 = vx[section[i].ia1];
2994  section[i].ya1 = vy[section[i].ia1];
2995  section[i].xb0 = vx[section[i].ib0];
2996  section[i].yb0 = vy[section[i].ib0];
2997  section[i].xb1 = vx[section[i].ib1];
2998  section[i].yb1 = vy[section[i].ib1];
2999  section[i].dxdya = (section[i].xa1 - section[i].xa0) / (section[i].ya1 - section[i].ya0);
3000  section[i].dxdyb = (section[i].xb1 - section[i].xb0) / (section[i].yb1 - section[i].yb0);
3001  }
3002 
3003  // initialize the pixel pipe
3004  pipeInit(&pipe, 0, 0, state->fillPattern, nullptr, (unsigned char)splashRound(state->fillAlpha * 255), true, false);
3005  if (vectorAntialias) {
3006  drawAAPixelInit();
3007  }
3008 
3009  // make sure narrow images cover at least one pixel
3010  if (nSections == 1) {
3011  if (section[0].y0 == section[0].y1) {
3012  ++section[0].y1;
3013  clipRes = opClipRes = splashClipPartial;
3014  }
3015  } else {
3016  if (section[0].y0 == section[2].y1) {
3017  ++section[1].y1;
3018  clipRes = opClipRes = splashClipPartial;
3019  }
3020  }
3021 
3022  // scan all pixels inside the target region
3023  for (i = 0; i < nSections; ++i) {
3024  for (y = section[i].y0; y <= section[i].y1; ++y) {
3025  xa = imgCoordMungeLowerC(section[i].xa0 + ((SplashCoord)y + 0.5 - section[i].ya0) * section[i].dxdya, glyphMode);
3026  xb = imgCoordMungeUpperC(section[i].xb0 + ((SplashCoord)y + 0.5 - section[i].yb0) * section[i].dxdyb, glyphMode);
3027  if (unlikely(xa < 0))
3028  xa = 0;
3029  // make sure narrow images cover at least one pixel
3030  if (xa == xb) {
3031  ++xb;
3032  }
3033  if (clipRes != splashClipAllInside) {
3034  clipRes2 = state->clip->testSpan(xa, xb - 1, y);
3035  } else {
3036  clipRes2 = clipRes;
3037  }
3038  for (x = xa; x < xb; ++x) {
3039  // map (x+0.5, y+0.5) back to the scaled image
3040  xx = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir00 + ((SplashCoord)y + 0.5 - mat[5]) * ir10);
3041  yy = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir01 + ((SplashCoord)y + 0.5 - mat[5]) * ir11);
3042  // xx should always be within bounds, but floating point
3043  // inaccuracy can cause problems
3044  if (unlikely(xx < 0)) {
3045  xx = 0;
3046  clipRes2 = splashClipPartial;
3047  } else if (unlikely(xx >= scaledWidth)) {
3048  xx = scaledWidth - 1;
3049  clipRes2 = splashClipPartial;
3050  }
3051  if (unlikely(yy < 0)) {
3052  yy = 0;
3053  clipRes2 = splashClipPartial;
3054  } else if (unlikely(yy >= scaledHeight)) {
3055  yy = scaledHeight - 1;
3056  clipRes2 = splashClipPartial;
3057  }
3058  pipe.shape = scaledMask->data[yy * scaledWidth + xx];
3059  if (vectorAntialias && clipRes2 != splashClipAllInside) {
3060  drawAAPixel(&pipe, x, y);
3061  } else {
3062  drawPixel(&pipe, x, y, clipRes2 == splashClipAllInside);
3063  }
3064  }
3065  }
3066  }
3067 
3068  delete scaledMask;
3069 }
3070 
3071 // Scale an image mask into a SplashBitmap.
3072 SplashBitmap *Splash::scaleMask(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight)
3073 {
3074  SplashBitmap *dest;
3075 
3076  dest = new SplashBitmap(scaledWidth, scaledHeight, 1, splashModeMono8, false);
3077  if (scaledHeight < srcHeight) {
3078  if (scaledWidth < srcWidth) {
3079  scaleMaskYdownXdown(src, srcData, srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
3080  } else {
3081  scaleMaskYdownXup(src, srcData, srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
3082  }
3083  } else {
3084  if (scaledWidth < srcWidth) {
3085  scaleMaskYupXdown(src, srcData, srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
3086  } else {
3087  scaleMaskYupXup(src, srcData, srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
3088  }
3089  }
3090  return dest;
3091 }
3092 
3093 void Splash::scaleMaskYdownXdown(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest)
3094 {
3095  unsigned char *lineBuf;
3096  unsigned int *pixBuf;
3097  unsigned int pix;
3098  unsigned char *destPtr;
3099  int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, d, d0, d1;
3100  int i, j;
3101 
3102  // Bresenham parameters for y scale
3103  yp = srcHeight / scaledHeight;
3104  yq = srcHeight % scaledHeight;
3105 
3106  // Bresenham parameters for x scale
3107  xp = srcWidth / scaledWidth;
3108  xq = srcWidth % scaledWidth;
3109 
3110  // allocate buffers
3111  lineBuf = (unsigned char *)gmalloc(srcWidth);
3112  pixBuf = (unsigned int *)gmallocn_checkoverflow(srcWidth, sizeof(int));
3113  if (unlikely(!pixBuf)) {
3114  error(errInternal, -1, "Couldn't allocate memory for pixBux in Splash::scaleMaskYdownXdown");
3115  gfree(lineBuf);
3116  return;
3117  }
3118 
3119  // init y scale Bresenham
3120  yt = 0;
3121 
3122  destPtr = dest->data;
3123  for (y = 0; y < scaledHeight; ++y) {
3124 
3125  // y scale Bresenham
3126  if ((yt += yq) >= scaledHeight) {
3127  yt -= scaledHeight;
3128  yStep = yp + 1;
3129  } else {
3130  yStep = yp;
3131  }
3132 
3133  // read rows from image
3134  memset(pixBuf, 0, srcWidth * sizeof(int));
3135  for (i = 0; i < yStep; ++i) {
3136  (*src)(srcData, lineBuf);
3137  for (j = 0; j < srcWidth; ++j) {
3138  pixBuf[j] += lineBuf[j];
3139  }
3140  }
3141 
3142  // init x scale Bresenham
3143  xt = 0;
3144  d0 = (255 << 23) / (yStep * xp);
3145  d1 = (255 << 23) / (yStep * (xp + 1));
3146 
3147  xx = 0;
3148  for (x = 0; x < scaledWidth; ++x) {
3149 
3150  // x scale Bresenham
3151  if ((xt += xq) >= scaledWidth) {
3152  xt -= scaledWidth;
3153  xStep = xp + 1;
3154  d = d1;
3155  } else {
3156  xStep = xp;
3157  d = d0;
3158  }
3159 
3160  // compute the final pixel
3161  pix = 0;
3162  for (i = 0; i < xStep; ++i) {
3163  pix += pixBuf[xx++];
3164  }
3165  // (255 * pix) / xStep * yStep
3166  pix = (pix * d) >> 23;
3167 
3168  // store the pixel
3169  *destPtr++ = (unsigned char)pix;
3170  }
3171  }
3172 
3173  gfree(pixBuf);
3174  gfree(lineBuf);
3175 }
3176 
3177 void Splash::scaleMaskYdownXup(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest)
3178 {
3179  unsigned char *lineBuf;
3180  unsigned int *pixBuf;
3181  unsigned int pix;
3182  unsigned char *destPtr;
3183  int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, d;
3184  int i, j;
3185 
3186  destPtr = dest->data;
3187  if (destPtr == nullptr) {
3188  error(errInternal, -1, "dest->data is NULL in Splash::scaleMaskYdownXup");
3189  return;
3190  }
3191 
3192  // Bresenham parameters for y scale
3193  yp = srcHeight / scaledHeight;
3194  yq = srcHeight % scaledHeight;
3195 
3196  // Bresenham parameters for x scale
3197  xp = scaledWidth / srcWidth;
3198  xq = scaledWidth % srcWidth;
3199 
3200  // allocate buffers
3201  lineBuf = (unsigned char *)gmalloc(srcWidth);
3202  pixBuf = (unsigned int *)gmallocn(srcWidth, sizeof(int));
3203 
3204  // init y scale Bresenham
3205  yt = 0;
3206 
3207  for (y = 0; y < scaledHeight; ++y) {
3208 
3209  // y scale Bresenham
3210  if ((yt += yq) >= scaledHeight) {
3211  yt -= scaledHeight;
3212  yStep = yp + 1;
3213  } else {
3214  yStep = yp;
3215  }
3216 
3217  // read rows from image
3218  memset(pixBuf, 0, srcWidth * sizeof(int));
3219  for (i = 0; i < yStep; ++i) {
3220  (*src)(srcData, lineBuf);
3221  for (j = 0; j < srcWidth; ++j) {
3222  pixBuf[j] += lineBuf[j];
3223  }
3224  }
3225 
3226  // init x scale Bresenham
3227  xt = 0;
3228  d = (255 << 23) / yStep;
3229 
3230  for (x = 0; x < srcWidth; ++x) {
3231 
3232  // x scale Bresenham
3233  if ((xt += xq) >= srcWidth) {
3234  xt -= srcWidth;
3235  xStep = xp + 1;
3236  } else {
3237  xStep = xp;
3238  }
3239 
3240  // compute the final pixel
3241  pix = pixBuf[x];
3242  // (255 * pix) / yStep
3243  pix = (pix * d) >> 23;
3244 
3245  // store the pixel
3246  for (i = 0; i < xStep; ++i) {
3247  *destPtr++ = (unsigned char)pix;
3248  }
3249  }
3250  }
3251 
3252  gfree(pixBuf);
3253  gfree(lineBuf);
3254 }
3255 
3256 void Splash::scaleMaskYupXdown(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest)
3257 {
3258  unsigned char *lineBuf;
3259  unsigned int pix;
3260  unsigned char *destPtr0, *destPtr;
3261  int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, d, d0, d1;
3262  int i;
3263 
3264  destPtr0 = dest->data;
3265  if (destPtr0 == nullptr) {
3266  error(errInternal, -1, "dest->data is NULL in Splash::scaleMaskYupXdown");
3267  return;
3268  }
3269 
3270  // Bresenham parameters for y scale
3271  yp = scaledHeight / srcHeight;
3272  yq = scaledHeight % srcHeight;
3273 
3274  // Bresenham parameters for x scale
3275  xp = srcWidth / scaledWidth;
3276  xq = srcWidth % scaledWidth;
3277 
3278  // allocate buffers
3279  lineBuf = (unsigned char *)gmalloc(srcWidth);
3280 
3281  // init y scale Bresenham
3282  yt = 0;
3283 
3284  for (y = 0; y < srcHeight; ++y) {
3285 
3286  // y scale Bresenham
3287  if ((yt += yq) >= srcHeight) {
3288  yt -= srcHeight;
3289  yStep = yp + 1;
3290  } else {
3291  yStep = yp;
3292  }
3293 
3294  // read row from image
3295  (*src)(srcData, lineBuf);
3296 
3297  // init x scale Bresenham
3298  xt = 0;
3299  d0 = (255 << 23) / xp;
3300  d1 = (255 << 23) / (xp + 1);
3301 
3302  xx = 0;
3303  for (x = 0; x < scaledWidth; ++x) {
3304 
3305  // x scale Bresenham
3306  if ((xt += xq) >= scaledWidth) {
3307  xt -= scaledWidth;
3308  xStep = xp + 1;
3309  d = d1;
3310  } else {
3311  xStep = xp;
3312  d = d0;
3313  }
3314 
3315  // compute the final pixel
3316  pix = 0;
3317  for (i = 0; i < xStep; ++i) {
3318  pix += lineBuf[xx++];
3319  }
3320  // (255 * pix) / xStep
3321  pix = (pix * d) >> 23;
3322 
3323  // store the pixel
3324  for (i = 0; i < yStep; ++i) {
3325  destPtr = destPtr0 + i * scaledWidth + x;
3326  *destPtr = (unsigned char)pix;
3327  }
3328  }
3329 
3330  destPtr0 += yStep * scaledWidth;
3331  }
3332 
3333  gfree(lineBuf);
3334 }
3335 
3336 void Splash::scaleMaskYupXup(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest)
3337 {
3338  unsigned char *lineBuf;
3339  unsigned int pix;
3340  unsigned char *destPtr0, *destPtr;
3341  int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx;
3342  int i, j;
3343 
3344  destPtr0 = dest->data;
3345  if (destPtr0 == nullptr) {
3346  error(errInternal, -1, "dest->data is NULL in Splash::scaleMaskYupXup");
3347  return;
3348  }
3349 
3350  if (unlikely(srcWidth <= 0 || srcHeight <= 0)) {
3351  error(errSyntaxError, -1, "srcWidth <= 0 || srcHeight <= 0 in Splash::scaleMaskYupXup");
3352  gfree(dest->takeData());
3353  return;
3354  }
3355 
3356  // Bresenham parameters for y scale
3357  yp = scaledHeight / srcHeight;
3358  yq = scaledHeight % srcHeight;
3359 
3360  // Bresenham parameters for x scale
3361  xp = scaledWidth / srcWidth;
3362  xq = scaledWidth % srcWidth;
3363 
3364  // allocate buffers
3365  lineBuf = (unsigned char *)gmalloc(srcWidth);
3366 
3367  // init y scale Bresenham
3368  yt = 0;
3369 
3370  for (y = 0; y < srcHeight; ++y) {
3371 
3372  // y scale Bresenham
3373  if ((yt += yq) >= srcHeight) {
3374  yt -= srcHeight;
3375  yStep = yp + 1;
3376  } else {
3377  yStep = yp;
3378  }
3379 
3380  // read row from image
3381  (*src)(srcData, lineBuf);
3382 
3383  // init x scale Bresenham
3384  xt = 0;
3385 
3386  xx = 0;
3387  for (x = 0; x < srcWidth; ++x) {
3388 
3389  // x scale Bresenham
3390  if ((xt += xq) >= srcWidth) {
3391  xt -= srcWidth;
3392  xStep = xp + 1;
3393  } else {
3394  xStep = xp;
3395  }
3396 
3397  // compute the final pixel
3398  pix = lineBuf[x] ? 255 : 0;
3399 
3400  // store the pixel
3401  for (i = 0; i < yStep; ++i) {
3402  for (j = 0; j < xStep; ++j) {
3403  destPtr = destPtr0 + i * scaledWidth + xx + j;
3404  *destPtr++ = (unsigned char)pix;
3405  }
3406  }
3407 
3408  xx += xStep;
3409  }
3410 
3411  destPtr0 += yStep * scaledWidth;
3412  }
3413 
3414  gfree(lineBuf);
3415 }
3416 
3417 void Splash::blitMask(SplashBitmap *src, int xDest, int yDest, SplashClipResult clipRes)
3418 {
3419  SplashPipe pipe;
3420  unsigned char *p;
3421  int w, h, x, y;
3422 
3423  w = src->getWidth();
3424  h = src->getHeight();
3425  p = src->getDataPtr();
3426  if (p == nullptr) {
3427  error(errInternal, -1, "src->getDataPtr() is NULL in Splash::blitMask");
3428  return;
3429  }
3430  if (vectorAntialias && clipRes != splashClipAllInside) {
3431  pipeInit(&pipe, xDest, yDest, state->fillPattern, nullptr, (unsigned char)splashRound(state->fillAlpha * 255), true, false);
3432  drawAAPixelInit();
3433  for (y = 0; y < h; ++y) {
3434  for (x = 0; x < w; ++x) {
3435  pipe.shape = *p++;
3436  drawAAPixel(&pipe, xDest + x, yDest + y);
3437  }
3438  }
3439  } else {
3440  pipeInit(&pipe, xDest, yDest, state->fillPattern, nullptr, (unsigned char)splashRound(state->fillAlpha * 255), true, false);
3441  if (clipRes == splashClipAllInside) {
3442  for (y = 0; y < h; ++y) {
3443  pipeSetXY(&pipe, xDest, yDest + y);
3444  for (x = 0; x < w; ++x) {
3445  if (*p) {
3446  pipe.shape = *p;
3447  (this->*pipe.run)(&pipe);
3448  } else {
3449  pipeIncX(&pipe);
3450  }
3451  ++p;
3452  }
3453  }
3454  } else {
3455  for (y = 0; y < h; ++y) {
3456  pipeSetXY(&pipe, xDest, yDest + y);
3457  for (x = 0; x < w; ++x) {
3458  if (*p && state->clip->test(xDest + x, yDest + y)) {
3459  pipe.shape = *p;
3460  (this->*pipe.run)(&pipe);
3461  } else {
3462  pipeIncX(&pipe);
3463  }
3464  ++p;
3465  }
3466  }
3467  }
3468  }
3469 }
3470 
3471 SplashError Splash::drawImage(SplashImageSource src, SplashICCTransform tf, void *srcData, SplashColorMode srcMode, bool srcAlpha, int w, int h, SplashCoord *mat, bool interpolate, bool tilingPattern)
3472 {
3473  bool ok;
3474  SplashBitmap *scaledImg;
3475  SplashClipResult clipRes;
3476  bool minorAxisZero;
3477  int x0, y0, x1, y1, scaledWidth, scaledHeight;
3478  int nComps;
3479  int yp;
3480 
3481  if (debugMode) {
3482  printf("drawImage: srcMode=%d srcAlpha=%d w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n", srcMode, srcAlpha, w, h, (double)mat[0], (double)mat[1], (double)mat[2], (double)mat[3], (double)mat[4], (double)mat[5]);
3483  }
3484 
3485  // check color modes
3486  ok = false; // make gcc happy
3487  nComps = 0; // make gcc happy
3488  switch (bitmap->mode) {
3489  case splashModeMono1:
3490  case splashModeMono8:
3491  ok = srcMode == splashModeMono8;
3492  nComps = 1;
3493  break;
3494  case splashModeRGB8:
3495  ok = srcMode == splashModeRGB8;
3496  nComps = 3;
3497  break;
3498  case splashModeXBGR8:
3499  ok = srcMode == splashModeXBGR8;
3500  nComps = 4;
3501  break;
3502  case splashModeBGR8:
3503  ok = srcMode == splashModeBGR8;
3504  nComps = 3;
3505  break;
3506  case splashModeCMYK8:
3507  ok = srcMode == splashModeCMYK8;
3508  nComps = 4;
3509  break;
3510  case splashModeDeviceN8:
3511  ok = srcMode == splashModeDeviceN8;
3512  nComps = SPOT_NCOMPS + 4;
3513  break;
3514  default:
3515  ok = false;
3516  break;
3517  }
3518  if (!ok) {
3519  return splashErrModeMismatch;
3520  }
3521 
3522  // check for singular matrix
3523  if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) {
3524  return splashErrSingularMatrix;
3525  }
3526 
3527  minorAxisZero = mat[1] == 0 && mat[2] == 0;
3528 
3529  // scaling only
3530  if (mat[0] > 0 && minorAxisZero && mat[3] > 0) {
3531  x0 = imgCoordMungeLower(mat[4]);
3532  y0 = imgCoordMungeLower(mat[5]);
3533  x1 = imgCoordMungeUpper(mat[0] + mat[4]);
3534  y1 = imgCoordMungeUpper(mat[3] + mat[5]);
3535  // make sure narrow images cover at least one pixel
3536  if (x0 == x1) {
3537  ++x1;
3538  }
3539  if (y0 == y1) {
3540  ++y1;
3541  }
3542  clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1);
3543  opClipRes = clipRes;
3544  if (clipRes != splashClipAllOutside) {
3545  scaledWidth = x1 - x0;
3546  scaledHeight = y1 - y0;
3547  yp = h / scaledHeight;
3548  if (yp < 0 || yp > INT_MAX - 1) {
3549  return splashErrBadArg;
3550  }
3551  scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, scaledWidth, scaledHeight, interpolate, tilingPattern);
3552  if (scaledImg == nullptr) {
3553  return splashErrBadArg;
3554  }
3555  if (tf != nullptr) {
3556  (*tf)(srcData, scaledImg);
3557  }
3558  blitImage(scaledImg, srcAlpha, x0, y0, clipRes);
3559  delete scaledImg;
3560  }
3561 
3562  // scaling plus vertical flip
3563  } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) {
3564  x0 = imgCoordMungeLower(mat[4]);
3565  y0 = imgCoordMungeLower(mat[3] + mat[5]);
3566  x1 = imgCoordMungeUpper(mat[0] + mat[4]);
3567  y1 = imgCoordMungeUpper(mat[5]);
3568  if (x0 == x1) {
3569  if (mat[4] + mat[0] * 0.5 < x0) {
3570  --x0;
3571  } else {
3572  ++x1;
3573  }
3574  }
3575  if (y0 == y1) {
3576  if (mat[5] + mat[1] * 0.5 < y0) {
3577  --y0;
3578  } else {
3579  ++y1;
3580  }
3581  }
3582  clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1);
3583  opClipRes = clipRes;
3584  if (clipRes != splashClipAllOutside) {
3585  scaledWidth = x1 - x0;
3586  scaledHeight = y1 - y0;
3587  yp = h / scaledHeight;
3588  if (yp < 0 || yp > INT_MAX - 1) {
3589  return splashErrBadArg;
3590  }
3591  scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, scaledWidth, scaledHeight, interpolate, tilingPattern);
3592  if (scaledImg == nullptr) {
3593  return splashErrBadArg;
3594  }
3595  if (tf != nullptr) {
3596  (*tf)(srcData, scaledImg);
3597  }
3598  vertFlipImage(scaledImg, scaledWidth, scaledHeight, nComps);
3599  blitImage(scaledImg, srcAlpha, x0, y0, clipRes);
3600  delete scaledImg;
3601  }
3602 
3603  // all other cases
3604  } else {
3605  return arbitraryTransformImage(src, tf, srcData, srcMode, nComps, srcAlpha, w, h, mat, interpolate, tilingPattern);
3606  }
3607 
3608  return splashOk;
3609 }
3610 
3611 SplashError Splash::arbitraryTransformImage(SplashImageSource src, SplashICCTransform tf, void *srcData, SplashColorMode srcMode, int nComps, bool srcAlpha, int srcWidth, int srcHeight, SplashCoord *mat, bool interpolate,
3612  bool tilingPattern)
3613 {
3614  SplashBitmap *scaledImg;
3615  SplashClipResult clipRes, clipRes2;
3616  SplashPipe pipe;
3617  SplashColor pixel = {};
3618  int scaledWidth, scaledHeight, t0, t1, th;
3619  SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11;
3620  SplashCoord vx[4], vy[4];
3621  int xMin, yMin, xMax, yMax;
3622  ImageSection section[3];
3623  int nSections;
3624  int y, xa, xb, x, i, xx, yy, yp;
3625 
3626  // compute the four vertices of the target quadrilateral
3627  vx[0] = mat[4];
3628  vy[0] = mat[5];
3629  vx[1] = mat[2] + mat[4];
3630  vy[1] = mat[3] + mat[5];
3631  vx[2] = mat[0] + mat[2] + mat[4];
3632  vy[2] = mat[1] + mat[3] + mat[5];
3633  vx[3] = mat[0] + mat[4];
3634  vy[3] = mat[1] + mat[5];
3635 
3636  // clipping
3637  xMin = imgCoordMungeLower(vx[0]);
3638  xMax = imgCoordMungeUpper(vx[0]);
3639  yMin = imgCoordMungeLower(vy[0]);
3640  yMax = imgCoordMungeUpper(vy[0]);
3641  for (i = 1; i < 4; ++i) {
3642  t0 = imgCoordMungeLower(vx[i]);
3643  if (t0 < xMin) {
3644  xMin = t0;
3645  }
3646  t0 = imgCoordMungeUpper(vx[i]);
3647  if (t0 > xMax) {
3648  xMax = t0;
3649  }
3650  t1 = imgCoordMungeLower(vy[i]);
3651  if (t1 < yMin) {
3652  yMin = t1;
3653  }
3654  t1 = imgCoordMungeUpper(vy[i]);
3655  if (t1 > yMax) {
3656  yMax = t1;
3657  }
3658  }
3659  clipRes = state->clip->testRect(xMin, yMin, xMax, yMax);
3660  opClipRes = clipRes;
3661  if (clipRes == splashClipAllOutside) {
3662  return splashOk;
3663  }
3664 
3665  // compute the scale factors
3666  if (splashAbs(mat[0]) >= splashAbs(mat[1])) {
3667  scaledWidth = xMax - xMin;
3668  scaledHeight = yMax - yMin;
3669  } else {
3670  scaledWidth = yMax - yMin;
3671  scaledHeight = xMax - xMin;
3672  }
3673  if (scaledHeight <= 1 || scaledWidth <= 1 || tilingPattern) {
3674  if (mat[0] >= 0) {
3675  t0 = imgCoordMungeUpper(mat[0] + mat[4]) - imgCoordMungeLower(mat[4]);
3676  } else {
3677  t0 = imgCoordMungeUpper(mat[4]) - imgCoordMungeLower(mat[0] + mat[4]);
3678  }
3679  if (mat[1] >= 0) {
3680  t1 = imgCoordMungeUpper(mat[1] + mat[5]) - imgCoordMungeLower(mat[5]);
3681  } else {
3682  t1 = imgCoordMungeUpper(mat[5]) - imgCoordMungeLower(mat[1] + mat[5]);
3683  }
3684  scaledWidth = t0 > t1 ? t0 : t1;
3685  if (mat[2] >= 0) {
3686  t0 = imgCoordMungeUpper(mat[2] + mat[4]) - imgCoordMungeLower(mat[4]);
3687  if (splashAbs(mat[1]) >= 1) {
3688  th = imgCoordMungeUpper(mat[2]) - imgCoordMungeLower(mat[0] * mat[3] / mat[1]);
3689  if (th > t0)
3690  t0 = th;
3691  }
3692  } else {
3693  t0 = imgCoordMungeUpper(mat[4]) - imgCoordMungeLower(mat[2] + mat[4]);
3694  if (splashAbs(mat[1]) >= 1) {
3695  th = imgCoordMungeUpper(mat[0] * mat[3] / mat[1]) - imgCoordMungeLower(mat[2]);
3696  if (th > t0)
3697  t0 = th;
3698  }
3699  }
3700  if (mat[3] >= 0) {
3701  t1 = imgCoordMungeUpper(mat[3] + mat[5]) - imgCoordMungeLower(mat[5]);
3702  if (splashAbs(mat[0]) >= 1) {
3703  th = imgCoordMungeUpper(mat[3]) - imgCoordMungeLower(mat[1] * mat[2] / mat[0]);
3704  if (th > t1)
3705  t1 = th;
3706  }
3707  } else {
3708  t1 = imgCoordMungeUpper(mat[5]) - imgCoordMungeLower(mat[3] + mat[5]);
3709  if (splashAbs(mat[0]) >= 1) {
3710  th = imgCoordMungeUpper(mat[1] * mat[2] / mat[0]) - imgCoordMungeLower(mat[3]);
3711  if (th > t1)
3712  t1 = th;
3713  }
3714  }
3715  scaledHeight = t0 > t1 ? t0 : t1;
3716  }
3717  if (scaledWidth == 0) {
3718  scaledWidth = 1;
3719  }
3720  if (scaledHeight == 0) {
3721  scaledHeight = 1;
3722  }
3723 
3724  // compute the inverse transform (after scaling) matrix
3725  r00 = mat[0] / scaledWidth;
3726  r01 = mat[1] / scaledWidth;
3727  r10 = mat[2] / scaledHeight;
3728  r11 = mat[3] / scaledHeight;
3729  det = r00 * r11 - r01 * r10;
3730  if (splashAbs(det) < 1e-6) {
3731  // this should be caught by the singular matrix check in drawImage
3732  return splashErrBadArg;
3733  }
3734  ir00 = r11 / det;
3735  ir01 = -r01 / det;
3736  ir10 = -r10 / det;
3737  ir11 = r00 / det;
3738 
3739  // scale the input image
3740  yp = srcHeight / scaledHeight;
3741  if (yp < 0 || yp > INT_MAX - 1) {
3742  return splashErrBadArg;
3743  }
3744  scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, interpolate);
3745 
3746  if (scaledImg == nullptr) {
3747  return splashErrBadArg;
3748  }
3749 
3750  if (tf != nullptr) {
3751  (*tf)(srcData, scaledImg);
3752  }
3753  // construct the three sections
3754  i = 0;
3755  if (vy[1] < vy[i]) {
3756  i = 1;
3757  }
3758  if (vy[2] < vy[i]) {
3759  i = 2;
3760  }
3761  if (vy[3] < vy[i]) {
3762  i = 3;
3763  }
3764  // NB: if using fixed point, 0.000001 will be truncated to zero,
3765  // so these two comparisons must be <=, not <
3766  if (splashAbs(vy[i] - vy[(i - 1) & 3]) <= 0.000001 && vy[(i - 1) & 3] < vy[(i + 1) & 3]) {
3767  i = (i - 1) & 3;
3768  }
3769  if (splashAbs(vy[i] - vy[(i + 1) & 3]) <= 0.000001) {
3770  section[0].y0 = imgCoordMungeLower(vy[i]);
3771  section[0].y1 = imgCoordMungeUpper(vy[(i + 2) & 3]) - 1;
3772  if (vx[i] < vx[(i + 1) & 3]) {
3773  section[0].ia0 = i;
3774  section[0].ia1 = (i + 3) & 3;
3775  section[0].ib0 = (i + 1) & 3;
3776  section[0].ib1 = (i + 2) & 3;
3777  } else {
3778  section[0].ia0 = (i + 1) & 3;
3779  section[0].ia1 = (i + 2) & 3;
3780  section[0].ib0 = i;
3781  section[0].ib1 = (i + 3) & 3;
3782  }
3783  nSections = 1;
3784  } else {
3785  section[0].y0 = imgCoordMungeLower(vy[i]);
3786  section[2].y1 = imgCoordMungeUpper(vy[(i + 2) & 3]) - 1;
3787  section[0].ia0 = section[0].ib0 = i;
3788  section[2].ia1 = section[2].ib1 = (i + 2) & 3;
3789  if (vx[(i + 1) & 3] < vx[(i + 3) & 3]) {
3790  section[0].ia1 = section[2].ia0 = (i + 1) & 3;
3791  section[0].ib1 = section[2].ib0 = (i + 3) & 3;
3792  } else {
3793  section[0].ia1 = section[2].ia0 = (i + 3) & 3;
3794  section[0].ib1 = section[2].ib0 = (i + 1) & 3;
3795  }
3796  if (vy[(i + 1) & 3] < vy[(i + 3) & 3]) {
3797  section[1].y0 = imgCoordMungeLower(vy[(i + 1) & 3]);
3798  section[2].y0 = imgCoordMungeUpper(vy[(i + 3) & 3]);
3799  if (vx[(i + 1) & 3] < vx[(i + 3) & 3]) {
3800  section[1].ia0 = (i + 1) & 3;
3801  section[1].ia1 = (i + 2) & 3;
3802  section[1].ib0 = i;
3803  section[1].ib1 = (i + 3) & 3;
3804  } else {
3805  section[1].ia0 = i;
3806  section[1].ia1 = (i + 3) & 3;
3807  section[1].ib0 = (i + 1) & 3;
3808  section[1].ib1 = (i + 2) & 3;
3809  }
3810  } else {
3811  section[1].y0 = imgCoordMungeLower(vy[(i + 3) & 3]);
3812  section[2].y0 = imgCoordMungeUpper(vy[(i + 1) & 3]);
3813  if (vx[(i + 1) & 3] < vx[(i + 3) & 3]) {
3814  section[1].ia0 = i;
3815  section[1].ia1 = (i + 1) & 3;
3816  section[1].ib0 = (i + 3) & 3;
3817  section[1].ib1 = (i + 2) & 3;
3818  } else {
3819  section[1].ia0 = (i + 3) & 3;
3820  section[1].ia1 = (i + 2) & 3;
3821  section[1].ib0 = i;
3822  section[1].ib1 = (i + 1) & 3;
3823  }
3824  }
3825  section[0].y1 = section[1].y0 - 1;
3826  section[1].y1 = section[2].y0 - 1;
3827  nSections = 3;
3828  }
3829  for (i = 0; i < nSections; ++i) {
3830  section[i].xa0 = vx[section[i].ia0];
3831  section[i].ya0 = vy[section[i].ia0];
3832  section[i].xa1 = vx[section[i].ia1];
3833  section[i].ya1 = vy[section[i].ia1];
3834  section[i].xb0 = vx[section[i].ib0];
3835  section[i].yb0 = vy[section[i].ib0];
3836  section[i].xb1 = vx[section[i].ib1];
3837  section[i].yb1 = vy[section[i].ib1];
3838  section[i].dxdya = (section[i].xa1 - section[i].xa0) / (section[i].ya1 - section[i].ya0);
3839  section[i].dxdyb = (section[i].xb1 - section[i].xb0) / (section[i].yb1 - section[i].yb0);
3840  }
3841 
3842  // initialize the pixel pipe
3843  pipeInit(&pipe, 0, 0, nullptr, pixel, (unsigned char)splashRound(state->fillAlpha * 255), srcAlpha || (vectorAntialias && clipRes != splashClipAllInside), false);
3844  if (vectorAntialias) {
3845  drawAAPixelInit();
3846  }
3847 
3848  // make sure narrow images cover at least one pixel
3849  if (nSections == 1) {
3850  if (section[0].y0 == section[0].y1) {
3851  ++section[0].y1;
3852  clipRes = opClipRes = splashClipPartial;
3853  }
3854  } else {
3855  if (section[0].y0 == section[2].y1) {
3856  ++section[1].y1;
3857  clipRes = opClipRes = splashClipPartial;
3858  }
3859  }
3860 
3861  // scan all pixels inside the target region
3862  for (i = 0; i < nSections; ++i) {
3863  for (y = section[i].y0; y <= section[i].y1; ++y) {
3864  xa = imgCoordMungeLower(section[i].xa0 + ((SplashCoord)y + 0.5 - section[i].ya0) * section[i].dxdya);
3865  if (unlikely(xa < 0))
3866  xa = 0;
3867  xb = imgCoordMungeUpper(section[i].xb0 + ((SplashCoord)y + 0.5 - section[i].yb0) * section[i].dxdyb);
3868  // make sure narrow images cover at least one pixel
3869  if (xa == xb) {
3870  ++xb;
3871  }
3872  if (clipRes != splashClipAllInside) {
3873  clipRes2 = state->clip->testSpan(xa, xb - 1, y);
3874  } else {
3875  clipRes2 = clipRes;
3876  }
3877  for (x = xa; x < xb; ++x) {
3878  // map (x+0.5, y+0.5) back to the scaled image
3879  xx = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir00 + ((SplashCoord)y + 0.5 - mat[5]) * ir10);
3880  yy = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir01 + ((SplashCoord)y + 0.5 - mat[5]) * ir11);
3881  // xx should always be within bounds, but floating point
3882  // inaccuracy can cause problems
3883  if (xx < 0) {
3884  xx = 0;
3885  } else if (xx >= scaledWidth) {
3886  xx = scaledWidth - 1;
3887  }
3888  if (yy < 0) {
3889  yy = 0;
3890  } else if (yy >= scaledHeight) {
3891  yy = scaledHeight - 1;
3892  }
3893  scaledImg->getPixel(xx, yy, pixel);
3894  if (srcAlpha) {
3895  pipe.shape = scaledImg->alpha[yy * scaledWidth + xx];
3896  } else {
3897  pipe.shape = 255;
3898  }
3899  if (vectorAntialias && clipRes2 != splashClipAllInside) {
3900  drawAAPixel(&pipe, x, y);
3901  } else {
3902  drawPixel(&pipe, x, y, clipRes2 == splashClipAllInside);
3903  }
3904  }
3905  }
3906  }
3907 
3908  delete scaledImg;
3909  return splashOk;
3910 }
3911 
3912 // determine if a scaled image requires interpolation based on the scale and
3913 // the interpolate flag from the image dictionary
3914 static bool isImageInterpolationRequired(int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, bool interpolate)
3915 {
3916  if (interpolate || srcWidth == 0 || srcHeight == 0)
3917  return true;
3918 
3919  /* When scale factor is >= 400% we don't interpolate. See bugs #25268, #9860 */
3920  if (scaledWidth / srcWidth >= 4 || scaledHeight / srcHeight >= 4)
3921  return false;
3922 
3923  return true;
3924 }
3925 
3926 // Scale an image into a SplashBitmap.
3927 SplashBitmap *Splash::scaleImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, bool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, bool interpolate, bool tilingPattern)
3928 {
3929  SplashBitmap *dest;
3930 
3931  dest = new SplashBitmap(scaledWidth, scaledHeight, 1, srcMode, srcAlpha, true, bitmap->getSeparationList());
3932  if (dest->getDataPtr() != nullptr && srcHeight > 0 && srcWidth > 0) {
3933  bool success = true;
3934  if (scaledHeight < srcHeight) {
3935  if (scaledWidth < srcWidth) {
3936  success = scaleImageYdownXdown(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
3937  } else {
3938  success = scaleImageYdownXup(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
3939  }
3940  } else {
3941  if (scaledWidth < srcWidth) {
3942  success = scaleImageYupXdown(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
3943  } else {
3944  if (!tilingPattern && isImageInterpolationRequired(srcWidth, srcHeight, scaledWidth, scaledHeight, interpolate)) {
3945  success = scaleImageYupXupBilinear(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
3946  } else {
3947  success = scaleImageYupXup(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
3948  }
3949  }
3950  }
3951  if (unlikely(!success)) {
3952  delete dest;
3953  dest = nullptr;
3954  }
3955  } else {
3956  delete dest;
3957  dest = nullptr;
3958  }
3959  return dest;
3960 }
3961 
3962 bool Splash::scaleImageYdownXdown(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, bool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest)
3963 {
3964  unsigned char *lineBuf, *alphaLineBuf;
3965  unsigned int *pixBuf, *alphaPixBuf;
3966  unsigned int pix0, pix1, pix2;
3967  unsigned int pix3;
3968  unsigned int pix[SPOT_NCOMPS + 4], cp;
3969  unsigned int alpha;
3970  unsigned char *destPtr, *destAlphaPtr;
3971  int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, xxa, d, d0, d1;
3972  int i, j;
3973 
3974  // Bresenham parameters for y scale
3975  yp = srcHeight / scaledHeight;
3976  yq = srcHeight % scaledHeight;
3977 
3978  // Bresenham parameters for x scale
3979  xp = srcWidth / scaledWidth;
3980  xq = srcWidth % scaledWidth;
3981 
3982  // allocate buffers
3983  lineBuf = (unsigned char *)gmallocn_checkoverflow(srcWidth, nComps);
3984  if (unlikely(!lineBuf)) {
3985  return false;
3986  }
3987  pixBuf = (unsigned int *)gmallocn_checkoverflow(srcWidth, nComps * sizeof(int));
3988  if (unlikely(!pixBuf)) {
3989  gfree(lineBuf);
3990  return false;
3991  }
3992  if (srcAlpha) {
3993  alphaLineBuf = (unsigned char *)gmalloc(srcWidth);
3994  alphaPixBuf = (unsigned int *)gmallocn(srcWidth, sizeof(int));
3995  } else {
3996  alphaLineBuf = nullptr;
3997  alphaPixBuf = nullptr;
3998  }
3999 
4000  // init y scale Bresenham
4001  yt = 0;
4002 
4003  destPtr = dest->data;
4004  destAlphaPtr = dest->alpha;
4005  for (y = 0; y < scaledHeight; ++y) {
4006 
4007  // y scale Bresenham
4008  if ((yt += yq) >= scaledHeight) {
4009  yt -= scaledHeight;
4010  yStep = yp + 1;
4011  } else {
4012  yStep = yp;
4013  }
4014 
4015  // read rows from image
4016  memset(pixBuf, 0, srcWidth * nComps * sizeof(int));
4017  if (srcAlpha) {
4018  memset(alphaPixBuf, 0, srcWidth * sizeof(int));
4019  }
4020  for (i = 0; i < yStep; ++i) {
4021  (*src)(srcData, lineBuf, alphaLineBuf);
4022  for (j = 0; j < srcWidth * nComps; ++j) {
4023  pixBuf[j] += lineBuf[j];
4024  }
4025  if (srcAlpha) {
4026  for (j = 0; j < srcWidth; ++j) {
4027  alphaPixBuf[j] += alphaLineBuf[j];
4028  }
4029  }
4030  }
4031 
4032  // init x scale Bresenham
4033  xt = 0;
4034  d0 = (1 << 23) / (yStep * xp);
4035  d1 = (1 << 23) / (yStep * (xp + 1));
4036 
4037  xx = xxa = 0;
4038  for (x = 0; x < scaledWidth; ++x) {
4039 
4040  // x scale Bresenham
4041  if ((xt += xq) >= scaledWidth) {
4042  xt -= scaledWidth;
4043  xStep = xp + 1;
4044  d = d1;
4045  } else {
4046  xStep = xp;
4047  d = d0;
4048  }
4049 
4050  switch (srcMode) {
4051 
4052  case splashModeMono8:
4053 
4054  // compute the final pixel
4055  pix0 = 0;
4056  for (i = 0; i < xStep; ++i) {
4057  pix0 += pixBuf[xx++];
4058  }
4059  // pix / xStep * yStep
4060  pix0 = (pix0 * d) >> 23;
4061 
4062  // store the pixel
4063  *destPtr++ = (unsigned char)pix0;
4064  break;
4065 
4066  case splashModeRGB8:
4067 
4068  // compute the final pixel
4069  pix0 = pix1 = pix2 = 0;
4070  for (i = 0; i < xStep; ++i) {
4071  pix0 += pixBuf[xx];
4072  pix1 += pixBuf[xx + 1];
4073  pix2 += pixBuf[xx + 2];
4074  xx += 3;
4075  }
4076  // pix / xStep * yStep
4077  pix0 = (pix0 * d) >> 23;
4078  pix1 = (pix1 * d) >> 23;
4079  pix2 = (pix2 * d) >> 23;
4080 
4081  // store the pixel
4082  *destPtr++ = (unsigned char)pix0;
4083  *destPtr++ = (unsigned char)pix1;
4084  *destPtr++ = (unsigned char)pix2;
4085  break;
4086 
4087  case splashModeXBGR8:
4088 
4089  // compute the final pixel
4090  pix0 = pix1 = pix2 = 0;
4091  for (i = 0; i < xStep; ++i) {
4092  pix0 += pixBuf[xx];
4093  pix1 += pixBuf[xx + 1];
4094  pix2 += pixBuf[xx + 2];
4095  xx += 4;
4096  }
4097  // pix / xStep * yStep
4098  pix0 = (pix0 * d) >> 23;
4099  pix1 = (pix1 * d) >> 23;
4100  pix2 = (pix2 * d) >> 23;
4101 
4102  // store the pixel
4103  *destPtr++ = (unsigned char)pix2;
4104  *destPtr++ = (unsigned char)pix1;
4105  *destPtr++ = (unsigned char)pix0;
4106  *destPtr++ = (unsigned char)255;
4107  break;
4108 
4109  case splashModeBGR8:
4110 
4111  // compute the final pixel
4112  pix0 = pix1 = pix2 = 0;
4113  for (i = 0; i < xStep; ++i) {
4114  pix0 += pixBuf[xx];
4115  pix1 += pixBuf[xx + 1];
4116  pix2 += pixBuf[xx + 2];
4117  xx += 3;
4118  }
4119  // pix / xStep * yStep
4120  pix0 = (pix0 * d) >> 23;
4121  pix1 = (pix1 * d) >> 23;
4122  pix2 = (pix2 * d) >> 23;
4123 
4124  // store the pixel
4125  *destPtr++ = (unsigned char)pix2;
4126  *destPtr++ = (unsigned char)pix1;
4127  *destPtr++ = (unsigned char)pix0;
4128  break;
4129 
4130  case splashModeCMYK8:
4131 
4132  // compute the final pixel
4133  pix0 = pix1 = pix2 = pix3 = 0;
4134  for (i = 0; i < xStep; ++i) {
4135  pix0 += pixBuf[xx];
4136  pix1 += pixBuf[xx + 1];
4137  pix2 += pixBuf[xx + 2];
4138  pix3 += pixBuf[xx + 3];
4139  xx += 4;
4140  }
4141  // pix / xStep * yStep
4142  pix0 = (pix0 * d) >> 23;
4143  pix1 = (pix1 * d) >> 23;
4144  pix2 = (pix2 * d) >> 23;
4145  pix3 = (pix3 * d) >> 23;
4146 
4147  // store the pixel
4148  *destPtr++ = (unsigned char)pix0;
4149  *destPtr++ = (unsigned char)pix1;
4150  *destPtr++ = (unsigned char)pix2;
4151  *destPtr++ = (unsigned char)pix3;
4152  break;
4153  case splashModeDeviceN8:
4154 
4155  // compute the final pixel
4156  for (cp = 0; cp < SPOT_NCOMPS + 4; cp++)
4157  pix[cp] = 0;
4158  for (i = 0; i < xStep; ++i) {
4159  for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) {
4160  pix[cp] += pixBuf[xx + cp];
4161  }
4162  xx += (SPOT_NCOMPS + 4);
4163  }
4164  // pix / xStep * yStep
4165  for (cp = 0; cp < SPOT_NCOMPS + 4; cp++)
4166  pix[cp] = (pix[cp] * d) >> 23;
4167 
4168  // store the pixel
4169  for (cp = 0; cp < SPOT_NCOMPS + 4; cp++)
4170  *destPtr++ = (unsigned char)pix[cp];
4171  break;
4172 
4173  case splashModeMono1: // mono1 is not allowed
4174  default:
4175  break;
4176  }
4177 
4178  // process alpha
4179  if (srcAlpha) {
4180  alpha = 0;
4181  for (i = 0; i < xStep; ++i, ++xxa) {
4182  alpha += alphaPixBuf[xxa];
4183  }
4184  // alpha / xStep * yStep
4185  alpha = (alpha * d) >> 23;
4186  *destAlphaPtr++ = (unsigned char)alpha;
4187  }
4188  }
4189  }
4190 
4191  gfree(alphaPixBuf);
4192  gfree(alphaLineBuf);
4193  gfree(pixBuf);
4194  gfree(lineBuf);
4195 
4196  return true;
4197 }
4198 
4199 bool Splash::scaleImageYdownXup(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, bool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest)
4200 {
4201  unsigned char *lineBuf, *alphaLineBuf;
4202  unsigned int *pixBuf, *alphaPixBuf;
4203  unsigned int pix[splashMaxColorComps];
4204  unsigned int alpha;
4205  unsigned char *destPtr, *destAlphaPtr;
4206  int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, d;
4207  int i, j;
4208 
4209  // Bresenham parameters for y scale
4210  yp = srcHeight / scaledHeight;
4211  yq = srcHeight % scaledHeight;
4212 
4213  // Bresenham parameters for x scale
4214  xp = scaledWidth / srcWidth;
4215  xq = scaledWidth % srcWidth;
4216 
4217  // allocate buffers
4218  pixBuf = (unsigned int *)gmallocn_checkoverflow(srcWidth, nComps * sizeof(int));
4219  if (unlikely(!pixBuf)) {
4220  error(errInternal, -1, "Splash::scaleImageYdownXup. Couldn't allocate pixBuf memory");
4221  return false;
4222  }
4223  lineBuf = (unsigned char *)gmallocn(srcWidth, nComps);
4224  if (srcAlpha) {
4225  alphaLineBuf = (unsigned char *)gmalloc(srcWidth);
4226  alphaPixBuf = (unsigned int *)gmallocn(srcWidth, sizeof(int));
4227  } else {
4228  alphaLineBuf = nullptr;
4229  alphaPixBuf = nullptr;
4230  }
4231 
4232  // init y scale Bresenham
4233  yt = 0;
4234 
4235  destPtr = dest->data;
4236  destAlphaPtr = dest->alpha;
4237  for (y = 0; y < scaledHeight; ++y) {
4238 
4239  // y scale Bresenham
4240  if ((yt += yq) >= scaledHeight) {
4241  yt -= scaledHeight;
4242  yStep = yp + 1;
4243  } else {
4244  yStep = yp;
4245  }
4246 
4247  // read rows from image
4248  memset(pixBuf, 0, srcWidth * nComps * sizeof(int));
4249  if (srcAlpha) {
4250  memset(alphaPixBuf, 0, srcWidth * sizeof(int));
4251  }
4252  for (i = 0; i < yStep; ++i) {
4253  (*src)(srcData, lineBuf, alphaLineBuf);
4254  for (j = 0; j < srcWidth * nComps; ++j) {
4255  pixBuf[j] += lineBuf[j];
4256  }
4257  if (srcAlpha) {
4258  for (j = 0; j < srcWidth; ++j) {
4259  alphaPixBuf[j] += alphaLineBuf[j];
4260  }
4261  }
4262  }
4263 
4264  // init x scale Bresenham
4265  xt = 0;
4266  d = (1 << 23) / yStep;
4267 
4268  for (x = 0; x < srcWidth; ++x) {
4269 
4270  // x scale Bresenham
4271  if ((xt += xq) >= srcWidth) {
4272  xt -= srcWidth;
4273  xStep = xp + 1;
4274  } else {
4275  xStep = xp;
4276  }
4277 
4278  // compute the final pixel
4279  for (i = 0; i < nComps; ++i) {
4280  // pixBuf[] / yStep
4281  pix[i] = (pixBuf[x * nComps + i] * d) >> 23;
4282  }
4283 
4284  // store the pixel
4285  switch (srcMode) {
4286  case splashModeMono1: // mono1 is not allowed
4287  break;
4288  case splashModeMono8:
4289  for (i = 0; i < xStep; ++i) {
4290  *destPtr++ = (unsigned char)pix[0];
4291  }
4292  break;
4293  case splashModeRGB8:
4294  for (i = 0; i < xStep; ++i) {
4295  *destPtr++ = (unsigned char)pix[0];
4296  *destPtr++ = (unsigned char)pix[1];
4297  *destPtr++ = (unsigned char)pix[2];
4298  }
4299  break;
4300  case splashModeXBGR8:
4301  for (i = 0; i < xStep; ++i) {
4302  *destPtr++ = (unsigned char)pix[2];
4303  *destPtr++ = (unsigned char)pix[1];
4304  *destPtr++ = (unsigned char)pix[0];
4305  *destPtr++ = (unsigned char)255;
4306  }
4307  break;
4308  case splashModeBGR8:
4309  for (i = 0; i < xStep; ++i) {
4310  *destPtr++ = (unsigned char)pix[2];
4311  *destPtr++ = (unsigned char)pix[1];
4312  *destPtr++ = (unsigned char)pix[0];
4313  }
4314  break;
4315  case splashModeCMYK8:
4316  for (i = 0; i < xStep; ++i) {
4317  *destPtr++ = (unsigned char)pix[0];
4318  *destPtr++ = (unsigned char)pix[1];
4319  *destPtr++ = (unsigned char)pix[2];
4320  *destPtr++ = (unsigned char)pix[3];
4321  }
4322  break;
4323  case splashModeDeviceN8:
4324  for (i = 0; i < xStep; ++i) {
4325  for (unsigned int cp : pix)
4326  *destPtr++ = (unsigned char)cp;
4327  }
4328  break;
4329  }
4330 
4331  // process alpha
4332  if (srcAlpha) {
4333  // alphaPixBuf[] / yStep
4334  alpha = (alphaPixBuf[x] * d) >> 23;
4335  for (i = 0; i < xStep; ++i) {
4336  *destAlphaPtr++ = (unsigned char)alpha;
4337  }
4338  }
4339  }
4340  }
4341 
4342  gfree(alphaPixBuf);
4343  gfree(alphaLineBuf);
4344  gfree(pixBuf);
4345  gfree(lineBuf);
4346 
4347  return true;
4348 }
4349 
4350 bool Splash::scaleImageYupXdown(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, bool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest)
4351 {
4352  unsigned char *lineBuf, *alphaLineBuf;
4353  unsigned int pix[splashMaxColorComps];
4354  unsigned int alpha;
4355  unsigned char *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr;
4356  int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, xxa, d, d0, d1;
4357  int i, j;
4358 
4359  // Bresenham parameters for y scale
4360  yp = scaledHeight / srcHeight;
4361  yq = scaledHeight % srcHeight;
4362 
4363  // Bresenham parameters for x scale
4364  xp = srcWidth / scaledWidth;
4365  xq = srcWidth % scaledWidth;
4366 
4367  // allocate buffers
4368  lineBuf = (unsigned char *)gmallocn_checkoverflow(srcWidth, nComps);
4369  if (unlikely(!lineBuf)) {
4370  gfree(dest->takeData());
4371  return false;
4372  }
4373  if (srcAlpha) {
4374  alphaLineBuf = (unsigned char *)gmalloc(srcWidth);
4375  } else {
4376  alphaLineBuf = nullptr;
4377  }
4378 
4379  // init y scale Bresenham
4380  yt = 0;
4381 
4382  destPtr0 = dest->data;
4383  destAlphaPtr0 = dest->alpha;
4384  for (y = 0; y < srcHeight; ++y) {
4385 
4386  // y scale Bresenham
4387  if ((yt += yq) >= srcHeight) {
4388  yt -= srcHeight;
4389  yStep = yp + 1;
4390  } else {
4391  yStep = yp;
4392  }
4393 
4394  // read row from image
4395  (*src)(srcData, lineBuf, alphaLineBuf);
4396 
4397  // init x scale Bresenham
4398  xt = 0;
4399  d0 = (1 << 23) / xp;
4400  d1 = (1 << 23) / (xp + 1);
4401 
4402  xx = xxa = 0;
4403  for (x = 0; x < scaledWidth; ++x) {
4404 
4405  // x scale Bresenham
4406  if ((xt += xq) >= scaledWidth) {
4407  xt -= scaledWidth;
4408  xStep = xp + 1;
4409  d = d1;
4410  } else {
4411  xStep = xp;
4412  d = d0;
4413  }
4414 
4415  // compute the final pixel
4416  for (i = 0; i < nComps; ++i) {
4417  pix[i] = 0;
4418  }
4419  for (i = 0; i < xStep; ++i) {
4420  for (j = 0; j < nComps; ++j, ++xx) {
4421  pix[j] += lineBuf[xx];
4422  }
4423  }
4424  for (i = 0; i < nComps; ++i) {
4425  // pix[] / xStep
4426  pix[i] = (pix[i] * d) >> 23;
4427  }
4428 
4429  // store the pixel
4430  switch (srcMode) {
4431  case splashModeMono1: // mono1 is not allowed
4432  break;
4433  case splashModeMono8:
4434  for (i = 0; i < yStep; ++i) {
4435  destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
4436  *destPtr++ = (unsigned char)pix[0];
4437  }
4438  break;
4439  case splashModeRGB8:
4440  for (i = 0; i < yStep; ++i) {
4441  destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
4442  *destPtr++ = (unsigned char)pix[0];
4443  *destPtr++ = (unsigned char)pix[1];
4444  *destPtr++ = (unsigned char)pix[2];
4445  }
4446  break;
4447  case splashModeXBGR8:
4448  for (i = 0; i < yStep; ++i) {
4449  destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
4450  *destPtr++ = (unsigned char)pix[2];
4451  *destPtr++ = (unsigned char)pix[1];
4452  *destPtr++ = (unsigned char)pix[0];
4453  *destPtr++ = (unsigned char)255;
4454  }
4455  break;
4456  case splashModeBGR8:
4457  for (i = 0; i < yStep; ++i) {
4458  destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
4459  *destPtr++ = (unsigned char)pix[2];
4460  *destPtr++ = (unsigned char)pix[1];
4461  *destPtr++ = (unsigned char)pix[0];
4462  }
4463  break;
4464  case splashModeCMYK8:
4465  for (i = 0; i < yStep; ++i) {
4466  destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
4467  *destPtr++ = (unsigned char)pix[0];
4468  *destPtr++ = (unsigned char)pix[1];
4469  *destPtr++ = (unsigned char)pix[2];
4470  *destPtr++ = (unsigned char)pix[3];
4471  }
4472  break;
4473  case splashModeDeviceN8:
4474  for (i = 0; i < yStep; ++i) {