aboutsummaryrefslogtreecommitdiff
path: root/splash/Splash.cc
diff options
context:
space:
mode:
authorCalvin Morrison <calvin@pobox.com>2023-04-05 14:13:39 -0400
committerCalvin Morrison <calvin@pobox.com>2023-04-05 14:13:39 -0400
commit835e373b3eeaabcd0621ed6798ab500f37982fae (patch)
treedfa16b0e2e1b4956b38f693220eac4e607802133 /splash/Splash.cc
xpdf-no-select-disableHEADmaster
Diffstat (limited to 'splash/Splash.cc')
-rw-r--r--splash/Splash.cc8521
1 files changed, 8521 insertions, 0 deletions
diff --git a/splash/Splash.cc b/splash/Splash.cc
new file mode 100644
index 0000000..4afe21b
--- /dev/null
+++ b/splash/Splash.cc
@@ -0,0 +1,8521 @@
+//========================================================================
+//
+// Splash.cc
+//
+// Copyright 2003-2020 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <math.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "GString.h"
+#include "SplashErrorCodes.h"
+#include "SplashMath.h"
+#include "SplashBitmap.h"
+#include "SplashState.h"
+#include "SplashPath.h"
+#include "SplashXPath.h"
+#include "SplashXPathScanner.h"
+#include "SplashPattern.h"
+#include "SplashScreen.h"
+#include "SplashFont.h"
+#include "SplashGlyphBitmap.h"
+#include "Splash.h"
+
+// the MSVC math.h doesn't define this
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+//------------------------------------------------------------------------
+
+// distance of Bezier control point from center for circle approximation
+// = (4 * (sqrt(2) - 1) / 3) * r
+#define bezierCircle ((SplashCoord)0.55228475)
+#define bezierCircle2 ((SplashCoord)(0.5 * 0.55228475))
+
+// Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result.
+static inline Guchar div255(int x) {
+ return (Guchar)((x + (x >> 8) + 0x80) >> 8);
+}
+
+// Clip x to lie in [0, 255].
+static inline Guchar clip255(int x) {
+ return x < 0 ? 0 : x > 255 ? 255 : (Guchar)x;
+}
+
+// Used by drawImage and fillImageMask to divide the target
+// quadrilateral into sections.
+struct ImageSection {
+ int y0, y1; // actual y range
+ int ia0, ia1; // vertex indices for edge A
+ int ib0, ib1; // vertex indices for edge B
+ SplashCoord xa0, ya0, xa1, ya1; // edge A
+ SplashCoord dxdya; // slope of edge A
+ SplashCoord xb0, yb0, xb1, yb1; // edge B
+ SplashCoord dxdyb; // slope of edge B
+};
+
+//------------------------------------------------------------------------
+// SplashPipe
+//------------------------------------------------------------------------
+
+#define splashPipeMaxStages 9
+
+struct SplashPipe {
+ // source pattern
+ SplashPattern *pattern;
+
+ // source alpha and color
+ Guchar aInput;
+ SplashColor cSrcVal;
+
+ // source overprint mask
+ //~ this is a kludge - this pointer should be passed as an arg to the
+ //~ pipeRun function, but that would require passing in a lot of
+ //~ null pointers, since it's rarely used
+ Guint *srcOverprintMaskPtr;
+
+ // special cases and result color
+ GBool noTransparency;
+ GBool shapeOnly;
+ SplashPipeResultColorCtrl resultColorCtrl;
+
+ // non-isolated group correction
+ // (this is only used when Splash::composite() is called to composite
+ // a non-isolated group onto the backdrop)
+ GBool nonIsolatedGroup;
+
+ // the "run" function
+ void (Splash::*run)(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
+};
+
+SplashPipeResultColorCtrl Splash::pipeResultColorNoAlphaBlend[] = {
+ splashPipeResultColorNoAlphaBlendMono,
+ splashPipeResultColorNoAlphaBlendMono,
+ splashPipeResultColorNoAlphaBlendRGB,
+ splashPipeResultColorNoAlphaBlendRGB
+#if SPLASH_CMYK
+ ,
+ splashPipeResultColorNoAlphaBlendCMYK
+#endif
+};
+
+SplashPipeResultColorCtrl Splash::pipeResultColorAlphaNoBlend[] = {
+ splashPipeResultColorAlphaNoBlendMono,
+ splashPipeResultColorAlphaNoBlendMono,
+ splashPipeResultColorAlphaNoBlendRGB,
+ splashPipeResultColorAlphaNoBlendRGB
+#if SPLASH_CMYK
+ ,
+ splashPipeResultColorAlphaNoBlendCMYK
+#endif
+};
+
+SplashPipeResultColorCtrl Splash::pipeResultColorAlphaBlend[] = {
+ splashPipeResultColorAlphaBlendMono,
+ splashPipeResultColorAlphaBlendMono,
+ splashPipeResultColorAlphaBlendRGB,
+ splashPipeResultColorAlphaBlendRGB
+#if SPLASH_CMYK
+ ,
+ splashPipeResultColorAlphaBlendCMYK
+#endif
+};
+
+//------------------------------------------------------------------------
+// modified region
+//------------------------------------------------------------------------
+
+void Splash::clearModRegion() {
+ modXMin = bitmap->width;
+ modYMin = bitmap->height;
+ modXMax = -1;
+ modYMax = -1;
+}
+
+inline void Splash::updateModX(int x) {
+ if (x < modXMin) {
+ modXMin = x;
+ }
+ if (x > modXMax) {
+ modXMax = x;
+ }
+}
+
+inline void Splash::updateModY(int y) {
+ if (y < modYMin) {
+ modYMin = y;
+ }
+ if (y > modYMax) {
+ modYMax = y;
+ }
+}
+
+//------------------------------------------------------------------------
+// pipeline
+//------------------------------------------------------------------------
+
+inline void Splash::pipeInit(SplashPipe *pipe, SplashPattern *pattern,
+ Guchar aInput, GBool usesShape,
+ GBool nonIsolatedGroup, GBool usesSrcOverprint) {
+ SplashColorMode mode;
+
+ mode = bitmap->mode;
+
+ pipe->pattern = NULL;
+
+ // source color
+ if (pattern && pattern->isStatic()) {
+ pattern->getColor(0, 0, pipe->cSrcVal);
+ pipe->pattern = NULL;
+ } else {
+ pipe->pattern = pattern;
+ }
+
+ // source alpha
+ pipe->aInput = aInput;
+
+ // source overprint mask
+ pipe->srcOverprintMaskPtr = NULL;
+
+ // special cases
+ pipe->noTransparency = aInput == 255 &&
+ !state->softMask &&
+ !usesShape &&
+ !state->inNonIsolatedGroup &&
+ !state->inKnockoutGroup &&
+ !nonIsolatedGroup &&
+ state->overprintMask == 0xffffffff;
+ pipe->shapeOnly = aInput == 255 &&
+ !state->softMask &&
+ usesShape &&
+ !state->inNonIsolatedGroup &&
+ !state->inKnockoutGroup &&
+ !nonIsolatedGroup &&
+ state->overprintMask == 0xffffffff;
+
+ // result color
+ if (pipe->noTransparency) {
+ // the !state->blendFunc case is handled separately in pipeRun
+ pipe->resultColorCtrl = pipeResultColorNoAlphaBlend[mode];
+ } else if (!state->blendFunc) {
+ pipe->resultColorCtrl = pipeResultColorAlphaNoBlend[mode];
+ } else {
+ pipe->resultColorCtrl = pipeResultColorAlphaBlend[mode];
+ }
+
+ // non-isolated group correction
+ pipe->nonIsolatedGroup = nonIsolatedGroup;
+
+ // select the 'run' function
+ pipe->run = &Splash::pipeRun;
+ if (overprintMaskBitmap || usesSrcOverprint) {
+ // use Splash::pipeRun
+ } else if (!pipe->pattern && pipe->noTransparency && !state->blendFunc) {
+ if (mode == splashModeMono1 && !bitmap->alpha) {
+ pipe->run = &Splash::pipeRunSimpleMono1;
+ } else if (mode == splashModeMono8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunSimpleMono8;
+ } else if (mode == splashModeRGB8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunSimpleRGB8;
+ } else if (mode == splashModeBGR8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunSimpleBGR8;
+#if SPLASH_CMYK
+ } else if (mode == splashModeCMYK8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunSimpleCMYK8;
+#endif
+ }
+ } else if (!pipe->pattern && pipe->shapeOnly && !state->blendFunc) {
+ if (mode == splashModeMono1 && !bitmap->alpha) {
+ pipe->run = &Splash::pipeRunShapeMono1;
+ } else if (mode == splashModeMono8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunShapeMono8;
+ } else if (mode == splashModeRGB8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunShapeRGB8;
+ } else if (mode == splashModeBGR8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunShapeBGR8;
+#if SPLASH_CMYK
+ } else if (mode == splashModeCMYK8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunShapeCMYK8;
+#endif
+ } else if (mode == splashModeMono8 && !bitmap->alpha) {
+ // this is used when drawing soft-masked images
+ pipe->run = &Splash::pipeRunShapeNoAlphaMono8;
+ }
+ } else if (!pipe->pattern && !pipe->noTransparency && !state->softMask &&
+ usesShape &&
+ !(state->inNonIsolatedGroup && groupBackBitmap->alpha) &&
+ !state->inKnockoutGroup &&
+ !state->blendFunc && !pipe->nonIsolatedGroup) {
+ if (mode == splashModeMono1 && !bitmap->alpha) {
+ pipe->run = &Splash::pipeRunAAMono1;
+ } else if (mode == splashModeMono8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunAAMono8;
+ } else if (mode == splashModeRGB8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunAARGB8;
+ } else if (mode == splashModeBGR8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunAABGR8;
+#if SPLASH_CMYK
+ } else if (mode == splashModeCMYK8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunAACMYK8;
+#endif
+ }
+ } else if (!pipe->pattern &&
+ aInput == 255 &&
+ state->softMask &&
+ usesShape &&
+ !state->inNonIsolatedGroup &&
+ !state->inKnockoutGroup &&
+ !nonIsolatedGroup &&
+ state->overprintMask == 0xffffffff &&
+ !state->blendFunc) {
+ if (mode == splashModeMono8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunSoftMaskMono8;
+ } else if (mode == splashModeRGB8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunSoftMaskRGB8;
+ } else if (mode == splashModeBGR8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunSoftMaskBGR8;
+#if SPLASH_CMYK
+ } else if (mode == splashModeCMYK8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunSoftMaskCMYK8;
+#endif
+ }
+ } else if (!pipe->pattern && !pipe->noTransparency && !state->softMask &&
+ usesShape &&
+ state->inNonIsolatedGroup && groupBackBitmap->alpha &&
+ !state->inKnockoutGroup &&
+ !state->blendFunc && !pipe->nonIsolatedGroup) {
+ if (mode == splashModeMono8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunNonIsoMono8;
+ } else if (mode == splashModeRGB8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunNonIsoRGB8;
+ } else if (mode == splashModeBGR8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunNonIsoBGR8;
+#if SPLASH_CMYK
+ } else if (mode == splashModeCMYK8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunNonIsoCMYK8;
+#endif
+ }
+ }
+}
+
+// general case
+void Splash::pipeRun(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar *shapePtr2;
+ Guchar shape, aSrc, aDest, alphaI, alphaIm1, alpha0, aResult;
+ SplashColor cSrc, cDest, cBlend;
+ Guchar shapeVal, cResult0, cResult1, cResult2, cResult3;
+ int cSrcStride, shapeStride, x, lastX, t;
+ SplashColorPtr destColorPtr;
+ Guchar destColorMask;
+ Guchar *destAlphaPtr;
+ SplashColorPtr color0Ptr;
+ Guchar color0Mask;
+ Guchar *alpha0Ptr;
+ SplashColorPtr softMaskPtr;
+ Guint overprintMask;
+ Guint *overprintMaskPtr;
+#if SPLASH_CMYK
+ Guchar aPrev;
+ SplashColor cSrc2, cDest2;
+#endif
+
+ if (cSrcPtr && !pipe->pattern) {
+ cSrcStride = bitmapComps;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+
+ if (shapePtr) {
+ shapePtr2 = shapePtr;
+ shapeStride = 1;
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr2) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr2;
+ if (pipe->srcOverprintMaskPtr) {
+ ++pipe->srcOverprintMaskPtr;
+ }
+ }
+ } else {
+ shapeVal = 0xff;
+ shapePtr2 = &shapeVal;
+ shapeStride = 0;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
+
+ useDestRow(y);
+
+ if (bitmap->mode == splashModeMono1) {
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)];
+ destColorMask = (Guchar)(0x80 >> (x0 & 7));
+ } else {
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * bitmapComps];
+ destColorMask = 0; // make gcc happy
+ }
+ if (bitmap->alpha) {
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+ } else {
+ destAlphaPtr = NULL;
+ }
+ if (state->softMask) {
+ softMaskPtr = &state->softMask->data[y * state->softMask->rowSize + x0];
+ } else {
+ softMaskPtr = NULL;
+ }
+ if (state->inKnockoutGroup) {
+ if (bitmap->mode == splashModeMono1) {
+ color0Ptr =
+ &groupBackBitmap->data[(groupBackY + y) * groupBackBitmap->rowSize +
+ ((groupBackX + x0) >> 3)];
+ color0Mask = (Guchar)(0x80 >> ((groupBackX + x0) & 7));
+ } else {
+ color0Ptr =
+ &groupBackBitmap->data[(groupBackY + y) * groupBackBitmap->rowSize +
+ (groupBackX + x0) * bitmapComps];
+ color0Mask = 0; // make gcc happy
+ }
+ } else {
+ color0Ptr = NULL;
+ color0Mask = 0; // make gcc happy
+ }
+ if (state->inNonIsolatedGroup && groupBackBitmap->alpha) {
+ alpha0Ptr =
+ &groupBackBitmap->alpha[(groupBackY + y)
+ * groupBackBitmap->alphaRowSize +
+ (groupBackX + x0)];
+ } else {
+ alpha0Ptr = NULL;
+ }
+ if (overprintMaskBitmap) {
+ overprintMaskPtr = overprintMaskBitmap + y * bitmap->width + x0;
+ } else {
+ overprintMaskPtr = NULL;
+ }
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+
+ shape = *shapePtr2;
+ if (!shape) {
+ if (bitmap->mode == splashModeMono1) {
+ destColorPtr += destColorMask & 1;
+ destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1));
+ } else {
+ destColorPtr += bitmapComps;
+ }
+ if (destAlphaPtr) {
+ ++destAlphaPtr;
+ }
+ if (softMaskPtr) {
+ ++softMaskPtr;
+ }
+ if (color0Ptr) {
+ if (bitmap->mode == splashModeMono1) {
+ color0Ptr += color0Mask & 1;
+ color0Mask = (Guchar)((color0Mask << 7) | (color0Mask >> 1));
+ } else {
+ color0Ptr += bitmapComps;
+ }
+ }
+ if (alpha0Ptr) {
+ ++alpha0Ptr;
+ }
+ cSrcPtr += cSrcStride;
+ shapePtr2 += shapeStride;
+ if (pipe->srcOverprintMaskPtr) {
+ ++pipe->srcOverprintMaskPtr;
+ }
+ if (overprintMaskPtr) {
+ ++overprintMaskPtr;
+ }
+ continue;
+ }
+ lastX = x;
+
+ //----- source color
+
+ // static pattern: handled in pipeInit
+ // fixed color: handled in pipeInit
+
+ // dynamic pattern
+ if (pipe->pattern) {
+ pipe->pattern->getColor(x, y, pipe->cSrcVal);
+ }
+
+ cResult0 = cResult1 = cResult2 = cResult3 = 0; // make gcc happy
+
+ if (pipe->noTransparency && !state->blendFunc) {
+
+ //----- handle overprint
+
+ if (overprintMaskPtr) {
+ *overprintMaskPtr++ = 0xffffffff;
+ }
+
+ //----- result color
+
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ case splashModeMono8:
+ cResult0 = state->grayTransfer[cSrcPtr[0]];
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ cResult0 = state->rgbTransferR[cSrcPtr[0]];
+ cResult1 = state->rgbTransferG[cSrcPtr[1]];
+ cResult2 = state->rgbTransferB[cSrcPtr[2]];
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ cResult0 = state->cmykTransferC[cSrcPtr[0]];
+ cResult1 = state->cmykTransferM[cSrcPtr[1]];
+ cResult2 = state->cmykTransferY[cSrcPtr[2]];
+ cResult3 = state->cmykTransferK[cSrcPtr[3]];
+ break;
+#endif
+ }
+ aResult = 255;
+
+ } else { // if (noTransparency && !blendFunc)
+
+ //----- read destination pixel
+ // (or backdrop color, for knockout groups)
+
+ if (color0Ptr) {
+
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ cDest[0] = (*color0Ptr & color0Mask) ? 0xff : 0x00;
+ color0Ptr += color0Mask & 1;
+ color0Mask = (Guchar)((color0Mask << 7) | (color0Mask >> 1));
+ break;
+ case splashModeMono8:
+ cDest[0] = *color0Ptr++;
+ break;
+ case splashModeRGB8:
+ cDest[0] = color0Ptr[0];
+ cDest[1] = color0Ptr[1];
+ cDest[2] = color0Ptr[2];
+ color0Ptr += 3;
+ break;
+ case splashModeBGR8:
+ cDest[2] = color0Ptr[0];
+ cDest[1] = color0Ptr[1];
+ cDest[0] = color0Ptr[2];
+ color0Ptr += 3;
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ cDest[0] = color0Ptr[0];
+ cDest[1] = color0Ptr[1];
+ cDest[2] = color0Ptr[2];
+ cDest[3] = color0Ptr[3];
+ color0Ptr += 4;
+ break;
+#endif
+ }
+
+ } else {
+
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ cDest[0] = (*destColorPtr & destColorMask) ? 0xff : 0x00;
+ break;
+ case splashModeMono8:
+ cDest[0] = *destColorPtr;
+ break;
+ case splashModeRGB8:
+ cDest[0] = destColorPtr[0];
+ cDest[1] = destColorPtr[1];
+ cDest[2] = destColorPtr[2];
+ break;
+ case splashModeBGR8:
+ cDest[0] = destColorPtr[2];
+ cDest[1] = destColorPtr[1];
+ cDest[2] = destColorPtr[0];
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ cDest[0] = destColorPtr[0];
+ cDest[1] = destColorPtr[1];
+ cDest[2] = destColorPtr[2];
+ cDest[3] = destColorPtr[3];
+ break;
+#endif
+ }
+
+ }
+
+ if (destAlphaPtr) {
+ aDest = *destAlphaPtr;
+ } else {
+ aDest = 0xff;
+ }
+
+ //----- read source color; handle overprint
+
+ if (pipe->srcOverprintMaskPtr) {
+ overprintMask = *pipe->srcOverprintMaskPtr++;
+ } else {
+ overprintMask = state->overprintMask;
+ }
+ if (overprintMaskPtr) {
+ *overprintMaskPtr++ |= overprintMask;
+ }
+
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ case splashModeMono8:
+ cSrc[0] = state->grayTransfer[cSrcPtr[0]];
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ cSrc[0] = state->rgbTransferR[cSrcPtr[0]];
+ cSrc[1] = state->rgbTransferG[cSrcPtr[1]];
+ cSrc[2] = state->rgbTransferB[cSrcPtr[2]];
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ if (alpha0Ptr) { // non-isolated group
+ if (color0Ptr) { // non-isolated, knockout group
+ aPrev = *alpha0Ptr;
+ } else { // non-isolated, non-knockout group
+ aPrev = (Guchar)(*alpha0Ptr + aDest - div255(*alpha0Ptr * aDest));
+ }
+ } else { // isolated group
+ if (color0Ptr) { // isolated, knockout group
+ aPrev = 0;
+ } else { // isolated, non-knockout group
+ aPrev = aDest;
+ }
+ }
+ if (overprintMask & 0x01) {
+ cSrc[0] = state->cmykTransferC[cSrcPtr[0]];
+ } else {
+ cSrc[0] = div255(aPrev * cDest[0]);
+ }
+ if (overprintMask & 0x02) {
+ cSrc[1] = state->cmykTransferM[cSrcPtr[1]];
+ } else {
+ cSrc[1] = div255(aPrev * cDest[1]);
+ }
+ if (overprintMask & 0x04) {
+ cSrc[2] = state->cmykTransferY[cSrcPtr[2]];
+ } else {
+ cSrc[2] = div255(aPrev * cDest[2]);
+ }
+ if (overprintMask & 0x08) {
+ cSrc[3] = state->cmykTransferK[cSrcPtr[3]];
+ } else {
+ cSrc[3] = div255(aPrev * cDest[3]);
+ }
+ break;
+#endif
+ }
+
+ //----- source alpha
+
+ if (softMaskPtr) {
+ if (shapePtr) {
+ aSrc = div255(div255(pipe->aInput * *softMaskPtr++) * shape);
+ } else {
+ aSrc = div255(pipe->aInput * *softMaskPtr++);
+ }
+ } else if (shapePtr) {
+ aSrc = div255(pipe->aInput * shape);
+ } else {
+ aSrc = pipe->aInput;
+ }
+
+ //----- non-isolated group correction
+
+ if (pipe->nonIsolatedGroup) {
+ // This path is only used when Splash::composite() is called to
+ // composite a non-isolated group onto the backdrop. In this
+ // case, shape is the source (group) alpha.
+ //
+ // In a nested non-isolated group, i.e., if the destination is
+ // also a non-isolated group (state->inNonIsolatedGroup), we
+ // need to compute the corrected alpha, because the
+ // destination is is storing group alpha (same computation as
+ // blitCorrectedAlpha).
+ if (alpha0Ptr) {
+ t = *alpha0Ptr;
+ t = (Guchar)(aDest + t - div255(aDest * t));
+ } else {
+ t = aDest;
+ }
+ t = (t * 255) / shape - t;
+ switch (bitmap->mode) {
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ cSrc[3] = clip255(cSrc[3] + ((cSrc[3] - cDest[3]) * t) / 255);
+#endif
+ case splashModeRGB8:
+ case splashModeBGR8:
+ cSrc[2] = clip255(cSrc[2] + ((cSrc[2] - cDest[2]) * t) / 255);
+ cSrc[1] = clip255(cSrc[1] + ((cSrc[1] - cDest[1]) * t) / 255);
+ case splashModeMono1:
+ case splashModeMono8:
+ cSrc[0] = clip255(cSrc[0] + ((cSrc[0] - cDest[0]) * t) / 255);
+ break;
+ }
+ }
+
+ //----- blend function
+
+ if (state->blendFunc) {
+#if SPLASH_CMYK
+ if (bitmap->mode == splashModeCMYK8) {
+ // convert colors to additive
+ cSrc2[0] = (Guchar)(0xff - cSrc[0]);
+ cSrc2[1] = (Guchar)(0xff - cSrc[1]);
+ cSrc2[2] = (Guchar)(0xff - cSrc[2]);
+ cSrc2[3] = (Guchar)(0xff - cSrc[3]);
+ cDest2[0] = (Guchar)(0xff - cDest[0]);
+ cDest2[1] = (Guchar)(0xff - cDest[1]);
+ cDest2[2] = (Guchar)(0xff - cDest[2]);
+ cDest2[3] = (Guchar)(0xff - cDest[3]);
+ (*state->blendFunc)(cSrc2, cDest2, cBlend, bitmap->mode);
+ // convert result back to subtractive
+ cBlend[0] = (Guchar)(0xff - cBlend[0]);
+ cBlend[1] = (Guchar)(0xff - cBlend[1]);
+ cBlend[2] = (Guchar)(0xff - cBlend[2]);
+ cBlend[3] = (Guchar)(0xff - cBlend[3]);
+ } else
+#endif
+ (*state->blendFunc)(cSrc, cDest, cBlend, bitmap->mode);
+ }
+
+ //----- result alpha and non-isolated group element correction
+
+ // alphaI = alpha_i
+ // alphaIm1 = alpha_(i-1)
+
+ if (pipe->noTransparency) {
+ alphaI = alphaIm1 = aResult = 255;
+ } else if (alpha0Ptr) {
+ if (color0Ptr) {
+ // non-isolated, knockout
+ aResult = aSrc;
+ alpha0 = *alpha0Ptr++;
+ alphaI = (Guchar)(aSrc + alpha0 - div255(aSrc * alpha0));
+ alphaIm1 = alpha0;
+ } else {
+ // non-isolated, non-knockout
+ aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
+ alpha0 = *alpha0Ptr++;
+ alphaI = (Guchar)(aResult + alpha0 - div255(aResult * alpha0));
+ alphaIm1 = (Guchar)(alpha0 + aDest - div255(alpha0 * aDest));
+ }
+ } else {
+ if (color0Ptr) {
+ // isolated, knockout
+ aResult = aSrc;
+ alphaI = aSrc;
+ alphaIm1 = 0;
+ } else {
+ // isolated, non-knockout
+ aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
+ alphaI = aResult;
+ alphaIm1 = aDest;
+ }
+ }
+
+ //----- result color
+
+ switch (pipe->resultColorCtrl) {
+
+ case splashPipeResultColorNoAlphaBlendMono:
+ cResult0 = div255((255 - aDest) * cSrc[0] + aDest * cBlend[0]);
+ break;
+ case splashPipeResultColorNoAlphaBlendRGB:
+ cResult0 = div255((255 - aDest) * cSrc[0] + aDest * cBlend[0]);
+ cResult1 = div255((255 - aDest) * cSrc[1] + aDest * cBlend[1]);
+ cResult2 = div255((255 - aDest) * cSrc[2] + aDest * cBlend[2]);
+ break;
+#if SPLASH_CMYK
+ case splashPipeResultColorNoAlphaBlendCMYK:
+ cResult0 = div255((255 - aDest) * cSrc[0] + aDest * cBlend[0]);
+ cResult1 = div255((255 - aDest) * cSrc[1] + aDest * cBlend[1]);
+ cResult2 = div255((255 - aDest) * cSrc[2] + aDest * cBlend[2]);
+ cResult3 = div255((255 - aDest) * cSrc[3] + aDest * cBlend[3]);
+ break;
+#endif
+
+ case splashPipeResultColorAlphaNoBlendMono:
+ if (alphaI == 0) {
+ cResult0 = 0;
+ } else {
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + aSrc * cSrc[0])
+ / alphaI);
+ }
+ break;
+ case splashPipeResultColorAlphaNoBlendRGB:
+ if (alphaI == 0) {
+ cResult0 = 0;
+ cResult1 = 0;
+ cResult2 = 0;
+ } else {
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + aSrc * cSrc[0])
+ / alphaI);
+ cResult1 = (Guchar)(((alphaI - aSrc) * cDest[1] + aSrc * cSrc[1])
+ / alphaI);
+ cResult2 = (Guchar)(((alphaI - aSrc) * cDest[2] + aSrc * cSrc[2])
+ / alphaI);
+ }
+ break;
+#if SPLASH_CMYK
+ case splashPipeResultColorAlphaNoBlendCMYK:
+ if (alphaI == 0) {
+ cResult0 = 0;
+ cResult1 = 0;
+ cResult2 = 0;
+ cResult3 = 0;
+ } else {
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + aSrc * cSrc[0])
+ / alphaI);
+ cResult1 = (Guchar)(((alphaI - aSrc) * cDest[1] + aSrc * cSrc[1])
+ / alphaI);
+ cResult2 = (Guchar)(((alphaI - aSrc) * cDest[2] + aSrc * cSrc[2])
+ / alphaI);
+ cResult3 = (Guchar)(((alphaI - aSrc) * cDest[3] + aSrc * cSrc[3])
+ / alphaI);
+ }
+ break;
+#endif
+
+ case splashPipeResultColorAlphaBlendMono:
+ if (alphaI == 0) {
+ cResult0 = 0;
+ } else {
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] +
+ aSrc * ((255 - alphaIm1) * cSrc[0] +
+ alphaIm1 * cBlend[0]) / 255)
+ / alphaI);
+ }
+ break;
+ case splashPipeResultColorAlphaBlendRGB:
+ if (alphaI == 0) {
+ cResult0 = 0;
+ cResult1 = 0;
+ cResult2 = 0;
+ } else {
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] +
+ aSrc * ((255 - alphaIm1) * cSrc[0] +
+ alphaIm1 * cBlend[0]) / 255)
+ / alphaI);
+ cResult1 = (Guchar)(((alphaI - aSrc) * cDest[1] +
+ aSrc * ((255 - alphaIm1) * cSrc[1] +
+ alphaIm1 * cBlend[1]) / 255)
+ / alphaI);
+ cResult2 = (Guchar)(((alphaI - aSrc) * cDest[2] +
+ aSrc * ((255 - alphaIm1) * cSrc[2] +
+ alphaIm1 * cBlend[2]) / 255)
+ / alphaI);
+ }
+ break;
+#if SPLASH_CMYK
+ case splashPipeResultColorAlphaBlendCMYK:
+ if (alphaI == 0) {
+ cResult0 = 0;
+ cResult1 = 0;
+ cResult2 = 0;
+ cResult3 = 0;
+ } else {
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] +
+ aSrc * ((255 - alphaIm1) * cSrc[0] +
+ alphaIm1 * cBlend[0]) / 255)
+ / alphaI);
+ cResult1 = (Guchar)(((alphaI - aSrc) * cDest[1] +
+ aSrc * ((255 - alphaIm1) * cSrc[1] +
+ alphaIm1 * cBlend[1]) / 255)
+ / alphaI);
+ cResult2 = (Guchar)(((alphaI - aSrc) * cDest[2] +
+ aSrc * ((255 - alphaIm1) * cSrc[2] +
+ alphaIm1 * cBlend[2]) / 255)
+ / alphaI);
+ cResult3 = (Guchar)(((alphaI - aSrc) * cDest[3] +
+ aSrc * ((255 - alphaIm1) * cSrc[3] +
+ alphaIm1 * cBlend[3]) / 255)
+ / alphaI);
+ }
+ break;
+#endif
+ }
+
+ } // if (noTransparency && !blendFunc)
+
+ //----- write destination pixel
+
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ if (state->screen->test(x, y, cResult0)) {
+ *destColorPtr |= destColorMask;
+ } else {
+ *destColorPtr &= (Guchar)~destColorMask;
+ }
+ destColorPtr += destColorMask & 1;
+ destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1));
+ break;
+ case splashModeMono8:
+ *destColorPtr++ = cResult0;
+ break;
+ case splashModeRGB8:
+ destColorPtr[0] = cResult0;
+ destColorPtr[1] = cResult1;
+ destColorPtr[2] = cResult2;
+ destColorPtr += 3;
+ break;
+ case splashModeBGR8:
+ destColorPtr[0] = cResult2;
+ destColorPtr[1] = cResult1;
+ destColorPtr[2] = cResult0;
+ destColorPtr += 3;
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ destColorPtr[0] = cResult0;
+ destColorPtr[1] = cResult1;
+ destColorPtr[2] = cResult2;
+ destColorPtr[3] = cResult3;
+ destColorPtr += 4;
+ break;
+#endif
+ }
+ if (destAlphaPtr) {
+ *destAlphaPtr++ = aResult;
+ }
+
+ cSrcPtr += cSrcStride;
+ shapePtr2 += shapeStride;
+ } // for (x ...)
+
+ updateModX(lastX);
+}
+
+// special case:
+// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
+// bitmap->mode == splashModeMono1 && !bitmap->alpha) {
+void Splash::pipeRunSimpleMono1(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar cResult0;
+ SplashColorPtr destColorPtr;
+ Guchar destColorMask;
+ SplashScreenCursor screenCursor;
+ int cSrcStride, x;
+
+ if (cSrcPtr) {
+ cSrcStride = 1;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModX(x1);
+ updateModY(y);
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)];
+ destColorMask = (Guchar)(0x80 >> (x0 & 7));
+
+ screenCursor = state->screen->getTestCursor(y);
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- write destination pixel
+ cResult0 = state->grayTransfer[cSrcPtr[0]];
+ if (state->screen->testWithCursor(screenCursor, x, cResult0)) {
+ *destColorPtr |= destColorMask;
+ } else {
+ *destColorPtr &= (Guchar)~destColorMask;
+ }
+ destColorPtr += destColorMask & 1;
+ destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1));
+
+ cSrcPtr += cSrcStride;
+ }
+}
+
+// special case:
+// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
+// bitmap->mode == splashModeMono8 && bitmap->alpha) {
+void Splash::pipeRunSimpleMono8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ int cSrcStride, x;
+
+ if (cSrcPtr) {
+ cSrcStride = 1;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModX(x1);
+ updateModY(y);
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- write destination pixel
+ *destColorPtr++ = state->grayTransfer[cSrcPtr[0]];
+ *destAlphaPtr++ = 255;
+
+ cSrcPtr += cSrcStride;
+ }
+}
+
+// special case:
+// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
+// bitmap->mode == splashModeRGB8 && bitmap->alpha) {
+void Splash::pipeRunSimpleRGB8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ int cSrcStride, x;
+
+ if (cSrcPtr) {
+ cSrcStride = 3;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModX(x1);
+ updateModY(y);
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- write destination pixel
+ destColorPtr[0] = state->rgbTransferR[cSrcPtr[0]];
+ destColorPtr[1] = state->rgbTransferG[cSrcPtr[1]];
+ destColorPtr[2] = state->rgbTransferB[cSrcPtr[2]];
+ destColorPtr += 3;
+ *destAlphaPtr++ = 255;
+
+ cSrcPtr += cSrcStride;
+ }
+}
+
+// special case:
+// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
+// bitmap->mode == splashModeBGR8 && bitmap->alpha) {
+void Splash::pipeRunSimpleBGR8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ int cSrcStride, x;
+
+ if (cSrcPtr) {
+ cSrcStride = 3;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModX(x1);
+ updateModY(y);
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- write destination pixel
+ destColorPtr[0] = state->rgbTransferB[cSrcPtr[2]];
+ destColorPtr[1] = state->rgbTransferG[cSrcPtr[1]];
+ destColorPtr[2] = state->rgbTransferR[cSrcPtr[0]];
+ destColorPtr += 3;
+ *destAlphaPtr++ = 255;
+
+ cSrcPtr += cSrcStride;
+ }
+}
+
+#if SPLASH_CMYK
+// special case:
+// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
+// bitmap->mode == splashModeCMYK8 && bitmap->alpha) {
+void Splash::pipeRunSimpleCMYK8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ int cSrcStride, x;
+
+ if (cSrcPtr) {
+ cSrcStride = 4;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModX(x1);
+ updateModY(y);
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- write destination pixel
+ destColorPtr[0] = state->cmykTransferC[cSrcPtr[0]];
+ destColorPtr[1] = state->cmykTransferM[cSrcPtr[1]];
+ destColorPtr[2] = state->cmykTransferY[cSrcPtr[2]];
+ destColorPtr[3] = state->cmykTransferK[cSrcPtr[3]];
+ destColorPtr += 4;
+ *destAlphaPtr++ = 255;
+
+ cSrcPtr += cSrcStride;
+ }
+}
+#endif
+
+
+// special case:
+// !pipe->pattern && pipe->shapeOnly && !state->blendFunc &&
+// bitmap->mode == splashModeMono1 && !bitmap->alpha
+void Splash::pipeRunShapeMono1(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, cSrc0, cDest0, cResult0;
+ SplashColorPtr destColorPtr;
+ Guchar destColorMask;
+ SplashScreenCursor screenCursor;
+ int cSrcStride, x, lastX;
+
+ if (cSrcPtr) {
+ cSrcStride = 1;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)];
+ destColorMask = (Guchar)(0x80 >> (x0 & 7));
+
+ screenCursor = state->screen->getTestCursor(y);
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += destColorMask & 1;
+ destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1));
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- source color
+ cSrc0 = state->grayTransfer[cSrcPtr[0]];
+
+ //----- source alpha
+ aSrc = shape;
+
+ //----- special case for aSrc = 255
+ if (aSrc == 255) {
+ cResult0 = cSrc0;
+ } else {
+
+ //----- read destination pixel
+ cDest0 = (*destColorPtr & destColorMask) ? 0xff : 0x00;
+
+ //----- result color
+ // note: aDest = alphaI = aResult = 0xff
+ cResult0 = (Guchar)div255((0xff - aSrc) * cDest0 + aSrc * cSrc0);
+ }
+
+ //----- write destination pixel
+ if (state->screen->testWithCursor(screenCursor, x, cResult0)) {
+ *destColorPtr |= destColorMask;
+ } else {
+ *destColorPtr &= (Guchar)~destColorMask;
+ }
+ destColorPtr += destColorMask & 1;
+ destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1));
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+
+ updateModX(lastX);
+}
+
+// special case:
+// !pipe->pattern && pipe->shapeOnly && !state->blendFunc &&
+// bitmap->mode == splashModeMono8 && bitmap->alpha
+void Splash::pipeRunShapeMono8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, aResult, cSrc0, cDest0, cResult0;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ int cSrcStride, x, lastX;
+
+ if (cSrcPtr) {
+ cSrcStride = 1;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ ++destColorPtr;
+ ++destAlphaPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- source color
+ cSrc0 = state->grayTransfer[cSrcPtr[0]];
+
+ //----- source alpha
+ aSrc = shape;
+
+ //----- special case for aSrc = 255
+ if (aSrc == 255) {
+ aResult = 255;
+ cResult0 = cSrc0;
+ } else {
+
+ //----- read destination alpha
+ aDest = *destAlphaPtr;
+
+ //----- special case for aDest = 0
+ if (aDest == 0) {
+ aResult = aSrc;
+ cResult0 = cSrc0;
+ } else {
+
+ //----- read destination pixel
+ cDest0 = *destColorPtr;
+
+ //----- result alpha and non-isolated group element correction
+ aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
+ alphaI = aResult;
+
+ //----- result color
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
+ }
+ }
+
+ //----- write destination pixel
+ *destColorPtr++ = cResult0;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+
+ updateModX(lastX);
+}
+
+// special case:
+// !pipe->pattern && pipe->shapeOnly && !state->blendFunc &&
+// bitmap->mode == splashModeRGB8 && bitmap->alpha
+void Splash::pipeRunShapeRGB8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, aResult;
+ Guchar cSrc0, cSrc1, cSrc2;
+ Guchar cDest0, cDest1, cDest2;
+ Guchar cResult0, cResult1, cResult2;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ int cSrcStride, x, lastX;
+
+ if (cSrcPtr) {
+ cSrcStride = 3;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += 3;
+ ++destAlphaPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- source color
+ cSrc0 = state->rgbTransferR[cSrcPtr[0]];
+ cSrc1 = state->rgbTransferG[cSrcPtr[1]];
+ cSrc2 = state->rgbTransferB[cSrcPtr[2]];
+
+ //----- source alpha
+ aSrc = shape;
+
+ //----- special case for aSrc = 255
+ if (aSrc == 255) {
+ aResult = 255;
+ cResult0 = cSrc0;
+ cResult1 = cSrc1;
+ cResult2 = cSrc2;
+ } else {
+
+ //----- read destination alpha
+ aDest = *destAlphaPtr;
+
+ //----- special case for aDest = 0
+ if (aDest == 0) {
+ aResult = aSrc;
+ cResult0 = cSrc0;
+ cResult1 = cSrc1;
+ cResult2 = cSrc2;
+ } else {
+
+ //----- read destination pixel
+ cDest0 = destColorPtr[0];
+ cDest1 = destColorPtr[1];
+ cDest2 = destColorPtr[2];
+
+ //----- result alpha and non-isolated group element correction
+ aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
+ alphaI = aResult;
+
+ //----- result color
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
+ cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
+ cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
+ }
+ }
+
+ //----- write destination pixel
+ destColorPtr[0] = cResult0;
+ destColorPtr[1] = cResult1;
+ destColorPtr[2] = cResult2;
+ destColorPtr += 3;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+
+ updateModX(lastX);
+}
+
+// special case:
+// !pipe->pattern && pipe->shapeOnly && !state->blendFunc &&
+// bitmap->mode == splashModeBGR8 && bitmap->alpha
+void Splash::pipeRunShapeBGR8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, aResult;
+ Guchar cSrc0, cSrc1, cSrc2;
+ Guchar cDest0, cDest1, cDest2;
+ Guchar cResult0, cResult1, cResult2;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ int cSrcStride, x, lastX;
+
+ if (cSrcPtr) {
+ cSrcStride = 3;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += 3;
+ ++destAlphaPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- source color
+ cSrc0 = state->rgbTransferR[cSrcPtr[0]];
+ cSrc1 = state->rgbTransferG[cSrcPtr[1]];
+ cSrc2 = state->rgbTransferB[cSrcPtr[2]];
+
+ //----- source alpha
+ aSrc = shape;
+
+ //----- special case for aSrc = 255
+ if (aSrc == 255) {
+ aResult = 255;
+ cResult0 = cSrc0;
+ cResult1 = cSrc1;
+ cResult2 = cSrc2;
+ } else {
+
+ //----- read destination alpha
+ aDest = *destAlphaPtr;
+
+ //----- special case for aDest = 0
+ if (aDest == 0) {
+ aResult = aSrc;
+ cResult0 = cSrc0;
+ cResult1 = cSrc1;
+ cResult2 = cSrc2;
+ } else {
+
+ //----- read destination pixel
+ cDest0 = destColorPtr[2];
+ cDest1 = destColorPtr[1];
+ cDest2 = destColorPtr[0];
+
+ //----- result alpha and non-isolated group element correction
+ aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
+ alphaI = aResult;
+
+ //----- result color
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
+ cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
+ cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
+ }
+ }
+
+ //----- write destination pixel
+ destColorPtr[0] = cResult2;
+ destColorPtr[1] = cResult1;
+ destColorPtr[2] = cResult0;
+ destColorPtr += 3;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+
+ updateModX(lastX);
+}
+
+#if SPLASH_CMYK
+// special case:
+// !pipe->pattern && pipe->shapeOnly && !state->blendFunc &&
+// bitmap->mode == splashModeCMYK8 && bitmap->alpha
+void Splash::pipeRunShapeCMYK8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, aResult;
+ Guchar cSrc0, cSrc1, cSrc2, cSrc3;
+ Guchar cDest0, cDest1, cDest2, cDest3;
+ Guchar cResult0, cResult1, cResult2, cResult3;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ int cSrcStride, x, lastX;
+
+ if (cSrcPtr) {
+ cSrcStride = 4;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += 4;
+ ++destAlphaPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read destination pixel
+ cDest0 = destColorPtr[0];
+ cDest1 = destColorPtr[1];
+ cDest2 = destColorPtr[2];
+ cDest3 = destColorPtr[3];
+ aDest = *destAlphaPtr;
+
+ //----- overprint
+ cSrc0 = state->cmykTransferC[cSrcPtr[0]];
+ cSrc1 = state->cmykTransferM[cSrcPtr[1]];
+ cSrc2 = state->cmykTransferY[cSrcPtr[2]];
+ cSrc3 = state->cmykTransferK[cSrcPtr[3]];
+
+ //----- source alpha
+ aSrc = shape;
+
+ //----- special case for aSrc = 255
+ if (aSrc == 255) {
+ aResult = 255;
+ cResult0 = cSrc0;
+ cResult1 = cSrc1;
+ cResult2 = cSrc2;
+ cResult3 = cSrc3;
+ } else {
+
+ //----- special case for aDest = 0
+ if (aDest == 0) {
+ aResult = aSrc;
+ cResult0 = cSrc0;
+ cResult1 = cSrc1;
+ cResult2 = cSrc2;
+ cResult3 = cSrc3;
+ } else {
+
+ //----- result alpha and non-isolated group element correction
+ aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
+ alphaI = aResult;
+
+ //----- result color
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
+ cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
+ cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
+ cResult3 = (Guchar)(((alphaI - aSrc) * cDest3 + aSrc * cSrc3) / alphaI);
+ }
+ }
+
+ //----- write destination pixel
+ destColorPtr[0] = cResult0;
+ destColorPtr[1] = cResult1;
+ destColorPtr[2] = cResult2;
+ destColorPtr[3] = cResult3;
+ destColorPtr += 4;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+
+ updateModX(lastX);
+}
+#endif
+
+
+// special case:
+// !pipe->pattern && pipe->shapeOnly && !state->blendFunc &&
+// bitmap->mode == splashModeMono8 && !bitmap->alpha
+void Splash::pipeRunShapeNoAlphaMono8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr,
+ SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, cSrc0, cDest0, cResult0;
+ SplashColorPtr destColorPtr;
+ int cSrcStride, x, lastX;
+
+ if (cSrcPtr) {
+ cSrcStride = 1;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ ++destColorPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- source color
+ cSrc0 = state->grayTransfer[cSrcPtr[0]];
+
+ //----- source alpha
+ aSrc = shape;
+
+ //----- special case for aSrc = 255
+ if (aSrc == 255) {
+ cResult0 = cSrc0;
+ } else {
+
+ //----- read destination pixel
+ cDest0 = *destColorPtr;
+
+ //----- result color
+ cResult0 = div255((255 - aSrc) * cDest0 + aSrc * cSrc0);
+ }
+
+ //----- write destination pixel
+ *destColorPtr++ = cResult0;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+
+ updateModX(lastX);
+}
+
+// special case:
+// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
+// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
+// !pipe->nonIsolatedGroup &&
+// bitmap->mode == splashModeMono1 && !bitmap->alpha
+void Splash::pipeRunAAMono1(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, cSrc0, cDest0, cResult0;
+ SplashColorPtr destColorPtr;
+ Guchar destColorMask;
+ SplashScreenCursor screenCursor;
+ int cSrcStride, x, lastX;
+
+ if (cSrcPtr) {
+ cSrcStride = 1;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)];
+ destColorMask = (Guchar)(0x80 >> (x0 & 7));
+
+ screenCursor = state->screen->getTestCursor(y);
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += destColorMask & 1;
+ destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1));
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read destination pixel
+ cDest0 = (*destColorPtr & destColorMask) ? 0xff : 0x00;
+
+ //----- source color
+ cSrc0 = state->grayTransfer[cSrcPtr[0]];
+
+ //----- source alpha
+ aSrc = div255(pipe->aInput * shape);
+
+ //----- result color
+ // note: aDest = alphaI = aResult = 0xff
+ cResult0 = (Guchar)div255((0xff - aSrc) * cDest0 + aSrc * cSrc0);
+
+ //----- write destination pixel
+ if (state->screen->testWithCursor(screenCursor, x, cResult0)) {
+ *destColorPtr |= destColorMask;
+ } else {
+ *destColorPtr &= (Guchar)~destColorMask;
+ }
+ destColorPtr += destColorMask & 1;
+ destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1));
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+
+ updateModX(lastX);
+}
+
+// special case:
+// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
+// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
+// !pipe->nonIsolatedGroup &&
+// bitmap->mode == splashModeMono8 && bitmap->alpha
+void Splash::pipeRunAAMono8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, aResult, cSrc0, cDest0, cResult0;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ int cSrcStride, x, lastX;
+
+ if (cSrcPtr) {
+ cSrcStride = 1;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ ++destColorPtr;
+ ++destAlphaPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read destination pixel
+ cDest0 = *destColorPtr;
+ aDest = *destAlphaPtr;
+
+ //----- source color
+ cSrc0 = state->grayTransfer[cSrcPtr[0]];
+
+ //----- source alpha
+ aSrc = div255(pipe->aInput * shape);
+
+ //----- result alpha and non-isolated group element correction
+ aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
+ alphaI = aResult;
+
+ //----- result color
+ if (alphaI == 0) {
+ cResult0 = 0;
+ } else {
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
+ }
+
+ //----- write destination pixel
+ *destColorPtr++ = cResult0;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+
+ updateModX(lastX);
+}
+
+// special case:
+// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
+// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
+// !pipe->nonIsolatedGroup &&
+// bitmap->mode == splashModeRGB8 && bitmap->alpha
+void Splash::pipeRunAARGB8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, aResult;
+ Guchar cSrc0, cSrc1, cSrc2;
+ Guchar cDest0, cDest1, cDest2;
+ Guchar cResult0, cResult1, cResult2;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ int cSrcStride, x, lastX;
+
+ if (cSrcPtr) {
+ cSrcStride = 3;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += 3;
+ ++destAlphaPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read destination pixel
+ cDest0 = destColorPtr[0];
+ cDest1 = destColorPtr[1];
+ cDest2 = destColorPtr[2];
+ aDest = *destAlphaPtr;
+
+ //----- source color
+ cSrc0 = state->rgbTransferR[cSrcPtr[0]];
+ cSrc1 = state->rgbTransferG[cSrcPtr[1]];
+ cSrc2 = state->rgbTransferB[cSrcPtr[2]];
+
+ //----- source alpha
+ aSrc = div255(pipe->aInput * shape);
+
+ //----- result alpha and non-isolated group element correction
+ aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
+ alphaI = aResult;
+
+ //----- result color
+ if (alphaI == 0) {
+ cResult0 = 0;
+ cResult1 = 0;
+ cResult2 = 0;
+ } else {
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
+ cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
+ cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
+ }
+
+ //----- write destination pixel
+ destColorPtr[0] = cResult0;
+ destColorPtr[1] = cResult1;
+ destColorPtr[2] = cResult2;
+ destColorPtr += 3;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+
+ updateModX(lastX);
+}
+
+// special case:
+// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
+// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
+// !pipe->nonIsolatedGroup &&
+// bitmap->mode == splashModeBGR8 && bitmap->alpha
+void Splash::pipeRunAABGR8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, aResult;
+ Guchar cSrc0, cSrc1, cSrc2;
+ Guchar cDest0, cDest1, cDest2;
+ Guchar cResult0, cResult1, cResult2;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ int cSrcStride, x, lastX;
+
+ if (cSrcPtr) {
+ cSrcStride = 3;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += 3;
+ ++destAlphaPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read destination pixel
+ cDest0 = destColorPtr[2];
+ cDest1 = destColorPtr[1];
+ cDest2 = destColorPtr[0];
+ aDest = *destAlphaPtr;
+
+ //----- source color
+ cSrc0 = state->rgbTransferR[cSrcPtr[0]];
+ cSrc1 = state->rgbTransferG[cSrcPtr[1]];
+ cSrc2 = state->rgbTransferB[cSrcPtr[2]];
+
+ //----- source alpha
+ aSrc = div255(pipe->aInput * shape);
+
+ //----- result alpha and non-isolated group element correction
+ aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
+ alphaI = aResult;
+
+ //----- result color
+ if (alphaI == 0) {
+ cResult0 = 0;
+ cResult1 = 0;
+ cResult2 = 0;
+ } else {
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
+ cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
+ cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
+ }
+
+ //----- write destination pixel
+ destColorPtr[0] = cResult2;
+ destColorPtr[1] = cResult1;
+ destColorPtr[2] = cResult0;
+ destColorPtr += 3;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+
+ updateModX(lastX);
+}
+
+#if SPLASH_CMYK
+// special case:
+// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
+// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
+// !pipe->nonIsolatedGroup &&
+// bitmap->mode == splashModeCMYK8 && bitmap->alpha
+void Splash::pipeRunAACMYK8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, aResult;
+ Guchar cSrc0, cSrc1, cSrc2, cSrc3;
+ Guchar cDest0, cDest1, cDest2, cDest3;
+ Guchar cResult0, cResult1, cResult2, cResult3;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ int cSrcStride, x, lastX;
+
+ if (cSrcPtr) {
+ cSrcStride = 4;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += 4;
+ ++destAlphaPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read destination pixel
+ cDest0 = destColorPtr[0];
+ cDest1 = destColorPtr[1];
+ cDest2 = destColorPtr[2];
+ cDest3 = destColorPtr[3];
+ aDest = *destAlphaPtr;
+
+ //----- overprint
+ if (state->overprintMask & 1) {
+ cSrc0 = state->cmykTransferC[cSrcPtr[0]];
+ } else {
+ cSrc0 = div255(aDest * cDest0);
+ }
+ if (state->overprintMask & 2) {
+ cSrc1 = state->cmykTransferM[cSrcPtr[1]];
+ } else {
+ cSrc1 = div255(aDest * cDest1);
+ }
+ if (state->overprintMask & 4) {
+ cSrc2 = state->cmykTransferY[cSrcPtr[2]];
+ } else {
+ cSrc2 = div255(aDest * cDest2);
+ }
+ if (state->overprintMask & 8) {
+ cSrc3 = state->cmykTransferK[cSrcPtr[3]];
+ } else {
+ cSrc3 = div255(aDest * cDest3);
+ }
+
+ //----- source alpha
+ aSrc = div255(pipe->aInput * shape);
+
+ //----- result alpha and non-isolated group element correction
+ aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
+ alphaI = aResult;
+
+ //----- result color
+ if (alphaI == 0) {
+ cResult0 = 0;
+ cResult1 = 0;
+ cResult2 = 0;
+ cResult3 = 0;
+ } else {
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
+ cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
+ cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
+ cResult3 = (Guchar)(((alphaI - aSrc) * cDest3 + aSrc * cSrc3) / alphaI);
+ }
+
+ //----- write destination pixel
+ destColorPtr[0] = cResult0;
+ destColorPtr[1] = cResult1;
+ destColorPtr[2] = cResult2;
+ destColorPtr[3] = cResult3;
+ destColorPtr += 4;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+
+ updateModX(lastX);
+}
+#endif
+
+
+// special case:
+// !pipe->pattern && aInput == 255 && state->softMask && usesShape &&
+// !state->inNonIsolatedGroup && !state->inKnockoutGroup &&
+// !nonIsolatedGroup && state->overprintMask == 0xffffffff &&
+// !state->blendFunc &&
+// bitmap->mode == splashModeMono8 && bitmap->alpha
+void Splash::pipeRunSoftMaskMono8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, aResult;
+ Guchar cSrc0, cDest0, cResult0;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ SplashColorPtr softMaskPtr;
+ int cSrcStride, x, lastX;
+
+ if (cSrcPtr) {
+ cSrcStride = 1;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+ softMaskPtr = &state->softMask->data[y * state->softMask->rowSize + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ ++destColorPtr;
+ ++destAlphaPtr;
+ ++softMaskPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read source color
+ cSrc0 = state->grayTransfer[cSrcPtr[0]];
+
+ //----- source alpha
+ aSrc = div255(*softMaskPtr++ * shape);
+
+ //----- special case for aSrc = 255
+ if (aSrc == 255) {
+ aResult = 255;
+ cResult0 = cSrc0;
+ } else {
+
+ //----- read destination alpha
+ aDest = *destAlphaPtr;
+
+ //----- special case for aDest = 0
+ if (aDest == 0) {
+ aResult = aSrc;
+ cResult0 = cSrc0;
+ } else {
+
+ //----- read destination pixel
+ cDest0 = destColorPtr[0];
+
+ //----- result alpha and non-isolated group element correction
+ aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
+ alphaI = aResult;
+
+ //----- result color
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
+ }
+ }
+
+ //----- write destination pixel
+ destColorPtr[0] = cResult0;
+ ++destColorPtr;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+
+ updateModX(lastX);
+}
+
+// special case:
+// !pipe->pattern && aInput == 255 && state->softMask && usesShape &&
+// !state->inNonIsolatedGroup && !state->inKnockoutGroup &&
+// !nonIsolatedGroup && state->overprintMask == 0xffffffff &&
+// !state->blendFunc &&
+// bitmap->mode == splashModeRGB8 && bitmap->alpha
+void Splash::pipeRunSoftMaskRGB8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, aResult;
+ Guchar cSrc0, cSrc1, cSrc2;
+ Guchar cDest0, cDest1, cDest2;
+ Guchar cResult0, cResult1, cResult2;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ SplashColorPtr softMaskPtr;
+ int cSrcStride, x, lastX;
+
+ if (cSrcPtr) {
+ cSrcStride = 3;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * 3];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+ softMaskPtr = &state->softMask->data[y * state->softMask->rowSize + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += 3;
+ ++destAlphaPtr;
+ ++softMaskPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read source color
+ cSrc0 = state->rgbTransferR[cSrcPtr[0]];
+ cSrc1 = state->rgbTransferG[cSrcPtr[1]];
+ cSrc2 = state->rgbTransferB[cSrcPtr[2]];
+
+ //----- source alpha
+ aSrc = div255(*softMaskPtr++ * shape);
+
+ //----- special case for aSrc = 255
+ if (aSrc == 255) {
+ aResult = 255;
+ cResult0 = cSrc0;
+ cResult1 = cSrc1;
+ cResult2 = cSrc2;
+ } else {
+
+ //----- read destination alpha
+ aDest = *destAlphaPtr;
+
+ //----- special case for aDest = 0
+ if (aDest == 0) {
+ aResult = aSrc;
+ cResult0 = cSrc0;
+ cResult1 = cSrc1;
+ cResult2 = cSrc2;
+ } else {
+
+ //----- read destination pixel
+ cDest0 = destColorPtr[0];
+ cDest1 = destColorPtr[1];
+ cDest2 = destColorPtr[2];
+
+ //----- result alpha and non-isolated group element correction
+ aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
+ alphaI = aResult;
+
+ //----- result color
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
+ cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
+ cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
+ }
+ }
+
+ //----- write destination pixel
+ destColorPtr[0] = cResult0;
+ destColorPtr[1] = cResult1;
+ destColorPtr[2] = cResult2;
+ destColorPtr += 3;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+
+ updateModX(lastX);
+}
+
+// special case:
+// !pipe->pattern && aInput == 255 && state->softMask && usesShape &&
+// !state->inNonIsolatedGroup && !state->inKnockoutGroup &&
+// !nonIsolatedGroup && state->overprintMask == 0xffffffff &&
+// !state->blendFunc &&
+// bitmap->mode == splashModeBGR8 && bitmap->alpha
+void Splash::pipeRunSoftMaskBGR8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, aResult;
+ Guchar cSrc0, cSrc1, cSrc2;
+ Guchar cDest0, cDest1, cDest2;
+ Guchar cResult0, cResult1, cResult2;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ SplashColorPtr softMaskPtr;
+ int cSrcStride, x, lastX;
+
+ if (cSrcPtr) {
+ cSrcStride = 3;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * 3];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+ softMaskPtr = &state->softMask->data[y * state->softMask->rowSize + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += 3;
+ ++destAlphaPtr;
+ ++softMaskPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read source color
+ cSrc0 = state->rgbTransferR[cSrcPtr[0]];
+ cSrc1 = state->rgbTransferG[cSrcPtr[1]];
+ cSrc2 = state->rgbTransferB[cSrcPtr[2]];
+
+ //----- source alpha
+ aSrc = div255(*softMaskPtr++ * shape);
+
+ //----- special case for aSrc = 255
+ if (aSrc == 255) {
+ aResult = 255;
+ cResult0 = cSrc0;
+ cResult1 = cSrc1;
+ cResult2 = cSrc2;
+ } else {
+
+ //----- read destination alpha
+ aDest = *destAlphaPtr;
+
+ //----- special case for aDest = 0
+ if (aDest == 0) {
+ aResult = aSrc;
+ cResult0 = cSrc0;
+ cResult1 = cSrc1;
+ cResult2 = cSrc2;
+ } else {
+
+ //----- read destination pixel
+ cDest0 = destColorPtr[2];
+ cDest1 = destColorPtr[1];
+ cDest2 = destColorPtr[0];
+
+ //----- result alpha and non-isolated group element correction
+ aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
+ alphaI = aResult;
+
+ //----- result color
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
+ cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
+ cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
+ }
+ }
+
+ //----- write destination pixel
+ destColorPtr[2] = cResult0;
+ destColorPtr[1] = cResult1;
+ destColorPtr[0] = cResult2;
+ destColorPtr += 3;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+
+ updateModX(lastX);
+}
+
+#if SPLASH_CMYK
+// special case:
+// !pipe->pattern && aInput == 255 && state->softMask && usesShape &&
+// !state->inNonIsolatedGroup && !state->inKnockoutGroup &&
+// !nonIsolatedGroup && state->overprintMask == 0xffffffff &&
+// !state->blendFunc &&
+// bitmap->mode == splashModeCMYK8 && bitmap->alpha
+void Splash::pipeRunSoftMaskCMYK8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, aResult;
+ Guchar cSrc0, cSrc1, cSrc2, cSrc3;
+ Guchar cDest0, cDest1, cDest2, cDest3;
+ Guchar cResult0, cResult1, cResult2, cResult3;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ SplashColorPtr softMaskPtr;
+ int cSrcStride, x, lastX;
+
+ if (cSrcPtr) {
+ cSrcStride = 4;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * 4];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+ softMaskPtr = &state->softMask->data[y * state->softMask->rowSize + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += 4;
+ ++destAlphaPtr;
+ ++softMaskPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read destination pixel
+ cDest0 = destColorPtr[0];
+ cDest1 = destColorPtr[1];
+ cDest2 = destColorPtr[2];
+ cDest3 = destColorPtr[3];
+
+ //----- read destination alpha
+ aDest = *destAlphaPtr;
+
+ //----- overprint
+ cSrc0 = state->cmykTransferC[cSrcPtr[0]];
+ cSrc1 = state->cmykTransferM[cSrcPtr[1]];
+ cSrc2 = state->cmykTransferY[cSrcPtr[2]];
+ cSrc3 = state->cmykTransferK[cSrcPtr[3]];
+
+ //----- source alpha
+ aSrc = div255(*softMaskPtr++ * shape);
+
+ //----- special case for aSrc = 255
+ if (aSrc == 255) {
+ aResult = 255;
+ cResult0 = cSrc0;
+ cResult1 = cSrc1;
+ cResult2 = cSrc2;
+ cResult3 = cSrc3;
+ } else {
+
+ //----- special case for aDest = 0
+ if (aDest == 0) {
+ aResult = aSrc;
+ cResult0 = cSrc0;
+ cResult1 = cSrc1;
+ cResult2 = cSrc2;
+ cResult3 = cSrc3;
+ } else {
+
+ //----- result alpha and non-isolated group element correction
+ aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
+ alphaI = aResult;
+
+ //----- result color
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
+ cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
+ cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
+ cResult3 = (Guchar)(((alphaI - aSrc) * cDest3 + aSrc * cSrc3) / alphaI);
+ }
+ }
+
+ //----- write destination pixel
+ destColorPtr[0] = cResult0;
+ destColorPtr[1] = cResult1;
+ destColorPtr[2] = cResult2;
+ destColorPtr[3] = cResult3;
+ destColorPtr += 4;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+
+ updateModX(lastX);
+}
+#endif
+
+
+void Splash::pipeRunNonIsoMono8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, alpha0, aResult;
+ Guchar cSrc0, cDest0, cResult0;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ Guchar *alpha0Ptr;
+ int cSrcStride, x, lastX;
+
+ if (cSrcPtr) {
+ cSrcStride = 1;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+ alpha0Ptr = &groupBackBitmap->alpha[(groupBackY + y)
+ * groupBackBitmap->alphaRowSize +
+ (groupBackX + x0)];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += 1;
+ ++destAlphaPtr;
+ ++alpha0Ptr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read destination pixel
+ cDest0 = destColorPtr[0];
+ aDest = *destAlphaPtr;
+
+ //----- source color
+ cSrc0 = state->grayTransfer[cSrcPtr[0]];
+
+ //----- source alpha
+ aSrc = div255(pipe->aInput * shape);
+
+ //----- result alpha and non-isolated group element correction
+ aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
+ alpha0 = *alpha0Ptr++;
+ alphaI = (Guchar)(aResult + alpha0 - div255(aResult * alpha0));
+
+ //----- result color
+ if (alphaI == 0) {
+ cResult0 = 0;
+ } else {
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
+ }
+
+ //----- write destination pixel
+ *destColorPtr++ = cResult0;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ } // for (x ...)
+
+ updateModX(lastX);
+}
+
+void Splash::pipeRunNonIsoRGB8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, alpha0, aResult;
+ Guchar cSrc0, cSrc1, cSrc2;
+ Guchar cDest0, cDest1, cDest2;
+ Guchar cResult0, cResult1, cResult2;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ Guchar *alpha0Ptr;
+ int cSrcStride, x, lastX;
+
+ if (cSrcPtr) {
+ cSrcStride = 3;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * 3];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+ alpha0Ptr = &groupBackBitmap->alpha[(groupBackY + y)
+ * groupBackBitmap->alphaRowSize +
+ (groupBackX + x0)];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += 3;
+ ++destAlphaPtr;
+ ++alpha0Ptr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read destination pixel
+ cDest0 = destColorPtr[0];
+ cDest1 = destColorPtr[1];
+ cDest2 = destColorPtr[2];
+ aDest = *destAlphaPtr;
+
+ //----- source color
+ cSrc0 = state->rgbTransferR[cSrcPtr[0]];
+ cSrc1 = state->rgbTransferG[cSrcPtr[1]];
+ cSrc2 = state->rgbTransferB[cSrcPtr[2]];
+
+ //----- source alpha
+ aSrc = div255(pipe->aInput * shape);
+
+ //----- result alpha and non-isolated group element correction
+ aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
+ alpha0 = *alpha0Ptr++;
+ alphaI = (Guchar)(aResult + alpha0 - div255(aResult * alpha0));
+
+ //----- result color
+ if (alphaI == 0) {
+ cResult0 = 0;
+ cResult1 = 0;
+ cResult2 = 0;
+ } else {
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
+ cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
+ cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
+ }
+
+ //----- write destination pixel
+ destColorPtr[0] = cResult0;
+ destColorPtr[1] = cResult1;
+ destColorPtr[2] = cResult2;
+ destColorPtr += 3;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ } // for (x ...)
+
+ updateModX(lastX);
+}
+
+void Splash::pipeRunNonIsoBGR8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, alpha0, aResult;
+ Guchar cSrc0, cSrc1, cSrc2;
+ Guchar cDest0, cDest1, cDest2;
+ Guchar cResult0, cResult1, cResult2;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ Guchar *alpha0Ptr;
+ int cSrcStride, x, lastX;
+
+ if (cSrcPtr) {
+ cSrcStride = 3;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * 3];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+ alpha0Ptr = &groupBackBitmap->alpha[(groupBackY + y)
+ * groupBackBitmap->alphaRowSize +
+ (groupBackX + x0)];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += 3;
+ ++destAlphaPtr;
+ ++alpha0Ptr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read destination pixel
+ cDest0 = destColorPtr[2];
+ cDest1 = destColorPtr[1];
+ cDest2 = destColorPtr[0];
+ aDest = *destAlphaPtr;
+
+ //----- source color
+ cSrc0 = state->rgbTransferR[cSrcPtr[0]];
+ cSrc1 = state->rgbTransferG[cSrcPtr[1]];
+ cSrc2 = state->rgbTransferB[cSrcPtr[2]];
+
+ //----- source alpha
+ aSrc = div255(pipe->aInput * shape);
+
+ //----- result alpha and non-isolated group element correction
+ aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
+ alpha0 = *alpha0Ptr++;
+ alphaI = (Guchar)(aResult + alpha0 - div255(aResult * alpha0));
+
+ //----- result color
+ if (alphaI == 0) {
+ cResult0 = 0;
+ cResult1 = 0;
+ cResult2 = 0;
+ } else {
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
+ cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
+ cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
+ }
+
+ //----- write destination pixel
+ destColorPtr[0] = cResult2;
+ destColorPtr[1] = cResult1;
+ destColorPtr[2] = cResult0;
+ destColorPtr += 3;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ } // for (x ...)
+
+ updateModX(lastX);
+}
+
+#if SPLASH_CMYK
+void Splash::pipeRunNonIsoCMYK8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, alpha0, aResult, aPrev;
+ Guchar cSrc0, cSrc1, cSrc2, cSrc3;
+ Guchar cDest0, cDest1, cDest2, cDest3;
+ Guchar cResult0, cResult1, cResult2, cResult3;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ Guchar *alpha0Ptr;
+ int cSrcStride, x, lastX;
+
+ if (cSrcPtr) {
+ cSrcStride = 4;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * 4];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+ alpha0Ptr = &groupBackBitmap->alpha[(groupBackY + y)
+ * groupBackBitmap->alphaRowSize +
+ (groupBackX + x0)];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += 4;
+ ++destAlphaPtr;
+ ++alpha0Ptr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read destination pixel
+ cDest0 = destColorPtr[0];
+ cDest1 = destColorPtr[1];
+ cDest2 = destColorPtr[2];
+ cDest3 = destColorPtr[3];
+ aDest = *destAlphaPtr;
+
+ //----- overprint
+ aPrev = (Guchar)(*alpha0Ptr + aDest - div255(*alpha0Ptr * aDest));
+ if (state->overprintMask & 0x01) {
+ cSrc0 = state->cmykTransferC[cSrcPtr[0]];
+ } else {
+ cSrc0 = div255(aPrev * cDest0);
+ }
+ if (state->overprintMask & 0x02) {
+ cSrc1 = state->cmykTransferM[cSrcPtr[1]];
+ } else {
+ cSrc1 = div255(aPrev * cDest1);
+ }
+ if (state->overprintMask & 0x04) {
+ cSrc2 = state->cmykTransferY[cSrcPtr[2]];
+ } else {
+ cSrc2 = div255(aPrev * cDest2);
+ }
+ if (state->overprintMask & 0x08) {
+ cSrc3 = state->cmykTransferK[cSrcPtr[3]];
+ } else {
+ cSrc3 = div255(aPrev * cDest3);
+ }
+
+ //----- source alpha
+ aSrc = div255(pipe->aInput * shape);
+
+ //----- result alpha and non-isolated group element correction
+ aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
+ alpha0 = *alpha0Ptr++;
+ alphaI = (Guchar)(aResult + alpha0 - div255(aResult * alpha0));
+
+ //----- result color
+ if (alphaI == 0) {
+ cResult0 = 0;
+ cResult1 = 0;
+ cResult2 = 0;
+ cResult3 = 0;
+ } else {
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
+ cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
+ cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
+ cResult3 = (Guchar)(((alphaI - aSrc) * cDest3 + aSrc * cSrc3) / alphaI);
+ }
+
+ //----- write destination pixel
+ destColorPtr[0] = cResult0;
+ destColorPtr[1] = cResult1;
+ destColorPtr[2] = cResult2;
+ destColorPtr[3] = cResult3;
+ destColorPtr += 4;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ } // for (x ...)
+
+ updateModX(lastX);
+}
+#endif
+
+
+void Splash::useDestRow(int y) {
+ int y0, y1, yy;
+
+ if (groupDestInitMode == splashGroupDestPreInit) {
+ return;
+ }
+ if (groupDestInitYMin > groupDestInitYMax) {
+ y0 = y1 = y;
+ groupDestInitYMin = groupDestInitYMax = y;
+ } else if (y < groupDestInitYMin) {
+ y0 = y;
+ y1 = groupDestInitYMin - 1;
+ groupDestInitYMin = y;
+ } else if (y > groupDestInitYMax) {
+ y0 = groupDestInitYMax + 1;
+ y1 = y;
+ groupDestInitYMax = y;
+ } else {
+ return;
+ }
+ for (yy = y0; yy <= y1; ++yy) {
+ if (groupDestInitMode == splashGroupDestInitZero) {
+ // same as clear(color=0, alpha=0)
+ memset(bitmap->data + bitmap->rowSize * yy, 0,
+ bitmap->rowSize < 0 ? -bitmap->rowSize : bitmap->rowSize);
+ if (bitmap->alpha) {
+ memset(bitmap->alpha + bitmap->alphaRowSize * yy, 0,
+ bitmap->alphaRowSize);
+ }
+ } else { // (groupDestInitMode == splashGroupDestInitCopy)
+ // same as blitTransparent
+ copyGroupBackdropRow(yy);
+ }
+ }
+}
+
+void Splash::copyGroupBackdropRow(int y) {
+ SplashColorPtr p, q;
+ Guchar mask, srcMask;
+ int x;
+
+ if (groupBackBitmap->mode != bitmap->mode) {
+ return;
+ }
+
+ if (bitmap->mode == splashModeMono1) {
+ p = &bitmap->data[y * bitmap->rowSize];
+ mask = (Guchar)0x80;
+ q = &groupBackBitmap->data[(groupBackY + y) * groupBackBitmap->rowSize
+ + (groupBackX >> 3)];
+ srcMask = (Guchar)(0x80 >> (groupBackX & 7));
+ for (x = 0; x < bitmap->width; ++x) {
+ if (*q & srcMask) {
+ *p |= mask;
+ } else {
+ *p &= (Guchar)~mask;
+ }
+ if (!(mask = (Guchar)(mask >> 1))) {
+ mask = 0x80;
+ ++p;
+ }
+ if (!(srcMask = (Guchar)(srcMask >> 1))) {
+ srcMask = 0x80;
+ ++q;
+ }
+ }
+ } else {
+ p = &bitmap->data[y * bitmap->rowSize];
+ q = &groupBackBitmap->data[(groupBackY + y) * groupBackBitmap->rowSize
+ + bitmapComps * groupBackX];
+ memcpy(p, q, bitmapComps * bitmap->width);
+ }
+
+ if (bitmap->alpha) {
+ memset(&bitmap->alpha[y * bitmap->alphaRowSize], 0, bitmap->width);
+ }
+}
+
+//------------------------------------------------------------------------
+
+// Transform a point from user space to device space.
+inline void Splash::transform(SplashCoord *matrix,
+ SplashCoord xi, SplashCoord yi,
+ SplashCoord *xo, SplashCoord *yo) {
+ // [ m[0] m[1] 0 ]
+ // [xo yo 1] = [xi yi 1] * [ m[2] m[3] 0 ]
+ // [ m[4] m[5] 1 ]
+ *xo = xi * matrix[0] + yi * matrix[2] + matrix[4];
+ *yo = xi * matrix[1] + yi * matrix[3] + matrix[5];
+}
+
+//------------------------------------------------------------------------
+// SplashImageCache
+//------------------------------------------------------------------------
+
+SplashImageCache::SplashImageCache() {
+ tag = NULL;
+ width = 0;
+ height = 0;
+ mode = splashModeRGB8;
+ alpha = gFalse;
+ interpolate = gFalse;
+ colorData = NULL;
+ alphaData = NULL;
+ refCount = 1;
+}
+
+SplashImageCache::~SplashImageCache() {
+ if (tag) {
+ delete tag;
+ }
+ gfree(colorData);
+ gfree(alphaData);
+}
+
+GBool SplashImageCache::match(GString *aTag, int aWidth, int aHeight,
+ SplashColorMode aMode, GBool aAlpha,
+ GBool aInterpolate) {
+ return aTag && tag && !aTag->cmp(tag) &&
+ aWidth == width && aHeight == height &&
+ aMode == mode && aAlpha == alpha &&
+ aInterpolate == interpolate;
+}
+
+void SplashImageCache::reset(GString *aTag, int aWidth, int aHeight,
+ SplashColorMode aMode, GBool aAlpha,
+ GBool aInterpolate) {
+ if (tag) {
+ delete tag;
+ }
+ if (aTag) {
+ tag = aTag->copy();
+ } else {
+ tag = NULL;
+ }
+ width = aWidth;
+ height = aHeight;
+ mode = aMode;
+ alpha = aAlpha;
+ interpolate = aInterpolate;
+ gfree(colorData);
+ colorData = NULL;
+ gfree(alphaData);
+ alphaData = NULL;
+}
+
+void SplashImageCache::incRefCount() {
+ ++refCount;
+}
+
+void SplashImageCache::decRefCount() {
+ --refCount;
+ if (refCount == 0) {
+ delete this;
+ }
+}
+
+//------------------------------------------------------------------------
+// ImageScaler
+//------------------------------------------------------------------------
+
+// Abstract base class.
+class ImageScaler {
+public:
+
+ ImageScaler() {}
+ virtual ~ImageScaler() {}
+
+ // Compute the next line of the scaled image. This can be called up
+ // to [scaledHeight] times.
+ virtual void nextLine() = 0;
+
+ // Retrieve the color and alpha data generated by the most recent
+ // call to nextLine().
+ virtual Guchar *colorData() = 0;
+ virtual Guchar *alphaData() = 0;
+};
+
+//------------------------------------------------------------------------
+// BasicImageScaler
+//------------------------------------------------------------------------
+
+// The actual image scaler.
+class BasicImageScaler: public ImageScaler {
+public:
+
+ BasicImageScaler(SplashImageSource aSrc, void *aSrcData,
+ int aSrcWidth, int aSrcHeight, int aNComps, GBool aHasAlpha,
+ int aScaledWidth, int aScaledHeight, GBool aInterpolate);
+ virtual ~BasicImageScaler();
+ virtual void nextLine();
+ virtual Guchar *colorData() { return colorLine; }
+ virtual Guchar *alphaData() { return alphaLine; }
+
+protected:
+
+ void vertDownscaleHorizDownscale();
+ void vertDownscaleHorizUpscaleNoInterp();
+ void vertDownscaleHorizUpscaleInterp();
+ void vertUpscaleHorizDownscaleNoInterp();
+ void vertUpscaleHorizDownscaleInterp();
+ void vertUpscaleHorizUpscaleNoInterp();
+ void vertUpscaleHorizUpscaleInterp();
+
+ // source image data function
+ SplashImageSource src;
+ void *srcData;
+
+ // source image size
+ int srcWidth;
+ int srcHeight;
+
+ // scaled image size
+ int scaledWidth;
+ int scaledHeight;
+
+ // number of color and alpha components
+ int nComps;
+ GBool hasAlpha;
+
+ // params/state for vertical scaling
+ int yp, yq;
+ int yt, yn;
+ int ySrcCur, yScaledCur;
+ SplashCoord yInvScale;
+
+ // params for horizontal scaling
+ int xp, xq;
+ SplashCoord xInvScale;
+
+ // scaling function
+ void (BasicImageScaler::*scalingFunc)();
+
+ // temporary buffers for vertical scaling
+ Guchar *colorTmpBuf0;
+ Guchar *colorTmpBuf1;
+ Guchar *colorTmpBuf2;
+ Guchar *alphaTmpBuf0;
+ Guchar *alphaTmpBuf1;
+ Guchar *alphaTmpBuf2;
+ Guint *colorAccBuf;
+ Guint *alphaAccBuf;
+
+ // output of horizontal scaling
+ Guchar *colorLine;
+ Guchar *alphaLine;
+};
+
+BasicImageScaler::BasicImageScaler(SplashImageSource aSrc, void *aSrcData,
+ int aSrcWidth, int aSrcHeight,
+ int aNComps, GBool aHasAlpha,
+ int aScaledWidth, int aScaledHeight,
+ GBool aInterpolate) {
+ colorTmpBuf0 = NULL;
+ colorTmpBuf1 = NULL;
+ colorTmpBuf2 = NULL;
+ alphaTmpBuf0 = NULL;
+ alphaTmpBuf1 = NULL;
+ alphaTmpBuf2 = NULL;
+ colorAccBuf = NULL;
+ alphaAccBuf = NULL;
+ colorLine = NULL;
+ alphaLine = NULL;
+
+ src = aSrc;
+ srcData = aSrcData;
+ srcWidth = aSrcWidth;
+ srcHeight = aSrcHeight;
+ scaledWidth = aScaledWidth;
+ scaledHeight = aScaledHeight;
+ nComps = aNComps;
+ hasAlpha = aHasAlpha;
+
+ // select scaling function; allocate buffers
+ if (scaledHeight <= srcHeight) {
+ // vertical downscaling
+ yp = srcHeight / scaledHeight;
+ yq = srcHeight % scaledHeight;
+ yt = 0;
+ colorTmpBuf0 = (Guchar *)gmallocn(srcWidth, nComps);
+ colorAccBuf = (Guint *)gmallocn(srcWidth, nComps * (int)sizeof(Guint));
+ if (hasAlpha) {
+ alphaTmpBuf0 = (Guchar *)gmalloc(srcWidth);
+ alphaAccBuf = (Guint *)gmallocn(srcWidth, sizeof(Guint));
+ }
+ if (scaledWidth <= srcWidth) {
+ scalingFunc = &BasicImageScaler::vertDownscaleHorizDownscale;
+ } else {
+ if (aInterpolate) {
+ scalingFunc = &BasicImageScaler::vertDownscaleHorizUpscaleInterp;
+ } else {
+ scalingFunc = &BasicImageScaler::vertDownscaleHorizUpscaleNoInterp;
+ }
+ }
+ } else {
+ // vertical upscaling
+ yp = scaledHeight / srcHeight;
+ yq = scaledHeight % srcHeight;
+ yt = 0;
+ yn = 0;
+ if (aInterpolate) {
+ yInvScale = (SplashCoord)srcHeight / (SplashCoord)scaledHeight;
+ colorTmpBuf0 = (Guchar *)gmallocn(srcWidth, nComps);
+ colorTmpBuf1 = (Guchar *)gmallocn(srcWidth, nComps);
+ if (hasAlpha) {
+ alphaTmpBuf0 = (Guchar *)gmalloc(srcWidth);
+ alphaTmpBuf1 = (Guchar *)gmalloc(srcWidth);
+ }
+ ySrcCur = 0;
+ yScaledCur = 0;
+ if (scaledWidth <= srcWidth) {
+ scalingFunc = &BasicImageScaler::vertUpscaleHorizDownscaleInterp;
+ } else {
+ colorTmpBuf2 = (Guchar *)gmallocn(srcWidth, nComps);
+ if (hasAlpha) {
+ alphaTmpBuf2 = (Guchar *)gmalloc(srcWidth);
+ }
+ scalingFunc = &BasicImageScaler::vertUpscaleHorizUpscaleInterp;
+ }
+ } else {
+ colorTmpBuf0 = (Guchar *)gmallocn(srcWidth, nComps);
+ if (hasAlpha) {
+ alphaTmpBuf0 = (Guchar *)gmalloc(srcWidth);
+ }
+ if (scaledWidth <= srcWidth) {
+ scalingFunc = &BasicImageScaler::vertUpscaleHorizDownscaleNoInterp;
+ } else {
+ scalingFunc = &BasicImageScaler::vertUpscaleHorizUpscaleNoInterp;
+ }
+ }
+ }
+ if (scaledWidth <= srcWidth) {
+ xp = srcWidth / scaledWidth;
+ xq = srcWidth % scaledWidth;
+ } else {
+ xp = scaledWidth / srcWidth;
+ xq = scaledWidth % srcWidth;
+ if (aInterpolate) {
+ xInvScale = (SplashCoord)srcWidth / (SplashCoord)scaledWidth;
+ }
+ }
+ colorLine = (Guchar *)gmallocn(scaledWidth, nComps);
+ if (hasAlpha) {
+ alphaLine = (Guchar *)gmalloc(scaledWidth);
+ }
+}
+
+BasicImageScaler::~BasicImageScaler() {
+ gfree(colorTmpBuf0);
+ gfree(colorTmpBuf1);
+ gfree(colorTmpBuf2);
+ gfree(alphaTmpBuf0);
+ gfree(alphaTmpBuf1);
+ gfree(alphaTmpBuf2);
+ gfree(colorAccBuf);
+ gfree(alphaAccBuf);
+ gfree(colorLine);
+ gfree(alphaLine);
+}
+
+void BasicImageScaler::nextLine() {
+ (this->*scalingFunc)();
+}
+
+void BasicImageScaler::vertDownscaleHorizDownscale() {
+ //--- vert downscale
+ int yStep = yp;
+ yt += yq;
+ if (yt >= scaledHeight) {
+ yt -= scaledHeight;
+ ++yStep;
+ }
+ memset(colorAccBuf, 0, (srcWidth * nComps) * sizeof(Guint));
+ if (hasAlpha) {
+ memset(alphaAccBuf, 0, srcWidth * sizeof(Guint));
+ }
+ int nRowComps = srcWidth * nComps;
+ for (int i = 0; i < yStep; ++i) {
+ (*src)(srcData, colorTmpBuf0, alphaTmpBuf0);
+ for (int j = 0; j < nRowComps; ++j) {
+ colorAccBuf[j] += colorTmpBuf0[j];
+ }
+ if (hasAlpha) {
+ for (int j = 0; j < srcWidth; ++j) {
+ alphaAccBuf[j] += alphaTmpBuf0[j];
+ }
+ }
+ }
+
+ //--- horiz downscale
+ int colorAcc[splashMaxColorComps];
+ int xt = 0;
+ int unscaledColorIdx = 0;
+ int unscaledAlphaIdx = 0;
+ int scaledColorIdx = 0;
+
+ for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
+ int xStep = xp;
+ xt += xq;
+ if (xt >= scaledWidth) {
+ xt -= scaledWidth;
+ ++xStep;
+ }
+
+ for (int j = 0; j < nComps; ++j) {
+ colorAcc[j] = 0;
+ }
+ for (int i = 0; i < xStep; ++i) {
+ for (int j = 0; j < nComps; ++j) {
+ colorAcc[j] += colorAccBuf[unscaledColorIdx + j];
+ }
+ unscaledColorIdx += nComps;
+ }
+ int nPixels = yStep * xStep;
+ for (int j = 0; j < nComps; ++j) {
+ colorLine[scaledColorIdx + j] = (Guchar)(colorAcc[j] / nPixels);
+ }
+ scaledColorIdx += nComps;
+
+ if (hasAlpha) {
+ int alphaAcc = 0;
+ for (int i = 0; i < xStep; ++i) {
+ alphaAcc += alphaAccBuf[unscaledAlphaIdx];
+ ++unscaledAlphaIdx;
+ }
+ alphaLine[scaledIdx] = (Guchar)(alphaAcc / nPixels);
+ }
+ }
+}
+
+void BasicImageScaler::vertDownscaleHorizUpscaleNoInterp() {
+ //--- vert downscale
+ int yStep = yp;
+ yt += yq;
+ if (yt >= scaledHeight) {
+ yt -= scaledHeight;
+ ++yStep;
+ }
+ memset(colorAccBuf, 0, (srcWidth * nComps) * sizeof(Guint));
+ if (hasAlpha) {
+ memset(alphaAccBuf, 0, srcWidth * sizeof(Guint));
+ }
+ int nRowComps = srcWidth * nComps;
+ for (int i = 0; i < yStep; ++i) {
+ (*src)(srcData, colorTmpBuf0, alphaTmpBuf0);
+ for (int j = 0; j < nRowComps; ++j) {
+ colorAccBuf[j] += colorTmpBuf0[j];
+ }
+ if (hasAlpha) {
+ for (int j = 0; j < srcWidth; ++j) {
+ alphaAccBuf[j] += alphaTmpBuf0[j];
+ }
+ }
+ }
+
+ //--- horiz upscale
+ Guchar colorBuf[splashMaxColorComps];
+ int xt = 0;
+ int scaledColorIdx = 0;
+ int srcColorIdx = 0;
+ int scaledAlphaIdx = 0;
+ for (int srcIdx = 0; srcIdx < srcWidth; ++srcIdx) {
+ int xStep = xp;
+ xt += xq;
+ if (xt >= srcWidth) {
+ xt -= srcWidth;
+ ++xStep;
+ }
+ for (int j = 0; j < nComps; ++j) {
+ colorBuf[j] = (Guchar)(colorAccBuf[srcColorIdx + j] / yStep);
+ }
+ srcColorIdx += nComps;
+ for (int i = 0; i < xStep; ++i) {
+ for (int j = 0; j < nComps; ++j) {
+ colorLine[scaledColorIdx + j] = colorBuf[j];
+ }
+ scaledColorIdx += nComps;
+ }
+ if (hasAlpha) {
+ Guchar alphaBuf = (Guchar)(alphaAccBuf[srcIdx] / yStep);
+ for (int i = 0; i < xStep; ++i) {
+ alphaLine[scaledAlphaIdx] = alphaBuf;
+ ++scaledAlphaIdx;
+ }
+ }
+ }
+}
+
+void BasicImageScaler::vertDownscaleHorizUpscaleInterp() {
+ //--- vert downscale
+ int yStep = yp;
+ yt += yq;
+ if (yt >= scaledHeight) {
+ yt -= scaledHeight;
+ ++yStep;
+ }
+ memset(colorAccBuf, 0, (srcWidth * nComps) * sizeof(Guint));
+ if (hasAlpha) {
+ memset(alphaAccBuf, 0, srcWidth * sizeof(Guint));
+ }
+ int nRowComps = srcWidth * nComps;
+ for (int i = 0; i < yStep; ++i) {
+ (*src)(srcData, colorTmpBuf0, alphaTmpBuf0);
+ for (int j = 0; j < nRowComps; ++j) {
+ colorAccBuf[j] += colorTmpBuf0[j];
+ }
+ if (hasAlpha) {
+ for (int j = 0; j < srcWidth; ++j) {
+ alphaAccBuf[j] += alphaTmpBuf0[j];
+ }
+ }
+ }
+ for (int j = 0; j < srcWidth * nComps; ++j) {
+ colorAccBuf[j] /= yStep;
+ }
+ if (hasAlpha) {
+ for (int j = 0; j < srcWidth; ++j) {
+ alphaAccBuf[j] /= yStep;
+ }
+ }
+
+ //--- horiz upscale
+ int scaledColorIdx = 0;
+ for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
+ SplashCoord xs = ((SplashCoord)scaledIdx + 0.5) * xInvScale;
+ int x0 = splashFloor(xs - 0.5);
+ int x1 = x0 + 1;
+ SplashCoord s0 = (SplashCoord)x1 + 0.5 - xs;
+ SplashCoord s1 = (SplashCoord)1 - s0;
+ if (x0 < 0) {
+ x0 = 0;
+ }
+ if (x1 >= srcWidth) {
+ x1 = srcWidth - 1;
+ }
+ for (int j = 0; j < nComps; ++j) {
+ colorLine[scaledColorIdx + j] =
+ (Guchar)(int)(s0 * colorAccBuf[x0 * nComps + j] +
+ s1 * colorAccBuf[x1 * nComps + j]);
+ }
+ scaledColorIdx += nComps;
+ if (hasAlpha) {
+ alphaLine[scaledIdx] = (Guchar)(int)(s0 * alphaAccBuf[x0] +
+ s1 * alphaAccBuf[x1]);
+ }
+ }
+}
+
+void BasicImageScaler::vertUpscaleHorizDownscaleNoInterp() {
+ //--- vert upscale
+ if (yn == 0) {
+ yn = yp;
+ yt += yq;
+ if (yt >= srcHeight) {
+ yt -= srcHeight;
+ ++yn;
+ }
+ (*src)(srcData, colorTmpBuf0, alphaTmpBuf0);
+ }
+ --yn;
+
+ //--- horiz downscale
+ int colorAcc[splashMaxColorComps];
+ int xt = 0;
+ int unscaledColorIdx = 0;
+ int unscaledAlphaIdx = 0;
+ int scaledColorIdx = 0;
+ for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
+ int xStep = xp;
+ xt += xq;
+ if (xt >= scaledWidth) {
+ xt -= scaledWidth;
+ ++xStep;
+ }
+
+ for (int j = 0; j < nComps; ++j) {
+ colorAcc[j] = 0;
+ }
+ for (int i = 0; i < xStep; ++i) {
+ for (int j = 0; j < nComps; ++j) {
+ colorAcc[j] += colorTmpBuf0[unscaledColorIdx + j];
+ }
+ unscaledColorIdx += nComps;
+ }
+ for (int j = 0; j < nComps; ++j) {
+ colorLine[scaledColorIdx + j] = (Guchar)(colorAcc[j] / xStep);
+ }
+ scaledColorIdx += nComps;
+
+ if (hasAlpha) {
+ int alphaAcc = 0;
+ for (int i = 0; i < xStep; ++i) {
+ alphaAcc += alphaTmpBuf0[unscaledAlphaIdx];
+ ++unscaledAlphaIdx;
+ }
+ alphaLine[scaledIdx] = (Guchar)(alphaAcc / xStep);
+ }
+ }
+}
+
+void BasicImageScaler::vertUpscaleHorizDownscaleInterp() {
+ //--- vert upscale
+ if (ySrcCur == 0) {
+ (*src)(srcData, colorTmpBuf0, alphaTmpBuf0);
+ (*src)(srcData, colorTmpBuf1, alphaTmpBuf1);
+ ySrcCur = 1;
+ }
+ SplashCoord ys = ((SplashCoord)yScaledCur + 0.5) * yInvScale;
+ int y0 = splashFloor(ys - 0.5);
+ int y1 = y0 + 1;
+ SplashCoord vs0 = (SplashCoord)y1 + 0.5 - ys;
+ SplashCoord vs1 = (SplashCoord)1 - vs0;
+ if (y1 > ySrcCur && ySrcCur < srcHeight - 1) {
+ Guchar *t = colorTmpBuf0;
+ colorTmpBuf0 = colorTmpBuf1;
+ colorTmpBuf1 = t;
+ if (hasAlpha) {
+ t = alphaTmpBuf0;
+ alphaTmpBuf0 = alphaTmpBuf1;
+ alphaTmpBuf1 = t;
+ }
+ (*src)(srcData, colorTmpBuf1, alphaTmpBuf1);
+ ++ySrcCur;
+ }
+ Guchar *color0 = colorTmpBuf0;
+ Guchar *color1 = colorTmpBuf1;
+ Guchar *alpha0 = alphaTmpBuf0;
+ Guchar *alpha1 = alphaTmpBuf1;
+ if (y0 < 0) {
+ y0 = 0;
+ color1 = color0;
+ alpha1 = alpha0;
+ }
+ if (y1 >= srcHeight) {
+ y1 = srcHeight - 1;
+ color0 = color1;
+ alpha0 = alpha1;
+ }
+ ++yScaledCur;
+
+ //--- horiz downscale
+ int colorAcc[splashMaxColorComps];
+ int xt = 0;
+ int unscaledColorIdx = 0;
+ int unscaledAlphaIdx = 0;
+ int scaledColorIdx = 0;
+ for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
+ int xStep = xp;
+ xt += xq;
+ if (xt >= scaledWidth) {
+ xt -= scaledWidth;
+ ++xStep;
+ }
+
+ for (int j = 0; j < nComps; ++j) {
+ colorAcc[j] = 0;
+ }
+ for (int i = 0; i < xStep; ++i) {
+ for (int j = 0; j < nComps; ++j) {
+ colorAcc[j] += (int)(vs0 * (int)color0[unscaledColorIdx + j] +
+ vs1 * (int)color1[unscaledColorIdx + j]);
+ }
+ unscaledColorIdx += nComps;
+ }
+ for (int j = 0; j < nComps; ++j) {
+ colorLine[scaledColorIdx + j] = (Guchar)(colorAcc[j] / xStep);
+ }
+ scaledColorIdx += nComps;
+
+ if (hasAlpha) {
+ int alphaAcc = 0;
+ for (int i = 0; i < xStep; ++i) {
+ alphaAcc += (int)(vs0 * (int)alpha0[unscaledAlphaIdx] +
+ vs1 * (int)alpha1[unscaledAlphaIdx]);
+ ++unscaledAlphaIdx;
+ }
+ alphaLine[scaledIdx] = (Guchar)(alphaAcc / xStep);
+ }
+ }
+}
+
+void BasicImageScaler::vertUpscaleHorizUpscaleNoInterp() {
+ //--- vert upscale
+ if (yn == 0) {
+ yn = yp;
+ yt += yq;
+ if (yt >= srcHeight) {
+ yt -= srcHeight;
+ ++yn;
+ }
+ (*src)(srcData, colorTmpBuf0, alphaTmpBuf0);
+ }
+ --yn;
+
+ //--- horiz upscale
+ int xt = 0;
+ int scaledColorIdx = 0;
+ int srcColorIdx = 0;
+ int scaledAlphaIdx = 0;
+ for (int srcIdx = 0; srcIdx < srcWidth; ++srcIdx) {
+ int xStep = xp;
+ xt += xq;
+ if (xt >= srcWidth) {
+ xt -= srcWidth;
+ ++xStep;
+ }
+ for (int i = 0; i < xStep; ++i) {
+ for (int j = 0; j < nComps; ++j) {
+ colorLine[scaledColorIdx + j] = colorTmpBuf0[srcColorIdx + j];
+ }
+ scaledColorIdx += nComps;
+ }
+ srcColorIdx += nComps;
+ if (hasAlpha) {
+ Guchar alphaBuf = alphaTmpBuf0[srcIdx];
+ for (int i = 0; i < xStep; ++i) {
+ alphaLine[scaledAlphaIdx] = alphaBuf;
+ ++scaledAlphaIdx;
+ }
+ }
+ }
+}
+
+void BasicImageScaler::vertUpscaleHorizUpscaleInterp() {
+ //--- vert upscale
+ if (ySrcCur == 0) {
+ (*src)(srcData, colorTmpBuf0, alphaTmpBuf0);
+ (*src)(srcData, colorTmpBuf1, alphaTmpBuf1);
+ ySrcCur = 1;
+ }
+ SplashCoord ys = ((SplashCoord)yScaledCur + 0.5) * yInvScale;
+ int y0 = splashFloor(ys - 0.5);
+ int y1 = y0 + 1;
+ SplashCoord vs0 = (SplashCoord)y1 + 0.5 - ys;
+ SplashCoord vs1 = (SplashCoord)1 - vs0;
+ if (y1 > ySrcCur && ySrcCur < srcHeight - 1) {
+ Guchar *t = colorTmpBuf0;
+ colorTmpBuf0 = colorTmpBuf1;
+ colorTmpBuf1 = t;
+ if (hasAlpha) {
+ t = alphaTmpBuf0;
+ alphaTmpBuf0 = alphaTmpBuf1;
+ alphaTmpBuf1 = t;
+ }
+ (*src)(srcData, colorTmpBuf1, alphaTmpBuf1);
+ ++ySrcCur;
+ }
+ Guchar *color0 = colorTmpBuf0;
+ Guchar *color1 = colorTmpBuf1;
+ Guchar *alpha0 = alphaTmpBuf0;
+ Guchar *alpha1 = alphaTmpBuf1;
+ if (y0 < 0) {
+ y0 = 0;
+ color1 = color0;
+ alpha1 = alpha0;
+ }
+ if (y1 >= srcHeight) {
+ y1 = srcHeight - 1;
+ color0 = color1;
+ alpha0 = alpha1;
+ }
+ ++yScaledCur;
+ for (int srcIdx = 0; srcIdx < srcWidth * nComps; ++srcIdx) {
+ colorTmpBuf2[srcIdx] = (Guchar)(int)(vs0 * (int)color0[srcIdx] +
+ vs1 * (int)color1[srcIdx]);
+ }
+ if (hasAlpha) {
+ for (int srcIdx = 0; srcIdx < srcWidth; ++srcIdx) {
+ alphaTmpBuf2[srcIdx] = (Guchar)(int)(vs0 * (int)alpha0[srcIdx] +
+ vs1 * (int)alpha1[srcIdx]);
+ }
+ }
+
+ //--- horiz upscale
+ int scaledColorIdx = 0;
+ for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
+ SplashCoord xs = ((SplashCoord)scaledIdx + 0.5) * xInvScale;
+ int x0 = splashFloor(xs - 0.5);
+ int x1 = x0 + 1;
+ SplashCoord hs0 = (SplashCoord)x1 + 0.5 - xs;
+ SplashCoord hs1 = (SplashCoord)1 - hs0;
+ if (x0 < 0) {
+ x0 = 0;
+ }
+ if (x1 >= srcWidth) {
+ x1 = srcWidth - 1;
+ }
+ for (int j = 0; j < nComps; ++j) {
+ colorLine[scaledColorIdx + j] =
+ (Guchar)(int)(hs0 * (int)colorTmpBuf2[x0 * nComps + j] +
+ hs1 * (int)colorTmpBuf2[x1 * nComps + j]);
+ }
+ scaledColorIdx += nComps;
+ if (hasAlpha) {
+ alphaLine[scaledIdx] = (Guchar)(int)(hs0 * (int)alphaTmpBuf2[x0] +
+ hs1 * (int)alphaTmpBuf2[x1]);
+ }
+ }
+}
+
+//------------------------------------------------------------------------
+// SavingImageScaler
+//------------------------------------------------------------------------
+
+// Wrapper around BasicImageScaler that saves the scaled image for use
+// by ReplayImageScaler.
+class SavingImageScaler: public BasicImageScaler {
+public:
+
+ SavingImageScaler(SplashImageSource aSrc, void *aSrcData,
+ int aSrcWidth, int aSrcHeight, int aNComps, GBool aHasAlpha,
+ int aScaledWidth, int aScaledHeight, GBool aInterpolate,
+ Guchar *aColorCache, Guchar *aAlphaCache);
+ virtual void nextLine();
+
+private:
+
+ Guchar *colorPtr;
+ Guchar *alphaPtr;
+};
+
+SavingImageScaler::SavingImageScaler(SplashImageSource aSrc, void *aSrcData,
+ int aSrcWidth, int aSrcHeight,
+ int aNComps, GBool aHasAlpha,
+ int aScaledWidth, int aScaledHeight,
+ GBool aInterpolate,
+ Guchar *aColorCache, Guchar *aAlphaCache):
+ BasicImageScaler(aSrc, aSrcData, aSrcWidth, aSrcHeight, aNComps, aHasAlpha,
+ aScaledWidth, aScaledHeight, aInterpolate)
+{
+ colorPtr = aColorCache;
+ alphaPtr = aAlphaCache;
+}
+
+void SavingImageScaler::nextLine() {
+ BasicImageScaler::nextLine();
+ memcpy(colorPtr, colorData(), scaledWidth * nComps);
+ colorPtr += scaledWidth * nComps;
+ if (hasAlpha) {
+ memcpy(alphaPtr, alphaData(), scaledWidth);
+ alphaPtr += scaledWidth;
+ }
+}
+
+//------------------------------------------------------------------------
+// ReplayImageScaler
+//------------------------------------------------------------------------
+
+// "Replay" a scaled image saved by SavingImageScaler.
+class ReplayImageScaler: public ImageScaler {
+public:
+
+ ReplayImageScaler(int aNComps, GBool aHasAlpha,
+ int aScaledWidth,
+ Guchar *aColorCache, Guchar *aAlphaCache);
+ virtual void nextLine();
+ virtual Guchar *colorData() { return colorLine; }
+ virtual Guchar *alphaData() { return alphaLine; }
+
+private:
+
+ int nComps;
+ GBool hasAlpha;
+ int scaledWidth;
+ Guchar *colorPtr;
+ Guchar *alphaPtr;
+ Guchar *colorLine;
+ Guchar *alphaLine;
+};
+
+ReplayImageScaler::ReplayImageScaler(int aNComps, GBool aHasAlpha,
+ int aScaledWidth,
+ Guchar *aColorCache, Guchar *aAlphaCache) {
+ nComps = aNComps;
+ hasAlpha = aHasAlpha;
+ scaledWidth = aScaledWidth;
+ colorPtr = aColorCache;
+ alphaPtr = aAlphaCache;
+ colorLine = NULL;
+ alphaLine = NULL;
+}
+
+void ReplayImageScaler::nextLine() {
+ colorLine = colorPtr;
+ alphaLine = alphaPtr;
+ colorPtr += scaledWidth * nComps;
+ if (hasAlpha) {
+ alphaPtr += scaledWidth;
+ }
+}
+
+//------------------------------------------------------------------------
+// ImageMaskScaler
+//------------------------------------------------------------------------
+
+class ImageMaskScaler {
+public:
+
+ // Set up a MaskScaler to scale from [srcWidth]x[srcHeight] to
+ // [scaledWidth]x[scaledHeight]. The [interpolate] flag controls
+ // filtering on upsampling, and the [antialias] flag controls
+ // filtering on downsampling.
+ ImageMaskScaler(SplashImageMaskSource aSrc, void *aSrcData,
+ int aSrcWidth, int aSrcHeight,
+ int aScaledWidth, int aScaledHeight,
+ GBool aInterpolate, GBool aAntialias);
+
+ ~ImageMaskScaler();
+
+ // Compute the next line of the scaled image mask. This can be
+ // called up to [scaledHeight] times.
+ void nextLine();
+
+ // Retrieve the data generated by the most recent call to
+ // nextLine().
+ Guchar *data() { return line; }
+
+private:
+
+ void vertDownscaleHorizDownscale();
+ void vertDownscaleHorizDownscaleThresh();
+ void vertDownscaleHorizUpscaleNoInterp();
+ void vertDownscaleHorizUpscaleInterp();
+ void vertDownscaleHorizUpscaleThresh();
+ void vertUpscaleHorizDownscaleNoInterp();
+ void vertUpscaleHorizDownscaleInterp();
+ void vertUpscaleHorizDownscaleThresh();
+ void vertUpscaleHorizUpscaleNoInterp();
+ void vertUpscaleHorizUpscaleInterp();
+
+ // source image data function
+ SplashImageMaskSource src;
+ void *srcData;
+
+ // source image size
+ int srcWidth;
+ int srcHeight;
+
+ // scaled image size
+ int scaledWidth;
+ int scaledHeight;
+
+ // params/state for vertical scaling
+ int yp, yq;
+ int yt, yn;
+ int ySrcCur, yScaledCur;
+ SplashCoord yInvScale;
+
+ // params for horizontal scaling
+ int xp, xq;
+ SplashCoord xInvScale;
+
+ // vertical and horizontal scaling functions
+ void (ImageMaskScaler::*scalingFunc)();
+
+ // temporary buffers for vertical scaling
+ Guchar *tmpBuf0;
+ Guchar *tmpBuf1;
+ Guchar *tmpBuf2;
+ Guint *accBuf;
+
+ // output of horizontal scaling
+ Guchar *line;
+};
+
+ImageMaskScaler::ImageMaskScaler(SplashImageMaskSource aSrc, void *aSrcData,
+ int aSrcWidth, int aSrcHeight,
+ int aScaledWidth, int aScaledHeight,
+ GBool aInterpolate, GBool aAntialias) {
+ tmpBuf0 = NULL;
+ tmpBuf1 = NULL;
+ tmpBuf2 = NULL;
+ accBuf = NULL;
+ line = NULL;
+
+ src = aSrc;
+ srcData = aSrcData;
+ srcWidth = aSrcWidth;
+ srcHeight = aSrcHeight;
+ scaledWidth = aScaledWidth;
+ scaledHeight = aScaledHeight;
+
+ // select scaling function; allocate buffers
+ if (scaledHeight <= srcHeight) {
+ // vertical downscaling
+ yp = srcHeight / scaledHeight;
+ yq = srcHeight % scaledHeight;
+ yt = 0;
+ tmpBuf0 = (Guchar *)gmalloc(srcWidth);
+ accBuf = (Guint *)gmallocn(srcWidth, sizeof(Guint));
+ if (scaledWidth <= srcWidth) {
+ if (!aAntialias) {
+ scalingFunc = &ImageMaskScaler::vertDownscaleHorizDownscaleThresh;
+ } else {
+ scalingFunc = &ImageMaskScaler::vertDownscaleHorizDownscale;
+ }
+ } else {
+ if (!aAntialias) {
+ scalingFunc = &ImageMaskScaler::vertDownscaleHorizUpscaleThresh;
+ } else if (aInterpolate) {
+ scalingFunc = &ImageMaskScaler::vertDownscaleHorizUpscaleInterp;
+ } else {
+ scalingFunc = &ImageMaskScaler::vertDownscaleHorizUpscaleNoInterp;
+ }
+ }
+ } else {
+ // vertical upscaling
+ yp = scaledHeight / srcHeight;
+ yq = scaledHeight % srcHeight;
+ yt = 0;
+ yn = 0;
+ if (!aAntialias) {
+ tmpBuf0 = (Guchar *)gmalloc(srcWidth);
+ if (scaledWidth <= srcWidth) {
+ scalingFunc = &ImageMaskScaler::vertUpscaleHorizDownscaleThresh;
+ } else {
+ scalingFunc = &ImageMaskScaler::vertUpscaleHorizUpscaleNoInterp;
+ }
+ } else if (aInterpolate) {
+ yInvScale = (SplashCoord)srcHeight / (SplashCoord)scaledHeight;
+ tmpBuf0 = (Guchar *)gmalloc(srcWidth);
+ tmpBuf1 = (Guchar *)gmalloc(srcWidth);
+ ySrcCur = 0;
+ yScaledCur = 0;
+ if (scaledWidth <= srcWidth) {
+ scalingFunc = &ImageMaskScaler::vertUpscaleHorizDownscaleInterp;
+ } else {
+ tmpBuf2 = (Guchar *)gmalloc(srcWidth);
+ scalingFunc = &ImageMaskScaler::vertUpscaleHorizUpscaleInterp;
+ }
+ } else {
+ tmpBuf0 = (Guchar *)gmalloc(srcWidth);
+ if (scaledWidth <= srcWidth) {
+ scalingFunc = &ImageMaskScaler::vertUpscaleHorizDownscaleNoInterp;
+ } else {
+ scalingFunc = &ImageMaskScaler::vertUpscaleHorizUpscaleNoInterp;
+ }
+ }
+ }
+ if (scaledWidth <= srcWidth) {
+ xp = srcWidth / scaledWidth;
+ xq = srcWidth % scaledWidth;
+ } else {
+ xp = scaledWidth / srcWidth;
+ xq = scaledWidth % srcWidth;
+ if (aInterpolate) {
+ xInvScale = (SplashCoord)srcWidth / (SplashCoord)scaledWidth;
+ }
+ }
+ line = (Guchar *)gmalloc(scaledWidth);
+}
+
+ImageMaskScaler::~ImageMaskScaler() {
+ gfree(tmpBuf0);
+ gfree(tmpBuf1);
+ gfree(tmpBuf2);
+ gfree(accBuf);
+ gfree(line);
+}
+
+void ImageMaskScaler::nextLine() {
+ (this->*scalingFunc)();
+}
+
+void ImageMaskScaler::vertDownscaleHorizDownscale() {
+ //--- vert downscale
+ int yStep = yp;
+ yt += yq;
+ if (yt >= scaledHeight) {
+ yt -= scaledHeight;
+ ++yStep;
+ }
+ memset(accBuf, 0, srcWidth * sizeof(Guint));
+ for (int i = 0; i < yStep; ++i) {
+ (*src)(srcData, tmpBuf0);
+ for (int j = 0; j < srcWidth; ++j) {
+ accBuf[j] += tmpBuf0[j];
+ }
+ }
+
+ //--- horiz downscale
+ int acc;
+ int xt = 0;
+ int unscaledIdx = 0;
+ for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
+ int xStep = xp;
+ xt += xq;
+ if (xt >= scaledWidth) {
+ xt -= scaledWidth;
+ ++xStep;
+ }
+
+ acc = 0;
+ for (int i = 0; i < xStep; ++i) {
+ acc += accBuf[unscaledIdx];
+ ++unscaledIdx;
+ }
+ line[scaledIdx] = (Guchar)((255 * acc) / (xStep * yStep));
+ }
+}
+
+void ImageMaskScaler::vertDownscaleHorizDownscaleThresh() {
+ //--- vert downscale
+ int yStep = yp;
+ yt += yq;
+ if (yt >= scaledHeight) {
+ yt -= scaledHeight;
+ ++yStep;
+ }
+ memset(accBuf, 0, srcWidth * sizeof(Guint));
+ for (int i = 0; i < yStep; ++i) {
+ (*src)(srcData, tmpBuf0);
+ for (int j = 0; j < srcWidth; ++j) {
+ accBuf[j] += tmpBuf0[j];
+ }
+ }
+
+ //--- horiz downscale
+ int acc;
+ int xt = 0;
+ int unscaledIdx = 0;
+ for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
+ int xStep = xp;
+ xt += xq;
+ if (xt >= scaledWidth) {
+ xt -= scaledWidth;
+ ++xStep;
+ }
+
+ acc = 0;
+ for (int i = 0; i < xStep; ++i) {
+ acc += accBuf[unscaledIdx];
+ ++unscaledIdx;
+ }
+ line[scaledIdx] = acc > ((xStep * yStep) >> 1) ? (Guchar)255 : (Guchar)0;
+ }
+}
+
+void ImageMaskScaler::vertDownscaleHorizUpscaleNoInterp() {
+ //--- vert downscale
+ int yStep = yp;
+ yt += yq;
+ if (yt >= scaledHeight) {
+ yt -= scaledHeight;
+ ++yStep;
+ }
+ memset(accBuf, 0, srcWidth * sizeof(Guint));
+ for (int i = 0; i < yStep; ++i) {
+ (*src)(srcData, tmpBuf0);
+ for (int j = 0; j < srcWidth; ++j) {
+ accBuf[j] += tmpBuf0[j];
+ }
+ }
+
+ //--- horiz upscale
+ int xt = 0;
+ int scaledIdx = 0;
+ for (int srcIdx = 0; srcIdx < srcWidth; ++srcIdx) {
+ int xStep = xp;
+ xt += xq;
+ if (xt >= srcWidth) {
+ xt -= srcWidth;
+ ++xStep;
+ }
+ Guchar buf = (Guchar)((255 * accBuf[srcIdx]) / yStep);
+ for (int i = 0; i < xStep; ++i) {
+ line[scaledIdx] = buf;
+ ++scaledIdx;
+ }
+ }
+}
+
+void ImageMaskScaler::vertDownscaleHorizUpscaleInterp() {
+ //--- vert downscale
+ int yStep = yp;
+ yt += yq;
+ if (yt >= scaledHeight) {
+ yt -= scaledHeight;
+ ++yStep;
+ }
+ memset(accBuf, 0, srcWidth * sizeof(Guint));
+ for (int i = 0; i < yStep; ++i) {
+ (*src)(srcData, tmpBuf0);
+ for (int j = 0; j < srcWidth; ++j) {
+ accBuf[j] += tmpBuf0[j];
+ }
+ }
+ for (int j = 0; j < srcWidth; ++j) {
+ accBuf[j] = (255 * accBuf[j]) / yStep;
+ }
+
+ //--- horiz upscale
+ for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
+ SplashCoord xs = ((SplashCoord)scaledIdx + 0.5) * xInvScale;
+ int x0 = splashFloor(xs - 0.5);
+ int x1 = x0 + 1;
+ SplashCoord s0 = (SplashCoord)x1 + 0.5 - xs;
+ SplashCoord s1 = (SplashCoord)1 - s0;
+ if (x0 < 0) {
+ x0 = 0;
+ }
+ if (x1 >= srcWidth) {
+ x1 = srcWidth - 1;
+ }
+ line[scaledIdx] = (Guchar)(int)(s0 * accBuf[x0] + s1 * accBuf[x1]);
+ }
+}
+
+void ImageMaskScaler::vertDownscaleHorizUpscaleThresh() {
+ //--- vert downscale
+ int yStep = yp;
+ yt += yq;
+ if (yt >= scaledHeight) {
+ yt -= scaledHeight;
+ ++yStep;
+ }
+ memset(accBuf, 0, srcWidth * sizeof(Guint));
+ for (int i = 0; i < yStep; ++i) {
+ (*src)(srcData, tmpBuf0);
+ for (int j = 0; j < srcWidth; ++j) {
+ accBuf[j] += tmpBuf0[j];
+ }
+ }
+
+ //--- horiz upscale
+ int xt = 0;
+ int scaledIdx = 0;
+ for (int srcIdx = 0; srcIdx < srcWidth; ++srcIdx) {
+ int xStep = xp;
+ xt += xq;
+ if (xt >= srcWidth) {
+ xt -= srcWidth;
+ ++xStep;
+ }
+ Guchar buf = accBuf[srcIdx] > (Guint)(yStep >> 1) ? (Guchar)255 : (Guchar)0;
+ for (int i = 0; i < xStep; ++i) {
+ line[scaledIdx] = buf;
+ ++scaledIdx;
+ }
+ }
+}
+
+void ImageMaskScaler::vertUpscaleHorizDownscaleNoInterp() {
+ //--- vert upscale
+ if (yn == 0) {
+ yn = yp;
+ yt += yq;
+ if (yt >= srcHeight) {
+ yt -= srcHeight;
+ ++yn;
+ }
+ (*src)(srcData, tmpBuf0);
+ }
+ --yn;
+
+ //--- horiz downscale
+ int acc;
+ int xt = 0;
+ int unscaledIdx = 0;
+ for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
+ int xStep = xp;
+ xt += xq;
+ if (xt >= scaledWidth) {
+ xt -= scaledWidth;
+ ++xStep;
+ }
+
+ acc = 0;
+ for (int i = 0; i < xStep; ++i) {
+ acc += tmpBuf0[unscaledIdx];
+ ++unscaledIdx;
+ }
+ line[scaledIdx] = (Guchar)((255 * acc) / xStep);
+ }
+}
+
+void ImageMaskScaler::vertUpscaleHorizDownscaleInterp() {
+ //--- vert upscale
+ if (ySrcCur == 0) {
+ (*src)(srcData, tmpBuf0);
+ (*src)(srcData, tmpBuf1);
+ ySrcCur = 1;
+ }
+ SplashCoord ys = ((SplashCoord)yScaledCur + 0.5) * yInvScale;
+ int y0 = splashFloor(ys - 0.5);
+ int y1 = y0 + 1;
+ SplashCoord vs0 = (SplashCoord)y1 + 0.5 - ys;
+ SplashCoord vs1 = (SplashCoord)1 - vs0;
+ if (y1 > ySrcCur && ySrcCur < srcHeight - 1) {
+ Guchar *t = tmpBuf0;
+ tmpBuf0 = tmpBuf1;
+ tmpBuf1 = t;
+ (*src)(srcData, tmpBuf1);
+ ++ySrcCur;
+ }
+ Guchar *mask0 = tmpBuf0;
+ Guchar *mask1 = tmpBuf1;
+ if (y0 < 0) {
+ y0 = 0;
+ mask1 = mask0;
+ }
+ if (y1 >= srcHeight) {
+ y1 = srcHeight - 1;
+ mask0 = mask1;
+ }
+ ++yScaledCur;
+
+ //--- horiz downscale
+ int acc;
+ int xt = 0;
+ int unscaledIdx = 0;
+ for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
+ int xStep = xp;
+ xt += xq;
+ if (xt >= scaledWidth) {
+ xt -= scaledWidth;
+ ++xStep;
+ }
+
+ acc = 0;
+ for (int i = 0; i < xStep; ++i) {
+ acc += (int)(vs0 * (int)mask0[unscaledIdx] +
+ vs1 * (int)mask1[unscaledIdx]);
+ ++unscaledIdx;
+ }
+ line[scaledIdx] = (Guchar)((255 * acc) / xStep);
+ }
+}
+
+void ImageMaskScaler::vertUpscaleHorizDownscaleThresh() {
+ //--- vert upscale
+ if (yn == 0) {
+ yn = yp;
+ yt += yq;
+ if (yt >= srcHeight) {
+ yt -= srcHeight;
+ ++yn;
+ }
+ (*src)(srcData, tmpBuf0);
+ }
+ --yn;
+
+ //--- horiz downscale
+ int acc;
+ int xt = 0;
+ int unscaledIdx = 0;
+ for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
+ int xStep = xp;
+ xt += xq;
+ if (xt >= scaledWidth) {
+ xt -= scaledWidth;
+ ++xStep;
+ }
+
+ acc = 0;
+ for (int i = 0; i < xStep; ++i) {
+ acc += tmpBuf0[unscaledIdx];
+ ++unscaledIdx;
+ }
+ line[scaledIdx] = acc > (xStep >> 1) ? (Guchar)255 : (Guchar)0;
+ }
+}
+
+void ImageMaskScaler::vertUpscaleHorizUpscaleNoInterp() {
+ //--- vert upscale
+ if (yn == 0) {
+ yn = yp;
+ yt += yq;
+ if (yt >= srcHeight) {
+ yt -= srcHeight;
+ ++yn;
+ }
+ (*src)(srcData, tmpBuf0);
+ }
+ --yn;
+
+ //--- horiz upscale
+ int xt = 0;
+ int scaledIdx = 0;
+ for (int srcIdx = 0; srcIdx < srcWidth; ++srcIdx) {
+ int xStep = xp;
+ xt += xq;
+ if (xt >= srcWidth) {
+ xt -= srcWidth;
+ ++xStep;
+ }
+ Guchar buf = (Guchar)(255 * tmpBuf0[srcIdx]);
+ for (int i = 0; i < xStep; ++i) {
+ line[scaledIdx] = buf;
+ ++scaledIdx;
+ }
+ }
+}
+
+void ImageMaskScaler::vertUpscaleHorizUpscaleInterp() {
+ //--- vert upscale
+ if (ySrcCur == 0) {
+ (*src)(srcData, tmpBuf0);
+ (*src)(srcData, tmpBuf1);
+ ySrcCur = 1;
+ }
+ SplashCoord ys = ((SplashCoord)yScaledCur + 0.5) * yInvScale;
+ int y0 = splashFloor(ys - 0.5);
+ int y1 = y0 + 1;
+ SplashCoord vs0 = (SplashCoord)255 * ((SplashCoord)y1 + 0.5 - ys);
+ SplashCoord vs1 = (SplashCoord)255 - vs0;
+ if (y1 > ySrcCur && ySrcCur < srcHeight - 1) {
+ Guchar *t = tmpBuf0;
+ tmpBuf0 = tmpBuf1;
+ tmpBuf1 = t;
+ (*src)(srcData, tmpBuf1);
+ ++ySrcCur;
+ }
+ Guchar *mask0 = tmpBuf0;
+ Guchar *mask1 = tmpBuf1;
+ if (y0 < 0) {
+ y0 = 0;
+ mask1 = mask0;
+ }
+ if (y1 >= srcHeight) {
+ y1 = srcHeight - 1;
+ mask0 = mask1;
+ }
+ ++yScaledCur;
+ for (int srcIdx = 0; srcIdx < srcWidth; ++srcIdx) {
+ tmpBuf2[srcIdx] = (Guchar)(int)(vs0 * (int)mask0[srcIdx] +
+ vs1 * (int)mask1[srcIdx]);
+ }
+
+ //--- horiz upscale
+ for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
+ SplashCoord xs = ((SplashCoord)scaledIdx + 0.5) * xInvScale;
+ int x0 = splashFloor(xs - 0.5);
+ int x1 = x0 + 1;
+ SplashCoord hs0 = (SplashCoord)x1 + 0.5 - xs;
+ SplashCoord hs1 = (SplashCoord)1 - hs0;
+ if (x0 < 0) {
+ x0 = 0;
+ }
+ if (x1 >= srcWidth) {
+ x1 = srcWidth - 1;
+ }
+ line[scaledIdx] = (Guchar)(int)(hs0 * (int)tmpBuf2[x0] +
+ hs1 * (int)tmpBuf2[x1]);
+ }
+}
+
+//------------------------------------------------------------------------
+// Splash
+//------------------------------------------------------------------------
+
+Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,
+ SplashImageCache *imageCacheA,
+ SplashScreenParams *screenParams) {
+ bitmap = bitmapA;
+ bitmapComps = splashColorModeNComps[bitmap->mode];
+ vectorAntialias = vectorAntialiasA;
+ inShading = gFalse;
+ state = new SplashState(bitmap->width, bitmap->height, vectorAntialias,
+ screenParams);
+ scanBuf = (Guchar *)gmalloc(bitmap->width);
+ if (bitmap->mode == splashModeMono1) {
+ scanBuf2 = (Guchar *)gmalloc(bitmap->width);
+ } else {
+ scanBuf2 = NULL;
+ }
+ groupBackBitmap = NULL;
+ groupDestInitMode = splashGroupDestPreInit;
+ overprintMaskBitmap = NULL;
+ minLineWidth = 0;
+ clearModRegion();
+ debugMode = gFalse;
+
+ if (imageCacheA) {
+ imageCache = imageCacheA;
+ imageCache->incRefCount();
+ } else {
+ imageCache = new SplashImageCache();
+ }
+}
+
+Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,
+ SplashImageCache *imageCacheA, SplashScreen *screenA) {
+ bitmap = bitmapA;
+ bitmapComps = splashColorModeNComps[bitmap->mode];
+ vectorAntialias = vectorAntialiasA;
+ inShading = gFalse;
+ state = new SplashState(bitmap->width, bitmap->height, vectorAntialias,
+ screenA);
+ scanBuf = (Guchar *)gmalloc(bitmap->width);
+ if (bitmap->mode == splashModeMono1) {
+ scanBuf2 = (Guchar *)gmalloc(bitmap->width);
+ } else {
+ scanBuf2 = NULL;
+ }
+ groupBackBitmap = NULL;
+ groupDestInitMode = splashGroupDestPreInit;
+ overprintMaskBitmap = NULL;
+ minLineWidth = 0;
+ clearModRegion();
+ debugMode = gFalse;
+
+ if (imageCacheA) {
+ imageCache = imageCacheA;
+ imageCache->incRefCount();
+ } else {
+ imageCache = new SplashImageCache();
+ }
+}
+
+Splash::~Splash() {
+ imageCache->decRefCount();
+
+ while (state->next) {
+ restoreState();
+ }
+ delete state;
+ gfree(scanBuf);
+ gfree(scanBuf2);
+}
+
+//------------------------------------------------------------------------
+// state read
+//------------------------------------------------------------------------
+
+SplashCoord *Splash::getMatrix() {
+ return state->matrix;
+}
+
+SplashPattern *Splash::getStrokePattern() {
+ return state->strokePattern;
+}
+
+SplashPattern *Splash::getFillPattern() {
+ return state->fillPattern;
+}
+
+SplashScreen *Splash::getScreen() {
+ return state->screen;
+}
+
+SplashBlendFunc Splash::getBlendFunc() {
+ return state->blendFunc;
+}
+
+SplashCoord Splash::getStrokeAlpha() {
+ return state->strokeAlpha;
+}
+
+SplashCoord Splash::getFillAlpha() {
+ return state->fillAlpha;
+}
+
+SplashCoord Splash::getLineWidth() {
+ return state->lineWidth;
+}
+
+int Splash::getLineCap() {
+ return state->lineCap;
+}
+
+int Splash::getLineJoin() {
+ return state->lineJoin;
+}
+
+SplashCoord Splash::getMiterLimit() {
+ return state->miterLimit;
+}
+
+SplashCoord Splash::getFlatness() {
+ return state->flatness;
+}
+
+SplashCoord *Splash::getLineDash() {
+ return state->lineDash;
+}
+
+int Splash::getLineDashLength() {
+ return state->lineDashLength;
+}
+
+SplashCoord Splash::getLineDashPhase() {
+ return state->lineDashPhase;
+}
+
+SplashStrokeAdjustMode Splash::getStrokeAdjust() {
+ return state->strokeAdjust;
+}
+
+SplashClip *Splash::getClip() {
+ return state->clip;
+}
+
+SplashBitmap *Splash::getSoftMask() {
+ return state->softMask;
+}
+
+GBool Splash::getInNonIsolatedGroup() {
+ return state->inNonIsolatedGroup;
+}
+
+GBool Splash::getInKnockoutGroup() {
+ return state->inKnockoutGroup;
+}
+
+//------------------------------------------------------------------------
+// state write
+//------------------------------------------------------------------------
+
+void Splash::setMatrix(SplashCoord *matrix) {
+ memcpy(state->matrix, matrix, 6 * sizeof(SplashCoord));
+}
+
+void Splash::setStrokePattern(SplashPattern *strokePattern) {
+ state->setStrokePattern(strokePattern);
+}
+
+void Splash::setFillPattern(SplashPattern *fillPattern) {
+ state->setFillPattern(fillPattern);
+}
+
+void Splash::setScreen(SplashScreen *screen) {
+ state->setScreen(screen);
+}
+
+void Splash::setBlendFunc(SplashBlendFunc func) {
+ state->blendFunc = func;
+}
+
+void Splash::setStrokeAlpha(SplashCoord alpha) {
+ state->strokeAlpha = alpha;
+}
+
+void Splash::setFillAlpha(SplashCoord alpha) {
+ state->fillAlpha = alpha;
+}
+
+void Splash::setLineWidth(SplashCoord lineWidth) {
+ state->lineWidth = lineWidth;
+}
+
+void Splash::setLineCap(int lineCap) {
+ if (lineCap >= 0 && lineCap <= 2) {
+ state->lineCap = lineCap;
+ } else {
+ state->lineCap = 0;
+ }
+}
+
+void Splash::setLineJoin(int lineJoin) {
+ if (lineJoin >= 0 && lineJoin <= 2) {
+ state->lineJoin = lineJoin;
+ } else {
+ state->lineJoin = 0;
+ }
+}
+
+void Splash::setMiterLimit(SplashCoord miterLimit) {
+ state->miterLimit = miterLimit;
+}
+
+void Splash::setFlatness(SplashCoord flatness) {
+ if (flatness < 1) {
+ state->flatness = 1;
+ } else {
+ state->flatness = flatness;
+ }
+}
+
+void Splash::setLineDash(SplashCoord *lineDash, int lineDashLength,
+ SplashCoord lineDashPhase) {
+ state->setLineDash(lineDash, lineDashLength, lineDashPhase);
+}
+
+void Splash::setStrokeAdjust(SplashStrokeAdjustMode strokeAdjust) {
+ state->strokeAdjust = strokeAdjust;
+}
+
+void Splash::clipResetToRect(SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1) {
+ state->clipResetToRect(x0, y0, x1, y1);
+}
+
+SplashError Splash::clipToRect(SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1) {
+ return state->clipToRect(x0, y0, x1, y1);
+}
+
+SplashError Splash::clipToPath(SplashPath *path, GBool eo) {
+ return state->clipToPath(path, eo);
+}
+
+void Splash::setSoftMask(SplashBitmap *softMask, GBool deleteBitmap) {
+ state->setSoftMask(softMask, deleteBitmap);
+}
+
+void Splash::setInTransparencyGroup(SplashBitmap *groupBackBitmapA,
+ int groupBackXA, int groupBackYA,
+ SplashGroupDestInitMode groupDestInitModeA,
+ GBool nonIsolated, GBool knockout) {
+ groupBackBitmap = groupBackBitmapA;
+ groupBackX = groupBackXA;
+ groupBackY = groupBackYA;
+ groupDestInitMode = groupDestInitModeA;
+ groupDestInitYMin = 1;
+ groupDestInitYMax = 0;
+ state->inNonIsolatedGroup = nonIsolated;
+ state->inKnockoutGroup = knockout;
+}
+
+void Splash::forceDeferredInit(int y, int h) {
+ useDestRow(y);
+ useDestRow(y + h - 1);
+}
+
+// Check that alpha is 0 in the specified rectangle.
+// NB: This doesn't work correctly in a non-isolated group, because
+// the rasterizer temporarily stores alpha_g instead of alpha.
+GBool Splash::checkTransparentRect(int x, int y, int w, int h) {
+ if (state->inNonIsolatedGroup) {
+ return gFalse;
+ }
+
+
+ if (!bitmap->alpha) {
+ return gFalse;
+ }
+ int yy0, yy1;
+ if (groupDestInitMode == splashGroupDestPreInit) {
+ yy0 = y;
+ yy1 = y + h - 1;
+ } else {
+ // both splashGroupDestInitZero and splashGroupDestInitCopy set
+ // alpha to zero, so anything outside of groupDestInit[YMin,YMax]
+ // will have alpha=0
+ yy0 = (y > groupDestInitYMin) ? y : groupDestInitYMin;
+ yy1 = (y + h - 1 < groupDestInitYMax) ? y + h - 1 : groupDestInitYMax;
+ }
+ Guchar *alphaP = &bitmap->alpha[yy0 * bitmap->alphaRowSize + x];
+ for (int yy = yy0; yy <= yy1; ++yy) {
+ for (int xx = 0; xx < w; ++xx) {
+ if (alphaP[xx] != 0) {
+ return gFalse;
+ }
+ }
+ alphaP += bitmap->getAlphaRowSize();
+ }
+ return gTrue;
+}
+
+void Splash::setTransfer(Guchar *red, Guchar *green, Guchar *blue,
+ Guchar *gray) {
+ state->setTransfer(red, green, blue, gray);
+}
+
+void Splash::setOverprintMask(Guint overprintMask) {
+ state->overprintMask = overprintMask;
+}
+
+
+void Splash::setEnablePathSimplification(GBool en) {
+ state->enablePathSimplification = en;
+}
+
+//------------------------------------------------------------------------
+// state save/restore
+//------------------------------------------------------------------------
+
+void Splash::saveState() {
+ SplashState *newState;
+
+ newState = state->copy();
+ newState->next = state;
+ state = newState;
+}
+
+SplashError Splash::restoreState() {
+ SplashState *oldState;
+
+ if (!state->next) {
+ return splashErrNoSave;
+ }
+ oldState = state;
+ state = state->next;
+ delete oldState;
+ return splashOk;
+}
+
+//------------------------------------------------------------------------
+// drawing operations
+//------------------------------------------------------------------------
+
+void Splash::clear(SplashColorPtr color, Guchar alpha) {
+ SplashColorPtr row, p;
+ Guchar mono;
+ int x, y;
+
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ mono = (color[0] & 0x80) ? 0xff : 0x00;
+ if (bitmap->rowSize < 0) {
+ memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
+ mono, -bitmap->rowSize * bitmap->height);
+ } else {
+ memset(bitmap->data, mono, bitmap->rowSize * bitmap->height);
+ }
+ break;
+ case splashModeMono8:
+ if (bitmap->rowSize < 0) {
+ memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
+ color[0], -bitmap->rowSize * bitmap->height);
+ } else {
+ memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
+ }
+ break;
+ case splashModeRGB8:
+ if (color[0] == color[1] && color[1] == color[2]) {
+ if (bitmap->rowSize < 0) {
+ memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
+ color[0], -bitmap->rowSize * bitmap->height);
+ } else {
+ memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
+ }
+ } else {
+ row = bitmap->data;
+ for (y = 0; y < bitmap->height; ++y) {
+ p = row;
+ for (x = 0; x < bitmap->width; ++x) {
+ *p++ = color[0];
+ *p++ = color[1];
+ *p++ = color[2];
+ }
+ row += bitmap->rowSize;
+ }
+ }
+ break;
+ case splashModeBGR8:
+ if (color[0] == color[1] && color[1] == color[2]) {
+ if (bitmap->rowSize < 0) {
+ memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
+ color[0], -bitmap->rowSize * bitmap->height);
+ } else {
+ memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
+ }
+ } else {
+ row = bitmap->data;
+ for (y = 0; y < bitmap->height; ++y) {
+ p = row;
+ for (x = 0; x < bitmap->width; ++x) {
+ *p++ = color[2];
+ *p++ = color[1];
+ *p++ = color[0];
+ }
+ row += bitmap->rowSize;
+ }
+ }
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ if (color[0] == color[1] && color[1] == color[2] && color[2] == color[3]) {
+ if (bitmap->rowSize < 0) {
+ memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
+ color[0], -bitmap->rowSize * bitmap->height);
+ } else {
+ memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
+ }
+ } else {
+ row = bitmap->data;
+ for (y = 0; y < bitmap->height; ++y) {
+ p = row;
+ for (x = 0; x < bitmap->width; ++x) {
+ *p++ = color[0];
+ *p++ = color[1];
+ *p++ = color[2];
+ *p++ = color[3];
+ }
+ row += bitmap->rowSize;
+ }
+ }
+ break;
+#endif
+ }
+
+ if (bitmap->alpha) {
+ memset(bitmap->alpha, alpha, bitmap->alphaRowSize * bitmap->height);
+ }
+
+ updateModX(0);
+ updateModY(0);
+ updateModX(bitmap->width - 1);
+ updateModY(bitmap->height - 1);
+}
+
+SplashError Splash::stroke(SplashPath *path) {
+ SplashPath *path2, *dPath;
+ SplashCoord t0, t1, t2, t3, w, w2, lineDashMax, lineDashTotal;
+ int lineCap, lineJoin, i;
+
+ if (debugMode) {
+ printf("stroke [dash:%d] [width:%.2f]:\n",
+ state->lineDashLength, (double)state->lineWidth);
+ dumpPath(path);
+ }
+ opClipRes = splashClipAllOutside;
+ if (path->length == 0) {
+ return splashErrEmptyPath;
+ }
+ path2 = flattenPath(path, state->matrix, state->flatness);
+
+ // Compute an approximation of the transformed line width.
+ // Given a CTM of [m0 m1],
+ // [m2 m3]
+ // if |m0|*|m3| >= |m1|*|m2| then use min{|m0|,|m3|}, else
+ // use min{|m1|,|m2|}.
+ // This handles the common cases -- [s 0 ] and [0 s] --
+ // [0 +/-s] [+/-s 0]
+ // well, and still does something reasonable for the uncommon
+ // case transforms.
+ t0 = splashAbs(state->matrix[0]);
+ t1 = splashAbs(state->matrix[1]);
+ t2 = splashAbs(state->matrix[2]);
+ t3 = splashAbs(state->matrix[3]);
+ if (t0 * t3 >= t1 * t2) {
+ w = (t0 < t3) ? t0 : t3;
+ } else {
+ w = (t1 < t2) ? t1 : t2;
+ }
+ w2 = w * state->lineWidth;
+
+ // construct the dashed path
+ if (state->lineDashLength > 0) {
+
+ // check the maximum transformed dash element length (using the
+ // same approximation as for line width) -- if it's less than 0.1
+ // pixel, don't apply the dash pattern; this avoids a huge
+ // performance/memory hit with PDF files that use absurd dash
+ // patterns like [0.0007 0.0003]
+ lineDashTotal = 0;
+ lineDashMax = 0;
+ for (i = 0; i < state->lineDashLength; ++i) {
+ lineDashTotal += state->lineDash[i];
+ if (state->lineDash[i] > lineDashMax) {
+ lineDashMax = state->lineDash[i];
+ }
+ }
+ // Acrobat simply draws nothing if the dash array is [0]
+ if (lineDashTotal == 0) {
+ delete path2;
+ return splashOk;
+ }
+ if (w * lineDashMax > 0.1) {
+
+ dPath = makeDashedPath(path2);
+ delete path2;
+ path2 = dPath;
+ if (path2->length == 0) {
+ delete path2;
+ return splashErrEmptyPath;
+ }
+ }
+ }
+
+ // round caps on narrow lines look bad, and can't be
+ // stroke-adjusted, so use projecting caps instead (but we can't do
+ // this if there are zero-length dashes or segments, because those
+ // turn into round dots)
+ lineCap = state->lineCap;
+ lineJoin = state->lineJoin;
+ if (state->strokeAdjust == splashStrokeAdjustCAD &&
+ w2 < 3.5) {
+ if (lineCap == splashLineCapRound &&
+ !state->lineDashContainsZeroLengthDashes() &&
+ !path->containsZeroLengthSubpaths()) {
+ lineCap = splashLineCapProjecting;
+ }
+ if (lineJoin == splashLineJoinRound) {
+ lineJoin = splashLineJoinBevel;
+ }
+ }
+
+ // if there is a min line width set, and the transformed line width
+ // is smaller, use the min line width
+ if (w > 0 && w2 < minLineWidth) {
+ strokeWide(path2, minLineWidth / w, splashLineCapButt, splashLineJoinBevel);
+ } else if (bitmap->mode == splashModeMono1 || !vectorAntialias) {
+ // in monochrome mode or if antialiasing is disabled, use 0-width
+ // lines for any transformed line width <= 1 -- lines less than 1
+ // pixel wide look too fat without antialiasing
+ if (w2 < 1.001) {
+ strokeNarrow(path2);
+ } else {
+ strokeWide(path2, state->lineWidth, lineCap, lineJoin);
+ }
+ } else {
+ // in gray and color modes, only use 0-width lines if the line
+ // width is explicitly set to 0
+ if (state->lineWidth == 0) {
+ strokeNarrow(path2);
+ } else {
+ strokeWide(path2, state->lineWidth, lineCap, lineJoin);
+ }
+ }
+
+ delete path2;
+ return splashOk;
+}
+
+void Splash::strokeNarrow(SplashPath *path) {
+ SplashPipe pipe;
+ SplashXPath *xPath;
+ SplashXPathSeg *seg;
+ int x0, x1, y0, y1, xa, xb, y;
+ SplashCoord dxdy;
+ SplashClipResult clipRes;
+ int nClipRes[3];
+ int i;
+
+ nClipRes[0] = nClipRes[1] = nClipRes[2] = 0;
+
+ xPath = new SplashXPath(path, state->matrix, state->flatness, gFalse,
+ state->enablePathSimplification,
+ state->strokeAdjust);
+
+ pipeInit(&pipe, state->strokePattern,
+ (Guchar)splashRound(state->strokeAlpha * 255),
+ gTrue, gFalse);
+
+ for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) {
+ if (seg->y0 <= seg->y1) {
+ y0 = splashFloor(seg->y0);
+ y1 = splashFloor(seg->y1);
+ x0 = splashFloor(seg->x0);
+ x1 = splashFloor(seg->x1);
+ } else {
+ y0 = splashFloor(seg->y1);
+ y1 = splashFloor(seg->y0);
+ x0 = splashFloor(seg->x1);
+ x1 = splashFloor(seg->x0);
+ }
+ if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0,
+ x0 <= x1 ? x1 : x0, y1,
+ state->strokeAdjust))
+ != splashClipAllOutside) {
+ if (y0 == y1) {
+ if (x0 <= x1) {
+ drawStrokeSpan(&pipe, x0, x1, y0, clipRes == splashClipAllInside);
+ } else {
+ drawStrokeSpan(&pipe, x1, x0, y0, clipRes == splashClipAllInside);
+ }
+ } else {
+ dxdy = seg->dxdy;
+ y = state->clip->getYMinI(state->strokeAdjust);
+ if (y0 < y) {
+ y0 = y;
+ x0 = splashFloor(seg->x0 + ((SplashCoord)y0 - seg->y0) * dxdy);
+ }
+ y = state->clip->getYMaxI(state->strokeAdjust);
+ if (y1 > y) {
+ y1 = y;
+ x1 = splashFloor(seg->x0 + ((SplashCoord)y1 - seg->y0) * dxdy);
+ }
+ if (x0 <= x1) {
+ xa = x0;
+ for (y = y0; y <= y1; ++y) {
+ if (y < y1) {
+ xb = splashFloor(seg->x0 +
+ ((SplashCoord)y + 1 - seg->y0) * dxdy);
+ } else {
+ xb = x1 + 1;
+ }
+ if (xa == xb) {
+ drawStrokeSpan(&pipe, xa, xa, y, clipRes == splashClipAllInside);
+ } else {
+ drawStrokeSpan(&pipe, xa, xb - 1, y,
+ clipRes == splashClipAllInside);
+ }
+ xa = xb;
+ }
+ } else {
+ xa = x0;
+ for (y = y0; y <= y1; ++y) {
+ if (y < y1) {
+ xb = splashFloor(seg->x0 +
+ ((SplashCoord)y + 1 - seg->y0) * dxdy);
+ } else {
+ xb = x1 - 1;
+ }
+ if (xa == xb) {
+ drawStrokeSpan(&pipe, xa, xa, y, clipRes == splashClipAllInside);
+ } else {
+ drawStrokeSpan(&pipe, xb + 1, xa, y,
+ clipRes == splashClipAllInside);
+ }
+ xa = xb;
+ }
+ }
+ }
+ }
+ ++nClipRes[clipRes];
+ }
+ if (nClipRes[splashClipPartial] ||
+ (nClipRes[splashClipAllInside] && nClipRes[splashClipAllOutside])) {
+ opClipRes = splashClipPartial;
+ } else if (nClipRes[splashClipAllInside]) {
+ opClipRes = splashClipAllInside;
+ } else {
+ opClipRes = splashClipAllOutside;
+ }
+
+ delete xPath;
+}
+
+void Splash::drawStrokeSpan(SplashPipe *pipe, int x0, int x1, int y,
+ GBool noClip) {
+ int x;
+
+ x = state->clip->getXMinI(state->strokeAdjust);
+ if (x > x0) {
+ x0 = x;
+ }
+ x = state->clip->getXMaxI(state->strokeAdjust);
+ if (x < x1) {
+ x1 = x;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ for (x = x0; x <= x1; ++x) {
+ scanBuf[x] = 0xff;
+ }
+ if (!noClip) {
+ if (!state->clip->clipSpanBinary(scanBuf, y, x0, x1, state->strokeAdjust)) {
+ return;
+ }
+ }
+ (this->*pipe->run)(pipe, x0, x1, y, scanBuf + x0, NULL);
+}
+
+void Splash::strokeWide(SplashPath *path, SplashCoord w,
+ int lineCap, int lineJoin) {
+ SplashPath *path2;
+
+ path2 = makeStrokePath(path, w, lineCap, lineJoin, gFalse);
+ fillWithPattern(path2, gFalse, state->strokePattern, state->strokeAlpha);
+ delete path2;
+}
+
+SplashPath *Splash::flattenPath(SplashPath *path, SplashCoord *matrix,
+ SplashCoord flatness) {
+ SplashPath *fPath;
+ SplashCoord flatness2;
+ Guchar flag;
+ int i;
+
+ fPath = new SplashPath();
+#if USE_FIXEDPOINT
+ flatness2 = flatness;
+#else
+ flatness2 = flatness * flatness;
+#endif
+ i = 0;
+ while (i < path->length) {
+ flag = path->flags[i];
+ if (flag & splashPathFirst) {
+ fPath->moveTo(path->pts[i].x, path->pts[i].y);
+ ++i;
+ } else {
+ if (flag & splashPathCurve) {
+ 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);
+ i += 3;
+ } else {
+ fPath->lineTo(path->pts[i].x, path->pts[i].y);
+ ++i;
+ }
+ if (path->flags[i-1] & splashPathClosed) {
+ fPath->close();
+ }
+ }
+ }
+ return fPath;
+}
+
+void Splash::flattenCurve(SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1,
+ SplashCoord x2, SplashCoord y2,
+ SplashCoord x3, SplashCoord y3,
+ SplashCoord *matrix, SplashCoord flatness2,
+ SplashPath *fPath) {
+ SplashCoord cx[splashMaxCurveSplits + 1][3];
+ SplashCoord cy[splashMaxCurveSplits + 1][3];
+ int cNext[splashMaxCurveSplits + 1];
+ SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh;
+ SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh;
+ SplashCoord dx, dy, mx, my, tx, ty, d1, d2;
+ int p1, p2, p3;
+
+ // initial segment
+ p1 = 0;
+ p2 = splashMaxCurveSplits;
+ cx[p1][0] = x0; cy[p1][0] = y0;
+ cx[p1][1] = x1; cy[p1][1] = y1;
+ cx[p1][2] = x2; cy[p1][2] = y2;
+ cx[p2][0] = x3; cy[p2][0] = y3;
+ cNext[p1] = p2;
+
+ while (p1 < splashMaxCurveSplits) {
+
+ // get the next segment
+ xl0 = cx[p1][0]; yl0 = cy[p1][0];
+ xx1 = cx[p1][1]; yy1 = cy[p1][1];
+ xx2 = cx[p1][2]; yy2 = cy[p1][2];
+ p2 = cNext[p1];
+ xr3 = cx[p2][0]; yr3 = cy[p2][0];
+
+ // compute the distances (in device space) from the control points
+ // to the midpoint of the straight line (this is a bit of a hack,
+ // but it's much faster than computing the actual distances to the
+ // line)
+ transform(matrix, (xl0 + xr3) * 0.5, (yl0 + yr3) * 0.5, &mx, &my);
+ transform(matrix, xx1, yy1, &tx, &ty);
+#if USE_FIXEDPOINT
+ d1 = splashDist(tx, ty, mx, my);
+#else
+ dx = tx - mx;
+ dy = ty - my;
+ d1 = dx*dx + dy*dy;
+#endif
+ transform(matrix, xx2, yy2, &tx, &ty);
+#if USE_FIXEDPOINT
+ d2 = splashDist(tx, ty, mx, my);
+#else
+ dx = tx - mx;
+ dy = ty - my;
+ d2 = dx*dx + dy*dy;
+#endif
+
+ // if the curve is flat enough, or no more subdivisions are
+ // allowed, add the straight line segment
+ if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) {
+ fPath->lineTo(xr3, yr3);
+ p1 = p2;
+
+ // otherwise, subdivide the curve
+ } else {
+ xl1 = splashAvg(xl0, xx1);
+ yl1 = splashAvg(yl0, yy1);
+ xh = splashAvg(xx1, xx2);
+ yh = splashAvg(yy1, yy2);
+ xl2 = splashAvg(xl1, xh);
+ yl2 = splashAvg(yl1, yh);
+ xr2 = splashAvg(xx2, xr3);
+ yr2 = splashAvg(yy2, yr3);
+ xr1 = splashAvg(xh, xr2);
+ yr1 = splashAvg(yh, yr2);
+ xr0 = splashAvg(xl2, xr1);
+ yr0 = splashAvg(yl2, yr1);
+ // add the new subdivision points
+ p3 = (p1 + p2) / 2;
+ cx[p1][1] = xl1; cy[p1][1] = yl1;
+ cx[p1][2] = xl2; cy[p1][2] = yl2;
+ cNext[p1] = p3;
+ cx[p3][0] = xr0; cy[p3][0] = yr0;
+ cx[p3][1] = xr1; cy[p3][1] = yr1;
+ cx[p3][2] = xr2; cy[p3][2] = yr2;
+ cNext[p3] = p2;
+ }
+ }
+}
+
+SplashPath *Splash::makeDashedPath(SplashPath *path) {
+ SplashPath *dPath;
+ SplashCoord lineDashTotal;
+ SplashCoord lineDashStartPhase, lineDashDist, segLen;
+ SplashCoord x0, y0, x1, y1, xa, ya;
+ GBool lineDashStartOn, lineDashEndOn, lineDashOn, newPath;
+ int lineDashStartIdx, lineDashIdx, subpathStart, nDashes;
+ int i, j, k;
+
+ lineDashTotal = 0;
+ for (i = 0; i < state->lineDashLength; ++i) {
+ lineDashTotal += state->lineDash[i];
+ }
+ // Acrobat simply draws nothing if the dash array is [0]
+ if (lineDashTotal == 0) {
+ return new SplashPath();
+ }
+ lineDashStartPhase = state->lineDashPhase;
+ if (lineDashStartPhase > lineDashTotal * 2) {
+ i = splashFloor(lineDashStartPhase / (lineDashTotal * 2));
+ lineDashStartPhase -= lineDashTotal * i * 2;
+ } else if (lineDashStartPhase < 0) {
+ i = splashCeil(-lineDashStartPhase / (lineDashTotal * 2));
+ lineDashStartPhase += lineDashTotal * i * 2;
+ }
+ i = splashFloor(lineDashStartPhase / lineDashTotal);
+ lineDashStartPhase -= (SplashCoord)i * lineDashTotal;
+ lineDashStartOn = gTrue;
+ lineDashStartIdx = 0;
+ if (lineDashStartPhase > 0) {
+ while (lineDashStartPhase >= state->lineDash[lineDashStartIdx]) {
+ lineDashStartOn = !lineDashStartOn;
+ lineDashStartPhase -= state->lineDash[lineDashStartIdx];
+ if (++lineDashStartIdx == state->lineDashLength) {
+ lineDashStartIdx = 0;
+ }
+ }
+ }
+
+ dPath = new SplashPath();
+
+ // process each subpath
+ i = 0;
+ while (i < path->length) {
+
+ // find the end of the subpath
+ for (j = i;
+ j < path->length - 1 && !(path->flags[j] & splashPathLast);
+ ++j) ;
+
+ // initialize the dash parameters
+ lineDashOn = lineDashStartOn;
+ lineDashEndOn = lineDashStartOn;
+ lineDashIdx = lineDashStartIdx;
+ lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase;
+ subpathStart = dPath->length;
+ nDashes = 0;
+
+ // process each segment of the subpath
+ newPath = gTrue;
+ for (k = i; k < j; ++k) {
+
+ // grab the segment
+ x0 = path->pts[k].x;
+ y0 = path->pts[k].y;
+ x1 = path->pts[k+1].x;
+ y1 = path->pts[k+1].y;
+ segLen = splashDist(x0, y0, x1, y1);
+
+ // process the segment
+ while (segLen > 0) {
+
+ // Special case for zero-length dash segments: draw a very
+ // short -- but not zero-length -- segment. This ensures that
+ // we get the correct behavior with butt and projecting line
+ // caps. The PS/PDF specs imply that zero-length segments are
+ // not drawn unless the line cap is round, but Acrobat and
+ // Ghostscript both draw very short segments (for butt caps)
+ // and squares (for projecting caps).
+ if (lineDashDist == 0) {
+ if (lineDashOn) {
+ if (newPath) {
+ dPath->moveTo(x0, y0);
+ newPath = gFalse;
+ ++nDashes;
+ }
+ xa = x0 + ((SplashCoord)0.001 / segLen) * (x1 - x0);
+ ya = y0 + ((SplashCoord)0.001 / segLen) * (y1 - y0);
+ dPath->lineTo(xa, ya);
+ }
+
+ } else if (lineDashDist >= segLen) {
+ if (lineDashOn) {
+ if (newPath) {
+ dPath->moveTo(x0, y0);
+ newPath = gFalse;
+ ++nDashes;
+ }
+ dPath->lineTo(x1, y1);
+ }
+ lineDashDist -= segLen;
+ segLen = 0;
+
+ } else {
+ xa = x0 + (lineDashDist / segLen) * (x1 - x0);
+ ya = y0 + (lineDashDist / segLen) * (y1 - y0);
+ if (lineDashOn) {
+ if (newPath) {
+ dPath->moveTo(x0, y0);
+ newPath = gFalse;
+ ++nDashes;
+ }
+ dPath->lineTo(xa, ya);
+ }
+ x0 = xa;
+ y0 = ya;
+ segLen -= lineDashDist;
+ lineDashDist = 0;
+ }
+
+ lineDashEndOn = lineDashOn;
+
+ // get the next entry in the dash array
+ if (lineDashDist <= 0) {
+ lineDashOn = !lineDashOn;
+ if (++lineDashIdx == state->lineDashLength) {
+ lineDashIdx = 0;
+ }
+ lineDashDist = state->lineDash[lineDashIdx];
+ newPath = gTrue;
+ }
+ }
+ }
+
+ // in a closed subpath, where the dash pattern is "on" at both the
+ // start and end of the subpath, we need to merge the start and
+ // end to get a proper line join
+ if ((path->flags[j] & splashPathClosed) &&
+ lineDashStartOn &&
+ lineDashEndOn) {
+ if (nDashes == 1) {
+ dPath->close();
+ } else if (nDashes > 1) {
+ k = subpathStart;
+ do {
+ ++k;
+ dPath->lineTo(dPath->pts[k].x, dPath->pts[k].y);
+ } while (!(dPath->flags[k] & splashPathLast));
+ ++k;
+ memmove(&dPath->pts[subpathStart], &dPath->pts[k],
+ (dPath->length - k) * sizeof(SplashPathPoint));
+ memmove(&dPath->flags[subpathStart], &dPath->flags[k],
+ (dPath->length - k) * sizeof(Guchar));
+ dPath->length -= k - subpathStart;
+ dPath->curSubpath -= k - subpathStart;
+ }
+ }
+
+ i = j + 1;
+ }
+
+ return dPath;
+}
+
+SplashError Splash::fill(SplashPath *path, GBool eo) {
+ if (debugMode) {
+ printf("fill [eo:%d]:\n", eo);
+ dumpPath(path);
+ }
+ return fillWithPattern(path, eo, state->fillPattern, state->fillAlpha);
+}
+
+SplashError Splash::fillWithPattern(SplashPath *path, GBool eo,
+ SplashPattern *pattern,
+ SplashCoord alpha) {
+ SplashPipe pipe;
+ SplashPath *path2;
+ SplashXPath *xPath;
+ SplashXPathScanner *scanner;
+ int xMin, yMin, xMax, xMin2, xMax2, yMax, y, t;
+ SplashClipResult clipRes;
+
+ if (path->length == 0) {
+ return splashErrEmptyPath;
+ }
+ if (pathAllOutside(path)) {
+ opClipRes = splashClipAllOutside;
+ return splashOk;
+ }
+
+ path2 = tweakFillPath(path);
+
+ xPath = new SplashXPath(path2, state->matrix, state->flatness, gTrue,
+ state->enablePathSimplification,
+ state->strokeAdjust);
+ if (path2 != path) {
+ delete path2;
+ }
+ xMin = xPath->getXMin();
+ yMin = xPath->getYMin();
+ xMax = xPath->getXMax();
+ yMax = xPath->getYMax();
+ if (xMin > xMax || yMin > yMax) {
+ delete xPath;
+ return splashOk;
+ }
+ scanner = new SplashXPathScanner(xPath, eo, yMin, yMax);
+
+ // check clipping
+ if ((clipRes = state->clip->testRect(xMin, yMin, xMax, yMax,
+ state->strokeAdjust))
+ != splashClipAllOutside) {
+
+ if ((t = state->clip->getXMinI(state->strokeAdjust)) > xMin) {
+ xMin = t;
+ }
+ if ((t = state->clip->getXMaxI(state->strokeAdjust)) < xMax) {
+ xMax = t;
+ }
+ if ((t = state->clip->getYMinI(state->strokeAdjust)) > yMin) {
+ yMin = t;
+ }
+ if ((t = state->clip->getYMaxI(state->strokeAdjust)) < yMax) {
+ yMax = t;
+ }
+ if (xMin > xMax || yMin > yMax) {
+ delete scanner;
+ delete xPath;
+ return splashOk;
+ }
+
+ pipeInit(&pipe, pattern, (Guchar)splashRound(alpha * 255),
+ gTrue, gFalse);
+
+ // draw the spans
+ if (vectorAntialias && !inShading) {
+ for (y = yMin; y <= yMax; ++y) {
+ scanner->getSpan(scanBuf, y, xMin, xMax, &xMin2, &xMax2);
+ if (xMin2 <= xMax2) {
+ if (clipRes != splashClipAllInside) {
+ state->clip->clipSpan(scanBuf, y, xMin2, xMax2,
+ state->strokeAdjust);
+ }
+ (this->*pipe.run)(&pipe, xMin2, xMax2, y, scanBuf + xMin2, NULL);
+ }
+ }
+ } else {
+ for (y = yMin; y <= yMax; ++y) {
+ scanner->getSpanBinary(scanBuf, y, xMin, xMax, &xMin2, &xMax2);
+ if (xMin2 <= xMax2) {
+ if (clipRes != splashClipAllInside) {
+ state->clip->clipSpanBinary(scanBuf, y, xMin2, xMax2,
+ state->strokeAdjust);
+ }
+ (this->*pipe.run)(&pipe, xMin2, xMax2, y, scanBuf + xMin2, NULL);
+ }
+ }
+ }
+ }
+ opClipRes = clipRes;
+
+ delete scanner;
+ delete xPath;
+ return splashOk;
+}
+
+// Applies various tweaks to a fill path:
+// (1) add stroke adjust hints to a filled rectangle
+// (2) applies a minimum width to a zero-width filled rectangle (so
+// stroke adjustment works correctly
+// (3) convert a degenerate fill ('moveto lineto fill' and 'moveto
+// lineto closepath fill') to a minimum-width filled rectangle
+//
+// These tweaks only apply to paths with a single subpath.
+//
+// Returns either the unchanged input path or a new path (in which
+// case the returned path must be deleted by the caller).
+SplashPath *Splash::tweakFillPath(SplashPath *path) {
+ SplashPath *path2;
+ SplashCoord xx0, yy0, xx1, yy1, dx, dy, d, wx, wy, w;
+ int n;
+
+ if (state->strokeAdjust == splashStrokeAdjustOff || path->hints) {
+ return path;
+ }
+
+ n = path->getLength();
+ if (!((n == 2) ||
+ (n == 3 &&
+ path->flags[1] == 0) ||
+ (n == 4 &&
+ path->flags[1] == 0 &&
+ path->flags[2] == 0) ||
+ (n == 5 &&
+ path->flags[1] == 0 &&
+ path->flags[2] == 0 &&
+ path->flags[3] == 0))) {
+ return path;
+ }
+
+ path2 = path;
+
+ // degenerate fill (2 or 3 points) or rectangle of (nearly) zero
+ // width --> replace with a min-width rectangle and hint
+ if (n == 2 ||
+ (n == 3 && (path->flags[0] & splashPathClosed)) ||
+ (n == 3 && (splashAbs(path->pts[0].x - path->pts[2].x) < 0.001 &&
+ splashAbs(path->pts[0].y - path->pts[2].y) < 0.001)) ||
+ ((n == 4 ||
+ (n == 5 && (path->flags[0] & splashPathClosed))) &&
+ ((splashAbs(path->pts[0].x - path->pts[1].x) < 0.001 &&
+ splashAbs(path->pts[0].y - path->pts[1].y) < 0.001 &&
+ splashAbs(path->pts[2].x - path->pts[3].x) < 0.001 &&
+ splashAbs(path->pts[2].y - path->pts[3].y) < 0.001) ||
+ (splashAbs(path->pts[0].x - path->pts[3].x) < 0.001 &&
+ splashAbs(path->pts[0].y - path->pts[3].y) < 0.001 &&
+ splashAbs(path->pts[1].x - path->pts[2].x) < 0.001 &&
+ splashAbs(path->pts[1].y - path->pts[2].y) < 0.001)))) {
+ wx = state->matrix[0] + state->matrix[2];
+ wy = state->matrix[1] + state->matrix[3];
+ w = splashSqrt(wx*wx + wy*wy);
+ if (w < 0.001) {
+ w = 0;
+ } else {
+ // min width is 0.1 -- this constant is minWidth * sqrt(2)
+ w = (SplashCoord)0.1414 / w;
+ }
+ xx0 = path->pts[0].x;
+ yy0 = path->pts[0].y;
+ if (n <= 3) {
+ xx1 = path->pts[1].x;
+ yy1 = path->pts[1].y;
+ } else {
+ xx1 = path->pts[2].x;
+ yy1 = path->pts[2].y;
+ }
+ dx = xx1 - xx0;
+ dy = yy1 - yy0;
+ d = splashSqrt(dx * dx + dy * dy);
+ if (d < 0.001) {
+ d = 0;
+ } else {
+ d = w / d;
+ }
+ dx *= d;
+ dy *= d;
+ path2 = new SplashPath();
+ path2->moveTo(xx0 + dy, yy0 - dx);
+ path2->lineTo(xx1 + dy, yy1 - dx);
+ path2->lineTo(xx1 - dy, yy1 + dx);
+ path2->lineTo(xx0 - dy, yy0 + dx);
+ path2->close(gTrue);
+ path2->addStrokeAdjustHint(0, 2, 0, 4);
+ path2->addStrokeAdjustHint(1, 3, 0, 4);
+
+ // unclosed rectangle --> close and hint
+ } else if (n == 4 && !(path->flags[0] & splashPathClosed)) {
+ path2->close(gTrue);
+ path2->addStrokeAdjustHint(0, 2, 0, 4);
+ path2->addStrokeAdjustHint(1, 3, 0, 4);
+
+ // closed rectangle --> hint
+ } else if (n == 5 && (path->flags[0] & splashPathClosed)) {
+ path2->addStrokeAdjustHint(0, 2, 0, 4);
+ path2->addStrokeAdjustHint(1, 3, 0, 4);
+ }
+
+ return path2;
+}
+
+GBool Splash::pathAllOutside(SplashPath *path) {
+ SplashCoord xMin1, yMin1, xMax1, yMax1;
+ SplashCoord xMin2, yMin2, xMax2, yMax2;
+ SplashCoord x, y;
+ int xMinI, yMinI, xMaxI, yMaxI;
+ int i;
+
+ xMin1 = xMax1 = path->pts[0].x;
+ yMin1 = yMax1 = path->pts[0].y;
+ for (i = 1; i < path->length; ++i) {
+ if (path->pts[i].x < xMin1) {
+ xMin1 = path->pts[i].x;
+ } else if (path->pts[i].x > xMax1) {
+ xMax1 = path->pts[i].x;
+ }
+ if (path->pts[i].y < yMin1) {
+ yMin1 = path->pts[i].y;
+ } else if (path->pts[i].y > yMax1) {
+ yMax1 = path->pts[i].y;
+ }
+ }
+
+ transform(state->matrix, xMin1, yMin1, &x, &y);
+ xMin2 = xMax2 = x;
+ yMin2 = yMax2 = y;
+ transform(state->matrix, xMin1, yMax1, &x, &y);
+ if (x < xMin2) {
+ xMin2 = x;
+ } else if (x > xMax2) {
+ xMax2 = x;
+ }
+ if (y < yMin2) {
+ yMin2 = y;
+ } else if (y > yMax2) {
+ yMax2 = y;
+ }
+ transform(state->matrix, xMax1, yMin1, &x, &y);
+ if (x < xMin2) {
+ xMin2 = x;
+ } else if (x > xMax2) {
+ xMax2 = x;
+ }
+ if (y < yMin2) {
+ yMin2 = y;
+ } else if (y > yMax2) {
+ yMax2 = y;
+ }
+ transform(state->matrix, xMax1, yMax1, &x, &y);
+ if (x < xMin2) {
+ xMin2 = x;
+ } else if (x > xMax2) {
+ xMax2 = x;
+ }
+ if (y < yMin2) {
+ yMin2 = y;
+ } else if (y > yMax2) {
+ yMax2 = y;
+ }
+ // sanity-check the coordinates - xMinI/yMinI/xMaxI/yMaxI are
+ // 32-bit integers, so coords need to be < 2^31
+ SplashXPath::clampCoords(&xMin2, &yMin2);
+ SplashXPath::clampCoords(&xMax2, &yMax2);
+ xMinI = splashFloor(xMin2);
+ yMinI = splashFloor(yMin2);
+ xMaxI = splashFloor(xMax2);
+ yMaxI = splashFloor(yMax2);
+
+ return state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI,
+ state->strokeAdjust) ==
+ splashClipAllOutside;
+}
+
+SplashError Splash::fillChar(SplashCoord x, SplashCoord y,
+ int c, SplashFont *font) {
+ SplashGlyphBitmap glyph;
+ SplashCoord xt, yt;
+ int x0, y0, xFrac, yFrac;
+ SplashError err;
+
+ if (debugMode) {
+ printf("fillChar: x=%.2f y=%.2f c=%3d=0x%02x='%c'\n",
+ (double)x, (double)y, c, c, c);
+ }
+ transform(state->matrix, x, y, &xt, &yt);
+ x0 = splashFloor(xt);
+ xFrac = splashFloor((xt - x0) * splashFontFraction);
+ y0 = splashFloor(yt);
+ yFrac = splashFloor((yt - y0) * splashFontFraction);
+ if (!font->getGlyph(c, xFrac, yFrac, &glyph)) {
+ return splashErrNoGlyph;
+ }
+ err = fillGlyph2(x0, y0, &glyph);
+ if (glyph.freeData) {
+ gfree(glyph.data);
+ }
+ return err;
+}
+
+SplashError Splash::fillGlyph(SplashCoord x, SplashCoord y,
+ SplashGlyphBitmap *glyph) {
+ SplashCoord xt, yt;
+ int x0, y0;
+
+ transform(state->matrix, x, y, &xt, &yt);
+ x0 = splashFloor(xt);
+ y0 = splashFloor(yt);
+ return fillGlyph2(x0, y0, glyph);
+}
+
+SplashError Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph) {
+ SplashPipe pipe;
+ SplashClipResult clipRes;
+ Guchar alpha;
+ Guchar *p;
+ int xMin, yMin, xMax, yMax;
+ int x, y, xg, yg, xx, t;
+
+ xg = x0 - glyph->x;
+ yg = y0 - glyph->y;
+ xMin = xg;
+ xMax = xg + glyph->w - 1;
+ yMin = yg;
+ yMax = yg + glyph->h - 1;
+ if ((clipRes = state->clip->testRect(xMin, yMin, xMax, yMax,
+ state->strokeAdjust))
+ != splashClipAllOutside) {
+ pipeInit(&pipe, state->fillPattern,
+ (Guchar)splashRound(state->fillAlpha * 255),
+ gTrue, gFalse);
+ if (clipRes == splashClipAllInside) {
+ if (glyph->aa) {
+ p = glyph->data;
+ for (y = yMin; y <= yMax; ++y) {
+ (this->*pipe.run)(&pipe, xMin, xMax, y,
+ glyph->data + (y - yMin) * glyph->w, NULL);
+ }
+ } else {
+ p = glyph->data;
+ for (y = yMin; y <= yMax; ++y) {
+ for (x = xMin; x <= xMax; x += 8) {
+ alpha = *p++;
+ for (xx = 0; xx < 8 && x + xx <= xMax; ++xx) {
+ scanBuf[x + xx] = (alpha & 0x80) ? 0xff : 0x00;
+ alpha = (Guchar)(alpha << 1);
+ }
+ }
+ (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL);
+ }
+ }
+ } else {
+ if ((t = state->clip->getXMinI(state->strokeAdjust)) > xMin) {
+ xMin = t;
+ }
+ if ((t = state->clip->getXMaxI(state->strokeAdjust)) < xMax) {
+ xMax = t;
+ }
+ if ((t = state->clip->getYMinI(state->strokeAdjust)) > yMin) {
+ yMin = t;
+ }
+ if ((t = state->clip->getYMaxI(state->strokeAdjust)) < yMax) {
+ yMax = t;
+ }
+ if (xMin <= xMax && yMin <= yMax) {
+ if (glyph->aa) {
+ for (y = yMin; y <= yMax; ++y) {
+ p = glyph->data + (y - yg) * glyph->w + (xMin - xg);
+ memcpy(scanBuf + xMin, p, xMax - xMin + 1);
+ state->clip->clipSpan(scanBuf, y, xMin, xMax,
+ state->strokeAdjust);
+ (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL);
+ }
+ } else {
+ for (y = yMin; y <= yMax; ++y) {
+ p = glyph->data + (y - yg) * ((glyph->w + 7) >> 3)
+ + ((xMin - xg) >> 3);
+ alpha = *p++;
+ xx = (xMin - xg) & 7;
+ alpha = (Guchar)(alpha << xx);
+ for (x = xMin; xx < 8 && x <= xMax; ++x, ++xx) {
+ scanBuf[x] = (alpha & 0x80) ? 255 : 0;
+ alpha = (Guchar)(alpha << 1);
+ }
+ for (; x <= xMax; x += 8) {
+ alpha = *p++;
+ for (xx = 0; xx < 8 && x + xx <= xMax; ++xx) {
+ scanBuf[x + xx] = (alpha & 0x80) ? 255 : 0;
+ alpha = (Guchar)(alpha << 1);
+ }
+ }
+ state->clip->clipSpanBinary(scanBuf, y, xMin, xMax,
+ state->strokeAdjust);
+ (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL);
+ }
+ }
+ }
+ }
+ }
+ opClipRes = clipRes;
+
+ return splashOk;
+}
+
+void Splash::getImageBounds(SplashCoord xyMin, SplashCoord xyMax,
+ int *xyMinI, int *xyMaxI) {
+ if (state->strokeAdjust == splashStrokeAdjustOff) {
+ // make sure the coords fit in 32-bit ints
+#if USE_FIXEDPOINT
+ if (xyMin < -32767) {
+ xyMin = -32767;
+ } else if (xyMin > 32767) {
+ xyMin = 32767;
+ }
+ if (xyMax < -32767) {
+ xyMax = -32767;
+ } else if (xyMax > 32767) {
+ xyMax = 32767;
+ }
+#else
+ if (xyMin < -1e9) {
+ xyMin = -1e9;
+ } else if (xyMin > 1e9) {
+ xyMin = 1e9;
+ }
+ if (xyMax < -1e9) {
+ xyMax = -1e9;
+ } else if (xyMax > 1e9) {
+ xyMax = 1e9;
+ }
+#endif
+ *xyMinI = splashFloor(xyMin);
+ *xyMaxI = splashFloor(xyMax);
+ if (*xyMaxI <= *xyMinI) {
+ *xyMaxI = *xyMinI + 1;
+ }
+ } else {
+ splashStrokeAdjust(xyMin, xyMax, xyMinI, xyMaxI, state->strokeAdjust);
+ }
+}
+
+struct SplashDrawImageMaskRowData {
+ SplashPipe pipe;
+};
+
+// The glyphMode flag is not currently used, but may be useful if the
+// stroke adjustment behavior is changed.
+SplashError Splash::fillImageMask(GString *imageTag,
+ SplashImageMaskSource src, void *srcData,
+ int w, int h, SplashCoord *mat,
+ GBool glyphMode, GBool interpolate,
+ GBool antialias) {
+ if (debugMode) {
+ 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]);
+ }
+
+ //--- check for singular matrix
+ if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) {
+ return splashErrSingularMatrix;
+ }
+
+ //--- compute image bbox, check clipping
+ GBool flipsOnly = splashAbs(mat[1]) <= 0.0001 && splashAbs(mat[2]) <= 0.0001;
+ GBool rot90Only = splashAbs(mat[0]) <= 0.0001 && splashAbs(mat[3]) <= 0.0001;
+ GBool horizFlip = gFalse;
+ GBool vertFlip = gFalse;
+ int xMin, yMin, xMax, yMax;
+ if (flipsOnly) {
+ horizFlip = mat[0] < 0;
+ vertFlip = mat[3] < 0;
+ if (horizFlip) {
+ getImageBounds(mat[0] + mat[4], mat[4], &xMin, &xMax);
+ } else {
+ getImageBounds(mat[4], mat[0] + mat[4], &xMin, &xMax);
+ }
+ if (vertFlip) {
+ getImageBounds(mat[3] + mat[5], mat[5], &yMin, &yMax);
+ } else {
+ getImageBounds(mat[5], mat[3] + mat[5], &yMin, &yMax);
+ }
+ } else if (rot90Only) {
+ horizFlip = mat[2] < 0;
+ vertFlip = mat[1] < 0;
+ if (horizFlip) {
+ getImageBounds(mat[2] + mat[4], mat[4], &xMin, &xMax);
+ } else {
+ getImageBounds(mat[4], mat[2] + mat[4], &xMin, &xMax);
+ }
+ if (vertFlip) {
+ getImageBounds(mat[1] + mat[5], mat[5], &yMin, &yMax);
+ } else {
+ getImageBounds(mat[5], mat[1] + mat[5], &yMin, &yMax);
+ }
+ } else {
+ int xx = splashRound(mat[4]); // (0,0)
+ int yy = splashRound(mat[5]);
+ xMin = xMax = xx;
+ yMin = yMax = yy;
+ xx = splashRound(mat[0] + mat[4]); // (1,0)
+ yy = splashRound(mat[1] + mat[5]);
+ if (xx < xMin) {
+ xMin = xx;
+ } else if (xx > xMax) {
+ xMax = xx;
+ }
+ if (yy < yMin) {
+ yMin = yy;
+ } else if (yy > yMax) {
+ yMax = yy;
+ }
+ xx = splashRound(mat[2] + mat[4]); // (0,1)
+ yy = splashRound(mat[3] + mat[5]);
+ if (xx < xMin) {
+ xMin = xx;
+ } else if (xx > xMax) {
+ xMax = xx;
+ }
+ if (yy < yMin) {
+ yMin = yy;
+ } else if (yy > yMax) {
+ yMax = yy;
+ }
+ xx = splashRound(mat[0] + mat[2] + mat[4]); // (1,1)
+ yy = splashRound(mat[1] + mat[3] + mat[5]);
+ if (xx < xMin) {
+ xMin = xx;
+ } else if (xx > xMax) {
+ xMax = xx;
+ }
+ if (yy < yMin) {
+ yMin = yy;
+ } else if (yy > yMax) {
+ yMax = yy;
+ }
+ if (xMax <= xMin) {
+ xMax = xMin + 1;
+ }
+ if (yMax <= yMin) {
+ yMax = yMin + 1;
+ }
+ }
+ SplashClipResult clipRes =
+ state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1,
+ state->strokeAdjust);
+ // If the scaled mask is much wider and/or taller than the clip
+ // region, we use the "arbitrary" scaling path, to avoid a
+ // potentially very slow loop in the flips-only path (which scans
+ // the full width and height of the scaled mask, regardless of the
+ // clip region).
+ int clipW = state->clip->getXMaxI(state->strokeAdjust)
+ - state->clip->getXMinI(state->strokeAdjust);
+ int clipH = state->clip->getYMaxI(state->strokeAdjust)
+ - state->clip->getYMinI(state->strokeAdjust);
+ GBool veryLarge = ((xMax - xMin) / 8 > clipW && xMax - xMin > 1000) ||
+ ((yMax - yMin) / 8 > clipH && yMax - yMin > 1000);
+
+ //--- set up the SplashDrawImageMaskRowData object and the pipes
+ SplashDrawImageMaskRowData dd;
+ pipeInit(&dd.pipe, state->fillPattern,
+ (Guchar)splashRound(state->fillAlpha * 255),
+ gTrue, gFalse);
+
+ //--- choose the drawRow function
+ SplashDrawImageMaskRowFunc drawRowFunc;
+ if (clipRes == splashClipAllInside) {
+ drawRowFunc = &Splash::drawImageMaskRowNoClip;
+ } else {
+ if (antialias) {
+ drawRowFunc = &Splash::drawImageMaskRowClipAA;
+ } else {
+ drawRowFunc = &Splash::drawImageMaskRowClipNoAA;
+ }
+ }
+
+ //--- horizontal/vertical flips only
+ if (flipsOnly && !veryLarge) {
+ if (clipRes != splashClipAllOutside) {
+ int scaledWidth = xMax - xMin;
+ int scaledHeight = yMax - yMin;
+ ImageMaskScaler scaler(src, srcData, w, h,
+ scaledWidth, scaledHeight, interpolate, antialias);
+ Guchar *tmpLine = NULL;
+ if (horizFlip) {
+ tmpLine = (Guchar *)gmalloc(scaledWidth);
+ }
+ if (vertFlip) {
+ if (horizFlip) { // bottom-up, mirrored
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler.nextLine();
+ mirrorImageMaskRow(scaler.data(), tmpLine, scaledWidth);
+ (this->*drawRowFunc)(&dd, tmpLine,
+ xMin, yMax - 1 - y, scaledWidth);
+ }
+ } else { // bottom-up
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler.nextLine();
+ (this->*drawRowFunc)(&dd, scaler.data(),
+ xMin, yMax - 1 - y, scaledWidth);
+ }
+ }
+ } else {
+ if (horizFlip) { // top-down, mirrored
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler.nextLine();
+ mirrorImageMaskRow(scaler.data(), tmpLine, scaledWidth);
+ (this->*drawRowFunc)(&dd, tmpLine,
+ xMin, yMin + y, scaledWidth);
+ }
+ } else { // top-down
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler.nextLine();
+ (this->*drawRowFunc)(&dd, scaler.data(),
+ xMin, yMin + y, scaledWidth);
+ }
+ }
+ }
+ gfree(tmpLine);
+ }
+
+ //--- 90/270 rotation
+ } else if (rot90Only && !veryLarge) {
+ if (clipRes != splashClipAllOutside) {
+
+ // scale the mask
+ int scaledWidth = yMax - yMin;
+ int scaledHeight = xMax - xMin;
+ ImageMaskScaler scaler(src, srcData, w, h,
+ scaledWidth, scaledHeight, interpolate, antialias);
+ Guchar *scaledMask = (Guchar *)gmallocn(scaledHeight, scaledWidth);
+ Guchar *ptr = scaledMask;
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler.nextLine();
+ memcpy(ptr, scaler.data(), scaledWidth);
+ ptr += scaledWidth;
+ }
+
+ // draw it
+ Guchar *tmpLine = (Guchar *)gmalloc(scaledHeight);
+ for (int y = 0; y < scaledWidth; ++y) {
+ if (vertFlip) {
+ ptr = scaledMask + (scaledWidth - 1 - y);
+ } else {
+ ptr = scaledMask + y;
+ }
+ if (horizFlip) {
+ ptr += (scaledHeight - 1) * scaledWidth;
+ for (int x = 0; x < scaledHeight; ++x) {
+ tmpLine[x] = *ptr;
+ ptr -= scaledWidth;
+ }
+ } else {
+ for (int x = 0; x < scaledHeight; ++x) {
+ tmpLine[x] = *ptr;
+ ptr += scaledWidth;
+ }
+ }
+ (this->*drawRowFunc)(&dd, tmpLine, xMin, yMin + y, scaledHeight);
+ }
+
+ gfree(tmpLine);
+ gfree(scaledMask);
+ }
+
+ //--- arbitrary transform
+ } else {
+ // estimate of size of scaled image
+ int scaledWidth = splashRound(splashSqrt(mat[0] * mat[0]
+ + mat[1] * mat[1]));
+ int scaledHeight = splashRound(splashSqrt(mat[2] * mat[2]
+ + mat[3] * mat[3]));
+ if (scaledWidth < 1) {
+ scaledWidth = 1;
+ }
+ if (scaledHeight < 1) {
+ scaledHeight = 1;
+ }
+ GBool downscaling = gTrue;
+ if (veryLarge || (scaledWidth >= w && scaledHeight >= h)) {
+ downscaling = gFalse;
+ scaledWidth = w;
+ scaledHeight = h;
+ }
+
+ // compute mapping from device space to scaled image space
+ SplashCoord mat1[6];
+ mat1[0] = mat[0] / scaledWidth;
+ mat1[1] = mat[1] / scaledWidth;
+ mat1[2] = mat[2] / scaledHeight;
+ mat1[3] = mat[3] / scaledHeight;
+ mat1[4] = mat[4];
+ mat1[5] = mat[5];
+ SplashCoord det = mat1[0] * mat1[3] - mat1[1] * mat1[2];
+ if (splashAbs(det) < 1e-6) {
+ // this should be caught by the singular matrix check in drawImage
+ return splashErrSingularMatrix;
+ }
+ SplashCoord invMat[6];
+ invMat[0] = mat1[3] / det;
+ invMat[1] = -mat1[1] / det;
+ invMat[2] = -mat1[2] / det;
+ invMat[3] = mat1[0] / det;
+ // the extra "+ 0.5 * (...)" terms are here because the
+ // drawImageArbitrary(No)Interp functions multiply by pixel
+ // centers, (x + 0.5, y + 0.5)
+ invMat[4] = (mat1[2] * mat1[5] - mat1[3] * mat1[4]) / det
+ + (invMat[0] + invMat[2]) * 0.5;
+ invMat[5] = (mat1[1] * mat1[4] - mat1[0] * mat1[5]) / det
+ + (invMat[1] + invMat[3]) * 0.5;
+
+ // if downscaling: store the downscaled image mask
+ // if upscaling: store the unscaled image mask
+ Guchar *scaledMask = (Guchar *)gmallocn(scaledHeight, scaledWidth);
+ if (downscaling) {
+ ImageMaskScaler scaler(src, srcData, w, h,
+ scaledWidth, scaledHeight, interpolate, antialias);
+ Guchar *ptr = scaledMask;
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler.nextLine();
+ memcpy(ptr, scaler.data(), scaledWidth);
+ ptr += scaledWidth;
+ }
+ } else {
+ Guchar *ptr = scaledMask;
+ for (int y = 0; y < scaledHeight; ++y) {
+ (*src)(srcData, ptr);
+ for (int x = 0; x < scaledWidth; ++x) {
+ *ptr = (Guchar)(*ptr * 255);
+ ++ptr;
+ }
+ }
+ }
+
+ // draw it
+ if (interpolate && antialias) {
+ drawImageMaskArbitraryInterp(scaledMask,
+ &dd, drawRowFunc, invMat,
+ scaledWidth, scaledHeight,
+ xMin, yMin, xMax, yMax);
+ } else {
+ drawImageMaskArbitraryNoInterp(scaledMask,
+ &dd, drawRowFunc, invMat,
+ scaledWidth, scaledHeight,
+ xMin, yMin, xMax, yMax);
+ }
+
+ // free the downscaled/unscaled image
+ gfree(scaledMask);
+ }
+
+ return splashOk;
+}
+
+void Splash::drawImageMaskArbitraryNoInterp(
+ Guchar *scaledMask,
+ SplashDrawImageMaskRowData *dd,
+ SplashDrawImageMaskRowFunc drawRowFunc,
+ SplashCoord *invMat,
+ int scaledWidth, int scaledHeight,
+ int xMin, int yMin, int xMax, int yMax) {
+ int tt = state->clip->getXMinI(state->strokeAdjust);
+ if (tt > xMin) {
+ xMin = tt;
+ }
+ tt = state->clip->getXMaxI(state->strokeAdjust) + 1;
+ if (tt < xMax) {
+ xMax = tt;
+ }
+ tt = state->clip->getYMinI(state->strokeAdjust);
+ if (tt > yMin) {
+ yMin = tt;
+ }
+ tt = state->clip->getYMaxI(state->strokeAdjust) + 1;
+ if (tt < yMax) {
+ yMax = tt;
+ }
+ if (xMax <= xMin || yMax <= yMin) {
+ return;
+ }
+
+ Guchar *buf = (Guchar *)gmalloc(xMax - xMin);
+
+ for (int y = yMin; y < yMax; ++y) {
+ int rowMin = xMax;
+ int rowMax = 0;
+ for (int x = xMin; x < xMax; ++x) {
+ // note: invMat includes a "+0.5" factor so that this is really
+ // a multiply by (x+0.5, y+0.5)
+ int xx = splashFloor((SplashCoord)x * invMat[0]
+ + (SplashCoord)y * invMat[2] + invMat[4]);
+ int yy = splashFloor((SplashCoord)x * invMat[1]
+ + (SplashCoord)y * invMat[3] + invMat[5]);
+ if (xx >= 0 && xx < scaledWidth &&
+ yy >= 0 && yy < scaledHeight) {
+ Guchar *p = scaledMask + (yy * scaledWidth + xx);
+ Guchar *q = buf + (x - xMin);
+ *q = *p;
+ if (x < rowMin) {
+ rowMin = x;
+ }
+ rowMax = x + 1;
+ }
+ }
+ if (rowMin < rowMax) {
+ (this->*drawRowFunc)(dd, buf + (rowMin - xMin),
+ rowMin, y, rowMax - rowMin);
+ }
+ }
+
+ gfree(buf);
+}
+
+void Splash::drawImageMaskArbitraryInterp(
+ Guchar *scaledMask,
+ SplashDrawImageMaskRowData *dd,
+ SplashDrawImageMaskRowFunc drawRowFunc,
+ SplashCoord *invMat,
+ int scaledWidth, int scaledHeight,
+ int xMin, int yMin, int xMax, int yMax) {
+ int tt = state->clip->getXMinI(state->strokeAdjust);
+ if (tt > xMin) {
+ xMin = tt;
+ }
+ tt = state->clip->getXMaxI(state->strokeAdjust) + 1;
+ if (tt < xMax) {
+ xMax = tt;
+ }
+ tt = state->clip->getYMinI(state->strokeAdjust);
+ if (tt > yMin) {
+ yMin = tt;
+ }
+ tt = state->clip->getYMaxI(state->strokeAdjust) + 1;
+ if (tt < yMax) {
+ yMax = tt;
+ }
+ if (xMax <= xMin || yMax <= yMin) {
+ return;
+ }
+
+ Guchar *buf = (Guchar *)gmalloc(xMax - xMin);
+
+ for (int y = yMin; y < yMax; ++y) {
+ int rowMin = xMax;
+ int rowMax = 0;
+ for (int x = xMin; x < xMax; ++x) {
+ // note: invMat includes a "+0.5" factor so that this is really
+ // a multiply by (x+0.5, y+0.5)
+ SplashCoord xs = (SplashCoord)x * invMat[0]
+ + (SplashCoord)y * invMat[2] + invMat[4];
+ SplashCoord ys = (SplashCoord)x * invMat[1]
+ + (SplashCoord)y * invMat[3] + invMat[5];
+ int x0 = splashFloor(xs - 0.5);
+ int x1 = x0 + 1;
+ int y0 = splashFloor(ys - 0.5);
+ int y1 = y0 + 1;
+ if (x1 >= 0 && x0 < scaledWidth && y1 >= 0 && y0 < scaledHeight) {
+ SplashCoord sx0 = (SplashCoord)x1 + 0.5 - xs;
+ SplashCoord sx1 = (SplashCoord)1 - sx0;
+ SplashCoord sy0 = (SplashCoord)y1 + 0.5 - ys;
+ SplashCoord sy1 = (SplashCoord)1 - sy0;
+ if (x0 < 0) {
+ x0 = 0;
+ }
+ if (x1 >= scaledWidth) {
+ x1 = scaledWidth - 1;
+ }
+ if (y0 < 0) {
+ y0 = 0;
+ }
+ if (y1 >= scaledHeight) {
+ y1 = scaledHeight - 1;
+ }
+ Guchar *p00 = scaledMask + (y0 * scaledWidth + x0);
+ Guchar *p10 = scaledMask + (y0 * scaledWidth + x1);
+ Guchar *p01 = scaledMask + (y1 * scaledWidth + x0);
+ Guchar *p11 = scaledMask + (y1 * scaledWidth + x1);
+ Guchar *q = buf + (x - xMin);
+ *q = (Guchar)(int)(sx0 * (sy0 * (int)*p00 + sy1 * (int)*p01) +
+ sx1 * (sy0 * (int)*p10 + sy1 * (int)*p11));
+ if (x < rowMin) {
+ rowMin = x;
+ }
+ rowMax = x + 1;
+ }
+ }
+ if (rowMin < rowMax) {
+ (this->*drawRowFunc)(dd, buf + (rowMin - xMin),
+ rowMin, y, rowMax - rowMin);
+ }
+ }
+
+ gfree(buf);
+}
+
+void Splash::mirrorImageMaskRow(Guchar *maskIn, Guchar *maskOut, int width) {
+ Guchar *p, *q;
+
+ p = maskIn;
+ q = maskOut + (width - 1);
+ for (int i = 0; i < width; ++i) {
+ *q = *p;
+ ++p;
+ --q;
+ }
+}
+
+void Splash::drawImageMaskRowNoClip(SplashDrawImageMaskRowData *data,
+ Guchar *maskData,
+ int x, int y, int width) {
+ (this->*data->pipe.run)(&data->pipe, x, x + width - 1, y, maskData, NULL);
+}
+
+void Splash::drawImageMaskRowClipNoAA(SplashDrawImageMaskRowData *data,
+ Guchar *maskData,
+ int x, int y, int width) {
+ if (y < 0 || y >= bitmap->height) {
+ return;
+ }
+ if (x < 0) {
+ maskData -= x;
+ width += x;
+ x = 0;
+ }
+ if (x + width > bitmap->width) {
+ width = bitmap->width - x;
+ }
+ if (width <= 0) {
+ return;
+ }
+ memcpy(scanBuf + x, maskData, width);
+ state->clip->clipSpanBinary(scanBuf, y, x, x + width - 1,
+ state->strokeAdjust);
+ (this->*data->pipe.run)(&data->pipe, x, x + width - 1, y,
+ scanBuf + x, NULL);
+}
+
+void Splash::drawImageMaskRowClipAA(SplashDrawImageMaskRowData *data,
+ Guchar *maskData,
+ int x, int y, int width) {
+ if (y < 0 || y >= bitmap->height) {
+ return;
+ }
+ if (x < 0) {
+ maskData -= x;
+ width += x;
+ x = 0;
+ }
+ if (x + width > bitmap->width) {
+ width = bitmap->width - x;
+ }
+ if (width <= 0) {
+ return;
+ }
+ memcpy(scanBuf + x, maskData, width);
+ state->clip->clipSpan(scanBuf, y, x, x + width - 1, state->strokeAdjust);
+ (this->*data->pipe.run)(&data->pipe, x, x + width - 1, y,
+ scanBuf + x, NULL);
+}
+
+struct SplashDrawImageRowData {
+ int nComps;
+ GBool srcAlpha;
+ SplashPipe pipe;
+};
+
+SplashError Splash::drawImage(GString *imageTag,
+ SplashImageSource src, void *srcData,
+ SplashColorMode srcMode, GBool srcAlpha,
+ int w, int h, SplashCoord *mat,
+ GBool interpolate) {
+ if (debugMode) {
+ 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]);
+ }
+
+ //--- check color modes
+ GBool ok = gFalse;
+ int nComps = 0;
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ case splashModeMono8:
+ ok = srcMode == splashModeMono8;
+ nComps = 1;
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ ok = srcMode == splashModeRGB8;
+ nComps = 3;
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ ok = srcMode == splashModeCMYK8;
+ nComps = 4;
+ break;
+#endif
+ default:
+ ok = gFalse;
+ break;
+ }
+ if (!ok) {
+ return splashErrModeMismatch;
+ }
+
+ //--- check for singular matrix
+ if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) {
+ return splashErrSingularMatrix;
+ }
+
+ //--- compute image bbox, check clipping
+ GBool flipsOnly = splashAbs(mat[1]) <= 0.0001 && splashAbs(mat[2]) <= 0.0001;
+ GBool rot90Only = splashAbs(mat[0]) <= 0.0001 && splashAbs(mat[3]) <= 0.0001;
+ GBool horizFlip = gFalse;
+ GBool vertFlip = gFalse;
+ int xMin, yMin, xMax, yMax;
+ if (flipsOnly) {
+ horizFlip = mat[0] < 0;
+ vertFlip = mat[3] < 0;
+ if (horizFlip) {
+ getImageBounds(mat[0] + mat[4], mat[4], &xMin, &xMax);
+ } else {
+ getImageBounds(mat[4], mat[0] + mat[4], &xMin, &xMax);
+ }
+ if (vertFlip) {
+ getImageBounds(mat[3] + mat[5], mat[5], &yMin, &yMax);
+ } else {
+ getImageBounds(mat[5], mat[3] + mat[5], &yMin, &yMax);
+ }
+ } else if (rot90Only) {
+ horizFlip = mat[2] < 0;
+ vertFlip = mat[1] < 0;
+ if (horizFlip) {
+ getImageBounds(mat[2] + mat[4], mat[4], &xMin, &xMax);
+ } else {
+ getImageBounds(mat[4], mat[2] + mat[4], &xMin, &xMax);
+ }
+ if (vertFlip) {
+ getImageBounds(mat[1] + mat[5], mat[5], &yMin, &yMax);
+ } else {
+ getImageBounds(mat[5], mat[1] + mat[5], &yMin, &yMax);
+ }
+ } else {
+ int xx = splashRound(mat[4]); // (0,0)
+ int yy = splashRound(mat[5]);
+ xMin = xMax = xx;
+ yMin = yMax = yy;
+ xx = splashRound(mat[0] + mat[4]); // (1,0)
+ yy = splashRound(mat[1] + mat[5]);
+ if (xx < xMin) {
+ xMin = xx;
+ } else if (xx > xMax) {
+ xMax = xx;
+ }
+ if (yy < yMin) {
+ yMin = yy;
+ } else if (yy > yMax) {
+ yMax = yy;
+ }
+ xx = splashRound(mat[2] + mat[4]); // (0,1)
+ yy = splashRound(mat[3] + mat[5]);
+ if (xx < xMin) {
+ xMin = xx;
+ } else if (xx > xMax) {
+ xMax = xx;
+ }
+ if (yy < yMin) {
+ yMin = yy;
+ } else if (yy > yMax) {
+ yMax = yy;
+ }
+ xx = splashRound(mat[0] + mat[2] + mat[4]); // (1,1)
+ yy = splashRound(mat[1] + mat[3] + mat[5]);
+ if (xx < xMin) {
+ xMin = xx;
+ } else if (xx > xMax) {
+ xMax = xx;
+ }
+ if (yy < yMin) {
+ yMin = yy;
+ } else if (yy > yMax) {
+ yMax = yy;
+ }
+ if (xMax <= xMin) {
+ xMax = xMin + 1;
+ }
+ if (yMax <= yMin) {
+ yMax = yMin + 1;
+ }
+ }
+ SplashClipResult clipRes =
+ state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1,
+ state->strokeAdjust);
+ // If the scaled image is much wider and/or taller than the clip
+ // region, we use the arbitrary transform path, to avoid a
+ // potentially very slow loop in the flips-only path (which scans
+ // the full width and height of the scaled image, regardless of the
+ // clip region).
+ int clipW = state->clip->getXMaxI(state->strokeAdjust)
+ - state->clip->getXMinI(state->strokeAdjust);
+ int clipH = state->clip->getYMaxI(state->strokeAdjust)
+ - state->clip->getYMinI(state->strokeAdjust);
+ GBool veryLarge = ((xMax - xMin) / 8 > clipW && xMax - xMin > 1000) ||
+ ((yMax - yMin) / 8 > clipH && yMax - yMin > 1000);
+
+ //--- set up the SplashDrawImageRowData object and the pipes
+ SplashDrawImageRowData dd;
+ dd.nComps = nComps;
+ dd.srcAlpha = srcAlpha;
+ pipeInit(&dd.pipe, NULL,
+ (Guchar)splashRound(state->fillAlpha * 255),
+ clipRes != splashClipAllInside || srcAlpha,
+ gFalse);
+
+ //--- choose the drawRow function
+ SplashDrawImageRowFunc drawRowFunc;
+ if (clipRes == splashClipAllInside) {
+ if (srcAlpha) {
+ drawRowFunc = &Splash::drawImageRowNoClipAlpha;
+ } else {
+ drawRowFunc = &Splash::drawImageRowNoClipNoAlpha;
+ }
+ } else {
+ if (srcAlpha) {
+ if (vectorAntialias) {
+ drawRowFunc = &Splash::drawImageRowClipAlphaAA;
+ } else {
+ drawRowFunc = &Splash::drawImageRowClipAlphaNoAA;
+ }
+ } else {
+ if (vectorAntialias) {
+ drawRowFunc = &Splash::drawImageRowClipNoAlphaAA;
+ } else {
+ drawRowFunc = &Splash::drawImageRowClipNoAlphaNoAA;
+ }
+ }
+ }
+
+ //--- horizontal/vertical flips only
+ if (flipsOnly && !veryLarge) {
+ if (clipRes != splashClipAllOutside) {
+ int scaledWidth = xMax - xMin;
+ int scaledHeight = yMax - yMin;
+ ImageScaler *scaler = getImageScaler(imageTag, src, srcData,
+ w, h, nComps,
+ scaledWidth, scaledHeight,
+ srcMode, srcAlpha, interpolate);
+ Guchar *tmpLine = NULL;
+ Guchar *tmpAlphaLine = NULL;
+ if (horizFlip) {
+ tmpLine = (Guchar *)gmallocn(scaledWidth, nComps);
+ if (srcAlpha) {
+ tmpAlphaLine = (Guchar *)gmalloc(scaledWidth);
+ }
+ }
+ if (vertFlip) {
+ if (horizFlip) { // bottom-up, mirrored
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler->nextLine();
+ mirrorImageRow(scaler->colorData(), scaler->alphaData(),
+ tmpLine, tmpAlphaLine,
+ scaledWidth, nComps, srcAlpha);
+ (this->*drawRowFunc)(&dd, tmpLine, tmpAlphaLine,
+ xMin, yMax - 1 - y, scaledWidth);
+ }
+ } else { // bottom-up
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler->nextLine();
+ (this->*drawRowFunc)(&dd, scaler->colorData(), scaler->alphaData(),
+ xMin, yMax - 1 - y, scaledWidth);
+ }
+ }
+ } else {
+ if (horizFlip) { // top-down, mirrored
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler->nextLine();
+ mirrorImageRow(scaler->colorData(), scaler->alphaData(),
+ tmpLine, tmpAlphaLine,
+ scaledWidth, nComps, srcAlpha);
+ (this->*drawRowFunc)(&dd, tmpLine, tmpAlphaLine,
+ xMin, yMin + y, scaledWidth);
+ }
+ } else { // top-down
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler->nextLine();
+ (this->*drawRowFunc)(&dd, scaler->colorData(), scaler->alphaData(),
+ xMin, yMin + y, scaledWidth);
+ }
+ }
+ }
+ gfree(tmpLine);
+ gfree(tmpAlphaLine);
+ delete scaler;
+ }
+
+ //--- 90/270 rotation
+ } else if (rot90Only && !veryLarge) {
+ if (clipRes != splashClipAllOutside) {
+
+ // scale the image
+ int scaledWidth = yMax - yMin;
+ int scaledHeight = xMax - xMin;
+ Guchar *scaledColor, *scaledAlpha;
+ GBool freeScaledImage;
+ getScaledImage(imageTag, src, srcData, w, h, nComps,
+ scaledWidth, scaledHeight, srcMode, srcAlpha, interpolate,
+ &scaledColor, &scaledAlpha, &freeScaledImage);
+
+ // draw it
+ Guchar *tmpLine = (Guchar *)gmallocn(scaledHeight, nComps);
+ Guchar *tmpAlphaLine = NULL;
+ if (srcAlpha) {
+ tmpAlphaLine = (Guchar *)gmalloc(scaledHeight);
+ }
+ for (int y = 0; y < scaledWidth; ++y) {
+ Guchar *ptr, *alphaPtr;
+ if (vertFlip) {
+ ptr = scaledColor + (scaledWidth - 1 - y) * nComps;
+ if (srcAlpha) {
+ alphaPtr = scaledAlpha + (scaledWidth - 1 - y);
+ }
+ } else {
+ ptr = scaledColor + y * nComps;
+ if (srcAlpha) {
+ alphaPtr = scaledAlpha + y;
+ }
+ }
+ if (horizFlip) {
+ ptr += (scaledHeight - 1) * scaledWidth * nComps;
+ Guchar *q = tmpLine;
+ for (int x = 0; x < scaledHeight; ++x) {
+ for (int i = 0; i < nComps; ++i) {
+ *q++ = ptr[i];
+ }
+ ptr -= scaledWidth * nComps;
+ }
+ if (srcAlpha) {
+ alphaPtr += (scaledHeight - 1) * scaledWidth;
+ q = tmpAlphaLine;
+ for (int x = 0; x < scaledHeight; ++x) {
+ *q++ = *alphaPtr;
+ alphaPtr -= scaledWidth;
+ }
+ }
+ } else {
+ Guchar *q = tmpLine;
+ for (int x = 0; x < scaledHeight; ++x) {
+ for (int i = 0; i < nComps; ++i) {
+ *q++ = ptr[i];
+ }
+ ptr += scaledWidth * nComps;
+ }
+ if (srcAlpha) {
+ q = tmpAlphaLine;
+ for (int x = 0; x < scaledHeight; ++x) {
+ *q++ = *alphaPtr;
+ alphaPtr += scaledWidth;
+ }
+ }
+ }
+ (this->*drawRowFunc)(&dd, tmpLine, tmpAlphaLine,
+ xMin, yMin + y, scaledHeight);
+ }
+
+ gfree(tmpLine);
+ gfree(tmpAlphaLine);
+ if (freeScaledImage) {
+ gfree(scaledColor);
+ gfree(scaledAlpha);
+ }
+ }
+
+ //--- arbitrary transform
+ } else {
+ // estimate of size of scaled image
+ int scaledWidth = splashRound(splashSqrt(mat[0] * mat[0]
+ + mat[1] * mat[1]));
+ int scaledHeight = splashRound(splashSqrt(mat[2] * mat[2]
+ + mat[3] * mat[3]));
+ if (scaledWidth < 1) {
+ scaledWidth = 1;
+ }
+ if (scaledHeight < 1) {
+ scaledHeight = 1;
+ }
+ if (veryLarge || (scaledWidth >= w && scaledHeight >= h)) {
+ scaledWidth = w;
+ scaledHeight = h;
+ }
+
+ // compute mapping from device space to scaled image space
+ SplashCoord mat1[6];
+ mat1[0] = mat[0] / scaledWidth;
+ mat1[1] = mat[1] / scaledWidth;
+ mat1[2] = mat[2] / scaledHeight;
+ mat1[3] = mat[3] / scaledHeight;
+ mat1[4] = mat[4];
+ mat1[5] = mat[5];
+ SplashCoord det = mat1[0] * mat1[3] - mat1[1] * mat1[2];
+ if (splashAbs(det) < 1e-6) {
+ // this should be caught by the singular matrix check in drawImage
+ return splashErrSingularMatrix;
+ }
+ SplashCoord invMat[6];
+ invMat[0] = mat1[3] / det;
+ invMat[1] = -mat1[1] / det;
+ invMat[2] = -mat1[2] / det;
+ invMat[3] = mat1[0] / det;
+ // the extra "+ 0.5 * (...)" terms are here because the
+ // drawImageArbitrary(No)Interp functions multiply by pixel
+ // centers, (x + 0.5, y + 0.5)
+ invMat[4] = (mat1[2] * mat1[5] - mat1[3] * mat1[4]) / det
+ + (invMat[0] + invMat[2]) * 0.5;
+ invMat[5] = (mat1[1] * mat1[4] - mat1[0] * mat1[5]) / det
+ + (invMat[1] + invMat[3]) * 0.5;
+
+ Guchar *scaledColor, *scaledAlpha;
+ GBool freeScaledImage;
+ getScaledImage(imageTag, src, srcData, w, h, nComps,
+ scaledWidth, scaledHeight, srcMode, srcAlpha, interpolate,
+ &scaledColor, &scaledAlpha, &freeScaledImage);
+
+ // draw it
+ if (interpolate) {
+ drawImageArbitraryInterp(scaledColor, scaledAlpha,
+ &dd, drawRowFunc, invMat,
+ scaledWidth, scaledHeight,
+ xMin, yMin, xMax, yMax,
+ nComps, srcAlpha);
+ } else {
+ drawImageArbitraryNoInterp(scaledColor, scaledAlpha,
+ &dd, drawRowFunc, invMat,
+ scaledWidth, scaledHeight,
+ xMin, yMin, xMax, yMax,
+ nComps, srcAlpha);
+ }
+
+ // free the downscaled/unscaled image
+ if (freeScaledImage) {
+ gfree(scaledColor);
+ gfree(scaledAlpha);
+ }
+ }
+
+ return splashOk;
+}
+
+ImageScaler *Splash::getImageScaler(GString *imageTag,
+ SplashImageSource src, void *srcData,
+ int w, int h, int nComps,
+ int scaledWidth, int scaledHeight,
+ SplashColorMode srcMode,
+ GBool srcAlpha, GBool interpolate) {
+ // Notes:
+ //
+ // * If the scaled image is more than 8 Mpixels, we don't cache it.
+ //
+ // * Caching is done on the third consecutive use (second
+ // consecutive reuse) of an image; this avoids overhead on the
+ // common case of single-use images.
+
+ if (scaledWidth < 8000000 / scaledHeight &&
+ imageCache->match(imageTag, scaledWidth, scaledHeight,
+ srcMode, srcAlpha, interpolate)) {
+ if (imageCache->colorData) {
+ return new ReplayImageScaler(nComps, srcAlpha, scaledWidth,
+ imageCache->colorData,
+ imageCache->alphaData);
+ } else {
+ int lineSize;
+ if (scaledWidth < INT_MAX / nComps) {
+ lineSize = scaledWidth * nComps;
+ } else {
+ lineSize = -1;
+ }
+ imageCache->colorData = (Guchar *)gmallocn(scaledHeight, lineSize);
+ if (srcAlpha) {
+ imageCache->alphaData = (Guchar *)gmallocn(scaledHeight, scaledWidth);
+ }
+ return new SavingImageScaler(src, srcData,
+ w, h, nComps, srcAlpha,
+ scaledWidth, scaledHeight,
+ interpolate,
+ imageCache->colorData,
+ imageCache->alphaData);
+ }
+ } else {
+ imageCache->reset(imageTag, scaledWidth, scaledHeight,
+ srcMode, srcAlpha, interpolate);
+ return new BasicImageScaler(src, srcData,
+ w, h, nComps, srcAlpha,
+ scaledWidth, scaledHeight,
+ interpolate);
+ }
+}
+
+void Splash::getScaledImage(GString *imageTag,
+ SplashImageSource src, void *srcData,
+ int w, int h, int nComps,
+ int scaledWidth, int scaledHeight,
+ SplashColorMode srcMode,
+ GBool srcAlpha, GBool interpolate,
+ Guchar **scaledColor, Guchar **scaledAlpha,
+ GBool *freeScaledImage) {
+ // Notes:
+ //
+ // * If the scaled image is more than 8 Mpixels, we don't cache it.
+ //
+ // * This buffers the whole image anyway, so there's no reason to
+ // skip caching on the first reuse.
+
+ if (scaledWidth >= 8000000 / scaledHeight) {
+ int lineSize;
+ if (scaledWidth < INT_MAX / nComps) {
+ lineSize = scaledWidth * nComps;
+ } else {
+ lineSize = -1;
+ }
+ *scaledColor = (Guchar *)gmallocn(scaledHeight, lineSize);
+ if (srcAlpha) {
+ *scaledAlpha = (Guchar *)gmallocn(scaledHeight, scaledWidth);
+ } else {
+ *scaledAlpha = NULL;
+ }
+ *freeScaledImage = gTrue;
+ if (scaledWidth == w && scaledHeight == h) {
+ Guchar *colorPtr = *scaledColor;
+ Guchar *alphaPtr = *scaledAlpha;
+ for (int y = 0; y < scaledHeight; ++y) {
+ (*src)(srcData, colorPtr, alphaPtr);
+ colorPtr += scaledWidth * nComps;
+ if (srcAlpha) {
+ alphaPtr += scaledWidth;
+ }
+ }
+ } else {
+ BasicImageScaler scaler(src, srcData, w, h, nComps, srcAlpha,
+ scaledWidth, scaledHeight, interpolate);
+ Guchar *colorPtr = *scaledColor;
+ Guchar *alphaPtr = *scaledAlpha;
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler.nextLine();
+ memcpy(colorPtr, scaler.colorData(), scaledWidth * nComps);
+ colorPtr += scaledWidth * nComps;
+ if (srcAlpha) {
+ memcpy(alphaPtr, scaler.alphaData(), scaledWidth);
+ alphaPtr += scaledWidth;
+ }
+ }
+ }
+ } else {
+ if (!imageCache->match(imageTag, scaledWidth, scaledHeight,
+ srcMode, srcAlpha, interpolate) ||
+ !imageCache->colorData) {
+ imageCache->reset(imageTag, scaledWidth, scaledHeight,
+ srcMode, srcAlpha, interpolate);
+ int lineSize;
+ if (scaledWidth < INT_MAX / nComps) {
+ lineSize = scaledWidth * nComps;
+ } else {
+ lineSize = -1;
+ }
+ imageCache->colorData = (Guchar *)gmallocn(scaledHeight, lineSize);
+ if (srcAlpha) {
+ imageCache->alphaData = (Guchar *)gmallocn(scaledHeight, scaledWidth);
+ }
+ if (scaledWidth == w && scaledHeight == h) {
+ Guchar *colorPtr = imageCache->colorData;
+ Guchar *alphaPtr = imageCache->alphaData;
+ for (int y = 0; y < scaledHeight; ++y) {
+ (*src)(srcData, colorPtr, alphaPtr);
+ colorPtr += scaledWidth * nComps;
+ if (srcAlpha) {
+ alphaPtr += scaledWidth;
+ }
+ }
+ } else {
+ SavingImageScaler scaler(src, srcData, w, h, nComps, srcAlpha,
+ scaledWidth, scaledHeight, interpolate,
+ imageCache->colorData, imageCache->alphaData);
+ Guchar *colorPtr = imageCache->colorData;
+ Guchar *alphaPtr = imageCache->alphaData;
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler.nextLine();
+ memcpy(colorPtr, scaler.colorData(), scaledWidth * nComps);
+ colorPtr += scaledWidth * nComps;
+ if (srcAlpha) {
+ memcpy(alphaPtr, scaler.alphaData(), scaledWidth);
+ alphaPtr += scaledWidth;
+ }
+ }
+ }
+ }
+ *scaledColor = imageCache->colorData;
+ *scaledAlpha = imageCache->alphaData;
+ *freeScaledImage = gFalse;
+ }
+}
+
+void Splash::drawImageArbitraryNoInterp(Guchar *scaledColor,
+ Guchar *scaledAlpha,
+ SplashDrawImageRowData *dd,
+ SplashDrawImageRowFunc drawRowFunc,
+ SplashCoord *invMat,
+ int scaledWidth, int scaledHeight,
+ int xMin, int yMin, int xMax, int yMax,
+ int nComps, GBool srcAlpha) {
+ int tt = state->clip->getXMinI(state->strokeAdjust);
+ if (tt > xMin) {
+ xMin = tt;
+ }
+ tt = state->clip->getXMaxI(state->strokeAdjust) + 1;
+ if (tt < xMax) {
+ xMax = tt;
+ }
+ tt = state->clip->getYMinI(state->strokeAdjust);
+ if (tt > yMin) {
+ yMin = tt;
+ }
+ tt = state->clip->getYMaxI(state->strokeAdjust) + 1;
+ if (tt < yMax) {
+ yMax = tt;
+ }
+ if (xMax <= xMin || yMax <= yMin) {
+ return;
+ }
+
+ Guchar *colorBuf = (Guchar *)gmallocn(xMax - xMin, nComps);
+ Guchar *alphaBuf = NULL;
+ if (srcAlpha) {
+ alphaBuf = (Guchar *)gmalloc(xMax - xMin);
+ }
+
+ for (int y = yMin; y < yMax; ++y) {
+ int rowMin = xMax;
+ int rowMax = 0;
+ for (int x = xMin; x < xMax; ++x) {
+ // note: invMat includes a "+0.5" factor so that this is really
+ // a multiply by (x+0.5, y+0.5)
+ int xx = splashFloor((SplashCoord)x * invMat[0]
+ + (SplashCoord)y * invMat[2] + invMat[4]);
+ int yy = splashFloor((SplashCoord)x * invMat[1]
+ + (SplashCoord)y * invMat[3] + invMat[5]);
+ if (xx >= 0 && xx < scaledWidth &&
+ yy >= 0 && yy < scaledHeight) {
+ Guchar *p = scaledColor + (yy * scaledWidth + xx) * nComps;
+ Guchar *q = colorBuf + (x - xMin) * nComps;
+ for (int i = 0; i < nComps; ++i) {
+ *q++ = *p++;
+ }
+ if (srcAlpha) {
+ alphaBuf[x - xMin] = scaledAlpha[yy * scaledWidth + xx];
+ }
+ if (x < rowMin) {
+ rowMin = x;
+ }
+ rowMax = x + 1;
+ }
+ }
+ if (rowMin < rowMax) {
+ (this->*drawRowFunc)(dd,
+ colorBuf + (rowMin - xMin) * nComps,
+ alphaBuf + (rowMin - xMin),
+ rowMin, y, rowMax - rowMin);
+ }
+ }
+
+ gfree(colorBuf);
+ gfree(alphaBuf);
+}
+
+void Splash::drawImageArbitraryInterp(Guchar *scaledColor, Guchar *scaledAlpha,
+ SplashDrawImageRowData *dd,
+ SplashDrawImageRowFunc drawRowFunc,
+ SplashCoord *invMat,
+ int scaledWidth, int scaledHeight,
+ int xMin, int yMin, int xMax, int yMax,
+ int nComps, GBool srcAlpha) {
+ int tt = state->clip->getXMinI(state->strokeAdjust);
+ if (tt > xMin) {
+ xMin = tt;
+ }
+ tt = state->clip->getXMaxI(state->strokeAdjust) + 1;
+ if (tt < xMax) {
+ xMax = tt;
+ }
+ tt = state->clip->getYMinI(state->strokeAdjust);
+ if (tt > yMin) {
+ yMin = tt;
+ }
+ tt = state->clip->getYMaxI(state->strokeAdjust) + 1;
+ if (tt < yMax) {
+ yMax = tt;
+ }
+ if (xMax <= xMin || yMax <= yMin) {
+ return;
+ }
+
+ Guchar *colorBuf = (Guchar *)gmallocn(xMax - xMin, nComps);
+ Guchar *alphaBuf = NULL;
+ if (srcAlpha) {
+ alphaBuf = (Guchar *)gmalloc(xMax - xMin);
+ }
+
+ for (int y = yMin; y < yMax; ++y) {
+ int rowMin = xMax;
+ int rowMax = 0;
+ for (int x = xMin; x < xMax; ++x) {
+ // note: invMat includes a "+0.5" factor so that this is really
+ // a multiply by (x+0.5, y+0.5)
+ SplashCoord xs = (SplashCoord)x * invMat[0]
+ + (SplashCoord)y * invMat[2] + invMat[4];
+ SplashCoord ys = (SplashCoord)x * invMat[1]
+ + (SplashCoord)y * invMat[3] + invMat[5];
+ int x0 = splashFloor(xs - 0.5);
+ int x1 = x0 + 1;
+ int y0 = splashFloor(ys - 0.5);
+ int y1 = y0 + 1;
+ if (x1 >= 0 && x0 < scaledWidth && y1 >= 0 && y0 < scaledHeight) {
+ SplashCoord sx0 = (SplashCoord)x1 + 0.5 - xs;
+ SplashCoord sx1 = (SplashCoord)1 - sx0;
+ SplashCoord sy0 = (SplashCoord)y1 + 0.5 - ys;
+ SplashCoord sy1 = (SplashCoord)1 - sy0;
+ if (x0 < 0) {
+ x0 = 0;
+ }
+ if (x1 >= scaledWidth) {
+ x1 = scaledWidth - 1;
+ }
+ if (y0 < 0) {
+ y0 = 0;
+ }
+ if (y1 >= scaledHeight) {
+ y1 = scaledHeight - 1;
+ }
+ Guchar *p00 = scaledColor + (y0 * scaledWidth + x0) * nComps;
+ Guchar *p10 = scaledColor + (y0 * scaledWidth + x1) * nComps;
+ Guchar *p01 = scaledColor + (y1 * scaledWidth + x0) * nComps;
+ Guchar *p11 = scaledColor + (y1 * scaledWidth + x1) * nComps;
+ Guchar *q = colorBuf + (x - xMin) * nComps;
+ for (int i = 0; i < nComps; ++i) {
+ *q++ = (Guchar)(int)(sx0 * (sy0 * (int)*p00++ + sy1 * (int)*p01++) +
+ sx1 * (sy0 * (int)*p10++ + sy1 * (int)*p11++));
+ }
+ if (srcAlpha) {
+ p00 = scaledAlpha + (y0 * scaledWidth + x0);
+ p10 = scaledAlpha + (y0 * scaledWidth + x1);
+ p01 = scaledAlpha + (y1 * scaledWidth + x0);
+ p11 = scaledAlpha + (y1 * scaledWidth + x1);
+ q = alphaBuf + (x - xMin);
+ *q = (Guchar)(int)(sx0 * (sy0 * (int)*p00 + sy1 * (int)*p01) +
+ sx1 * (sy0 * (int)*p10 + sy1 * (int)*p11));
+ }
+ if (x < rowMin) {
+ rowMin = x;
+ }
+ rowMax = x + 1;
+ }
+ }
+ if (rowMin < rowMax) {
+ (this->*drawRowFunc)(dd,
+ colorBuf + (rowMin - xMin) * nComps,
+ alphaBuf + (rowMin - xMin),
+ rowMin, y, rowMax - rowMin);
+ }
+ }
+
+ gfree(colorBuf);
+ gfree(alphaBuf);
+}
+
+void Splash::mirrorImageRow(Guchar *colorIn, Guchar *alphaIn,
+ Guchar *colorOut, Guchar *alphaOut,
+ int width, int nComps, GBool srcAlpha) {
+ Guchar *p, *q;
+
+ p = colorIn;
+ q = colorOut + (width - 1) * nComps;
+ for (int i = 0; i < width; ++i) {
+ for (int j = 0; j < nComps; ++j) {
+ q[j] = p[j];
+ }
+ p += nComps;
+ q -= nComps;
+ }
+
+ if (srcAlpha) {
+ p = alphaIn;
+ q = alphaOut + (width - 1);
+ for (int i = 0; i < width; ++i) {
+ *q = *p;
+ ++p;
+ --q;
+ }
+ }
+}
+
+void Splash::drawImageRowNoClipNoAlpha(SplashDrawImageRowData *data,
+ Guchar *colorData, Guchar *alphaData,
+ int x, int y, int width) {
+ (this->*data->pipe.run)(&data->pipe, x, x + width - 1, y, NULL, colorData);
+}
+
+void Splash::drawImageRowNoClipAlpha(SplashDrawImageRowData *data,
+ Guchar *colorData, Guchar *alphaData,
+ int x, int y, int width) {
+ (this->*data->pipe.run)(&data->pipe, x, x + width - 1, y,
+ alphaData, colorData);
+}
+
+void Splash::drawImageRowClipNoAlphaNoAA(SplashDrawImageRowData *data,
+ Guchar *colorData,
+ Guchar *alphaData,
+ int x, int y, int width) {
+ if (y < 0 || y >= bitmap->height) {
+ return;
+ }
+ if (x < 0) {
+ colorData -= x * data->nComps;
+ width += x;
+ x = 0;
+ }
+ if (x + width > bitmap->width) {
+ width = bitmap->width - x;
+ }
+ if (width <= 0) {
+ return;
+ }
+ memset(scanBuf + x, 0xff, width);
+ state->clip->clipSpanBinary(scanBuf, y, x, x + width - 1,
+ state->strokeAdjust);
+ (this->*data->pipe.run)(&data->pipe, x, x + width - 1, y,
+ scanBuf + x, colorData);
+}
+
+void Splash::drawImageRowClipNoAlphaAA(SplashDrawImageRowData *data,
+ Guchar *colorData,
+ Guchar *alphaData,
+ int x, int y, int width) {
+ if (y < 0 || y >= bitmap->height) {
+ return;
+ }
+ if (x < 0) {
+ colorData -= x * data->nComps;
+ width += x;
+ x = 0;
+ }
+ if (x + width > bitmap->width) {
+ width = bitmap->width - x;
+ }
+ if (width <= 0) {
+ return;
+ }
+ memset(scanBuf + x, 0xff, width);
+ state->clip->clipSpan(scanBuf, y, x, x + width - 1, state->strokeAdjust);
+ (this->*data->pipe.run)(&data->pipe, x, x + width - 1, y,
+ scanBuf + x, colorData);
+}
+
+void Splash::drawImageRowClipAlphaNoAA(SplashDrawImageRowData *data,
+ Guchar *colorData,
+ Guchar *alphaData,
+ int x, int y, int width) {
+ if (y < 0 || y >= bitmap->height) {
+ return;
+ }
+ if (x < 0) {
+ colorData -= x * data->nComps;
+ alphaData -= x;
+ width += x;
+ x = 0;
+ }
+ if (x + width > bitmap->width) {
+ width = bitmap->width - x;
+ }
+ if (width <= 0) {
+ return;
+ }
+ memcpy(scanBuf + x, alphaData, width);
+ state->clip->clipSpanBinary(scanBuf, y, x, x + width - 1,
+ state->strokeAdjust);
+ (this->*data->pipe.run)(&data->pipe, x, x + width - 1, y,
+ scanBuf + x, colorData);
+}
+
+void Splash::drawImageRowClipAlphaAA(SplashDrawImageRowData *data,
+ Guchar *colorData,
+ Guchar *alphaData,
+ int x, int y, int width) {
+ if (y < 0 || y >= bitmap->height) {
+ return;
+ }
+ if (x < 0) {
+ colorData -= x * data->nComps;
+ alphaData -= x;
+ width += x;
+ x = 0;
+ }
+ if (x + width > bitmap->width) {
+ width = bitmap->width - x;
+ }
+ if (width <= 0) {
+ return;
+ }
+ memcpy(scanBuf + x, alphaData, width);
+ state->clip->clipSpan(scanBuf, y, x, x + width - 1, state->strokeAdjust);
+ (this->*data->pipe.run)(&data->pipe, x, x + width - 1, y,
+ scanBuf + x, colorData);
+}
+
+SplashError Splash::composite(SplashBitmap *src, int xSrc, int ySrc,
+ int xDest, int yDest, int w, int h,
+ GBool noClip, GBool nonIsolated) {
+ SplashPipe pipe;
+ Guchar *mono1Ptr, *lineBuf, *linePtr;
+ Guchar mono1Mask, b;
+ int x0, x1, x, y0, y1, y, t;
+
+ if (!(src->mode == bitmap->mode ||
+ (src->mode == splashModeMono8 && bitmap->mode == splashModeMono1) ||
+ (src->mode == splashModeRGB8 && bitmap->mode == splashModeBGR8))) {
+ return splashErrModeMismatch;
+ }
+
+ pipeInit(&pipe, NULL,
+ (Guchar)splashRound(state->fillAlpha * 255),
+ !noClip || src->alpha != NULL, nonIsolated);
+ if (src->mode == splashModeMono1) {
+ // in mono1 mode, pipeRun expects the source to be in mono8
+ // format, so we need to extract the source color values into
+ // scanBuf, expanding them from mono1 to mono8
+ if (noClip) {
+ if (src->alpha) {
+ for (y = 0; y < h; ++y) {
+ mono1Ptr = src->data + (ySrc + y) * src->rowSize + (xSrc >> 3);
+ mono1Mask = (Guchar)(0x80 >> (xSrc & 7));
+ for (x = 0; x < w; ++x) {
+ scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00;
+ mono1Ptr += mono1Mask & 1;
+ mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1));
+ }
+ // this uses shape instead of alpha, which isn't technically
+ // correct, but works out the same
+ (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
+ src->alpha +
+ (ySrc + y) * src->alphaRowSize + xSrc,
+ scanBuf);
+ }
+ } else {
+ for (y = 0; y < h; ++y) {
+ mono1Ptr = src->data + (ySrc + y) * src->rowSize + (xSrc >> 3);
+ mono1Mask = (Guchar)(0x80 >> (xSrc & 7));
+ for (x = 0; x < w; ++x) {
+ scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00;
+ mono1Ptr += mono1Mask & 1;
+ mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1));
+ }
+ (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
+ NULL,
+ scanBuf);
+ }
+ }
+ } else {
+ x0 = xDest;
+ if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) {
+ x0 = t;
+ }
+ x1 = xDest + w;
+ if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) {
+ x1 = t;
+ }
+ y0 = yDest;
+ if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) {
+ y0 = t;
+ }
+ y1 = yDest + h;
+ if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) {
+ y1 = t;
+ }
+ if (x0 < x1 && y0 < y1) {
+ if (src->alpha) {
+ for (y = y0; y < y1; ++y) {
+ mono1Ptr = src->data
+ + (ySrc + y - yDest) * src->rowSize
+ + ((xSrc + x0 - xDest) >> 3);
+ mono1Mask = (Guchar)(0x80 >> ((xSrc + x0 - xDest) & 7));
+ for (x = x0; x < x1; ++x) {
+ scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00;
+ mono1Ptr += mono1Mask & 1;
+ mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1));
+ }
+ memcpy(scanBuf2 + x0,
+ src->alpha + (ySrc + y - yDest) * src->alphaRowSize +
+ (xSrc + x0 - xDest),
+ x1 - x0);
+ if (!state->clip->clipSpanBinary(scanBuf2, y, x0, x1 - 1,
+ state->strokeAdjust)) {
+ continue;
+ }
+ // this uses shape instead of alpha, which isn't technically
+ // correct, but works out the same
+ (this->*pipe.run)(&pipe, x0, x1 - 1, y,
+ scanBuf2 + x0,
+ scanBuf + x0);
+ }
+ } else {
+ for (y = y0; y < y1; ++y) {
+ mono1Ptr = src->data
+ + (ySrc + y - yDest) * src->rowSize
+ + ((xSrc + x0 - xDest) >> 3);
+ mono1Mask = (Guchar)(0x80 >> ((xSrc + x0 - xDest) & 7));
+ for (x = x0; x < x1; ++x) {
+ scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00;
+ mono1Ptr += mono1Mask & 1;
+ mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1));
+ }
+ memset(scanBuf2 + x0, 0xff, x1 - x0);
+ if (!state->clip->clipSpanBinary(scanBuf2, y, x0, x1 - 1,
+ state->strokeAdjust)) {
+ continue;
+ }
+ (this->*pipe.run)(&pipe, x0, x1 - 1, y,
+ scanBuf2 + x0,
+ scanBuf + x0);
+ }
+ }
+ }
+ }
+
+ } else if (src->mode == splashModeBGR8) {
+ // in BGR8 mode, pipeRun expects the source to be in RGB8 format,
+ // so we need to swap bytes
+ lineBuf = (Guchar *)gmallocn(w, 3);
+ if (noClip) {
+ if (src->alpha) {
+ for (y = 0; y < h; ++y) {
+ memcpy(lineBuf,
+ src->data + (ySrc + y) * src->rowSize + xSrc * 3,
+ w * 3);
+ for (x = 0, linePtr = lineBuf; x < w; ++x, linePtr += 3) {
+ b = linePtr[0];
+ linePtr[0] = linePtr[2];
+ linePtr[2] = b;
+ }
+ // this uses shape instead of alpha, which isn't technically
+ // correct, but works out the same
+ (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
+ src->alpha +
+ (ySrc + y) * src->alphaRowSize + xSrc,
+ lineBuf);
+ }
+ } else {
+ for (y = 0; y < h; ++y) {
+ memcpy(lineBuf,
+ src->data + (ySrc + y) * src->rowSize + xSrc * 3,
+ w * 3);
+ for (x = 0, linePtr = lineBuf; x < w; ++x, linePtr += 3) {
+ b = linePtr[0];
+ linePtr[0] = linePtr[2];
+ linePtr[2] = b;
+ }
+ (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
+ NULL, lineBuf);
+ }
+ }
+ } else {
+ x0 = xDest;
+ if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) {
+ x0 = t;
+ }
+ x1 = xDest + w;
+ if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) {
+ x1 = t;
+ }
+ y0 = yDest;
+ if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) {
+ y0 = t;
+ }
+ y1 = yDest + h;
+ if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) {
+ y1 = t;
+ }
+ if (x0 < x1 && y0 < y1) {
+ if (src->alpha) {
+ for (y = y0; y < y1; ++y) {
+ memcpy(scanBuf + x0,
+ src->alpha + (ySrc + y - yDest) * src->alphaRowSize +
+ (xSrc + x0 - xDest),
+ x1 - x0);
+ state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust);
+ memcpy(lineBuf,
+ src->data +
+ (ySrc + y - yDest) * src->rowSize +
+ (xSrc + x0 - xDest) * 3,
+ (x1 - x0) * 3);
+ for (x = 0, linePtr = lineBuf; x < x1 - x0; ++x, linePtr += 3) {
+ b = linePtr[0];
+ linePtr[0] = linePtr[2];
+ linePtr[2] = b;
+ }
+ // this uses shape instead of alpha, which isn't technically
+ // correct, but works out the same
+ (this->*pipe.run)(&pipe, x0, x1 - 1, y,
+ scanBuf + x0, lineBuf);
+ }
+ } else {
+ for (y = y0; y < y1; ++y) {
+ memset(scanBuf + x0, 0xff, x1 - x0);
+ state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust);
+ memcpy(lineBuf,
+ src->data +
+ (ySrc + y - yDest) * src->rowSize +
+ (xSrc + x0 - xDest) * 3,
+ (x1 - x0) * 3);
+ for (x = 0, linePtr = lineBuf; x < x1 - x0; ++x, linePtr += 3) {
+ b = linePtr[0];
+ linePtr[0] = linePtr[2];
+ linePtr[2] = b;
+ }
+ (this->*pipe.run)(&pipe, x0, x1 - 1, yDest + y,
+ scanBuf + x0,
+ src->data +
+ (ySrc + y - yDest) * src->rowSize +
+ (xSrc + x0 - xDest) * bitmapComps);
+ }
+ }
+ }
+ }
+ gfree(lineBuf);
+
+ } else { // src->mode not mono1 or BGR8
+ if (noClip) {
+ if (src->alpha) {
+ for (y = 0; y < h; ++y) {
+ // this uses shape instead of alpha, which isn't technically
+ // correct, but works out the same
+ (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
+ src->alpha +
+ (ySrc + y) * src->alphaRowSize + xSrc,
+ src->data + (ySrc + y) * src->rowSize +
+ xSrc * bitmapComps);
+ }
+ } else {
+ for (y = 0; y < h; ++y) {
+ (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
+ NULL,
+ src->data + (ySrc + y) * src->rowSize +
+ xSrc * bitmapComps);
+ }
+ }
+ } else {
+ x0 = xDest;
+ if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) {
+ x0 = t;
+ }
+ x1 = xDest + w;
+ if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) {
+ x1 = t;
+ }
+ y0 = yDest;
+ if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) {
+ y0 = t;
+ }
+ y1 = yDest + h;
+ if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) {
+ y1 = t;
+ }
+ if (x0 < x1 && y0 < y1) {
+ if (src->alpha) {
+ for (y = y0; y < y1; ++y) {
+ memcpy(scanBuf + x0,
+ src->alpha + (ySrc + y - yDest) * src->alphaRowSize +
+ (xSrc + x0 - xDest),
+ x1 - x0);
+ state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust);
+ // this uses shape instead of alpha, which isn't technically
+ // correct, but works out the same
+ (this->*pipe.run)(&pipe, x0, x1 - 1, y,
+ scanBuf + x0,
+ src->data +
+ (ySrc + y - yDest) * src->rowSize +
+ (xSrc + x0 - xDest) * bitmapComps);
+ }
+ } else {
+ for (y = y0; y < y1; ++y) {
+ memset(scanBuf + x0, 0xff, x1 - x0);
+ state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust);
+ (this->*pipe.run)(&pipe, x0, x1 - 1, yDest + y,
+ scanBuf + x0,
+ src->data +
+ (ySrc + y - yDest) * src->rowSize +
+ (xSrc + x0 - xDest) * bitmapComps);
+ }
+ }
+ }
+ }
+ }
+
+ return splashOk;
+}
+
+SplashError Splash::compositeWithOverprint(SplashBitmap *src,
+ Guint *srcOverprintMaskBitmap,
+ int xSrc, int ySrc,
+ int xDest, int yDest, int w, int h,
+ GBool noClip, GBool nonIsolated) {
+ SplashPipe pipe;
+ int x0, x1, y0, y1, y, t;
+
+ if (!(src->mode == bitmap->mode ||
+ (src->mode == splashModeMono8 && bitmap->mode == splashModeMono1) ||
+ (src->mode == splashModeRGB8 && bitmap->mode == splashModeBGR8))) {
+ return splashErrModeMismatch;
+ }
+
+ pipeInit(&pipe, NULL,
+ (Guchar)splashRound(state->fillAlpha * 255),
+ !noClip || src->alpha != NULL, nonIsolated, gTrue);
+
+ if (noClip) {
+ if (src->alpha) {
+ for (y = 0; y < h; ++y) {
+ pipe.srcOverprintMaskPtr = srcOverprintMaskBitmap + y * w + xSrc;
+ // this uses shape instead of alpha, which isn't technically
+ // correct, but works out the same
+ (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
+ src->alpha +
+ (ySrc + y) * src->alphaRowSize + xSrc,
+ src->data + (ySrc + y) * src->rowSize +
+ xSrc * bitmapComps);
+ }
+ } else {
+ for (y = 0; y < h; ++y) {
+ pipe.srcOverprintMaskPtr = srcOverprintMaskBitmap + y * w + xSrc;
+ (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
+ NULL,
+ src->data + (ySrc + y) * src->rowSize +
+ xSrc * bitmapComps);
+ }
+ }
+ } else {
+ x0 = xDest;
+ if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) {
+ x0 = t;
+ }
+ x1 = xDest + w;
+ if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) {
+ x1 = t;
+ }
+ y0 = yDest;
+ if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) {
+ y0 = t;
+ }
+ y1 = yDest + h;
+ if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) {
+ y1 = t;
+ }
+ if (x0 < x1 && y0 < y1) {
+ if (src->alpha) {
+ for (y = y0; y < y1; ++y) {
+ memcpy(scanBuf + x0,
+ src->alpha + (ySrc + y - yDest) * src->alphaRowSize +
+ (xSrc + x0 - xDest),
+ x1 - x0);
+ state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust);
+ pipe.srcOverprintMaskPtr = srcOverprintMaskBitmap
+ + (ySrc + y - yDest) * w
+ + (xSrc + x0 - xDest);
+ // this uses shape instead of alpha, which isn't technically
+ // correct, but works out the same
+ (this->*pipe.run)(&pipe, x0, x1 - 1, y,
+ scanBuf + x0,
+ src->data +
+ (ySrc + y - yDest) * src->rowSize +
+ (xSrc + x0 - xDest) * bitmapComps);
+ }
+ } else {
+ for (y = y0; y < y1; ++y) {
+ memset(scanBuf + x0, 0xff, x1 - x0);
+ state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust);
+ pipe.srcOverprintMaskPtr = srcOverprintMaskBitmap
+ + (ySrc + y - yDest) * w
+ + (xSrc + x0 - xDest);
+ (this->*pipe.run)(&pipe, x0, x1 - 1, y,
+ scanBuf + x0,
+ src->data +
+ (ySrc + y - yDest) * src->rowSize +
+ (xSrc + x0 - xDest) * bitmapComps);
+ }
+ }
+ }
+ }
+
+ return splashOk;
+}
+
+void Splash::compositeBackground(SplashColorPtr color) {
+ SplashColorPtr p;
+ Guchar *q;
+ Guchar alpha, alpha1, c, color0, color1, color2, mask;
+#if SPLASH_CMYK
+ Guchar color3;
+#endif
+ int x, y;
+
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ color0 = color[0];
+ for (y = 0; y < bitmap->height; ++y) {
+ p = &bitmap->data[y * bitmap->rowSize];
+ q = &bitmap->alpha[y * bitmap->alphaRowSize];
+ mask = 0x80;
+ for (x = 0; x < bitmap->width; ++x) {
+ alpha = *q++;
+ if (alpha == 0) {
+ if (color0 & 0x80) {
+ *p |= mask;
+ } else {
+ *p &= (Guchar)~mask;
+ }
+ } else if (alpha != 255) {
+ alpha1 = (Guchar)(255 - alpha);
+ c = (*p & mask) ? 0xff : 0x00;
+ c = div255(alpha1 * color0 + alpha * c);
+ if (c & 0x80) {
+ *p |= mask;
+ } else {
+ *p &= (Guchar)~mask;
+ }
+ }
+ if (!(mask = (Guchar)(mask >> 1))) {
+ mask = 0x80;
+ ++p;
+ }
+ }
+ }
+ break;
+ case splashModeMono8:
+ color0 = color[0];
+ for (y = 0; y < bitmap->height; ++y) {
+ p = &bitmap->data[y * bitmap->rowSize];
+ q = &bitmap->alpha[y * bitmap->alphaRowSize];
+ for (x = 0; x < bitmap->width; ++x) {
+ alpha = *q++;
+ if (alpha == 0) {
+ p[0] = color0;
+ } else if (alpha != 255) {
+ alpha1 = (Guchar)(255 - alpha);
+ p[0] = div255(alpha1 * color0 + alpha * p[0]);
+ }
+ ++p;
+ }
+ }
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ color0 = color[0];
+ color1 = color[1];
+ color2 = color[2];
+ for (y = 0; y < bitmap->height; ++y) {
+ p = &bitmap->data[y * bitmap->rowSize];
+ q = &bitmap->alpha[y * bitmap->alphaRowSize];
+ for (x = 0; x < bitmap->width; ++x) {
+ alpha = *q++;
+ if (alpha == 0) {
+ p[0] = color0;
+ p[1] = color1;
+ p[2] = color2;
+ } else if (alpha != 255) {
+ alpha1 = (Guchar)(255 - alpha);
+ p[0] = div255(alpha1 * color0 + alpha * p[0]);
+ p[1] = div255(alpha1 * color1 + alpha * p[1]);
+ p[2] = div255(alpha1 * color2 + alpha * p[2]);
+ }
+ p += 3;
+ }
+ }
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ color0 = color[0];
+ color1 = color[1];
+ color2 = color[2];
+ color3 = color[3];
+ for (y = 0; y < bitmap->height; ++y) {
+ p = &bitmap->data[y * bitmap->rowSize];
+ q = &bitmap->alpha[y * bitmap->alphaRowSize];
+ for (x = 0; x < bitmap->width; ++x) {
+ alpha = *q++;
+ if (alpha == 0) {
+ p[0] = color0;
+ p[1] = color1;
+ p[2] = color2;
+ p[3] = color3;
+ } else if (alpha != 255) {
+ alpha1 = (Guchar)(255 - alpha);
+ p[0] = div255(alpha1 * color0 + alpha * p[0]);
+ p[1] = div255(alpha1 * color1 + alpha * p[1]);
+ p[2] = div255(alpha1 * color2 + alpha * p[2]);
+ p[3] = div255(alpha1 * color3 + alpha * p[3]);
+ }
+ p += 4;
+ }
+ }
+ break;
+#endif
+ }
+ memset(bitmap->alpha, 255, bitmap->alphaRowSize * bitmap->height);
+}
+
+SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc,
+ int xDest, int yDest, int w, int h) {
+ SplashColorPtr p, q;
+ Guchar mask, srcMask;
+ int x, y;
+
+ if (src->mode != bitmap->mode) {
+ return splashErrModeMismatch;
+ }
+
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ for (y = 0; y < h; ++y) {
+ p = &bitmap->data[(yDest + y) * bitmap->rowSize + (xDest >> 3)];
+ mask = (Guchar)(0x80 >> (xDest & 7));
+ q = &src->data[(ySrc + y) * src->rowSize + (xSrc >> 3)];
+ srcMask = (Guchar)(0x80 >> (xSrc & 7));
+ for (x = 0; x < w; ++x) {
+ if (*q & srcMask) {
+ *p |= mask;
+ } else {
+ *p &= (Guchar)~mask;
+ }
+ if (!(mask = (Guchar)(mask >> 1))) {
+ mask = 0x80;
+ ++p;
+ }
+ if (!(srcMask = (Guchar)(srcMask >> 1))) {
+ srcMask = 0x80;
+ ++q;
+ }
+ }
+ }
+ break;
+ case splashModeMono8:
+ for (y = 0; y < h; ++y) {
+ p = &bitmap->data[(yDest + y) * bitmap->rowSize + xDest];
+ q = &src->data[(ySrc + y) * src->rowSize + xSrc];
+ memcpy(p, q, w);
+ }
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ for (y = 0; y < h; ++y) {
+ p = &bitmap->data[(yDest + y) * bitmap->rowSize + 3 * xDest];
+ q = &src->data[(ySrc + y) * src->rowSize + 3 * xSrc];
+ memcpy(p, q, 3 * w);
+ }
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ for (y = 0; y < h; ++y) {
+ p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest];
+ q = &src->data[(ySrc + y) * src->rowSize + 4 * xSrc];
+ memcpy(p, q, 4 * w);
+ }
+ break;
+#endif
+ }
+
+ if (bitmap->alpha) {
+ for (y = 0; y < h; ++y) {
+ q = &bitmap->alpha[(yDest + y) * bitmap->alphaRowSize + xDest];
+ memset(q, 0, w);
+ }
+ }
+
+ return splashOk;
+}
+
+SplashError Splash::blitCorrectedAlpha(SplashBitmap *dest, int xSrc, int ySrc,
+ int xDest, int yDest, int w, int h) {
+ SplashColorPtr p, q;
+ Guchar *alpha0Ptr;
+ Guchar alpha0, aSrc, mask, srcMask;
+ int x, y;
+
+ if (bitmap->mode != dest->mode ||
+ !bitmap->alpha ||
+ !dest->alpha ||
+ !groupBackBitmap) {
+ return splashErrModeMismatch;
+ }
+
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ for (y = 0; y < h; ++y) {
+ p = &dest->data[(yDest + y) * dest->rowSize + (xDest >> 3)];
+ mask = (Guchar)(0x80 >> (xDest & 7));
+ q = &bitmap->data[(ySrc + y) * bitmap->rowSize + (xSrc >> 3)];
+ srcMask = (Guchar)(0x80 >> (xSrc & 7));
+ for (x = 0; x < w; ++x) {
+ if (*q & srcMask) {
+ *p |= mask;
+ } else {
+ *p &= (Guchar)~mask;
+ }
+ if (!(mask = (Guchar)(mask >> 1))) {
+ mask = 0x80;
+ ++p;
+ }
+ if (!(srcMask = (Guchar)(srcMask >> 1))) {
+ srcMask = 0x80;
+ ++q;
+ }
+ }
+ }
+ break;
+ case splashModeMono8:
+ for (y = 0; y < h; ++y) {
+ p = &dest->data[(yDest + y) * dest->rowSize + xDest];
+ q = &bitmap->data[(ySrc + y) * bitmap->rowSize + xSrc];
+ memcpy(p, q, w);
+ }
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ for (y = 0; y < h; ++y) {
+ p = &dest->data[(yDest + y) * dest->rowSize + 3 * xDest];
+ q = &bitmap->data[(ySrc + y) * bitmap->rowSize + 3 * xSrc];
+ memcpy(p, q, 3 * w);
+ }
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ for (y = 0; y < h; ++y) {
+ p = &dest->data[(yDest + y) * dest->rowSize + 4 * xDest];
+ q = &bitmap->data[(ySrc + y) * bitmap->rowSize + 4 * xSrc];
+ memcpy(p, q, 4 * w);
+ }
+ break;
+#endif
+ }
+
+ for (y = 0; y < h; ++y) {
+ p = &dest->alpha[(yDest + y) * dest->alphaRowSize + xDest];
+ q = &bitmap->alpha[(ySrc + y) * bitmap->alphaRowSize + xSrc];
+ alpha0Ptr = &groupBackBitmap->alpha[(groupBackY + ySrc + y)
+ * groupBackBitmap->alphaRowSize +
+ (groupBackX + xSrc)];
+ for (x = 0; x < w; ++x) {
+ alpha0 = *alpha0Ptr++;
+ aSrc = *q++;
+ *p++ = (Guchar)(alpha0 + aSrc - div255(alpha0 * aSrc));
+ }
+ }
+
+ return splashOk;
+}
+
+SplashPath *Splash::makeStrokePath(SplashPath *path, SplashCoord w,
+ int lineCap, int lineJoin,
+ GBool flatten) {
+ SplashPath *pathIn, *dashPath, *pathOut;
+ SplashCoord d, dx, dy, wdx, wdy, dxNext, dyNext, wdxNext, wdyNext;
+ SplashCoord crossprod, dotprod, miter, m;
+ SplashCoord angle, angleNext, dAngle, xc, yc;
+ SplashCoord dxJoin, dyJoin, dJoin, kappa;
+ SplashCoord cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4;
+ GBool first, last, closed;
+ int subpathStart0, subpathStart1, seg, i0, i1, j0, j1, k0, k1;
+ int left0, left1, left2, right0, right1, right2, join0, join1, join2;
+ int leftFirst, rightFirst, firstPt;
+
+ pathOut = new SplashPath();
+
+ if (path->length == 0) {
+ return pathOut;
+ }
+
+ if (flatten) {
+ pathIn = flattenPath(path, state->matrix, state->flatness);
+ if (state->lineDashLength > 0) {
+ dashPath = makeDashedPath(pathIn);
+ delete pathIn;
+ pathIn = dashPath;
+ if (pathIn->length == 0) {
+ delete pathIn;
+ return pathOut;
+ }
+ }
+ } else {
+ pathIn = path;
+ }
+
+ subpathStart0 = subpathStart1 = 0; // make gcc happy
+ seg = 0; // make gcc happy
+ closed = gFalse; // make gcc happy
+ left0 = left1 = right0 = right1 = join0 = join1 = 0; // make gcc happy
+ leftFirst = rightFirst = firstPt = 0; // make gcc happy
+
+ i0 = 0;
+ for (i1 = i0;
+ !(pathIn->flags[i1] & splashPathLast) &&
+ i1 + 1 < pathIn->length &&
+ pathIn->pts[i1+1].x == pathIn->pts[i1].x &&
+ pathIn->pts[i1+1].y == pathIn->pts[i1].y;
+ ++i1) ;
+
+ while (i1 < pathIn->length) {
+ if ((first = pathIn->flags[i0] & splashPathFirst)) {
+ subpathStart0 = i0;
+ subpathStart1 = i1;
+ seg = 0;
+ closed = pathIn->flags[i0] & splashPathClosed;
+ }
+ j0 = i1 + 1;
+ if (j0 < pathIn->length) {
+ for (j1 = j0;
+ !(pathIn->flags[j1] & splashPathLast) &&
+ j1 + 1 < pathIn->length &&
+ pathIn->pts[j1+1].x == pathIn->pts[j1].x &&
+ pathIn->pts[j1+1].y == pathIn->pts[j1].y;
+ ++j1) ;
+ } else {
+ j1 = j0;
+ }
+ if (pathIn->flags[i1] & splashPathLast) {
+ if (first && lineCap == splashLineCapRound) {
+ // special case: zero-length subpath with round line caps -->
+ // draw a circle
+ pathOut->moveTo(pathIn->pts[i0].x + (SplashCoord)0.5 * w,
+ pathIn->pts[i0].y);
+ pathOut->curveTo(pathIn->pts[i0].x + (SplashCoord)0.5 * w,
+ pathIn->pts[i0].y + bezierCircle2 * w,
+ pathIn->pts[i0].x + bezierCircle2 * w,
+ pathIn->pts[i0].y + (SplashCoord)0.5 * w,
+ pathIn->pts[i0].x,
+ pathIn->pts[i0].y + (SplashCoord)0.5 * w);
+ pathOut->curveTo(pathIn->pts[i0].x - bezierCircle2 * w,
+ pathIn->pts[i0].y + (SplashCoord)0.5 * w,
+ pathIn->pts[i0].x - (SplashCoord)0.5 * w,
+ pathIn->pts[i0].y + bezierCircle2 * w,
+ pathIn->pts[i0].x - (SplashCoord)0.5 * w,
+ pathIn->pts[i0].y);
+ pathOut->curveTo(pathIn->pts[i0].x - (SplashCoord)0.5 * w,
+ pathIn->pts[i0].y - bezierCircle2 * w,
+ pathIn->pts[i0].x - bezierCircle2 * w,
+ pathIn->pts[i0].y - (SplashCoord)0.5 * w,
+ pathIn->pts[i0].x,
+ pathIn->pts[i0].y - (SplashCoord)0.5 * w);
+ pathOut->curveTo(pathIn->pts[i0].x + bezierCircle2 * w,
+ pathIn->pts[i0].y - (SplashCoord)0.5 * w,
+ pathIn->pts[i0].x + (SplashCoord)0.5 * w,
+ pathIn->pts[i0].y - bezierCircle2 * w,
+ pathIn->pts[i0].x + (SplashCoord)0.5 * w,
+ pathIn->pts[i0].y);
+ pathOut->close();
+ }
+ i0 = j0;
+ i1 = j1;
+ continue;
+ }
+ last = pathIn->flags[j1] & splashPathLast;
+ if (last) {
+ k0 = subpathStart1 + 1;
+ } else {
+ k0 = j1 + 1;
+ }
+ for (k1 = k0;
+ !(pathIn->flags[k1] & splashPathLast) &&
+ k1 + 1 < pathIn->length &&
+ pathIn->pts[k1+1].x == pathIn->pts[k1].x &&
+ pathIn->pts[k1+1].y == pathIn->pts[k1].y;
+ ++k1) ;
+
+ // compute the deltas for segment (i1, j0)
+#if USE_FIXEDPOINT
+ // the 1/d value can be small, which introduces significant
+ // inaccuracies in fixed point mode
+ d = splashDist(pathIn->pts[i1].x, pathIn->pts[i1].y,
+ pathIn->pts[j0].x, pathIn->pts[j0].y);
+ dx = (pathIn->pts[j0].x - pathIn->pts[i1].x) / d;
+ dy = (pathIn->pts[j0].y - pathIn->pts[i1].y) / d;
+#else
+ d = (SplashCoord)1 / splashDist(pathIn->pts[i1].x, pathIn->pts[i1].y,
+ pathIn->pts[j0].x, pathIn->pts[j0].y);
+ dx = d * (pathIn->pts[j0].x - pathIn->pts[i1].x);
+ dy = d * (pathIn->pts[j0].y - pathIn->pts[i1].y);
+#endif
+ wdx = (SplashCoord)0.5 * w * dx;
+ wdy = (SplashCoord)0.5 * w * dy;
+
+ // draw the start cap
+ if (i0 == subpathStart0) {
+ firstPt = pathOut->length;
+ }
+ if (first && !closed) {
+ switch (lineCap) {
+ case splashLineCapButt:
+ pathOut->moveTo(pathIn->pts[i0].x - wdy, pathIn->pts[i0].y + wdx);
+ pathOut->lineTo(pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx);
+ break;
+ case splashLineCapRound:
+ pathOut->moveTo(pathIn->pts[i0].x - wdy, pathIn->pts[i0].y + wdx);
+ pathOut->curveTo(pathIn->pts[i0].x - wdy - bezierCircle * wdx,
+ pathIn->pts[i0].y + wdx - bezierCircle * wdy,
+ pathIn->pts[i0].x - wdx - bezierCircle * wdy,
+ pathIn->pts[i0].y - wdy + bezierCircle * wdx,
+ pathIn->pts[i0].x - wdx,
+ pathIn->pts[i0].y - wdy);
+ pathOut->curveTo(pathIn->pts[i0].x - wdx + bezierCircle * wdy,
+ pathIn->pts[i0].y - wdy - bezierCircle * wdx,
+ pathIn->pts[i0].x + wdy - bezierCircle * wdx,
+ pathIn->pts[i0].y - wdx - bezierCircle * wdy,
+ pathIn->pts[i0].x + wdy,
+ pathIn->pts[i0].y - wdx);
+ break;
+ case splashLineCapProjecting:
+ pathOut->moveTo(pathIn->pts[i0].x - wdx - wdy,
+ pathIn->pts[i0].y + wdx - wdy);
+ pathOut->lineTo(pathIn->pts[i0].x - wdx + wdy,
+ pathIn->pts[i0].y - wdx - wdy);
+ break;
+ }
+ } else {
+ pathOut->moveTo(pathIn->pts[i0].x - wdy, pathIn->pts[i0].y + wdx);
+ pathOut->lineTo(pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx);
+ }
+
+ // draw the left side of the segment rectangle and the end cap
+ left2 = pathOut->length - 1;
+ if (last && !closed) {
+ switch (lineCap) {
+ case splashLineCapButt:
+ pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx);
+ pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx);
+ break;
+ case splashLineCapRound:
+ pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx);
+ pathOut->curveTo(pathIn->pts[j0].x + wdy + bezierCircle * wdx,
+ pathIn->pts[j0].y - wdx + bezierCircle * wdy,
+ pathIn->pts[j0].x + wdx + bezierCircle * wdy,
+ pathIn->pts[j0].y + wdy - bezierCircle * wdx,
+ pathIn->pts[j0].x + wdx,
+ pathIn->pts[j0].y + wdy);
+ pathOut->curveTo(pathIn->pts[j0].x + wdx - bezierCircle * wdy,
+ pathIn->pts[j0].y + wdy + bezierCircle * wdx,
+ pathIn->pts[j0].x - wdy + bezierCircle * wdx,
+ pathIn->pts[j0].y + wdx + bezierCircle * wdy,
+ pathIn->pts[j0].x - wdy,
+ pathIn->pts[j0].y + wdx);
+ break;
+ case splashLineCapProjecting:
+ pathOut->lineTo(pathIn->pts[j0].x + wdy + wdx,
+ pathIn->pts[j0].y - wdx + wdy);
+ pathOut->lineTo(pathIn->pts[j0].x - wdy + wdx,
+ pathIn->pts[j0].y + wdx + wdy);
+ break;
+ }
+ } else {
+ pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx);
+ pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx);
+ }
+
+ // draw the right side of the segment rectangle
+ // (NB: if stroke adjustment is enabled, the closepath operation MUST
+ // add a segment because this segment is used for a hint)
+ right2 = pathOut->length - 1;
+ pathOut->close(state->strokeAdjust != splashStrokeAdjustOff);
+
+ // draw the join
+ join2 = pathOut->length;
+ if (!last || closed) {
+
+ // compute the deltas for segment (j1, k0)
+#if USE_FIXEDPOINT
+ // the 1/d value can be small, which introduces significant
+ // inaccuracies in fixed point mode
+ d = splashDist(pathIn->pts[j1].x, pathIn->pts[j1].y,
+ pathIn->pts[k0].x, pathIn->pts[k0].y);
+ dxNext = (pathIn->pts[k0].x - pathIn->pts[j1].x) / d;
+ dyNext = (pathIn->pts[k0].y - pathIn->pts[j1].y) / d;
+#else
+ d = (SplashCoord)1 / splashDist(pathIn->pts[j1].x, pathIn->pts[j1].y,
+ pathIn->pts[k0].x, pathIn->pts[k0].y);
+ dxNext = d * (pathIn->pts[k0].x - pathIn->pts[j1].x);
+ dyNext = d * (pathIn->pts[k0].y - pathIn->pts[j1].y);
+#endif
+ wdxNext = (SplashCoord)0.5 * w * dxNext;
+ wdyNext = (SplashCoord)0.5 * w * dyNext;
+
+ // compute the join parameters
+ crossprod = dx * dyNext - dy * dxNext;
+ dotprod = -(dx * dxNext + dy * dyNext);
+ if (dotprod > 0.9999) {
+ // avoid a divide-by-zero -- set miter to something arbitrary
+ // such that sqrt(miter) will exceed miterLimit (and m is never
+ // used in that situation)
+ // (note: the comparison value (0.9999) has to be less than
+ // 1-epsilon, where epsilon is the smallest value
+ // representable in the fixed point format)
+ miter = (state->miterLimit + 1) * (state->miterLimit + 1);
+ m = 0;
+ } else {
+ miter = (SplashCoord)2 / ((SplashCoord)1 - dotprod);
+ if (miter < 1) {
+ // this can happen because of floating point inaccuracies
+ miter = 1;
+ }
+ m = splashSqrt(miter - 1);
+ }
+
+ // round join
+ if (lineJoin == splashLineJoinRound) {
+ // join angle < 180
+ if (crossprod < 0) {
+ angle = atan2((double)dx, (double)-dy);
+ angleNext = atan2((double)dxNext, (double)-dyNext);
+ if (angle < angleNext) {
+ angle += 2 * M_PI;
+ }
+ dAngle = (angle - angleNext) / M_PI;
+ if (dAngle < 0.501) {
+ // span angle is <= 90 degrees -> draw a single arc
+ kappa = dAngle * bezierCircle * w;
+ cx1 = pathIn->pts[j0].x - wdy + kappa * dx;
+ cy1 = pathIn->pts[j0].y + wdx + kappa * dy;
+ cx2 = pathIn->pts[j0].x - wdyNext - kappa * dxNext;
+ cy2 = pathIn->pts[j0].y + wdxNext - kappa * dyNext;
+ pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y);
+ pathOut->lineTo(pathIn->pts[j0].x - wdyNext,
+ pathIn->pts[j0].y + wdxNext);
+ pathOut->curveTo(cx2, cy2, cx1, cy1,
+ pathIn->pts[j0].x - wdy,
+ pathIn->pts[j0].y + wdx);
+ } else {
+ // span angle is > 90 degrees -> split into two arcs
+ dJoin = splashDist(-wdy, wdx, -wdyNext, wdxNext);
+ if (dJoin > 0) {
+ dxJoin = (-wdyNext + wdy) / dJoin;
+ dyJoin = (wdxNext - wdx) / dJoin;
+ xc = pathIn->pts[j0].x
+ + (SplashCoord)0.5 * w
+ * cos((double)((SplashCoord)0.5 * (angle + angleNext)));
+ yc = pathIn->pts[j0].y
+ + (SplashCoord)0.5 * w
+ * sin((double)((SplashCoord)0.5 * (angle + angleNext)));
+ kappa = dAngle * bezierCircle2 * w;
+ cx1 = pathIn->pts[j0].x - wdy + kappa * dx;
+ cy1 = pathIn->pts[j0].y + wdx + kappa * dy;
+ cx2 = xc - kappa * dxJoin;
+ cy2 = yc - kappa * dyJoin;
+ cx3 = xc + kappa * dxJoin;
+ cy3 = yc + kappa * dyJoin;
+ cx4 = pathIn->pts[j0].x - wdyNext - kappa * dxNext;
+ cy4 = pathIn->pts[j0].y + wdxNext - kappa * dyNext;
+ pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y);
+ pathOut->lineTo(pathIn->pts[j0].x - wdyNext,
+ pathIn->pts[j0].y + wdxNext);
+ pathOut->curveTo(cx4, cy4, cx3, cy3, xc, yc);
+ pathOut->curveTo(cx2, cy2, cx1, cy1,
+ pathIn->pts[j0].x - wdy,
+ pathIn->pts[j0].y + wdx);
+ }
+ }
+
+ // join angle >= 180
+ } else {
+ angle = atan2((double)-dx, (double)dy);
+ angleNext = atan2((double)-dxNext, (double)dyNext);
+ if (angleNext < angle) {
+ angleNext += 2 * M_PI;
+ }
+ dAngle = (angleNext - angle) / M_PI;
+ if (dAngle < 0.501) {
+ // span angle is <= 90 degrees -> draw a single arc
+ kappa = dAngle * bezierCircle * w;
+ cx1 = pathIn->pts[j0].x + wdy + kappa * dx;
+ cy1 = pathIn->pts[j0].y - wdx + kappa * dy;
+ cx2 = pathIn->pts[j0].x + wdyNext - kappa * dxNext;
+ cy2 = pathIn->pts[j0].y - wdxNext - kappa * dyNext;
+ pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y);
+ pathOut->lineTo(pathIn->pts[j0].x + wdy,
+ pathIn->pts[j0].y - wdx);
+ pathOut->curveTo(cx1, cy1, cx2, cy2,
+ pathIn->pts[j0].x + wdyNext,
+ pathIn->pts[j0].y - wdxNext);
+ } else {
+ // span angle is > 90 degrees -> split into two arcs
+ dJoin = splashDist(wdy, -wdx, wdyNext, -wdxNext);
+ if (dJoin > 0) {
+ dxJoin = (wdyNext - wdy) / dJoin;
+ dyJoin = (-wdxNext + wdx) / dJoin;
+ xc = pathIn->pts[j0].x
+ + (SplashCoord)0.5 * w
+ * cos((double)((SplashCoord)0.5 * (angle + angleNext)));
+ yc = pathIn->pts[j0].y
+ + (SplashCoord)0.5 * w
+ * sin((double)((SplashCoord)0.5 * (angle + angleNext)));
+ kappa = dAngle * bezierCircle2 * w;
+ cx1 = pathIn->pts[j0].x + wdy + kappa * dx;
+ cy1 = pathIn->pts[j0].y - wdx + kappa * dy;
+ cx2 = xc - kappa * dxJoin;
+ cy2 = yc - kappa * dyJoin;
+ cx3 = xc + kappa * dxJoin;
+ cy3 = yc + kappa * dyJoin;
+ cx4 = pathIn->pts[j0].x + wdyNext - kappa * dxNext;
+ cy4 = pathIn->pts[j0].y - wdxNext - kappa * dyNext;
+ pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y);
+ pathOut->lineTo(pathIn->pts[j0].x + wdy,
+ pathIn->pts[j0].y - wdx);
+ pathOut->curveTo(cx1, cy1, cx2, cy2, xc, yc);
+ pathOut->curveTo(cx3, cy3, cx4, cy4,
+ pathIn->pts[j0].x + wdyNext,
+ pathIn->pts[j0].y - wdxNext);
+ }
+ }
+ }
+
+ } else {
+ pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y);
+
+ // join angle < 180
+ if (crossprod < 0) {
+ pathOut->lineTo(pathIn->pts[j0].x - wdyNext,
+ pathIn->pts[j0].y + wdxNext);
+ // miter join inside limit
+ if (lineJoin == splashLineJoinMiter &&
+ splashSqrt(miter) <= state->miterLimit) {
+ pathOut->lineTo(pathIn->pts[j0].x - wdy + wdx * m,
+ pathIn->pts[j0].y + wdx + wdy * m);
+ pathOut->lineTo(pathIn->pts[j0].x - wdy,
+ pathIn->pts[j0].y + wdx);
+ // bevel join or miter join outside limit
+ } else {
+ pathOut->lineTo(pathIn->pts[j0].x - wdy,
+ pathIn->pts[j0].y + wdx);
+ }
+
+ // join angle >= 180
+ } else {
+ pathOut->lineTo(pathIn->pts[j0].x + wdy,
+ pathIn->pts[j0].y - wdx);
+ // miter join inside limit
+ if (lineJoin == splashLineJoinMiter &&
+ splashSqrt(miter) <= state->miterLimit) {
+ pathOut->lineTo(pathIn->pts[j0].x + wdy + wdx * m,
+ pathIn->pts[j0].y - wdx + wdy * m);
+ pathOut->lineTo(pathIn->pts[j0].x + wdyNext,
+ pathIn->pts[j0].y - wdxNext);
+ // bevel join or miter join outside limit
+ } else {
+ pathOut->lineTo(pathIn->pts[j0].x + wdyNext,
+ pathIn->pts[j0].y - wdxNext);
+ }
+ }
+ }
+
+ pathOut->close();
+ }
+
+ // add stroke adjustment hints
+ if (state->strokeAdjust != splashStrokeAdjustOff) {
+
+ // subpath with one segment
+ if (seg == 0 && last) {
+ switch (lineCap) {
+ case splashLineCapButt:
+ pathOut->addStrokeAdjustHint(firstPt, left2 + 1,
+ firstPt, pathOut->length - 1);
+ break;
+ case splashLineCapProjecting:
+ pathOut->addStrokeAdjustHint(firstPt, left2 + 1,
+ firstPt, pathOut->length - 1, gTrue);
+ break;
+ case splashLineCapRound:
+ break;
+ }
+ pathOut->addStrokeAdjustHint(left2, right2,
+ firstPt, pathOut->length - 1);
+ } else {
+
+ // start of subpath
+ if (seg == 1) {
+
+ // start cap
+ if (!closed) {
+ switch (lineCap) {
+ case splashLineCapButt:
+ pathOut->addStrokeAdjustHint(firstPt, left1 + 1,
+ firstPt, firstPt + 1);
+ pathOut->addStrokeAdjustHint(firstPt, left1 + 1,
+ right1 + 1, right1 + 1);
+ break;
+ case splashLineCapProjecting:
+ pathOut->addStrokeAdjustHint(firstPt, left1 + 1,
+ firstPt, firstPt + 1, gTrue);
+ pathOut->addStrokeAdjustHint(firstPt, left1 + 1,
+ right1 + 1, right1 + 1, gTrue);
+ break;
+ case splashLineCapRound:
+ break;
+ }
+ }
+
+ // first segment
+ pathOut->addStrokeAdjustHint(left1, right1, firstPt, left2);
+ pathOut->addStrokeAdjustHint(left1, right1, right2 + 1, right2 + 1);
+ }
+
+ // middle of subpath
+ if (seg > 1) {
+ pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0);
+ pathOut->addStrokeAdjustHint(left1, right1, join0, left2);
+ pathOut->addStrokeAdjustHint(left1, right1, right2 + 1, right2 + 1);
+ }
+
+ // end of subpath
+ if (last) {
+
+ if (closed) {
+ // first segment
+ pathOut->addStrokeAdjustHint(leftFirst, rightFirst,
+ left2 + 1, right2);
+ pathOut->addStrokeAdjustHint(leftFirst, rightFirst,
+ join2, pathOut->length - 1);
+
+ // last segment
+ pathOut->addStrokeAdjustHint(left2, right2,
+ left1 + 1, right1);
+ pathOut->addStrokeAdjustHint(left2, right2,
+ join1, pathOut->length - 1);
+ pathOut->addStrokeAdjustHint(left2, right2,
+ leftFirst - 1, leftFirst);
+ pathOut->addStrokeAdjustHint(left2, right2,
+ rightFirst + 1, rightFirst + 1);
+
+ } else {
+
+ // last segment
+ pathOut->addStrokeAdjustHint(left2, right2,
+ left1 + 1, right1);
+ pathOut->addStrokeAdjustHint(left2, right2,
+ join1, pathOut->length - 1);
+
+ // end cap
+ switch (lineCap) {
+ case splashLineCapButt:
+ pathOut->addStrokeAdjustHint(left2 - 1, left2 + 1,
+ left2 + 1, left2 + 2);
+ break;
+ case splashLineCapProjecting:
+ pathOut->addStrokeAdjustHint(left2 - 1, left2 + 1,
+ left2 + 1, left2 + 2, gTrue);
+ break;
+ case splashLineCapRound:
+ break;
+ }
+ }
+ }
+ }
+
+ left0 = left1;
+ left1 = left2;
+ right0 = right1;
+ right1 = right2;
+ join0 = join1;
+ join1 = join2;
+ if (seg == 0) {
+ leftFirst = left2;
+ rightFirst = right2;
+ }
+ }
+
+ i0 = j0;
+ i1 = j1;
+ ++seg;
+ }
+
+ if (pathIn != path) {
+ delete pathIn;
+ }
+
+ return pathOut;
+}
+
+SplashClipResult Splash::limitRectToClipRect(int *xMin, int *yMin,
+ int *xMax, int *yMax) {
+ int t;
+
+ if ((t = state->clip->getXMinI(state->strokeAdjust)) > *xMin) {
+ *xMin = t;
+ }
+ if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < *xMax) {
+ *xMax = t;
+ }
+ if ((t = state->clip->getYMinI(state->strokeAdjust)) > *yMin) {
+ *yMin = t;
+ }
+ if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < *yMax) {
+ *yMax = t;
+ }
+ if (*xMin >= *xMax || *yMin >= *yMax) {
+ return splashClipAllOutside;
+ }
+ return state->clip->testRect(*xMin, *yMin, *xMax - 1, *yMax - 1,
+ state->strokeAdjust);
+}
+
+void Splash::dumpPath(SplashPath *path) {
+ int i;
+
+ for (i = 0; i < path->length; ++i) {
+ printf(" %3d: x=%8.2f y=%8.2f%s%s%s%s\n",
+ i, (double)path->pts[i].x, (double)path->pts[i].y,
+ (path->flags[i] & splashPathFirst) ? " first" : "",
+ (path->flags[i] & splashPathLast) ? " last" : "",
+ (path->flags[i] & splashPathClosed) ? " closed" : "",
+ (path->flags[i] & splashPathCurve) ? " curve" : "");
+ }
+ if (path->hintsLength == 0) {
+ printf(" no hints\n");
+ } else {
+ for (i = 0; i < path->hintsLength; ++i) {
+ printf(" hint %3d: ctrl0=%d ctrl1=%d pts=%d..%d\n",
+ i, path->hints[i].ctrl0, path->hints[i].ctrl1,
+ path->hints[i].firstPt, path->hints[i].lastPt);
+ }
+ }
+}
+
+void Splash::dumpXPath(SplashXPath *path) {
+ int i;
+
+ for (i = 0; i < path->length; ++i) {
+ printf(" %4d: x0=%8.2f y0=%8.2f x1=%8.2f y1=%8.2f count=%d\n",
+ i, (double)path->segs[i].x0, (double)path->segs[i].y0,
+ (double)path->segs[i].x1, (double)path->segs[i].y1,
+ path->segs[i].count);
+ }
+}
+