diff options
Diffstat (limited to 'Build/source/libs/poppler/poppler-0.36.0/splash/Splash.cc')
-rw-r--r-- | Build/source/libs/poppler/poppler-0.36.0/splash/Splash.cc | 6461 |
1 files changed, 6461 insertions, 0 deletions
diff --git a/Build/source/libs/poppler/poppler-0.36.0/splash/Splash.cc b/Build/source/libs/poppler/poppler-0.36.0/splash/Splash.cc new file mode 100644 index 00000000000..7453822d291 --- /dev/null +++ b/Build/source/libs/poppler/poppler-0.36.0/splash/Splash.cc @@ -0,0 +1,6461 @@ +//======================================================================== +// +// Splash.cc +// +//======================================================================== + +//======================================================================== +// +// Modified under the Poppler project - http://poppler.freedesktop.org +// +// All changes made under the Poppler project to this file are licensed +// under GPL version 2 or later +// +// Copyright (C) 2005-2015 Albert Astals Cid <aacid@kde.org> +// Copyright (C) 2005 Marco Pesenti Gritti <mpg@redhat.com> +// Copyright (C) 2010-2015 Thomas Freitag <Thomas.Freitag@alfa.de> +// Copyright (C) 2010 Christian Feuersänger <cfeuersaenger@googlemail.com> +// Copyright (C) 2011-2013, 2015 William Bader <williambader@hotmail.com> +// Copyright (C) 2012 Markus Trippelsdorf <markus@trippelsdorf.de> +// Copyright (C) 2012 Adrian Johnson <ajohnson@redneon.com> +// Copyright (C) 2012 Matthias Kramm <kramm@quiss.org> +// +// To see a description of the changes please see the Changelog file that +// came with your tarball or type make ChangeLog if you are building from git +// +//======================================================================== + +#include <config.h> + +#ifdef USE_GCC_PRAGMAS +#pragma implementation +#endif + +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <assert.h> +#include <math.h> +#include "goo/gmem.h" +#include "goo/GooLikely.h" +#include "goo/GooList.h" +#include "poppler/Error.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" +#include <algorithm> + +//------------------------------------------------------------------------ + +#define splashAAGamma 1.5 + +// 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 : x; +} + +template<typename T> +inline void Guswap( T&a, T&b ) { T tmp = a; a=b; b=tmp; } + +// The PDF spec says that all pixels whose *centers* lie within the +// image target region get painted, so we want to round n+0.5 down to +// n. But this causes problems, e.g., with PDF files that fill a +// rectangle with black and then draw an image to the exact same +// rectangle, so we instead use the fill scan conversion rule. +// However, the correct rule works better for glyphs, so we also +// provide that option in fillImageMask. +#if 0 +static inline int imgCoordMungeLower(SplashCoord x) { + return splashCeil(x + 0.5) - 1; +} +static inline int imgCoordMungeUpper(SplashCoord x) { + return splashCeil(x + 0.5) - 1; +} +#else +static inline int imgCoordMungeLower(SplashCoord x) { + return splashFloor(x); +} +static inline int imgCoordMungeUpper(SplashCoord x) { + return splashFloor(x) + 1; +} +static inline int imgCoordMungeLowerC(SplashCoord x, GBool glyphMode) { + return glyphMode ? (splashCeil(x + 0.5) - 1) : splashFloor(x); +} +static inline int imgCoordMungeUpperC(SplashCoord x, GBool glyphMode) { + return glyphMode ? (splashCeil(x + 0.5) - 1) : (splashFloor(x) + 1); +} +#endif + +// 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 A + 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 { + // pixel coordinates + int x, y; + + // source pattern + SplashPattern *pattern; + + // source alpha and color + Guchar aInput; + GBool usesShape; + SplashColorPtr cSrc; + SplashColor cSrcVal; + + // non-isolated group alpha0 + Guchar *alpha0Ptr; + + // knockout groups + GBool knockout; + Guchar knockoutOpacity; + + // soft mask + SplashColorPtr softMaskPtr; + + // destination alpha and color + SplashColorPtr destColorPtr; + int destColorMask; + Guchar *destAlphaPtr; + + // shape + Guchar shape; + + // result alpha and color + GBool noTransparency; + SplashPipeResultColorCtrl resultColorCtrl; + + // non-isolated group correction + GBool nonIsolatedGroup; + + // the "run" function + void (Splash::*run)(SplashPipe *pipe); +}; + +SplashPipeResultColorCtrl Splash::pipeResultColorNoAlphaBlend[] = { + splashPipeResultColorNoAlphaBlendMono, + splashPipeResultColorNoAlphaBlendMono, + splashPipeResultColorNoAlphaBlendRGB, + splashPipeResultColorNoAlphaBlendRGB, + splashPipeResultColorNoAlphaBlendRGB +#if SPLASH_CMYK + , + splashPipeResultColorNoAlphaBlendCMYK, + splashPipeResultColorNoAlphaBlendDeviceN +#endif +}; + +SplashPipeResultColorCtrl Splash::pipeResultColorAlphaNoBlend[] = { + splashPipeResultColorAlphaNoBlendMono, + splashPipeResultColorAlphaNoBlendMono, + splashPipeResultColorAlphaNoBlendRGB, + splashPipeResultColorAlphaNoBlendRGB, + splashPipeResultColorAlphaNoBlendRGB +#if SPLASH_CMYK + , + splashPipeResultColorAlphaNoBlendCMYK, + splashPipeResultColorAlphaNoBlendDeviceN +#endif +}; + +SplashPipeResultColorCtrl Splash::pipeResultColorAlphaBlend[] = { + splashPipeResultColorAlphaBlendMono, + splashPipeResultColorAlphaBlendMono, + splashPipeResultColorAlphaBlendRGB, + splashPipeResultColorAlphaBlendRGB, + splashPipeResultColorAlphaBlendRGB +#if SPLASH_CMYK + , + splashPipeResultColorAlphaBlendCMYK, + splashPipeResultColorAlphaBlendDeviceN +#endif +}; + +//------------------------------------------------------------------------ + +static void blendXor(SplashColorPtr src, SplashColorPtr dest, + SplashColorPtr blend, SplashColorMode cm) { + int i; + + for (i = 0; i < splashColorModeNComps[cm]; ++i) { + blend[i] = src[i] ^ dest[i]; + } +} + +//------------------------------------------------------------------------ +// modified region +//------------------------------------------------------------------------ + +void Splash::clearModRegion() { + modXMin = bitmap->getWidth(); + modYMin = bitmap->getHeight(); + 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, int x, int y, + SplashPattern *pattern, SplashColorPtr cSrc, + Guchar aInput, GBool usesShape, + GBool nonIsolatedGroup, + GBool knockout, Guchar knockoutOpacity) { + pipeSetXY(pipe, x, y); + pipe->pattern = NULL; + + // source color + if (pattern) { + if (pattern->isStatic()) { + pattern->getColor(x, y, pipe->cSrcVal); + } else { + pipe->pattern = pattern; + } + pipe->cSrc = pipe->cSrcVal; + } else { + pipe->cSrc = cSrc; + } + + // source alpha + pipe->aInput = aInput; + pipe->usesShape = usesShape; + pipe->shape = 0; + + // knockout + pipe->knockout = knockout; + pipe->knockoutOpacity = knockoutOpacity; + + // result alpha + if (aInput == 255 && !state->softMask && !usesShape && + !state->inNonIsolatedGroup && !nonIsolatedGroup) { + pipe->noTransparency = gTrue; + } else { + pipe->noTransparency = gFalse; + } + + // result color + if (pipe->noTransparency) { + // the !state->blendFunc case is handled separately in pipeRun + pipe->resultColorCtrl = pipeResultColorNoAlphaBlend[bitmap->mode]; + } else if (!state->blendFunc) { + pipe->resultColorCtrl = pipeResultColorAlphaNoBlend[bitmap->mode]; + } else { + pipe->resultColorCtrl = pipeResultColorAlphaBlend[bitmap->mode]; + } + + // non-isolated group correction + pipe->nonIsolatedGroup = nonIsolatedGroup; + + // select the 'run' function + pipe->run = &Splash::pipeRun; + if (!pipe->pattern && pipe->noTransparency && !state->blendFunc) { + if (bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr) { + pipe->run = &Splash::pipeRunSimpleMono1; + } else if (bitmap->mode == splashModeMono8 && pipe->destAlphaPtr) { + pipe->run = &Splash::pipeRunSimpleMono8; + } else if (bitmap->mode == splashModeRGB8 && pipe->destAlphaPtr) { + pipe->run = &Splash::pipeRunSimpleRGB8; + } else if (bitmap->mode == splashModeXBGR8 && pipe->destAlphaPtr) { + pipe->run = &Splash::pipeRunSimpleXBGR8; + } else if (bitmap->mode == splashModeBGR8 && pipe->destAlphaPtr) { + pipe->run = &Splash::pipeRunSimpleBGR8; +#if SPLASH_CMYK + } else if (bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr) { + pipe->run = &Splash::pipeRunSimpleCMYK8; + } else if (bitmap->mode == splashModeDeviceN8 && pipe->destAlphaPtr) { + pipe->run = &Splash::pipeRunSimpleDeviceN8; +#endif + } + } else if (!pipe->pattern && !pipe->noTransparency && !state->softMask && + pipe->usesShape && + !(state->inNonIsolatedGroup && alpha0Bitmap->alpha) && + !state->blendFunc && !pipe->nonIsolatedGroup) { + if (bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr) { + pipe->run = &Splash::pipeRunAAMono1; + } else if (bitmap->mode == splashModeMono8 && pipe->destAlphaPtr) { + pipe->run = &Splash::pipeRunAAMono8; + } else if (bitmap->mode == splashModeRGB8 && pipe->destAlphaPtr) { + pipe->run = &Splash::pipeRunAARGB8; + } else if (bitmap->mode == splashModeXBGR8 && pipe->destAlphaPtr) { + pipe->run = &Splash::pipeRunAAXBGR8; + } else if (bitmap->mode == splashModeBGR8 && pipe->destAlphaPtr) { + pipe->run = &Splash::pipeRunAABGR8; +#if SPLASH_CMYK + } else if (bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr) { + pipe->run = &Splash::pipeRunAACMYK8; + } else if (bitmap->mode == splashModeDeviceN8 && pipe->destAlphaPtr) { + pipe->run = &Splash::pipeRunAADeviceN8; +#endif + } + } +} + +// general case +void Splash::pipeRun(SplashPipe *pipe) { + Guchar aSrc, aDest, alphaI, alphaIm1, alpha0, aResult; + SplashColor cSrcNonIso, cDest, cBlend; + SplashColorPtr cSrc; + Guchar cResult0, cResult1, cResult2, cResult3; + int t; +#if SPLASH_CMYK + int cp, mask; + Guchar cResult[SPOT_NCOMPS+4]; +#endif + + //----- source color + + // static pattern: handled in pipeInit + // fixed color: handled in pipeInit + + // dynamic pattern + if (pipe->pattern) { + if (!pipe->pattern->getColor(pipe->x, pipe->y, pipe->cSrcVal)) { + pipeIncX(pipe); + return; + } +#if SPLASH_CMYK + if (bitmap->mode == splashModeCMYK8 || bitmap->mode == splashModeDeviceN8) { + if (state->fillOverprint && state->overprintMode && pipe->pattern->isCMYK()) { + Guint mask = 15; + if (pipe->cSrcVal[0] == 0) { + mask &= ~1; + } + if (pipe->cSrcVal[1] == 0) { + mask &= ~2; + } + if (pipe->cSrcVal[2] == 0) { + mask &= ~4; + } + if (pipe->cSrcVal[3] == 0) { + mask &= ~8; + } + state->overprintMask = mask; + } + } +#endif + } + + if (pipe->noTransparency && !state->blendFunc) { + + //----- write destination pixel + + switch (bitmap->mode) { + case splashModeMono1: + cResult0 = state->grayTransfer[pipe->cSrc[0]]; + if (state->screen->test(pipe->x, pipe->y, cResult0)) { + *pipe->destColorPtr |= pipe->destColorMask; + } else { + *pipe->destColorPtr &= ~pipe->destColorMask; + } + if (!(pipe->destColorMask >>= 1)) { + pipe->destColorMask = 0x80; + ++pipe->destColorPtr; + } + break; + case splashModeMono8: + *pipe->destColorPtr++ = state->grayTransfer[pipe->cSrc[0]]; + break; + case splashModeRGB8: + *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]]; + *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]]; + *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]]; + break; + case splashModeXBGR8: + *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]]; + *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]]; + *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]]; + *pipe->destColorPtr++ = 255; + break; + case splashModeBGR8: + *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]]; + *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]]; + *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]]; + break; +#if SPLASH_CMYK + case splashModeCMYK8: + if (state->overprintMask & 1) { + pipe->destColorPtr[0] = (state->overprintAdditive) ? + std::min<int>(pipe->destColorPtr[0] + state->cmykTransferC[pipe->cSrc[0]], 255) : + state->cmykTransferC[pipe->cSrc[0]]; + } + if (state->overprintMask & 2) { + pipe->destColorPtr[1] = (state->overprintAdditive) ? + std::min<int>(pipe->destColorPtr[1] + state->cmykTransferM[pipe->cSrc[1]], 255) : + state->cmykTransferM[pipe->cSrc[1]]; + } + if (state->overprintMask & 4) { + pipe->destColorPtr[2] = (state->overprintAdditive) ? + std::min<int>(pipe->destColorPtr[2] + state->cmykTransferY[pipe->cSrc[2]], 255) : + state->cmykTransferY[pipe->cSrc[2]]; + } + if (state->overprintMask & 8) { + pipe->destColorPtr[3] = (state->overprintAdditive) ? + std::min<int>(pipe->destColorPtr[3] + state->cmykTransferK[pipe->cSrc[3]], 255) : + state->cmykTransferK[pipe->cSrc[3]]; + } + pipe->destColorPtr += 4; + break; + case splashModeDeviceN8: + mask = 1; + for (cp = 0; cp < SPOT_NCOMPS + 4; cp ++) { + if (state->overprintMask & mask) { + pipe->destColorPtr[cp] = state->deviceNTransfer[cp][pipe->cSrc[cp]]; + } + mask <<= 1; + } + pipe->destColorPtr += (SPOT_NCOMPS+4); + break; +#endif + } + if (pipe->destAlphaPtr) { + *pipe->destAlphaPtr++ = 255; + } + + } else { + + //----- read destination pixel + + Guchar *destColorPtr; + if (pipe->shape && state->blendFunc && pipe->knockout && alpha0Bitmap != NULL) { + destColorPtr = alpha0Bitmap->data + (alpha0Y+pipe->y)*alpha0Bitmap->rowSize; + switch (bitmap->mode) { + case splashModeMono1: + destColorPtr += (alpha0X+pipe->x) / 8; + break; + case splashModeMono8: + destColorPtr += (alpha0X+pipe->x); + break; + case splashModeRGB8: + case splashModeBGR8: + destColorPtr += (alpha0X+pipe->x) * 3; + break; + case splashModeXBGR8: +#if SPLASH_CMYK + case splashModeCMYK8: +#endif + destColorPtr += (alpha0X+pipe->x) * 4; + break; +#if SPLASH_CMYK + case splashModeDeviceN8: + destColorPtr += (alpha0X+pipe->x) * (SPOT_NCOMPS + 4); + break; +#endif + } + } else { + destColorPtr = pipe->destColorPtr; + } + switch (bitmap->mode) { + case splashModeMono1: + cDest[0] = (*destColorPtr & pipe->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 splashModeXBGR8: + cDest[0] = destColorPtr[2]; + cDest[1] = destColorPtr[1]; + cDest[2] = destColorPtr[0]; + cDest[3] = 255; + 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; + case splashModeDeviceN8: + for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) + cDest[cp] = destColorPtr[cp]; + break; +#endif + } + if (pipe->destAlphaPtr) { + aDest = *pipe->destAlphaPtr; + } else { + aDest = 0xff; + } + + //----- source alpha + + if (state->softMask) { + if (pipe->usesShape) { + aSrc = div255(div255(pipe->aInput * *pipe->softMaskPtr++) * + pipe->shape); + } else { + aSrc = div255(pipe->aInput * *pipe->softMaskPtr++); + } + } else if (pipe->usesShape) { + aSrc = div255(pipe->aInput * pipe->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, pipe->shape is the source (group) alpha. + if (pipe->shape == 0) { + // this value will be multiplied by zero later, so it doesn't + // matter what we use + cSrc = pipe->cSrc; + } else { + t = (aDest * 255) / pipe->shape - aDest; + switch (bitmap->mode) { +#if SPLASH_CMYK + case splashModeDeviceN8: + for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) + cSrcNonIso[cp] = clip255(pipe->cSrc[cp] + + ((pipe->cSrc[cp] - cDest[cp]) * t) / 255); + break; + case splashModeCMYK8: + for (cp = 0; cp < 4; cp++) + cSrcNonIso[cp] = clip255(pipe->cSrc[cp] + + ((pipe->cSrc[cp] - cDest[cp]) * t) / 255); + break; +#endif + case splashModeXBGR8: + cSrcNonIso[3] = 255; + case splashModeRGB8: + case splashModeBGR8: + cSrcNonIso[2] = clip255(pipe->cSrc[2] + + ((pipe->cSrc[2] - cDest[2]) * t) / 255); + cSrcNonIso[1] = clip255(pipe->cSrc[1] + + ((pipe->cSrc[1] - cDest[1]) * t) / 255); + case splashModeMono1: + case splashModeMono8: + cSrcNonIso[0] = clip255(pipe->cSrc[0] + + ((pipe->cSrc[0] - cDest[0]) * t) / 255); + break; + } + cSrc = cSrcNonIso; + // knockout: remove backdrop color + if (pipe->knockout && pipe->shape >= pipe->knockoutOpacity) { + aDest = 0; + } + } + } else { + cSrc = pipe->cSrc; + } + + //----- blend function + + if (state->blendFunc) { +#if SPLASH_CMYK + if (bitmap->mode == splashModeDeviceN8) { + for (int k = 4; k < 4 + SPOT_NCOMPS; k++) { + cBlend[k] = 0; + } + } +#endif + (*state->blendFunc)(cSrc, cDest, cBlend, bitmap->mode); + } + + //----- result alpha and non-isolated group element correction + + if (pipe->noTransparency) { + alphaI = alphaIm1 = aResult = 255; + } else { + aResult = aSrc + aDest - div255(aSrc * aDest); + + // alphaI = alpha_i + // alphaIm1 = alpha_(i-1) + if (pipe->alpha0Ptr) { + alpha0 = *pipe->alpha0Ptr++; + alphaI = aResult + alpha0 - div255(aResult * alpha0); + alphaIm1 = alpha0 + aDest - div255(alpha0 * aDest); + } else { + alphaI = aResult; + alphaIm1 = aDest; + } + } + + //----- result color + + cResult0 = cResult1 = cResult2 = cResult3 = 0; // make gcc happy + + switch (pipe->resultColorCtrl) { + + case splashPipeResultColorNoAlphaBlendMono: + cResult0 = state->grayTransfer[div255((255 - aDest) * cSrc[0] + + aDest * cBlend[0])]; + break; + case splashPipeResultColorNoAlphaBlendRGB: + cResult0 = state->rgbTransferR[div255((255 - aDest) * cSrc[0] + + aDest * cBlend[0])]; + cResult1 = state->rgbTransferG[div255((255 - aDest) * cSrc[1] + + aDest * cBlend[1])]; + cResult2 = state->rgbTransferB[div255((255 - aDest) * cSrc[2] + + aDest * cBlend[2])]; + break; +#if SPLASH_CMYK + case splashPipeResultColorNoAlphaBlendCMYK: + cResult0 = state->cmykTransferC[div255((255 - aDest) * cSrc[0] + + aDest * cBlend[0])]; + cResult1 = state->cmykTransferM[div255((255 - aDest) * cSrc[1] + + aDest * cBlend[1])]; + cResult2 = state->cmykTransferY[div255((255 - aDest) * cSrc[2] + + aDest * cBlend[2])]; + cResult3 = state->cmykTransferK[div255((255 - aDest) * cSrc[3] + + aDest * cBlend[3])]; + break; + case splashPipeResultColorNoAlphaBlendDeviceN: + for (cp = 0; cp < SPOT_NCOMPS+4; cp++) + cResult[cp] = state->deviceNTransfer[cp][div255((255 - aDest) * cSrc[cp] + + aDest * cBlend[cp])]; + break; +#endif + + case splashPipeResultColorAlphaNoBlendMono: + if (alphaI == 0) { + cResult0 = 0; + } else { + cResult0 = state->grayTransfer[((alphaI - aSrc) * cDest[0] + + aSrc * cSrc[0]) / alphaI]; + } + break; + case splashPipeResultColorAlphaNoBlendRGB: + if (alphaI == 0) { + cResult0 = 0; + cResult1 = 0; + cResult2 = 0; + } else { + cResult0 = state->rgbTransferR[((alphaI - aSrc) * cDest[0] + + aSrc * cSrc[0]) / alphaI]; + cResult1 = state->rgbTransferG[((alphaI - aSrc) * cDest[1] + + aSrc * cSrc[1]) / alphaI]; + cResult2 = state->rgbTransferB[((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 = state->cmykTransferC[((alphaI - aSrc) * cDest[0] + + aSrc * cSrc[0]) / alphaI]; + cResult1 = state->cmykTransferM[((alphaI - aSrc) * cDest[1] + + aSrc * cSrc[1]) / alphaI]; + cResult2 = state->cmykTransferY[((alphaI - aSrc) * cDest[2] + + aSrc * cSrc[2]) / alphaI]; + cResult3 = state->cmykTransferK[((alphaI - aSrc) * cDest[3] + + aSrc * cSrc[3]) / alphaI]; + } + break; + case splashPipeResultColorAlphaNoBlendDeviceN: + if (alphaI == 0) { + for (cp = 0; cp < SPOT_NCOMPS+4; cp++) + cResult[cp] = 0; + } else { + for (cp = 0; cp < SPOT_NCOMPS+4; cp++) + cResult[cp] = state->deviceNTransfer[cp][((alphaI - aSrc) * cDest[cp] + + aSrc * cSrc[cp]) / alphaI]; + } + break; +#endif + + case splashPipeResultColorAlphaBlendMono: + if (alphaI == 0) { + cResult0 = 0; + } else { + cResult0 = state->grayTransfer[((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 = state->rgbTransferR[((alphaI - aSrc) * cDest[0] + + aSrc * ((255 - alphaIm1) * cSrc[0] + + alphaIm1 * cBlend[0]) / 255) / + alphaI]; + cResult1 = state->rgbTransferG[((alphaI - aSrc) * cDest[1] + + aSrc * ((255 - alphaIm1) * cSrc[1] + + alphaIm1 * cBlend[1]) / 255) / + alphaI]; + cResult2 = state->rgbTransferB[((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 = state->cmykTransferC[((alphaI - aSrc) * cDest[0] + + aSrc * ((255 - alphaIm1) * cSrc[0] + + alphaIm1 * cBlend[0]) / 255) / + alphaI]; + cResult1 = state->cmykTransferM[((alphaI - aSrc) * cDest[1] + + aSrc * ((255 - alphaIm1) * cSrc[1] + + alphaIm1 * cBlend[1]) / 255) / + alphaI]; + cResult2 = state->cmykTransferY[((alphaI - aSrc) * cDest[2] + + aSrc * ((255 - alphaIm1) * cSrc[2] + + alphaIm1 * cBlend[2]) / 255) / + alphaI]; + cResult3 = state->cmykTransferK[((alphaI - aSrc) * cDest[3] + + aSrc * ((255 - alphaIm1) * cSrc[3] + + alphaIm1 * cBlend[3]) / 255) / + alphaI]; + } + break; + case splashPipeResultColorAlphaBlendDeviceN: + if (alphaI == 0) { + for (cp = 0; cp < SPOT_NCOMPS+4; cp++) + cResult[cp] = 0; + } else { + for (cp = 0; cp < SPOT_NCOMPS+4; cp++) + cResult[cp] = state->deviceNTransfer[cp][((alphaI - aSrc) * cDest[cp] + + aSrc * ((255 - alphaIm1) * cSrc[cp] + + alphaIm1 * cBlend[cp]) / 255) / + alphaI]; + } + break; +#endif + } + + //----- write destination pixel + + switch (bitmap->mode) { + case splashModeMono1: + if (state->screen->test(pipe->x, pipe->y, cResult0)) { + *pipe->destColorPtr |= pipe->destColorMask; + } else { + *pipe->destColorPtr &= ~pipe->destColorMask; + } + if (!(pipe->destColorMask >>= 1)) { + pipe->destColorMask = 0x80; + ++pipe->destColorPtr; + } + break; + case splashModeMono8: + *pipe->destColorPtr++ = cResult0; + break; + case splashModeRGB8: + *pipe->destColorPtr++ = cResult0; + *pipe->destColorPtr++ = cResult1; + *pipe->destColorPtr++ = cResult2; + break; + case splashModeXBGR8: + *pipe->destColorPtr++ = cResult2; + *pipe->destColorPtr++ = cResult1; + *pipe->destColorPtr++ = cResult0; + *pipe->destColorPtr++ = 255; + break; + case splashModeBGR8: + *pipe->destColorPtr++ = cResult2; + *pipe->destColorPtr++ = cResult1; + *pipe->destColorPtr++ = cResult0; + break; +#if SPLASH_CMYK + case splashModeCMYK8: + if (state->overprintMask & 1) { + pipe->destColorPtr[0] = (state->overprintAdditive) ? + std::min<int>(pipe->destColorPtr[0] + cResult0, 255) : + cResult0; + } + if (state->overprintMask & 2) { + pipe->destColorPtr[1] = (state->overprintAdditive) ? + std::min<int>(pipe->destColorPtr[1] + cResult1, 255) : + cResult1; + } + if (state->overprintMask & 4) { + pipe->destColorPtr[2] = (state->overprintAdditive) ? + std::min<int>(pipe->destColorPtr[2] + cResult2, 255) : + cResult2; + } + if (state->overprintMask & 8) { + pipe->destColorPtr[3] = (state->overprintAdditive) ? + std::min<int>(pipe->destColorPtr[3] + cResult3, 255) : + cResult3; + } + pipe->destColorPtr += 4; + break; + case splashModeDeviceN8: + mask = 1; + for (cp = 0; cp < SPOT_NCOMPS+4; cp++) { + if (state->overprintMask & mask) { + pipe->destColorPtr[cp] = cResult[cp]; + } + mask <<=1; + } + pipe->destColorPtr += (SPOT_NCOMPS+4); + break; +#endif + } + if (pipe->destAlphaPtr) { + *pipe->destAlphaPtr++ = aResult; + } + + } + + ++pipe->x; +} + +// special case: +// !pipe->pattern && pipe->noTransparency && !state->blendFunc && +// bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr) { +void Splash::pipeRunSimpleMono1(SplashPipe *pipe) { + Guchar cResult0; + + //----- write destination pixel + cResult0 = state->grayTransfer[pipe->cSrc[0]]; + if (state->screen->test(pipe->x, pipe->y, cResult0)) { + *pipe->destColorPtr |= pipe->destColorMask; + } else { + *pipe->destColorPtr &= ~pipe->destColorMask; + } + if (!(pipe->destColorMask >>= 1)) { + pipe->destColorMask = 0x80; + ++pipe->destColorPtr; + } + + ++pipe->x; +} + +// special case: +// !pipe->pattern && pipe->noTransparency && !state->blendFunc && +// bitmap->mode == splashModeMono8 && pipe->destAlphaPtr) { +void Splash::pipeRunSimpleMono8(SplashPipe *pipe) { + //----- write destination pixel + *pipe->destColorPtr++ = state->grayTransfer[pipe->cSrc[0]]; + *pipe->destAlphaPtr++ = 255; + + ++pipe->x; +} + +// special case: +// !pipe->pattern && pipe->noTransparency && !state->blendFunc && +// bitmap->mode == splashModeRGB8 && pipe->destAlphaPtr) { +void Splash::pipeRunSimpleRGB8(SplashPipe *pipe) { + //----- write destination pixel + *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]]; + *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]]; + *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]]; + *pipe->destAlphaPtr++ = 255; + + ++pipe->x; +} + +// special case: +// !pipe->pattern && pipe->noTransparency && !state->blendFunc && +// bitmap->mode == splashModeXBGR8 && pipe->destAlphaPtr) { +void Splash::pipeRunSimpleXBGR8(SplashPipe *pipe) { + //----- write destination pixel + *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]]; + *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]]; + *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]]; + *pipe->destColorPtr++ = 255; + *pipe->destAlphaPtr++ = 255; + + ++pipe->x; +} + +// special case: +// !pipe->pattern && pipe->noTransparency && !state->blendFunc && +// bitmap->mode == splashModeBGR8 && pipe->destAlphaPtr) { +void Splash::pipeRunSimpleBGR8(SplashPipe *pipe) { + //----- write destination pixel + *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]]; + *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]]; + *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]]; + *pipe->destAlphaPtr++ = 255; + + ++pipe->x; +} + +#if SPLASH_CMYK +// special case: +// !pipe->pattern && pipe->noTransparency && !state->blendFunc && +// bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr) { +void Splash::pipeRunSimpleCMYK8(SplashPipe *pipe) { + //----- write destination pixel + if (state->overprintMask & 1) { + pipe->destColorPtr[0] = (state->overprintAdditive) ? + std::min<int>(pipe->destColorPtr[0] + state->cmykTransferC[pipe->cSrc[0]], 255) : + state->cmykTransferC[pipe->cSrc[0]]; + } + if (state->overprintMask & 2) { + pipe->destColorPtr[1] = (state->overprintAdditive) ? + std::min<int>(pipe->destColorPtr[1] + state->cmykTransferM[pipe->cSrc[1]], 255) : + state->cmykTransferM[pipe->cSrc[1]]; + } + if (state->overprintMask & 4) { + pipe->destColorPtr[2] = (state->overprintAdditive) ? + std::min<int>(pipe->destColorPtr[2] + state->cmykTransferY[pipe->cSrc[2]], 255) : + state->cmykTransferY[pipe->cSrc[2]]; + } + if (state->overprintMask & 8) { + pipe->destColorPtr[3] = (state->overprintAdditive) ? + std::min<int>(pipe->destColorPtr[3] + state->cmykTransferK[pipe->cSrc[3]], 255) : + state->cmykTransferK[pipe->cSrc[3]]; + } + pipe->destColorPtr += 4; + *pipe->destAlphaPtr++ = 255; + + ++pipe->x; +} + +// special case: +// !pipe->pattern && pipe->noTransparency && !state->blendFunc && +// bitmap->mode == splashModeDeviceN8 && pipe->destAlphaPtr) { +void Splash::pipeRunSimpleDeviceN8(SplashPipe *pipe) { + //----- write destination pixel + int mask = 1; + for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) { + if (state->overprintMask & mask) { + pipe->destColorPtr[cp] = state->deviceNTransfer[cp][pipe->cSrc[cp]]; + } + mask <<=1; + } + pipe->destColorPtr += (SPOT_NCOMPS+4); + *pipe->destAlphaPtr++ = 255; + + ++pipe->x; +} +#endif + +// special case: +// !pipe->pattern && !pipe->noTransparency && !state->softMask && +// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && +// !pipe->nonIsolatedGroup && +// bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr +void Splash::pipeRunAAMono1(SplashPipe *pipe) { + Guchar aSrc; + SplashColor cDest; + Guchar cResult0; + + //----- read destination pixel + cDest[0] = (*pipe->destColorPtr & pipe->destColorMask) ? 0xff : 0x00; + + //----- source alpha + aSrc = div255(pipe->aInput * pipe->shape); + + //----- result color + // note: aDest = alpha2 = aResult = 0xff + cResult0 = state->grayTransfer[(Guchar)div255((0xff - aSrc) * cDest[0] + + aSrc * pipe->cSrc[0])]; + + //----- write destination pixel + if (state->screen->test(pipe->x, pipe->y, cResult0)) { + *pipe->destColorPtr |= pipe->destColorMask; + } else { + *pipe->destColorPtr &= ~pipe->destColorMask; + } + if (!(pipe->destColorMask >>= 1)) { + pipe->destColorMask = 0x80; + ++pipe->destColorPtr; + } + + ++pipe->x; +} + +// special case: +// !pipe->pattern && !pipe->noTransparency && !state->softMask && +// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && +// !pipe->nonIsolatedGroup && +// bitmap->mode == splashModeMono8 && pipe->destAlphaPtr +void Splash::pipeRunAAMono8(SplashPipe *pipe) { + Guchar aSrc, aDest, alpha2, aResult; + SplashColor cDest; + Guchar cResult0; + + //----- read destination pixel + cDest[0] = *pipe->destColorPtr; + aDest = *pipe->destAlphaPtr; + + //----- source alpha + aSrc = div255(pipe->aInput * pipe->shape); + + //----- result alpha and non-isolated group element correction + aResult = aSrc + aDest - div255(aSrc * aDest); + alpha2 = aResult; + + //----- result color + if (alpha2 == 0) { + cResult0 = 0; + } else { + cResult0 = state->grayTransfer[(Guchar)(((alpha2 - aSrc) * cDest[0] + + aSrc * pipe->cSrc[0]) / alpha2)]; + } + + //----- write destination pixel + *pipe->destColorPtr++ = cResult0; + *pipe->destAlphaPtr++ = aResult; + + ++pipe->x; +} + +// special case: +// !pipe->pattern && !pipe->noTransparency && !state->softMask && +// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && +// !pipe->nonIsolatedGroup && +// bitmap->mode == splashModeRGB8 && pipe->destAlphaPtr +void Splash::pipeRunAARGB8(SplashPipe *pipe) { + Guchar aSrc, aDest, alpha2, aResult; + SplashColor cDest; + Guchar cResult0, cResult1, cResult2; + + //----- read destination pixel + cDest[0] = pipe->destColorPtr[0]; + cDest[1] = pipe->destColorPtr[1]; + cDest[2] = pipe->destColorPtr[2]; + aDest = *pipe->destAlphaPtr; + + //----- source alpha + aSrc = div255(pipe->aInput * pipe->shape); + + //----- result alpha and non-isolated group element correction + aResult = aSrc + aDest - div255(aSrc * aDest); + alpha2 = aResult; + + //----- result color + if (alpha2 == 0) { + cResult0 = 0; + cResult1 = 0; + cResult2 = 0; + } else { + cResult0 = state->rgbTransferR[(Guchar)(((alpha2 - aSrc) * cDest[0] + + aSrc * pipe->cSrc[0]) / alpha2)]; + cResult1 = state->rgbTransferG[(Guchar)(((alpha2 - aSrc) * cDest[1] + + aSrc * pipe->cSrc[1]) / alpha2)]; + cResult2 = state->rgbTransferB[(Guchar)(((alpha2 - aSrc) * cDest[2] + + aSrc * pipe->cSrc[2]) / alpha2)]; + } + + //----- write destination pixel + *pipe->destColorPtr++ = cResult0; + *pipe->destColorPtr++ = cResult1; + *pipe->destColorPtr++ = cResult2; + *pipe->destAlphaPtr++ = aResult; + + ++pipe->x; +} + +// special case: +// !pipe->pattern && !pipe->noTransparency && !state->softMask && +// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && +// !pipe->nonIsolatedGroup && +// bitmap->mode == splashModeXBGR8 && pipe->destAlphaPtr +void Splash::pipeRunAAXBGR8(SplashPipe *pipe) { + Guchar aSrc, aDest, alpha2, aResult; + SplashColor cDest; + Guchar cResult0, cResult1, cResult2; + + //----- read destination pixel + cDest[0] = pipe->destColorPtr[2]; + cDest[1] = pipe->destColorPtr[1]; + cDest[2] = pipe->destColorPtr[0]; + aDest = *pipe->destAlphaPtr; + + //----- source alpha + aSrc = div255(pipe->aInput * pipe->shape); + + //----- result alpha and non-isolated group element correction + aResult = aSrc + aDest - div255(aSrc * aDest); + alpha2 = aResult; + + //----- result color + if (alpha2 == 0) { + cResult0 = 0; + cResult1 = 0; + cResult2 = 0; + } else { + cResult0 = state->rgbTransferR[(Guchar)(((alpha2 - aSrc) * cDest[0] + + aSrc * pipe->cSrc[0]) / alpha2)]; + cResult1 = state->rgbTransferG[(Guchar)(((alpha2 - aSrc) * cDest[1] + + aSrc * pipe->cSrc[1]) / alpha2)]; + cResult2 = state->rgbTransferB[(Guchar)(((alpha2 - aSrc) * cDest[2] + + aSrc * pipe->cSrc[2]) / alpha2)]; + } + + //----- write destination pixel + *pipe->destColorPtr++ = cResult2; + *pipe->destColorPtr++ = cResult1; + *pipe->destColorPtr++ = cResult0; + *pipe->destColorPtr++ = 255; + *pipe->destAlphaPtr++ = aResult; + + ++pipe->x; +} + +// special case: +// !pipe->pattern && !pipe->noTransparency && !state->softMask && +// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && +// !pipe->nonIsolatedGroup && +// bitmap->mode == splashModeBGR8 && pipe->destAlphaPtr +void Splash::pipeRunAABGR8(SplashPipe *pipe) { + Guchar aSrc, aDest, alpha2, aResult; + SplashColor cDest; + Guchar cResult0, cResult1, cResult2; + + //----- read destination pixel + cDest[0] = pipe->destColorPtr[2]; + cDest[1] = pipe->destColorPtr[1]; + cDest[2] = pipe->destColorPtr[0]; + aDest = *pipe->destAlphaPtr; + + //----- source alpha + aSrc = div255(pipe->aInput * pipe->shape); + + //----- result alpha and non-isolated group element correction + aResult = aSrc + aDest - div255(aSrc * aDest); + alpha2 = aResult; + + //----- result color + if (alpha2 == 0) { + cResult0 = 0; + cResult1 = 0; + cResult2 = 0; + } else { + cResult0 = state->rgbTransferR[(Guchar)(((alpha2 - aSrc) * cDest[0] + + aSrc * pipe->cSrc[0]) / alpha2)]; + cResult1 = state->rgbTransferG[(Guchar)(((alpha2 - aSrc) * cDest[1] + + aSrc * pipe->cSrc[1]) / alpha2)]; + cResult2 = state->rgbTransferB[(Guchar)(((alpha2 - aSrc) * cDest[2] + + aSrc * pipe->cSrc[2]) / alpha2)]; + } + + //----- write destination pixel + *pipe->destColorPtr++ = cResult2; + *pipe->destColorPtr++ = cResult1; + *pipe->destColorPtr++ = cResult0; + *pipe->destAlphaPtr++ = aResult; + + ++pipe->x; +} + +#if SPLASH_CMYK +// special case: +// !pipe->pattern && !pipe->noTransparency && !state->softMask && +// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && +// !pipe->nonIsolatedGroup && +// bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr +void Splash::pipeRunAACMYK8(SplashPipe *pipe) { + Guchar aSrc, aDest, alpha2, aResult; + SplashColor cDest; + Guchar cResult0, cResult1, cResult2, cResult3; + + //----- read destination pixel + cDest[0] = pipe->destColorPtr[0]; + cDest[1] = pipe->destColorPtr[1]; + cDest[2] = pipe->destColorPtr[2]; + cDest[3] = pipe->destColorPtr[3]; + aDest = *pipe->destAlphaPtr; + + //----- source alpha + aSrc = div255(pipe->aInput * pipe->shape); + + //----- result alpha and non-isolated group element correction + aResult = aSrc + aDest - div255(aSrc * aDest); + alpha2 = aResult; + + //----- result color + if (alpha2 == 0) { + cResult0 = 0; + cResult1 = 0; + cResult2 = 0; + cResult3 = 0; + } else { + cResult0 = state->cmykTransferC[(Guchar)(((alpha2 - aSrc) * cDest[0] + + aSrc * pipe->cSrc[0]) / alpha2)]; + cResult1 = state->cmykTransferM[(Guchar)(((alpha2 - aSrc) * cDest[1] + + aSrc * pipe->cSrc[1]) / alpha2)]; + cResult2 = state->cmykTransferY[(Guchar)(((alpha2 - aSrc) * cDest[2] + + aSrc * pipe->cSrc[2]) / alpha2)]; + cResult3 = state->cmykTransferK[(Guchar)(((alpha2 - aSrc) * cDest[3] + + aSrc * pipe->cSrc[3]) / alpha2)]; + } + + //----- write destination pixel + if (state->overprintMask & 1) { + pipe->destColorPtr[0] = (state->overprintAdditive && pipe->shape != 0) ? + std::min<int>(pipe->destColorPtr[0] + cResult0, 255) : + cResult0; + } + if (state->overprintMask & 2) { + pipe->destColorPtr[1] = (state->overprintAdditive && pipe->shape != 0) ? + std::min<int>(pipe->destColorPtr[1] + cResult1, 255) : + cResult1; + } + if (state->overprintMask & 4) { + pipe->destColorPtr[2] = (state->overprintAdditive && pipe->shape != 0) ? + std::min<int>(pipe->destColorPtr[2] + cResult2, 255) : + cResult2; + } + if (state->overprintMask & 8) { + pipe->destColorPtr[3] = (state->overprintAdditive && pipe->shape != 0) ? + std::min<int>(pipe->destColorPtr[3] + cResult3, 255) : + cResult3; + } + pipe->destColorPtr += 4; + *pipe->destAlphaPtr++ = aResult; + + ++pipe->x; +} + +// special case: +// !pipe->pattern && !pipe->noTransparency && !state->softMask && +// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && +// !pipe->nonIsolatedGroup && +// bitmap->mode == splashModeDeviceN8 && pipe->destAlphaPtr +void Splash::pipeRunAADeviceN8(SplashPipe *pipe) { + Guchar aSrc, aDest, alpha2, aResult; + SplashColor cDest; + Guchar cResult[SPOT_NCOMPS+4]; + int cp, mask; + + //----- read destination pixel + for (cp=0; cp < SPOT_NCOMPS+4; cp++) + cDest[cp] = pipe->destColorPtr[cp]; + aDest = *pipe->destAlphaPtr; + + //----- source alpha + aSrc = div255(pipe->aInput * pipe->shape); + + //----- result alpha and non-isolated group element correction + aResult = aSrc + aDest - div255(aSrc * aDest); + alpha2 = aResult; + + //----- result color + if (alpha2 == 0) { + for (cp=0; cp < SPOT_NCOMPS+4; cp++) + cResult[cp] = 0; + } else { + for (cp=0; cp < SPOT_NCOMPS+4; cp++) + cResult[cp] = state->deviceNTransfer[cp][(Guchar)(((alpha2 - aSrc) * cDest[cp] + + aSrc * pipe->cSrc[cp]) / alpha2)]; + } + + //----- write destination pixel + mask = 1; + for (cp=0; cp < SPOT_NCOMPS+4; cp++) { + if (state->overprintMask & mask) { + pipe->destColorPtr[cp] = cResult[cp]; + } + mask <<= 1; + } + pipe->destColorPtr += (SPOT_NCOMPS+4); + *pipe->destAlphaPtr++ = aResult; + + ++pipe->x; +} +#endif + +inline void Splash::pipeSetXY(SplashPipe *pipe, int x, int y) { + pipe->x = x; + pipe->y = y; + if (state->softMask) { + pipe->softMaskPtr = + &state->softMask->data[y * state->softMask->rowSize + x]; + } + switch (bitmap->mode) { + case splashModeMono1: + pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + (x >> 3)]; + pipe->destColorMask = 0x80 >> (x & 7); + break; + case splashModeMono8: + pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + x]; + break; + case splashModeRGB8: + case splashModeBGR8: + pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x]; + break; + case splashModeXBGR8: + pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x]; + break; +#if SPLASH_CMYK + case splashModeCMYK8: + pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x]; + break; + case splashModeDeviceN8: + pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + (SPOT_NCOMPS + 4) * x]; + break; +#endif + } + if (bitmap->alpha) { + pipe->destAlphaPtr = &bitmap->alpha[y * bitmap->width + x]; + } else { + pipe->destAlphaPtr = NULL; + } + if (state->inNonIsolatedGroup && alpha0Bitmap->alpha) { + pipe->alpha0Ptr = + &alpha0Bitmap->alpha[(alpha0Y + y) * alpha0Bitmap->width + + (alpha0X + x)]; + } else { + pipe->alpha0Ptr = NULL; + } +} + +inline void Splash::pipeIncX(SplashPipe *pipe) { + ++pipe->x; + if (state->softMask) { + ++pipe->softMaskPtr; + } + switch (bitmap->mode) { + case splashModeMono1: + if (!(pipe->destColorMask >>= 1)) { + pipe->destColorMask = 0x80; + ++pipe->destColorPtr; + } + break; + case splashModeMono8: + ++pipe->destColorPtr; + break; + case splashModeRGB8: + case splashModeBGR8: + pipe->destColorPtr += 3; + break; + case splashModeXBGR8: + pipe->destColorPtr += 4; + break; +#if SPLASH_CMYK + case splashModeCMYK8: + pipe->destColorPtr += 4; + break; + case splashModeDeviceN8: + pipe->destColorPtr += (SPOT_NCOMPS+4); + break; +#endif + } + if (pipe->destAlphaPtr) { + ++pipe->destAlphaPtr; + } + if (pipe->alpha0Ptr) { + ++pipe->alpha0Ptr; + } +} + +inline void Splash::drawPixel(SplashPipe *pipe, int x, int y, GBool noClip) { + if (unlikely(y < 0)) + return; + + if (noClip || state->clip->test(x, y)) { + pipeSetXY(pipe, x, y); + (this->*pipe->run)(pipe); + updateModX(x); + updateModY(y); + } +} + +inline void Splash::drawAAPixelInit() { + aaBufY = -1; +} + +inline void Splash::drawAAPixel(SplashPipe *pipe, int x, int y) { +#if splashAASize == 4 + static int bitCount4[16] = { 0, 1, 1, 2, 1, 2, 2, 3, + 1, 2, 2, 3, 2, 3, 3, 4 }; + int w; +#else + int xx, yy; +#endif + SplashColorPtr p; + int x0, x1, t; + + if (x < 0 || x >= bitmap->width || + y < state->clip->getYMinI() || y > state->clip->getYMaxI()) { + return; + } + + // update aaBuf + if (y != aaBufY) { + memset(aaBuf->getDataPtr(), 0xff, + aaBuf->getRowSize() * aaBuf->getHeight()); + x0 = 0; + x1 = bitmap->width - 1; + state->clip->clipAALine(aaBuf, &x0, &x1, y); + aaBufY = y; + } + + // compute the shape value +#if splashAASize == 4 + p = aaBuf->getDataPtr() + (x >> 1); + w = aaBuf->getRowSize(); + if (x & 1) { + t = bitCount4[*p & 0x0f] + bitCount4[p[w] & 0x0f] + + bitCount4[p[2*w] & 0x0f] + bitCount4[p[3*w] & 0x0f]; + } else { + t = bitCount4[*p >> 4] + bitCount4[p[w] >> 4] + + bitCount4[p[2*w] >> 4] + bitCount4[p[3*w] >> 4]; + } +#else + t = 0; + for (yy = 0; yy < splashAASize; ++yy) { + for (xx = 0; xx < splashAASize; ++xx) { + p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + + ((x * splashAASize + xx) >> 3); + t += (*p >> (7 - ((x * splashAASize + xx) & 7))) & 1; + } + } +#endif + + // draw the pixel + if (t != 0) { + pipeSetXY(pipe, x, y); + pipe->shape = div255(aaGamma[t] * pipe->shape); + (this->*pipe->run)(pipe); + updateModX(x); + updateModY(y); + } +} + +inline void Splash::drawSpan(SplashPipe *pipe, int x0, int x1, int y, + GBool noClip) { + int x; + + if (noClip) { + pipeSetXY(pipe, x0, y); + for (x = x0; x <= x1; ++x) { + (this->*pipe->run)(pipe); + } + updateModX(x0); + updateModX(x1); + updateModY(y); + } else { + if (x0 < state->clip->getXMinI()) { + x0 = state->clip->getXMinI(); + } + if (x1 > state->clip->getXMaxI()) { + x1 = state->clip->getXMaxI(); + } + pipeSetXY(pipe, x0, y); + for (x = x0; x <= x1; ++x) { + if (state->clip->test(x, y)) { + (this->*pipe->run)(pipe); + updateModX(x); + updateModY(y); + } else { + pipeIncX(pipe); + } + } + } +} + +inline void Splash::drawAALine(SplashPipe *pipe, int x0, int x1, int y, GBool adjustLine, Guchar lineOpacity) { +#if splashAASize == 4 + static int bitCount4[16] = { 0, 1, 1, 2, 1, 2, 2, 3, + 1, 2, 2, 3, 2, 3, 3, 4 }; + SplashColorPtr p0, p1, p2, p3; + int t; +#else + SplashColorPtr p; + int xx, yy, t; +#endif + int x; + +#if splashAASize == 4 + p0 = aaBuf->getDataPtr() + (x0 >> 1); + p1 = p0 + aaBuf->getRowSize(); + p2 = p1 + aaBuf->getRowSize(); + p3 = p2 + aaBuf->getRowSize(); +#endif + pipeSetXY(pipe, x0, y); + for (x = x0; x <= x1; ++x) { + + // compute the shape value +#if splashAASize == 4 + if (x & 1) { + t = bitCount4[*p0 & 0x0f] + bitCount4[*p1 & 0x0f] + + bitCount4[*p2 & 0x0f] + bitCount4[*p3 & 0x0f]; + ++p0; ++p1; ++p2; ++p3; + } else { + t = bitCount4[*p0 >> 4] + bitCount4[*p1 >> 4] + + bitCount4[*p2 >> 4] + bitCount4[*p3 >> 4]; + } +#else + t = 0; + for (yy = 0; yy < splashAASize; ++yy) { + for (xx = 0; xx < splashAASize; ++xx) { + p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + + ((x * splashAASize + xx) >> 3); + t += (*p >> (7 - ((x * splashAASize + xx) & 7))) & 1; + } + } +#endif + + if (t != 0) { + pipe->shape = (adjustLine) ? div255((int) lineOpacity * (double)aaGamma[t]) : (double)aaGamma[t]; + (this->*pipe->run)(pipe); + updateModX(x); + updateModY(y); + } else { + pipeIncX(pipe); + } + } +} + +//------------------------------------------------------------------------ + +// 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]; +} + +//------------------------------------------------------------------------ +// Splash +//------------------------------------------------------------------------ + +Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, + SplashScreenParams *screenParams) { + int i; + + bitmap = bitmapA; + vectorAntialias = vectorAntialiasA; + inShading = gFalse; + state = new SplashState(bitmap->width, bitmap->height, vectorAntialias, + screenParams); + if (vectorAntialias) { + aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize, + 1, splashModeMono1, gFalse); + for (i = 0; i <= splashAASize * splashAASize; ++i) { + aaGamma[i] = (Guchar)splashRound( + splashPow((SplashCoord)i / + (SplashCoord)(splashAASize * splashAASize), + splashAAGamma) * 255); + } + } else { + aaBuf = NULL; + } + minLineWidth = 0; + thinLineMode = splashThinLineDefault; + clearModRegion(); + debugMode = gFalse; + alpha0Bitmap = NULL; +} + +Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, + SplashScreen *screenA) { + int i; + + bitmap = bitmapA; + inShading = gFalse; + vectorAntialias = vectorAntialiasA; + state = new SplashState(bitmap->width, bitmap->height, vectorAntialias, + screenA); + if (vectorAntialias) { + aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize, + 1, splashModeMono1, gFalse); + for (i = 0; i <= splashAASize * splashAASize; ++i) { + aaGamma[i] = (Guchar)splashRound( + splashPow((SplashCoord)i / + (SplashCoord)(splashAASize * splashAASize), + splashAAGamma) * 255); + } + } else { + aaBuf = NULL; + } + minLineWidth = 0; + thinLineMode = splashThinLineDefault; + clearModRegion(); + debugMode = gFalse; + alpha0Bitmap = NULL; +} + +Splash::~Splash() { + while (state->next) { + restoreState(); + } + delete state; + if (vectorAntialias) { + delete aaBuf; + } +} + +//------------------------------------------------------------------------ +// 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; +} + +GBool Splash::getStrokeAdjust() { + return state->strokeAdjust; +} + +SplashClip *Splash::getClip() { + return state->clip; +} + +SplashBitmap *Splash::getSoftMask() { + return state->softMask; +} + +GBool Splash::getInNonIsolatedGroup() { + return state->inNonIsolatedGroup; +} + +//------------------------------------------------------------------------ +// 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::setFillOverprint(GBool fop) { + state->fillOverprint = fop; +} + +void Splash::setStrokeOverprint(GBool gop) { + state->strokeOverprint = gop; +} + +void Splash::setOverprintMode(int opm) { + state->overprintMode = opm; +} + +void Splash::setLineWidth(SplashCoord lineWidth) { + state->lineWidth = lineWidth; +} + +void Splash::setLineCap(int lineCap) { + state->lineCap = lineCap; +} + +void Splash::setLineJoin(int lineJoin) { + state->lineJoin = lineJoin; +} + +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(GBool strokeAdjust) { + state->strokeAdjust = strokeAdjust; +} + +void Splash::clipResetToRect(SplashCoord x0, SplashCoord y0, + SplashCoord x1, SplashCoord y1) { + state->clip->resetToRect(x0, y0, x1, y1); +} + +SplashError Splash::clipToRect(SplashCoord x0, SplashCoord y0, + SplashCoord x1, SplashCoord y1) { + return state->clip->clipToRect(x0, y0, x1, y1); +} + +SplashError Splash::clipToPath(SplashPath *path, GBool eo) { + return state->clip->clipToPath(path, state->matrix, state->flatness, eo); +} + +void Splash::setSoftMask(SplashBitmap *softMask) { + state->setSoftMask(softMask); +} + +void Splash::setInNonIsolatedGroup(SplashBitmap *alpha0BitmapA, + int alpha0XA, int alpha0YA) { + alpha0Bitmap = alpha0BitmapA; + alpha0X = alpha0XA; + alpha0Y = alpha0YA; + state->inNonIsolatedGroup = gTrue; +} + +void Splash::setTransfer(Guchar *red, Guchar *green, Guchar *blue, + Guchar *gray) { + state->setTransfer(red, green, blue, gray); +} + +void Splash::setOverprintMask(Guint overprintMask, GBool additive) { + state->overprintMask = overprintMask; + state->overprintAdditive = additive; +} + +//------------------------------------------------------------------------ +// 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[2]; + *p++ = color[1]; + *p++ = color[0]; + } + row += bitmap->rowSize; + } + } + break; + case splashModeXBGR8: + 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]; + *p++ = 255; + } + 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[0]; + *p++ = color[1]; + *p++ = color[2]; + } + 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; + case splashModeDeviceN8: + row = bitmap->data; + for (y = 0; y < bitmap->height; ++y) { + p = row; + for (x = 0; x < bitmap->width; ++x) { + for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) + *p++ = color[cp]; + } + row += bitmap->rowSize; + } + break; +#endif + } + + if (bitmap->alpha) { + memset(bitmap->alpha, alpha, bitmap->width * bitmap->height); + } + + updateModX(0); + updateModY(0); + updateModX(bitmap->width - 1); + updateModY(bitmap->height - 1); +} + +SplashError Splash::stroke(SplashPath *path) { + SplashPath *path2, *dPath; + SplashCoord d1, d2, t1, t2, w; + + 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); + if (state->lineDashLength > 0) { + dPath = makeDashedPath(path2); + delete path2; + path2 = dPath; + if (path2->length == 0) { + delete path2; + return splashErrEmptyPath; + } + } + + // transform a unit square, and take the half the max of the two + // diagonals; the product of this number and the line width is the + // (approximate) transformed line width + t1 = state->matrix[0] + state->matrix[2]; + t2 = state->matrix[1] + state->matrix[3]; + d1 = t1 * t1 + t2 * t2; + t1 = state->matrix[0] - state->matrix[2]; + t2 = state->matrix[1] - state->matrix[3]; + d2 = t1 * t1 + t2 * t2; + if (d2 > d1) { + d1 = d2; + } + d1 *= 0.5; + if (d1 > 0 && + d1 * state->lineWidth * state->lineWidth < minLineWidth * minLineWidth) { + w = minLineWidth / splashSqrt(d1); + strokeWide(path2, w); + } else if (bitmap->mode == splashModeMono1) { + // this gets close to Adobe's behavior in mono mode + if (d1 * state->lineWidth <= 2) { + strokeNarrow(path2); + } else { + strokeWide(path2, state->lineWidth); + } + } else { + if (state->lineWidth == 0) { + strokeNarrow(path2); + } else { + strokeWide(path2, state->lineWidth); + } + } + + 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); + + pipeInit(&pipe, 0, 0, state->strokePattern, NULL, + (Guchar)splashRound(state->strokeAlpha * 255), + gFalse, 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)) + != splashClipAllOutside) { + if (y0 == y1) { + if (x0 <= x1) { + drawSpan(&pipe, x0, x1, y0, clipRes == splashClipAllInside); + } else { + drawSpan(&pipe, x1, x0, y0, clipRes == splashClipAllInside); + } + } else { + dxdy = seg->dxdy; + if (y0 < state->clip->getYMinI()) { + y0 = state->clip->getYMinI(); + x0 = splashFloor(seg->x0 + ((SplashCoord)y0 - seg->y0) * dxdy); + } + if (y1 > state->clip->getYMaxI()) { + y1 = state->clip->getYMaxI(); + 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) { + drawPixel(&pipe, xa, y, clipRes == splashClipAllInside); + } else { + drawSpan(&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) { + drawPixel(&pipe, xa, y, clipRes == splashClipAllInside); + } else { + drawSpan(&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::strokeWide(SplashPath *path, SplashCoord w) { + SplashPath *path2; + + path2 = makeStrokePath(path, w, 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, lineDashOn, newPath; + int lineDashStartIdx, lineDashIdx; + 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; + i = splashFloor(lineDashStartPhase / lineDashTotal); + lineDashStartPhase -= (SplashCoord)i * lineDashTotal; + lineDashStartOn = gTrue; + lineDashStartIdx = 0; + if (lineDashStartPhase > 0) { + while (lineDashStartIdx < state->lineDashLength && lineDashStartPhase >= state->lineDash[lineDashStartIdx]) { + lineDashStartOn = !lineDashStartOn; + lineDashStartPhase -= state->lineDash[lineDashStartIdx]; + ++lineDashStartIdx; + } + if (unlikely(lineDashStartIdx == state->lineDashLength)) { + return new SplashPath(); + } + } + + 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; + lineDashIdx = lineDashStartIdx; + lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase; + + // 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) { + + if (lineDashDist >= segLen) { + if (lineDashOn) { + if (newPath) { + dPath->moveTo(x0, y0); + newPath = gFalse; + } + 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; + } + dPath->lineTo(xa, ya); + } + x0 = xa; + y0 = ya; + segLen -= lineDashDist; + lineDashDist = 0; + } + + // 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; + } + } + } + i = j + 1; + } + + if (dPath->length == 0) { + GBool allSame = gTrue; + for (int i = 0; allSame && i < path->length - 1; ++i) { + allSame = path->pts[i].x == path->pts[i + 1].x && path->pts[i].y == path->pts[i + 1].y; + } + if (allSame) { + x0 = path->pts[0].x; + y0 = path->pts[0].y; + dPath->moveTo(x0, y0); + dPath->lineTo(x0, y0); + } + } + + 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); +} + +inline void Splash::getBBoxFP(SplashPath *path, SplashCoord *xMinA, SplashCoord *yMinA, + SplashCoord *xMaxA, SplashCoord *yMaxA) { + SplashCoord xMinFP, yMinFP, xMaxFP, yMaxFP, tx, ty; + + // make compiler happy: + xMinFP = xMaxFP = yMinFP = yMaxFP = 0; + for (int i = 0; i < path->length; ++i) { + transform(state->matrix, path->pts[i].x, path->pts[i].y, &tx, &ty); + if (i == 0) { + xMinFP = xMaxFP = tx; + yMinFP = yMaxFP = ty; + } else { + if (tx < xMinFP) xMinFP = tx; + if (tx > xMaxFP) xMaxFP = tx; + if (ty < yMinFP) yMinFP = ty; + if (ty > yMaxFP) yMaxFP = ty; + } + } + + *xMinA = xMinFP; + *yMinA = yMinFP; + *xMaxA = xMaxFP; + *yMaxA = yMaxFP; +} + +SplashError Splash::fillWithPattern(SplashPath *path, GBool eo, + SplashPattern *pattern, + SplashCoord alpha) { + SplashPipe pipe; + SplashXPath *xPath; + SplashXPathScanner *scanner; + int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y; + SplashClipResult clipRes, clipRes2; + GBool adjustLine = gFalse; + int linePosI = 0; + + if (path->length == 0) { + return splashErrEmptyPath; + } + if (pathAllOutside(path)) { + opClipRes = splashClipAllOutside; + return splashOk; + } + + // add stroke adjustment hints for filled rectangles -- this only + // applies to paths that consist of a single subpath + // (this appears to match Acrobat's behavior) + if (state->strokeAdjust && !path->hints) { + int n; + n = path->getLength(); + if (n == 4 && + !(path->flags[0] & splashPathClosed) && + !(path->flags[1] & splashPathLast) && + !(path->flags[2] & splashPathLast)) { + path->close(gTrue); + path->addStrokeAdjustHint(0, 2, 0, 4); + path->addStrokeAdjustHint(1, 3, 0, 4); + } else if (n == 5 && + (path->flags[0] & splashPathClosed) && + !(path->flags[1] & splashPathLast) && + !(path->flags[2] & splashPathLast) && + !(path->flags[3] & splashPathLast)) { + path->addStrokeAdjustHint(0, 2, 0, 4); + path->addStrokeAdjustHint(1, 3, 0, 4); + } + } + + if (thinLineMode != splashThinLineDefault) { + if (state->clip->getXMinI() == state->clip->getXMaxI()) { + linePosI = state->clip->getXMinI(); + adjustLine = gTrue; + } else if (state->clip->getXMinI() == state->clip->getXMaxI() - 1) { + adjustLine = gTrue; + linePosI = splashFloor(state->clip->getXMin() + state->lineWidth); + } else if (state->clip->getYMinI() == state->clip->getYMaxI()) { + linePosI = state->clip->getYMinI(); + adjustLine = gTrue; + } else if (state->clip->getYMinI() == state->clip->getYMaxI() - 1) { + adjustLine = gTrue; + linePosI = splashFloor(state->clip->getYMin() + state->lineWidth); + } + } + + xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue, + adjustLine, linePosI); + if (vectorAntialias && !inShading) { + xPath->aaScale(); + } + xPath->sort(); + yMinI = state->clip->getYMinI(); + yMaxI = state->clip->getYMaxI(); + if (vectorAntialias && !inShading) { + yMinI = yMinI * splashAASize; + yMaxI = (yMaxI + 1) * splashAASize - 1; + } + scanner = new SplashXPathScanner(xPath, eo, yMinI, yMaxI); + + // get the min and max x and y values + if (vectorAntialias && !inShading) { + scanner->getBBoxAA(&xMinI, &yMinI, &xMaxI, &yMaxI); + } else { + scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI); + } + + if (eo && (yMinI == yMaxI || xMinI == xMaxI) && thinLineMode != splashThinLineDefault) { + SplashCoord delta, xMinFP, yMinFP, xMaxFP, yMaxFP; + getBBoxFP(path, &xMinFP, &yMinFP, &xMaxFP, &yMaxFP); + delta = (yMinI == yMaxI) ? yMaxFP - yMinFP : xMaxFP - xMinFP; + if (delta < 0.2) { + opClipRes = splashClipAllOutside; + delete scanner; + delete xPath; + return splashOk; + } + } + + // check clipping + if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI)) + != splashClipAllOutside) { + if (scanner->hasPartialClip()) { + clipRes = splashClipPartial; + } + + pipeInit(&pipe, 0, yMinI, pattern, NULL, (Guchar)splashRound(alpha * 255), + vectorAntialias && !inShading, gFalse); + + // draw the spans + if (vectorAntialias && !inShading) { + for (y = yMinI; y <= yMaxI; ++y) { + scanner->renderAALine(aaBuf, &x0, &x1, y, thinLineMode != splashThinLineDefault && xMinI == xMaxI); + if (clipRes != splashClipAllInside) { + state->clip->clipAALine(aaBuf, &x0, &x1, y, thinLineMode != splashThinLineDefault && xMinI == xMaxI); + } + Guchar lineShape = 255; + GBool adjustLine = gFalse; + if (thinLineMode == splashThinLineShape && (xMinI == xMaxI || yMinI == yMaxI)) { + // compute line shape for thin lines: + SplashCoord mx, my, delta; + transform(state->matrix, 0, 0, &mx, &my); + transform(state->matrix, state->lineWidth, 0, &delta, &my); + adjustLine = gTrue; + lineShape = clip255((delta - mx) * 255); + } + drawAALine(&pipe, x0, x1, y, adjustLine, lineShape); + } + } else { + for (y = yMinI; y <= yMaxI; ++y) { + while (scanner->getNextSpan(y, &x0, &x1)) { + if (clipRes == splashClipAllInside) { + drawSpan(&pipe, x0, x1, y, gTrue); + } else { + // limit the x range + if (x0 < state->clip->getXMinI()) { + x0 = state->clip->getXMinI(); + } + if (x1 > state->clip->getXMaxI()) { + x1 = state->clip->getXMaxI(); + } + clipRes2 = state->clip->testSpan(x0, x1, y); + drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside); + } + } + } + } + } + opClipRes = clipRes; + + delete scanner; + delete xPath; + return splashOk; +} + +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; + } + xMinI = splashFloor(xMin2); + yMinI = splashFloor(yMin2); + xMaxI = splashFloor(xMax2); + yMaxI = splashFloor(yMax2); + + return state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI) == + splashClipAllOutside; +} + +SplashError Splash::xorFill(SplashPath *path, GBool eo) { + SplashPipe pipe; + SplashXPath *xPath; + SplashXPathScanner *scanner; + int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y; + SplashClipResult clipRes, clipRes2; + SplashBlendFunc origBlendFunc; + + if (path->length == 0) { + return splashErrEmptyPath; + } + xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue); + xPath->sort(); + scanner = new SplashXPathScanner(xPath, eo, state->clip->getYMinI(), + state->clip->getYMaxI()); + + // get the min and max x and y values + scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI); + + // check clipping + if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI)) + != splashClipAllOutside) { + if (scanner->hasPartialClip()) { + clipRes = splashClipPartial; + } + + origBlendFunc = state->blendFunc; + state->blendFunc = &blendXor; + pipeInit(&pipe, 0, yMinI, state->fillPattern, NULL, 255, gFalse, gFalse); + + // draw the spans + for (y = yMinI; y <= yMaxI; ++y) { + while (scanner->getNextSpan(y, &x0, &x1)) { + if (clipRes == splashClipAllInside) { + drawSpan(&pipe, x0, x1, y, gTrue); + } else { + // limit the x range + if (x0 < state->clip->getXMinI()) { + x0 = state->clip->getXMinI(); + } + if (x1 > state->clip->getXMaxI()) { + x1 = state->clip->getXMaxI(); + } + clipRes2 = state->clip->testSpan(x0, x1, y); + drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside); + } + } + } + state->blendFunc = origBlendFunc; + } + opClipRes = clipRes; + + delete scanner; + delete xPath; + return splashOk; +} + +SplashError Splash::fillChar(SplashCoord x, SplashCoord y, + int c, SplashFont *font) { + SplashGlyphBitmap glyph; + SplashCoord xt, yt; + int x0, y0, xFrac, yFrac; + SplashClipResult clipRes; + + 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, x0, y0, state->clip, &clipRes)) { + return splashErrNoGlyph; + } + if (clipRes != splashClipAllOutside) { + fillGlyph2(x0, y0, &glyph, clipRes == splashClipAllInside); + } + opClipRes = clipRes; + if (glyph.freeData) { + gfree(glyph.data); + } + return splashOk; +} + +void 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); + SplashClipResult clipRes = state->clip->testRect(x0 - glyph->x, + y0 - glyph->y, + x0 - glyph->x + glyph->w - 1, + y0 - glyph->y + glyph->h - 1); + if (clipRes != splashClipAllOutside) { + fillGlyph2(x0, y0, glyph, clipRes == splashClipAllInside); + } + opClipRes = clipRes; +} + +void Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph, GBool noClip) { + SplashPipe pipe; + int alpha0; + Guchar alpha; + Guchar *p; + int x1, y1, xx, xx1, yy; + + p = glyph->data; + int xStart = x0 - glyph->x; + int yStart = y0 - glyph->y; + int xxLimit = glyph->w; + int yyLimit = glyph->h; + int xShift = 0; + + if (yStart < 0) + { + p += (glyph->aa ? glyph->w : splashCeil(glyph->w / 8.0)) * -yStart; // move p to the beginning of the first painted row + yyLimit += yStart; + yStart = 0; + } + + if (xStart < 0) + { + if (glyph->aa) { + p += -xStart; + } else { + p += (-xStart) / 8; + xShift = (-xStart) % 8; + } + xxLimit += xStart; + xStart = 0; + } + + if (xxLimit + xStart >= bitmap->width) xxLimit = bitmap->width - xStart; + if (yyLimit + yStart >= bitmap->height) yyLimit = bitmap->height - yStart; + + if (noClip) { + if (glyph->aa) { + pipeInit(&pipe, xStart, yStart, + state->fillPattern, NULL, (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); + for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) { + pipeSetXY(&pipe, xStart, y1); + for (xx = 0, x1 = xStart; xx < xxLimit; ++xx, ++x1) { + alpha = p[xx]; + if (alpha != 0) { + pipe.shape = alpha; + (this->*pipe.run)(&pipe); + updateModX(x1); + updateModY(y1); + } else { + pipeIncX(&pipe); + } + } + p += glyph->w; + } + } else { + const int widthEight = splashCeil(glyph->w / 8.0); + + pipeInit(&pipe, xStart, yStart, + state->fillPattern, NULL, (Guchar)splashRound(state->fillAlpha * 255), gFalse, gFalse); + for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) { + pipeSetXY(&pipe, xStart, y1); + for (xx = 0, x1 = xStart; xx < xxLimit; xx += 8) { + alpha0 = (xShift > 0 ? (p[xx / 8] << xShift) | (p[xx / 8 + 1] >> (8 - xShift)) : p[xx / 8]); + for (xx1 = 0; xx1 < 8 && xx + xx1 < xxLimit; ++xx1, ++x1) { + if (alpha0 & 0x80) { + (this->*pipe.run)(&pipe); + updateModX(x1); + updateModY(y1); + } else { + pipeIncX(&pipe); + } + alpha0 <<= 1; + } + } + p += widthEight; + } + } + } else { + if (glyph->aa) { + pipeInit(&pipe, xStart, yStart, + state->fillPattern, NULL, (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); + for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) { + pipeSetXY(&pipe, xStart, y1); + for (xx = 0, x1 = xStart; xx < xxLimit; ++xx, ++x1) { + if (state->clip->test(x1, y1)) { + alpha = p[xx]; + if (alpha != 0) { + pipe.shape = alpha; + (this->*pipe.run)(&pipe); + updateModX(x1); + updateModY(y1); + } else { + pipeIncX(&pipe); + } + } else { + pipeIncX(&pipe); + } + } + p += glyph->w; + } + } else { + const int widthEight = splashCeil(glyph->w / 8.0); + + pipeInit(&pipe, xStart, yStart, + state->fillPattern, NULL, (Guchar)splashRound(state->fillAlpha * 255), gFalse, gFalse); + for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) { + pipeSetXY(&pipe, xStart, y1); + for (xx = 0, x1 = xStart; xx < xxLimit; xx += 8) { + alpha0 = (xShift > 0 ? (p[xx / 8] << xShift) | (p[xx / 8 + 1] >> (8 - xShift)) : p[xx / 8]); + for (xx1 = 0; xx1 < 8 && xx + xx1 < xxLimit; ++xx1, ++x1) { + if (state->clip->test(x1, y1)) { + if (alpha0 & 0x80) { + (this->*pipe.run)(&pipe); + updateModX(x1); + updateModY(y1); + } else { + pipeIncX(&pipe); + } + } else { + pipeIncX(&pipe); + } + alpha0 <<= 1; + } + } + p += widthEight; + } + } + } +} + +SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData, + int w, int h, SplashCoord *mat, + GBool glyphMode) { + SplashBitmap *scaledMask; + SplashClipResult clipRes; + GBool minorAxisZero; + int x0, y0, x1, y1, scaledWidth, scaledHeight; + int yp; + + 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]); + } + + if (w == 0 && h == 0) return splashErrZeroImage; + + // check for singular matrix + if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) { + return splashErrSingularMatrix; + } + + minorAxisZero = mat[1] == 0 && mat[2] == 0; + + // scaling only + if (mat[0] > 0 && minorAxisZero && mat[3] > 0) { + x0 = imgCoordMungeLowerC(mat[4], glyphMode); + y0 = imgCoordMungeLowerC(mat[5], glyphMode); + x1 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode); + y1 = imgCoordMungeUpperC(mat[3] + mat[5], glyphMode); + // make sure narrow images cover at least one pixel + if (x0 == x1) { + ++x1; + } + if (y0 == y1) { + ++y1; + } + clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1); + opClipRes = clipRes; + if (clipRes != splashClipAllOutside) { + scaledWidth = x1 - x0; + scaledHeight = y1 - y0; + yp = h / scaledHeight; + if (yp < 0 || yp > INT_MAX - 1) { + return splashErrBadArg; + } + scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight); + blitMask(scaledMask, x0, y0, clipRes); + delete scaledMask; + } + + // scaling plus vertical flip + } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) { + x0 = imgCoordMungeLowerC(mat[4], glyphMode); + y0 = imgCoordMungeLowerC(mat[3] + mat[5], glyphMode); + x1 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode); + y1 = imgCoordMungeUpperC(mat[5], glyphMode); + // make sure narrow images cover at least one pixel + if (x0 == x1) { + ++x1; + } + if (y0 == y1) { + ++y1; + } + clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1); + opClipRes = clipRes; + if (clipRes != splashClipAllOutside) { + scaledWidth = x1 - x0; + scaledHeight = y1 - y0; + yp = h / scaledHeight; + if (yp < 0 || yp > INT_MAX - 1) { + return splashErrBadArg; + } + scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight); + vertFlipImage(scaledMask, scaledWidth, scaledHeight, 1); + blitMask(scaledMask, x0, y0, clipRes); + delete scaledMask; + } + + // all other cases + } else { + arbitraryTransformMask(src, srcData, w, h, mat, glyphMode); + } + + return splashOk; +} + +void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData, + int srcWidth, int srcHeight, + SplashCoord *mat, GBool glyphMode) { + SplashBitmap *scaledMask; + SplashClipResult clipRes, clipRes2; + SplashPipe pipe; + int scaledWidth, scaledHeight, t0, t1; + SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11; + SplashCoord vx[4], vy[4]; + int xMin, yMin, xMax, yMax; + ImageSection section[3]; + int nSections; + int y, xa, xb, x, i, xx, yy; + + // compute the four vertices of the target quadrilateral + vx[0] = mat[4]; vy[0] = mat[5]; + vx[1] = mat[2] + mat[4]; vy[1] = mat[3] + mat[5]; + vx[2] = mat[0] + mat[2] + mat[4]; vy[2] = mat[1] + mat[3] + mat[5]; + vx[3] = mat[0] + mat[4]; vy[3] = mat[1] + mat[5]; + + // clipping + xMin = imgCoordMungeLowerC(vx[0], glyphMode); + xMax = imgCoordMungeUpperC(vx[0], glyphMode); + yMin = imgCoordMungeLowerC(vy[0], glyphMode); + yMax = imgCoordMungeUpperC(vy[0], glyphMode); + for (i = 1; i < 4; ++i) { + t0 = imgCoordMungeLowerC(vx[i], glyphMode); + if (t0 < xMin) { + xMin = t0; + } + t0 = imgCoordMungeUpperC(vx[i], glyphMode); + if (t0 > xMax) { + xMax = t0; + } + t1 = imgCoordMungeLowerC(vy[i], glyphMode); + if (t1 < yMin) { + yMin = t1; + } + t1 = imgCoordMungeUpperC(vy[i], glyphMode); + if (t1 > yMax) { + yMax = t1; + } + } + clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1); + opClipRes = clipRes; + if (clipRes == splashClipAllOutside) { + return; + } + + // compute the scale factors + if (mat[0] >= 0) { + t0 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode) - + imgCoordMungeLowerC(mat[4], glyphMode); + } else { + t0 = imgCoordMungeUpperC(mat[4], glyphMode) - + imgCoordMungeLowerC(mat[0] + mat[4], glyphMode); + } + if (mat[1] >= 0) { + t1 = imgCoordMungeUpperC(mat[1] + mat[5], glyphMode) - + imgCoordMungeLowerC(mat[5], glyphMode); + } else { + t1 = imgCoordMungeUpperC(mat[5], glyphMode) - + imgCoordMungeLowerC(mat[1] + mat[5], glyphMode); + } + scaledWidth = t0 > t1 ? t0 : t1; + if (mat[2] >= 0) { + t0 = imgCoordMungeUpperC(mat[2] + mat[4], glyphMode) - + imgCoordMungeLowerC(mat[4], glyphMode); + } else { + t0 = imgCoordMungeUpperC(mat[4], glyphMode) - + imgCoordMungeLowerC(mat[2] + mat[4], glyphMode); + } + if (mat[3] >= 0) { + t1 = imgCoordMungeUpperC(mat[3] + mat[5], glyphMode) - + imgCoordMungeLowerC(mat[5], glyphMode); + } else { + t1 = imgCoordMungeUpperC(mat[5], glyphMode) - + imgCoordMungeLowerC(mat[3] + mat[5], glyphMode); + } + scaledHeight = t0 > t1 ? t0 : t1; + if (scaledWidth == 0) { + scaledWidth = 1; + } + if (scaledHeight == 0) { + scaledHeight = 1; + } + + // compute the inverse transform (after scaling) matrix + r00 = mat[0] / scaledWidth; + r01 = mat[1] / scaledWidth; + r10 = mat[2] / scaledHeight; + r11 = mat[3] / scaledHeight; + det = r00 * r11 - r01 * r10; + if (splashAbs(det) < 1e-6) { + // this should be caught by the singular matrix check in fillImageMask + return; + } + ir00 = r11 / det; + ir01 = -r01 / det; + ir10 = -r10 / det; + ir11 = r00 / det; + + // scale the input image + scaledMask = scaleMask(src, srcData, srcWidth, srcHeight, + scaledWidth, scaledHeight); + if (scaledMask->data == NULL) { + error(errInternal, -1, "scaledMask->data is NULL in Splash::arbitraryTransformMask"); + delete scaledMask; + return; + } + + // construct the three sections + i = (vy[2] <= vy[3]) ? 2 : 3; + if (vy[1] <= vy[i]) { + i = 1; + } + if (vy[0] < vy[i] || (i != 3 && vy[0] == vy[i])) { + i = 0; + } + if (vy[i] == vy[(i+1) & 3]) { + section[0].y0 = imgCoordMungeLowerC(vy[i], glyphMode); + section[0].y1 = imgCoordMungeUpperC(vy[(i+2) & 3], glyphMode) - 1; + if (vx[i] < vx[(i+1) & 3]) { + section[0].ia0 = i; + section[0].ia1 = (i+3) & 3; + section[0].ib0 = (i+1) & 3; + section[0].ib1 = (i+2) & 3; + } else { + section[0].ia0 = (i+1) & 3; + section[0].ia1 = (i+2) & 3; + section[0].ib0 = i; + section[0].ib1 = (i+3) & 3; + } + nSections = 1; + } else { + section[0].y0 = imgCoordMungeLowerC(vy[i], glyphMode); + section[2].y1 = imgCoordMungeUpperC(vy[(i+2) & 3], glyphMode) - 1; + section[0].ia0 = section[0].ib0 = i; + section[2].ia1 = section[2].ib1 = (i+2) & 3; + if (vx[(i+1) & 3] < vx[(i+3) & 3]) { + section[0].ia1 = section[2].ia0 = (i+1) & 3; + section[0].ib1 = section[2].ib0 = (i+3) & 3; + } else { + section[0].ia1 = section[2].ia0 = (i+3) & 3; + section[0].ib1 = section[2].ib0 = (i+1) & 3; + } + if (vy[(i+1) & 3] < vy[(i+3) & 3]) { + section[1].y0 = imgCoordMungeLowerC(vy[(i+1) & 3], glyphMode); + section[2].y0 = imgCoordMungeUpperC(vy[(i+3) & 3], glyphMode); + if (vx[(i+1) & 3] < vx[(i+3) & 3]) { + section[1].ia0 = (i+1) & 3; + section[1].ia1 = (i+2) & 3; + section[1].ib0 = i; + section[1].ib1 = (i+3) & 3; + } else { + section[1].ia0 = i; + section[1].ia1 = (i+3) & 3; + section[1].ib0 = (i+1) & 3; + section[1].ib1 = (i+2) & 3; + } + } else { + section[1].y0 = imgCoordMungeLowerC(vy[(i+3) & 3], glyphMode); + section[2].y0 = imgCoordMungeUpperC(vy[(i+1) & 3], glyphMode); + if (vx[(i+1) & 3] < vx[(i+3) & 3]) { + section[1].ia0 = i; + section[1].ia1 = (i+1) & 3; + section[1].ib0 = (i+3) & 3; + section[1].ib1 = (i+2) & 3; + } else { + section[1].ia0 = (i+3) & 3; + section[1].ia1 = (i+2) & 3; + section[1].ib0 = i; + section[1].ib1 = (i+1) & 3; + } + } + section[0].y1 = section[1].y0 - 1; + section[1].y1 = section[2].y0 - 1; + nSections = 3; + } + for (i = 0; i < nSections; ++i) { + section[i].xa0 = vx[section[i].ia0]; + section[i].ya0 = vy[section[i].ia0]; + section[i].xa1 = vx[section[i].ia1]; + section[i].ya1 = vy[section[i].ia1]; + section[i].xb0 = vx[section[i].ib0]; + section[i].yb0 = vy[section[i].ib0]; + section[i].xb1 = vx[section[i].ib1]; + section[i].yb1 = vy[section[i].ib1]; + section[i].dxdya = (section[i].xa1 - section[i].xa0) / + (section[i].ya1 - section[i].ya0); + section[i].dxdyb = (section[i].xb1 - section[i].xb0) / + (section[i].yb1 - section[i].yb0); + } + + // initialize the pixel pipe + pipeInit(&pipe, 0, 0, state->fillPattern, NULL, + (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); + if (vectorAntialias) { + drawAAPixelInit(); + } + + // make sure narrow images cover at least one pixel + if (nSections == 1) { + if (section[0].y0 == section[0].y1) { + ++section[0].y1; + clipRes = opClipRes = splashClipPartial; + } + } else { + if (section[0].y0 == section[2].y1) { + ++section[1].y1; + clipRes = opClipRes = splashClipPartial; + } + } + + // scan all pixels inside the target region + for (i = 0; i < nSections; ++i) { + for (y = section[i].y0; y <= section[i].y1; ++y) { + xa = imgCoordMungeLowerC(section[i].xa0 + + ((SplashCoord)y + 0.5 - section[i].ya0) * + section[i].dxdya, + glyphMode); + xb = imgCoordMungeUpperC(section[i].xb0 + + ((SplashCoord)y + 0.5 - section[i].yb0) * + section[i].dxdyb, + glyphMode); + if (unlikely(xa < 0)) + xa = 0; + // make sure narrow images cover at least one pixel + if (xa == xb) { + ++xb; + } + if (clipRes != splashClipAllInside) { + clipRes2 = state->clip->testSpan(xa, xb - 1, y); + } else { + clipRes2 = clipRes; + } + for (x = xa; x < xb; ++x) { + // map (x+0.5, y+0.5) back to the scaled image + xx = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir00 + + ((SplashCoord)y + 0.5 - mat[5]) * ir10); + yy = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir01 + + ((SplashCoord)y + 0.5 - mat[5]) * ir11); + // xx should always be within bounds, but floating point + // inaccuracy can cause problems + if (xx < 0) { + xx = 0; + } else if (xx >= scaledWidth) { + xx = scaledWidth - 1; + } + if (yy < 0) { + yy = 0; + } else if (yy >= scaledHeight) { + yy = scaledHeight - 1; + } + pipe.shape = scaledMask->data[yy * scaledWidth + xx]; + if (vectorAntialias && clipRes2 != splashClipAllInside) { + drawAAPixel(&pipe, x, y); + } else { + drawPixel(&pipe, x, y, clipRes2 == splashClipAllInside); + } + } + } + } + + delete scaledMask; +} + +// Scale an image mask into a SplashBitmap. +SplashBitmap *Splash::scaleMask(SplashImageMaskSource src, void *srcData, + int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight) { + SplashBitmap *dest; + + dest = new SplashBitmap(scaledWidth, scaledHeight, 1, splashModeMono8, + gFalse); + if (scaledHeight < srcHeight) { + if (scaledWidth < srcWidth) { + scaleMaskYdXd(src, srcData, srcWidth, srcHeight, + scaledWidth, scaledHeight, dest); + } else { + scaleMaskYdXu(src, srcData, srcWidth, srcHeight, + scaledWidth, scaledHeight, dest); + } + } else { + if (scaledWidth < srcWidth) { + scaleMaskYuXd(src, srcData, srcWidth, srcHeight, + scaledWidth, scaledHeight, dest); + } else { + scaleMaskYuXu(src, srcData, srcWidth, srcHeight, + scaledWidth, scaledHeight, dest); + } + } + return dest; +} + +void Splash::scaleMaskYdXd(SplashImageMaskSource src, void *srcData, + int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight, + SplashBitmap *dest) { + Guchar *lineBuf; + Guint *pixBuf; + Guint pix; + Guchar *destPtr; + int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, d, d0, d1; + int i, j; + + // Bresenham parameters for y scale + yp = srcHeight / scaledHeight; + yq = srcHeight % scaledHeight; + + // Bresenham parameters for x scale + xp = srcWidth / scaledWidth; + xq = srcWidth % scaledWidth; + + // allocate buffers + lineBuf = (Guchar *)gmalloc(srcWidth); + pixBuf = (Guint *)gmallocn(srcWidth, sizeof(int)); + + // init y scale Bresenham + yt = 0; + + destPtr = dest->data; + for (y = 0; y < scaledHeight; ++y) { + + // y scale Bresenham + if ((yt += yq) >= scaledHeight) { + yt -= scaledHeight; + yStep = yp + 1; + } else { + yStep = yp; + } + + // read rows from image + memset(pixBuf, 0, srcWidth * sizeof(int)); + for (i = 0; i < yStep; ++i) { + (*src)(srcData, lineBuf); + for (j = 0; j < srcWidth; ++j) { + pixBuf[j] += lineBuf[j]; + } + } + + // init x scale Bresenham + xt = 0; + d0 = (255 << 23) / (yStep * xp); + d1 = (255 << 23) / (yStep * (xp + 1)); + + xx = 0; + for (x = 0; x < scaledWidth; ++x) { + + // x scale Bresenham + if ((xt += xq) >= scaledWidth) { + xt -= scaledWidth; + xStep = xp + 1; + d = d1; + } else { + xStep = xp; + d = d0; + } + + // compute the final pixel + pix = 0; + for (i = 0; i < xStep; ++i) { + pix += pixBuf[xx++]; + } + // (255 * pix) / xStep * yStep + pix = (pix * d) >> 23; + + // store the pixel + *destPtr++ = (Guchar)pix; + } + } + + gfree(pixBuf); + gfree(lineBuf); +} + +void Splash::scaleMaskYdXu(SplashImageMaskSource src, void *srcData, + int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight, + SplashBitmap *dest) { + Guchar *lineBuf; + Guint *pixBuf; + Guint pix; + Guchar *destPtr; + int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, d; + int i, j; + + destPtr = dest->data; + if (destPtr == NULL) { + error(errInternal, -1, "dest->data is NULL in Splash::scaleMaskYdXu"); + return; + } + + // Bresenham parameters for y scale + yp = srcHeight / scaledHeight; + yq = srcHeight % scaledHeight; + + // Bresenham parameters for x scale + xp = scaledWidth / srcWidth; + xq = scaledWidth % srcWidth; + + // allocate buffers + lineBuf = (Guchar *)gmalloc(srcWidth); + pixBuf = (Guint *)gmallocn(srcWidth, sizeof(int)); + + // init y scale Bresenham + yt = 0; + + for (y = 0; y < scaledHeight; ++y) { + + // y scale Bresenham + if ((yt += yq) >= scaledHeight) { + yt -= scaledHeight; + yStep = yp + 1; + } else { + yStep = yp; + } + + // read rows from image + memset(pixBuf, 0, srcWidth * sizeof(int)); + for (i = 0; i < yStep; ++i) { + (*src)(srcData, lineBuf); + for (j = 0; j < srcWidth; ++j) { + pixBuf[j] += lineBuf[j]; + } + } + + // init x scale Bresenham + xt = 0; + d = (255 << 23) / yStep; + + for (x = 0; x < srcWidth; ++x) { + + // x scale Bresenham + if ((xt += xq) >= srcWidth) { + xt -= srcWidth; + xStep = xp + 1; + } else { + xStep = xp; + } + + // compute the final pixel + pix = pixBuf[x]; + // (255 * pix) / yStep + pix = (pix * d) >> 23; + + // store the pixel + for (i = 0; i < xStep; ++i) { + *destPtr++ = (Guchar)pix; + } + } + } + + gfree(pixBuf); + gfree(lineBuf); +} + +void Splash::scaleMaskYuXd(SplashImageMaskSource src, void *srcData, + int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight, + SplashBitmap *dest) { + Guchar *lineBuf; + Guint pix; + Guchar *destPtr0, *destPtr; + int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, d, d0, d1; + int i; + + destPtr0 = dest->data; + if (destPtr0 == NULL) { + error(errInternal, -1, "dest->data is NULL in Splash::scaleMaskYuXd"); + return; + } + + // Bresenham parameters for y scale + yp = scaledHeight / srcHeight; + yq = scaledHeight % srcHeight; + + // Bresenham parameters for x scale + xp = srcWidth / scaledWidth; + xq = srcWidth % scaledWidth; + + // allocate buffers + lineBuf = (Guchar *)gmalloc(srcWidth); + + // init y scale Bresenham + yt = 0; + + for (y = 0; y < srcHeight; ++y) { + + // y scale Bresenham + if ((yt += yq) >= srcHeight) { + yt -= srcHeight; + yStep = yp + 1; + } else { + yStep = yp; + } + + // read row from image + (*src)(srcData, lineBuf); + + // init x scale Bresenham + xt = 0; + d0 = (255 << 23) / xp; + d1 = (255 << 23) / (xp + 1); + + xx = 0; + for (x = 0; x < scaledWidth; ++x) { + + // x scale Bresenham + if ((xt += xq) >= scaledWidth) { + xt -= scaledWidth; + xStep = xp + 1; + d = d1; + } else { + xStep = xp; + d = d0; + } + + // compute the final pixel + pix = 0; + for (i = 0; i < xStep; ++i) { + pix += lineBuf[xx++]; + } + // (255 * pix) / xStep + pix = (pix * d) >> 23; + + // store the pixel + for (i = 0; i < yStep; ++i) { + destPtr = destPtr0 + i * scaledWidth + x; + *destPtr = (Guchar)pix; + } + } + + destPtr0 += yStep * scaledWidth; + } + + gfree(lineBuf); +} + +void Splash::scaleMaskYuXu(SplashImageMaskSource src, void *srcData, + int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight, + SplashBitmap *dest) { + Guchar *lineBuf; + Guint pix; + Guchar *destPtr0, *destPtr; + int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx; + int i, j; + + destPtr0 = dest->data; + if (destPtr0 == NULL) { + error(errInternal, -1, "dest->data is NULL in Splash::scaleMaskYuXu"); + return; + } + + // Bresenham parameters for y scale + yp = scaledHeight / srcHeight; + yq = scaledHeight % srcHeight; + + // Bresenham parameters for x scale + xp = scaledWidth / srcWidth; + xq = scaledWidth % srcWidth; + + // allocate buffers + lineBuf = (Guchar *)gmalloc(srcWidth); + + // init y scale Bresenham + yt = 0; + + for (y = 0; y < srcHeight; ++y) { + + // y scale Bresenham + if ((yt += yq) >= srcHeight) { + yt -= srcHeight; + yStep = yp + 1; + } else { + yStep = yp; + } + + // read row from image + (*src)(srcData, lineBuf); + + // init x scale Bresenham + xt = 0; + + xx = 0; + for (x = 0; x < srcWidth; ++x) { + + // x scale Bresenham + if ((xt += xq) >= srcWidth) { + xt -= srcWidth; + xStep = xp + 1; + } else { + xStep = xp; + } + + // compute the final pixel + pix = lineBuf[x] ? 255 : 0; + + // store the pixel + for (i = 0; i < yStep; ++i) { + for (j = 0; j < xStep; ++j) { + destPtr = destPtr0 + i * scaledWidth + xx + j; + *destPtr++ = (Guchar)pix; + } + } + + xx += xStep; + } + + destPtr0 += yStep * scaledWidth; + } + + gfree(lineBuf); +} + +void Splash::blitMask(SplashBitmap *src, int xDest, int yDest, + SplashClipResult clipRes) { + SplashPipe pipe; + Guchar *p; + int w, h, x, y; + + w = src->getWidth(); + h = src->getHeight(); + p = src->getDataPtr(); + if (p == NULL) { + error(errInternal, -1, "src->getDataPtr() is NULL in Splash::blitMask"); + return; + } + if (vectorAntialias && clipRes != splashClipAllInside) { + pipeInit(&pipe, xDest, yDest, state->fillPattern, NULL, + (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); + drawAAPixelInit(); + for (y = 0; y < h; ++y) { + for (x = 0; x < w; ++x) { + pipe.shape = *p++; + drawAAPixel(&pipe, xDest + x, yDest + y); + } + } + } else { + pipeInit(&pipe, xDest, yDest, state->fillPattern, NULL, + (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); + if (clipRes == splashClipAllInside) { + for (y = 0; y < h; ++y) { + pipeSetXY(&pipe, xDest, yDest + y); + for (x = 0; x < w; ++x) { + if (*p) { + pipe.shape = *p; + (this->*pipe.run)(&pipe); + } else { + pipeIncX(&pipe); + } + ++p; + } + } + updateModX(xDest); + updateModX(xDest + w - 1); + updateModY(yDest); + updateModY(yDest + h - 1); + } else { + for (y = 0; y < h; ++y) { + pipeSetXY(&pipe, xDest, yDest + y); + for (x = 0; x < w; ++x) { + if (*p && state->clip->test(xDest + x, yDest + y)) { + pipe.shape = *p; + (this->*pipe.run)(&pipe); + updateModX(xDest + x); + updateModY(yDest + y); + } else { + pipeIncX(&pipe); + } + ++p; + } + } + } + } +} + +SplashError Splash::drawImage(SplashImageSource src, SplashICCTransform tf, void *srcData, + SplashColorMode srcMode, GBool srcAlpha, + int w, int h, SplashCoord *mat, GBool interpolate, + GBool tilingPattern) { + GBool ok; + SplashBitmap *scaledImg; + SplashClipResult clipRes; + GBool minorAxisZero; + int x0, y0, x1, y1, scaledWidth, scaledHeight; + int nComps; + int yp; + + 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 + ok = gFalse; // make gcc happy + nComps = 0; // make gcc happy + switch (bitmap->mode) { + case splashModeMono1: + case splashModeMono8: + ok = srcMode == splashModeMono8; + nComps = 1; + break; + case splashModeRGB8: + ok = srcMode == splashModeRGB8; + nComps = 3; + break; + case splashModeXBGR8: + ok = srcMode == splashModeXBGR8; + nComps = 4; + break; + case splashModeBGR8: + ok = srcMode == splashModeBGR8; + nComps = 3; + break; +#if SPLASH_CMYK + case splashModeCMYK8: + ok = srcMode == splashModeCMYK8; + nComps = 4; + break; + case splashModeDeviceN8: + ok = srcMode == splashModeDeviceN8; + nComps = SPOT_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; + } + + minorAxisZero = mat[1] == 0 && mat[2] == 0; + + // scaling only + if (mat[0] > 0 && minorAxisZero && mat[3] > 0) { + x0 = imgCoordMungeLower(mat[4]); + y0 = imgCoordMungeLower(mat[5]); + x1 = imgCoordMungeUpper(mat[0] + mat[4]); + y1 = imgCoordMungeUpper(mat[3] + mat[5]); + // make sure narrow images cover at least one pixel + if (x0 == x1) { + ++x1; + } + if (y0 == y1) { + ++y1; + } + clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1); + opClipRes = clipRes; + if (clipRes != splashClipAllOutside) { + scaledWidth = x1 - x0; + scaledHeight = y1 - y0; + yp = h / scaledHeight; + if (yp < 0 || yp > INT_MAX - 1) { + return splashErrBadArg; + } + scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, + scaledWidth, scaledHeight, interpolate, tilingPattern); + if (scaledImg == NULL) { + return splashErrBadArg; + } + if (tf != NULL) { + (*tf)(srcData, scaledImg); + } + blitImage(scaledImg, srcAlpha, x0, y0, clipRes); + delete scaledImg; + } + + // scaling plus vertical flip + } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) { + x0 = imgCoordMungeLower(mat[4]); + y0 = imgCoordMungeLower(mat[3] + mat[5]); + x1 = imgCoordMungeUpper(mat[0] + mat[4]); + y1 = imgCoordMungeUpper(mat[5]); + if (x0 == x1) { + if (mat[4] + mat[0] * 0.5 < x0) { + --x0; + } else { + ++x1; + } + } + if (y0 == y1) { + if (mat[5] + mat[1] * 0.5 < y0) { + --y0; + } else { + ++y1; + } + } + clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1); + opClipRes = clipRes; + if (clipRes != splashClipAllOutside) { + scaledWidth = x1 - x0; + scaledHeight = y1 - y0; + yp = h / scaledHeight; + if (yp < 0 || yp > INT_MAX - 1) { + return splashErrBadArg; + } + scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, + scaledWidth, scaledHeight, interpolate, tilingPattern); + if (scaledImg == NULL) { + return splashErrBadArg; + } + if (tf != NULL) { + (*tf)(srcData, scaledImg); + } + vertFlipImage(scaledImg, scaledWidth, scaledHeight, nComps); + blitImage(scaledImg, srcAlpha, x0, y0, clipRes); + delete scaledImg; + } + + // all other cases + } else { + return arbitraryTransformImage(src, tf, srcData, srcMode, nComps, srcAlpha, + w, h, mat, interpolate, tilingPattern); + } + + return splashOk; +} + +SplashError Splash::arbitraryTransformImage(SplashImageSource src, SplashICCTransform tf, void *srcData, + SplashColorMode srcMode, int nComps, + GBool srcAlpha, + int srcWidth, int srcHeight, + SplashCoord *mat, GBool interpolate, + GBool tilingPattern) { + SplashBitmap *scaledImg; + SplashClipResult clipRes, clipRes2; + SplashPipe pipe; + SplashColor pixel; + int scaledWidth, scaledHeight, t0, t1, th; + SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11; + SplashCoord vx[4], vy[4]; + int xMin, yMin, xMax, yMax; + ImageSection section[3]; + int nSections; + int y, xa, xb, x, i, xx, yy, yp; + + // compute the four vertices of the target quadrilateral + vx[0] = mat[4]; vy[0] = mat[5]; + vx[1] = mat[2] + mat[4]; vy[1] = mat[3] + mat[5]; + vx[2] = mat[0] + mat[2] + mat[4]; vy[2] = mat[1] + mat[3] + mat[5]; + vx[3] = mat[0] + mat[4]; vy[3] = mat[1] + mat[5]; + + // clipping + xMin = imgCoordMungeLower(vx[0]); + xMax = imgCoordMungeUpper(vx[0]); + yMin = imgCoordMungeLower(vy[0]); + yMax = imgCoordMungeUpper(vy[0]); + for (i = 1; i < 4; ++i) { + t0 = imgCoordMungeLower(vx[i]); + if (t0 < xMin) { + xMin = t0; + } + t0 = imgCoordMungeUpper(vx[i]); + if (t0 > xMax) { + xMax = t0; + } + t1 = imgCoordMungeLower(vy[i]); + if (t1 < yMin) { + yMin = t1; + } + t1 = imgCoordMungeUpper(vy[i]); + if (t1 > yMax) { + yMax = t1; + } + } + clipRes = state->clip->testRect(xMin, yMin, xMax, yMax); + opClipRes = clipRes; + if (clipRes == splashClipAllOutside) { + return splashOk; + } + + // compute the scale factors + if (splashAbs(mat[0]) >= splashAbs(mat[1])) { + scaledWidth = xMax - xMin; + scaledHeight = yMax - yMin; + } else { + scaledWidth = yMax - yMin; + scaledHeight = xMax - xMin; + } + if (scaledHeight <= 1 || scaledWidth <= 1 || tilingPattern) { + if (mat[0] >= 0) { + t0 = imgCoordMungeUpper(mat[0] + mat[4]) - imgCoordMungeLower(mat[4]); + } else { + t0 = imgCoordMungeUpper(mat[4]) - imgCoordMungeLower(mat[0] + mat[4]); + } + if (mat[1] >= 0) { + t1 = imgCoordMungeUpper(mat[1] + mat[5]) - imgCoordMungeLower(mat[5]); + } else { + t1 = imgCoordMungeUpper(mat[5]) - imgCoordMungeLower(mat[1] + mat[5]); + } + scaledWidth = t0 > t1 ? t0 : t1; + if (mat[2] >= 0) { + t0 = imgCoordMungeUpper(mat[2] + mat[4]) - imgCoordMungeLower(mat[4]); + if (splashAbs(mat[1]) >= 1) { + th = imgCoordMungeUpper(mat[2]) - imgCoordMungeLower(mat[0] * mat[3] / mat[1]); + if (th > t0) t0 = th; + } + } else { + t0 = imgCoordMungeUpper(mat[4]) - imgCoordMungeLower(mat[2] + mat[4]); + if (splashAbs(mat[1]) >= 1) { + th = imgCoordMungeUpper(mat[0] * mat[3] / mat[1]) - imgCoordMungeLower(mat[2]); + if (th > t0) t0 = th; + } + } + if (mat[3] >= 0) { + t1 = imgCoordMungeUpper(mat[3] + mat[5]) - imgCoordMungeLower(mat[5]); + if (splashAbs(mat[0]) >= 1) { + th = imgCoordMungeUpper(mat[3]) - imgCoordMungeLower(mat[1] * mat[2] / mat[0]); + if (th > t1) t1 = th; + } + } else { + t1 = imgCoordMungeUpper(mat[5]) - imgCoordMungeLower(mat[3] + mat[5]); + if (splashAbs(mat[0]) >= 1) { + th = imgCoordMungeUpper(mat[1] * mat[2] / mat[0]) - imgCoordMungeLower(mat[3]); + if (th > t1) t1 = th; + } + } + scaledHeight = t0 > t1 ? t0 : t1; + } + if (scaledWidth == 0) { + scaledWidth = 1; + } + if (scaledHeight == 0) { + scaledHeight = 1; + } + + // compute the inverse transform (after scaling) matrix + r00 = mat[0] / scaledWidth; + r01 = mat[1] / scaledWidth; + r10 = mat[2] / scaledHeight; + r11 = mat[3] / scaledHeight; + det = r00 * r11 - r01 * r10; + if (splashAbs(det) < 1e-6) { + // this should be caught by the singular matrix check in drawImage + return splashErrBadArg; + } + ir00 = r11 / det; + ir01 = -r01 / det; + ir10 = -r10 / det; + ir11 = r00 / det; + + // scale the input image + yp = srcHeight / scaledHeight; + if (yp < 0 || yp > INT_MAX - 1) { + return splashErrBadArg; + } + scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, + srcWidth, srcHeight, scaledWidth, scaledHeight, interpolate); + + if (scaledImg == NULL) { + return splashErrBadArg; + } + + if (tf != NULL) { + (*tf)(srcData, scaledImg); + } + // construct the three sections + i = 0; + if (vy[1] < vy[i]) { + i = 1; + } + if (vy[2] < vy[i]) { + i = 2; + } + if (vy[3] < vy[i]) { + i = 3; + } + // NB: if using fixed point, 0.000001 will be truncated to zero, + // so these two comparisons must be <=, not < + if (splashAbs(vy[i] - vy[(i-1) & 3]) <= 0.000001 && + vy[(i-1) & 3] < vy[(i+1) & 3]) { + i = (i-1) & 3; + } + if (splashAbs(vy[i] - vy[(i+1) & 3]) <= 0.000001) { + section[0].y0 = imgCoordMungeLower(vy[i]); + section[0].y1 = imgCoordMungeUpper(vy[(i+2) & 3]) - 1; + if (vx[i] < vx[(i+1) & 3]) { + section[0].ia0 = i; + section[0].ia1 = (i+3) & 3; + section[0].ib0 = (i+1) & 3; + section[0].ib1 = (i+2) & 3; + } else { + section[0].ia0 = (i+1) & 3; + section[0].ia1 = (i+2) & 3; + section[0].ib0 = i; + section[0].ib1 = (i+3) & 3; + } + nSections = 1; + } else { + section[0].y0 = imgCoordMungeLower(vy[i]); + section[2].y1 = imgCoordMungeUpper(vy[(i+2) & 3]) - 1; + section[0].ia0 = section[0].ib0 = i; + section[2].ia1 = section[2].ib1 = (i+2) & 3; + if (vx[(i+1) & 3] < vx[(i+3) & 3]) { + section[0].ia1 = section[2].ia0 = (i+1) & 3; + section[0].ib1 = section[2].ib0 = (i+3) & 3; + } else { + section[0].ia1 = section[2].ia0 = (i+3) & 3; + section[0].ib1 = section[2].ib0 = (i+1) & 3; + } + if (vy[(i+1) & 3] < vy[(i+3) & 3]) { + section[1].y0 = imgCoordMungeLower(vy[(i+1) & 3]); + section[2].y0 = imgCoordMungeUpper(vy[(i+3) & 3]); + if (vx[(i+1) & 3] < vx[(i+3) & 3]) { + section[1].ia0 = (i+1) & 3; + section[1].ia1 = (i+2) & 3; + section[1].ib0 = i; + section[1].ib1 = (i+3) & 3; + } else { + section[1].ia0 = i; + section[1].ia1 = (i+3) & 3; + section[1].ib0 = (i+1) & 3; + section[1].ib1 = (i+2) & 3; + } + } else { + section[1].y0 = imgCoordMungeLower(vy[(i+3) & 3]); + section[2].y0 = imgCoordMungeUpper(vy[(i+1) & 3]); + if (vx[(i+1) & 3] < vx[(i+3) & 3]) { + section[1].ia0 = i; + section[1].ia1 = (i+1) & 3; + section[1].ib0 = (i+3) & 3; + section[1].ib1 = (i+2) & 3; + } else { + section[1].ia0 = (i+3) & 3; + section[1].ia1 = (i+2) & 3; + section[1].ib0 = i; + section[1].ib1 = (i+1) & 3; + } + } + section[0].y1 = section[1].y0 - 1; + section[1].y1 = section[2].y0 - 1; + nSections = 3; + } + for (i = 0; i < nSections; ++i) { + section[i].xa0 = vx[section[i].ia0]; + section[i].ya0 = vy[section[i].ia0]; + section[i].xa1 = vx[section[i].ia1]; + section[i].ya1 = vy[section[i].ia1]; + section[i].xb0 = vx[section[i].ib0]; + section[i].yb0 = vy[section[i].ib0]; + section[i].xb1 = vx[section[i].ib1]; + section[i].yb1 = vy[section[i].ib1]; + section[i].dxdya = (section[i].xa1 - section[i].xa0) / + (section[i].ya1 - section[i].ya0); + section[i].dxdyb = (section[i].xb1 - section[i].xb0) / + (section[i].yb1 - section[i].yb0); + } + + // initialize the pixel pipe + pipeInit(&pipe, 0, 0, NULL, pixel, + (Guchar)splashRound(state->fillAlpha * 255), + srcAlpha || (vectorAntialias && clipRes != splashClipAllInside), + gFalse); + if (vectorAntialias) { + drawAAPixelInit(); + } + + // make sure narrow images cover at least one pixel + if (nSections == 1) { + if (section[0].y0 == section[0].y1) { + ++section[0].y1; + clipRes = opClipRes = splashClipPartial; + } + } else { + if (section[0].y0 == section[2].y1) { + ++section[1].y1; + clipRes = opClipRes = splashClipPartial; + } + } + + // scan all pixels inside the target region + for (i = 0; i < nSections; ++i) { + for (y = section[i].y0; y <= section[i].y1; ++y) { + xa = imgCoordMungeLower(section[i].xa0 + + ((SplashCoord)y + 0.5 - section[i].ya0) * + section[i].dxdya); + if (unlikely(xa < 0)) + xa = 0; + xb = imgCoordMungeUpper(section[i].xb0 + + ((SplashCoord)y + 0.5 - section[i].yb0) * + section[i].dxdyb); + // make sure narrow images cover at least one pixel + if (xa == xb) { + ++xb; + } + if (clipRes != splashClipAllInside) { + clipRes2 = state->clip->testSpan(xa, xb - 1, y); + } else { + clipRes2 = clipRes; + } + for (x = xa; x < xb; ++x) { + // map (x+0.5, y+0.5) back to the scaled image + xx = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir00 + + ((SplashCoord)y + 0.5 - mat[5]) * ir10); + yy = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir01 + + ((SplashCoord)y + 0.5 - mat[5]) * ir11); + // xx should always be within bounds, but floating point + // inaccuracy can cause problems + if (xx < 0) { + xx = 0; + } else if (xx >= scaledWidth) { + xx = scaledWidth - 1; + } + if (yy < 0) { + yy = 0; + } else if (yy >= scaledHeight) { + yy = scaledHeight - 1; + } + scaledImg->getPixel(xx, yy, pixel); + if (srcAlpha) { + pipe.shape = scaledImg->alpha[yy * scaledWidth + xx]; + } else { + pipe.shape = 255; + } + if (vectorAntialias && clipRes2 != splashClipAllInside) { + drawAAPixel(&pipe, x, y); + } else { + drawPixel(&pipe, x, y, clipRes2 == splashClipAllInside); + } + } + } + } + + delete scaledImg; + return splashOk; +} + +// determine if a scaled image requires interpolation based on the scale and +// the interpolate flag from the image dictionary +static GBool isImageInterpolationRequired(int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight, + GBool interpolate) { + if (interpolate) + return gTrue; + + /* When scale factor is >= 400% we don't interpolate. See bugs #25268, #9860 */ + if (scaledWidth / srcWidth >= 4 || scaledHeight / srcHeight >= 4) + return gFalse; + + return gTrue; +} + +// Scale an image into a SplashBitmap. +SplashBitmap *Splash::scaleImage(SplashImageSource src, void *srcData, + SplashColorMode srcMode, int nComps, + GBool srcAlpha, int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight, GBool interpolate, GBool tilingPattern) { + SplashBitmap *dest; + + dest = new SplashBitmap(scaledWidth, scaledHeight, 1, srcMode, srcAlpha, gTrue, bitmap->getSeparationList()); + if (dest->getDataPtr() != NULL) { + if (scaledHeight < srcHeight) { + if (scaledWidth < srcWidth) { + scaleImageYdXd(src, srcData, srcMode, nComps, srcAlpha, + srcWidth, srcHeight, scaledWidth, scaledHeight, dest); + } else { + scaleImageYdXu(src, srcData, srcMode, nComps, srcAlpha, + srcWidth, srcHeight, scaledWidth, scaledHeight, dest); + } + } else { + if (scaledWidth < srcWidth) { + scaleImageYuXd(src, srcData, srcMode, nComps, srcAlpha, + srcWidth, srcHeight, scaledWidth, scaledHeight, dest); + } else { + if (!tilingPattern && isImageInterpolationRequired(srcWidth, srcHeight, scaledWidth, scaledHeight, interpolate)) { + scaleImageYuXuBilinear(src, srcData, srcMode, nComps, srcAlpha, + srcWidth, srcHeight, scaledWidth, scaledHeight, dest); + } else { + scaleImageYuXu(src, srcData, srcMode, nComps, srcAlpha, + srcWidth, srcHeight, scaledWidth, scaledHeight, dest); + } + } + } + } else { + delete dest; + dest = NULL; + } + return dest; +} + +void Splash::scaleImageYdXd(SplashImageSource src, void *srcData, + SplashColorMode srcMode, int nComps, + GBool srcAlpha, int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight, + SplashBitmap *dest) { + Guchar *lineBuf, *alphaLineBuf; + Guint *pixBuf, *alphaPixBuf; + Guint pix0, pix1, pix2; +#if SPLASH_CMYK + Guint pix3; + Guint pix[SPOT_NCOMPS+4], cp; +#endif + Guint alpha; + Guchar *destPtr, *destAlphaPtr; + int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, xxa, d, d0, d1; + int i, j; + + // Bresenham parameters for y scale + yp = srcHeight / scaledHeight; + yq = srcHeight % scaledHeight; + + // Bresenham parameters for x scale + xp = srcWidth / scaledWidth; + xq = srcWidth % scaledWidth; + + // allocate buffers + lineBuf = (Guchar *)gmallocn(srcWidth, nComps); + pixBuf = (Guint *)gmallocn(srcWidth, nComps * sizeof(int)); + if (srcAlpha) { + alphaLineBuf = (Guchar *)gmalloc(srcWidth); + alphaPixBuf = (Guint *)gmallocn(srcWidth, sizeof(int)); + } else { + alphaLineBuf = NULL; + alphaPixBuf = NULL; + } + + // init y scale Bresenham + yt = 0; + + destPtr = dest->data; + destAlphaPtr = dest->alpha; + for (y = 0; y < scaledHeight; ++y) { + + // y scale Bresenham + if ((yt += yq) >= scaledHeight) { + yt -= scaledHeight; + yStep = yp + 1; + } else { + yStep = yp; + } + + // read rows from image + memset(pixBuf, 0, srcWidth * nComps * sizeof(int)); + if (srcAlpha) { + memset(alphaPixBuf, 0, srcWidth * sizeof(int)); + } + for (i = 0; i < yStep; ++i) { + (*src)(srcData, lineBuf, alphaLineBuf); + for (j = 0; j < srcWidth * nComps; ++j) { + pixBuf[j] += lineBuf[j]; + } + if (srcAlpha) { + for (j = 0; j < srcWidth; ++j) { + alphaPixBuf[j] += alphaLineBuf[j]; + } + } + } + + // init x scale Bresenham + xt = 0; + d0 = (1 << 23) / (yStep * xp); + d1 = (1 << 23) / (yStep * (xp + 1)); + + xx = xxa = 0; + for (x = 0; x < scaledWidth; ++x) { + + // x scale Bresenham + if ((xt += xq) >= scaledWidth) { + xt -= scaledWidth; + xStep = xp + 1; + d = d1; + } else { + xStep = xp; + d = d0; + } + + switch (srcMode) { + + case splashModeMono8: + + // compute the final pixel + pix0 = 0; + for (i = 0; i < xStep; ++i) { + pix0 += pixBuf[xx++]; + } + // pix / xStep * yStep + pix0 = (pix0 * d) >> 23; + + // store the pixel + *destPtr++ = (Guchar)pix0; + break; + + case splashModeRGB8: + + // compute the final pixel + pix0 = pix1 = pix2 = 0; + for (i = 0; i < xStep; ++i) { + pix0 += pixBuf[xx]; + pix1 += pixBuf[xx+1]; + pix2 += pixBuf[xx+2]; + xx += 3; + } + // pix / xStep * yStep + pix0 = (pix0 * d) >> 23; + pix1 = (pix1 * d) >> 23; + pix2 = (pix2 * d) >> 23; + + // store the pixel + *destPtr++ = (Guchar)pix0; + *destPtr++ = (Guchar)pix1; + *destPtr++ = (Guchar)pix2; + break; + + case splashModeXBGR8: + + // compute the final pixel + pix0 = pix1 = pix2 = 0; + for (i = 0; i < xStep; ++i) { + pix0 += pixBuf[xx]; + pix1 += pixBuf[xx+1]; + pix2 += pixBuf[xx+2]; + xx += 4; + } + // pix / xStep * yStep + pix0 = (pix0 * d) >> 23; + pix1 = (pix1 * d) >> 23; + pix2 = (pix2 * d) >> 23; + + // store the pixel + *destPtr++ = (Guchar)pix2; + *destPtr++ = (Guchar)pix1; + *destPtr++ = (Guchar)pix0; + *destPtr++ = (Guchar)255; + break; + + case splashModeBGR8: + + // compute the final pixel + pix0 = pix1 = pix2 = 0; + for (i = 0; i < xStep; ++i) { + pix0 += pixBuf[xx]; + pix1 += pixBuf[xx+1]; + pix2 += pixBuf[xx+2]; + xx += 3; + } + // pix / xStep * yStep + pix0 = (pix0 * d) >> 23; + pix1 = (pix1 * d) >> 23; + pix2 = (pix2 * d) >> 23; + + // store the pixel + *destPtr++ = (Guchar)pix2; + *destPtr++ = (Guchar)pix1; + *destPtr++ = (Guchar)pix0; + break; + +#if SPLASH_CMYK + case splashModeCMYK8: + + // compute the final pixel + pix0 = pix1 = pix2 = pix3 = 0; + for (i = 0; i < xStep; ++i) { + pix0 += pixBuf[xx]; + pix1 += pixBuf[xx+1]; + pix2 += pixBuf[xx+2]; + pix3 += pixBuf[xx+3]; + xx += 4; + } + // pix / xStep * yStep + pix0 = (pix0 * d) >> 23; + pix1 = (pix1 * d) >> 23; + pix2 = (pix2 * d) >> 23; + pix3 = (pix3 * d) >> 23; + + // store the pixel + *destPtr++ = (Guchar)pix0; + *destPtr++ = (Guchar)pix1; + *destPtr++ = (Guchar)pix2; + *destPtr++ = (Guchar)pix3; + break; + case splashModeDeviceN8: + + // compute the final pixel + for (cp = 0; cp < SPOT_NCOMPS+4; cp++) + pix[cp] = 0; + for (i = 0; i < xStep; ++i) { + for (cp = 0; cp < SPOT_NCOMPS+4; cp++) { + pix[cp] += pixBuf[xx + cp]; + } + xx += (SPOT_NCOMPS+4); + } + // pix / xStep * yStep + for (cp = 0; cp < SPOT_NCOMPS+4; cp++) + pix[cp] = (pix[cp] * d) >> 23; + + // store the pixel + for (cp = 0; cp < SPOT_NCOMPS+4; cp++) + *destPtr++ = (Guchar)pix[cp]; + break; +#endif + + + case splashModeMono1: // mono1 is not allowed + default: + break; + } + + // process alpha + if (srcAlpha) { + alpha = 0; + for (i = 0; i < xStep; ++i, ++xxa) { + alpha += alphaPixBuf[xxa]; + } + // alpha / xStep * yStep + alpha = (alpha * d) >> 23; + *destAlphaPtr++ = (Guchar)alpha; + } + } + } + + gfree(alphaPixBuf); + gfree(alphaLineBuf); + gfree(pixBuf); + gfree(lineBuf); +} + +void Splash::scaleImageYdXu(SplashImageSource src, void *srcData, + SplashColorMode srcMode, int nComps, + GBool srcAlpha, int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight, + SplashBitmap *dest) { + Guchar *lineBuf, *alphaLineBuf; + Guint *pixBuf, *alphaPixBuf; + Guint pix[splashMaxColorComps]; + Guint alpha; + Guchar *destPtr, *destAlphaPtr; + int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, d; + int i, j; + + // Bresenham parameters for y scale + yp = srcHeight / scaledHeight; + yq = srcHeight % scaledHeight; + + // Bresenham parameters for x scale + xp = scaledWidth / srcWidth; + xq = scaledWidth % srcWidth; + + // allocate buffers + lineBuf = (Guchar *)gmallocn(srcWidth, nComps); + pixBuf = (Guint *)gmallocn(srcWidth, nComps * sizeof(int)); + if (srcAlpha) { + alphaLineBuf = (Guchar *)gmalloc(srcWidth); + alphaPixBuf = (Guint *)gmallocn(srcWidth, sizeof(int)); + } else { + alphaLineBuf = NULL; + alphaPixBuf = NULL; + } + + // init y scale Bresenham + yt = 0; + + destPtr = dest->data; + destAlphaPtr = dest->alpha; + for (y = 0; y < scaledHeight; ++y) { + + // y scale Bresenham + if ((yt += yq) >= scaledHeight) { + yt -= scaledHeight; + yStep = yp + 1; + } else { + yStep = yp; + } + + // read rows from image + memset(pixBuf, 0, srcWidth * nComps * sizeof(int)); + if (srcAlpha) { + memset(alphaPixBuf, 0, srcWidth * sizeof(int)); + } + for (i = 0; i < yStep; ++i) { + (*src)(srcData, lineBuf, alphaLineBuf); + for (j = 0; j < srcWidth * nComps; ++j) { + pixBuf[j] += lineBuf[j]; + } + if (srcAlpha) { + for (j = 0; j < srcWidth; ++j) { + alphaPixBuf[j] += alphaLineBuf[j]; + } + } + } + + // init x scale Bresenham + xt = 0; + d = (1 << 23) / yStep; + + for (x = 0; x < srcWidth; ++x) { + + // x scale Bresenham + if ((xt += xq) >= srcWidth) { + xt -= srcWidth; + xStep = xp + 1; + } else { + xStep = xp; + } + + // compute the final pixel + for (i = 0; i < nComps; ++i) { + // pixBuf[] / yStep + pix[i] = (pixBuf[x * nComps + i] * d) >> 23; + } + + // store the pixel + switch (srcMode) { + case splashModeMono1: // mono1 is not allowed + break; + case splashModeMono8: + for (i = 0; i < xStep; ++i) { + *destPtr++ = (Guchar)pix[0]; + } + break; + case splashModeRGB8: + for (i = 0; i < xStep; ++i) { + *destPtr++ = (Guchar)pix[0]; + *destPtr++ = (Guchar)pix[1]; + *destPtr++ = (Guchar)pix[2]; + } + break; + case splashModeXBGR8: + for (i = 0; i < xStep; ++i) { + *destPtr++ = (Guchar)pix[2]; + *destPtr++ = (Guchar)pix[1]; + *destPtr++ = (Guchar)pix[0]; + *destPtr++ = (Guchar)255; + } + break; + case splashModeBGR8: + for (i = 0; i < xStep; ++i) { + *destPtr++ = (Guchar)pix[2]; + *destPtr++ = (Guchar)pix[1]; + *destPtr++ = (Guchar)pix[0]; + } + break; +#if SPLASH_CMYK + case splashModeCMYK8: + for (i = 0; i < xStep; ++i) { + *destPtr++ = (Guchar)pix[0]; + *destPtr++ = (Guchar)pix[1]; + *destPtr++ = (Guchar)pix[2]; + *destPtr++ = (Guchar)pix[3]; + } + break; + case splashModeDeviceN8: + for (i = 0; i < xStep; ++i) { + for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) + *destPtr++ = (Guchar)pix[cp]; + } + break; +#endif + } + + // process alpha + if (srcAlpha) { + // alphaPixBuf[] / yStep + alpha = (alphaPixBuf[x] * d) >> 23; + for (i = 0; i < xStep; ++i) { + *destAlphaPtr++ = (Guchar)alpha; + } + } + } + } + + gfree(alphaPixBuf); + gfree(alphaLineBuf); + gfree(pixBuf); + gfree(lineBuf); +} + +void Splash::scaleImageYuXd(SplashImageSource src, void *srcData, + SplashColorMode srcMode, int nComps, + GBool srcAlpha, int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight, + SplashBitmap *dest) { + Guchar *lineBuf, *alphaLineBuf; + Guint pix[splashMaxColorComps]; + Guint alpha; + Guchar *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr; + int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, xxa, d, d0, d1; + int i, j; + + // Bresenham parameters for y scale + yp = scaledHeight / srcHeight; + yq = scaledHeight % srcHeight; + + // Bresenham parameters for x scale + xp = srcWidth / scaledWidth; + xq = srcWidth % scaledWidth; + + // allocate buffers + lineBuf = (Guchar *)gmallocn_checkoverflow(srcWidth, nComps); + if (unlikely(!lineBuf)) + return; + if (srcAlpha) { + alphaLineBuf = (Guchar *)gmalloc(srcWidth); + } else { + alphaLineBuf = NULL; + } + + // init y scale Bresenham + yt = 0; + + destPtr0 = dest->data; + destAlphaPtr0 = dest->alpha; + for (y = 0; y < srcHeight; ++y) { + + // y scale Bresenham + if ((yt += yq) >= srcHeight) { + yt -= srcHeight; + yStep = yp + 1; + } else { + yStep = yp; + } + + // read row from image + (*src)(srcData, lineBuf, alphaLineBuf); + + // init x scale Bresenham + xt = 0; + d0 = (1 << 23) / xp; + d1 = (1 << 23) / (xp + 1); + + xx = xxa = 0; + for (x = 0; x < scaledWidth; ++x) { + + // x scale Bresenham + if ((xt += xq) >= scaledWidth) { + xt -= scaledWidth; + xStep = xp + 1; + d = d1; + } else { + xStep = xp; + d = d0; + } + + // compute the final pixel + for (i = 0; i < nComps; ++i) { + pix[i] = 0; + } + for (i = 0; i < xStep; ++i) { + for (j = 0; j < nComps; ++j, ++xx) { + pix[j] += lineBuf[xx]; + } + } + for (i = 0; i < nComps; ++i) { + // pix[] / xStep + pix[i] = (pix[i] * d) >> 23; + } + + // store the pixel + switch (srcMode) { + case splashModeMono1: // mono1 is not allowed + break; + case splashModeMono8: + for (i = 0; i < yStep; ++i) { + destPtr = destPtr0 + (i * scaledWidth + x) * nComps; + *destPtr++ = (Guchar)pix[0]; + } + break; + case splashModeRGB8: + for (i = 0; i < yStep; ++i) { + destPtr = destPtr0 + (i * scaledWidth + x) * nComps; + *destPtr++ = (Guchar)pix[0]; + *destPtr++ = (Guchar)pix[1]; + *destPtr++ = (Guchar)pix[2]; + } + break; + case splashModeXBGR8: + for (i = 0; i < yStep; ++i) { + destPtr = destPtr0 + (i * scaledWidth + x) * nComps; + *destPtr++ = (Guchar)pix[2]; + *destPtr++ = (Guchar)pix[1]; + *destPtr++ = (Guchar)pix[0]; + *destPtr++ = (Guchar)255; + } + break; + case splashModeBGR8: + for (i = 0; i < yStep; ++i) { + destPtr = destPtr0 + (i * scaledWidth + x) * nComps; + *destPtr++ = (Guchar)pix[2]; + *destPtr++ = (Guchar)pix[1]; + *destPtr++ = (Guchar)pix[0]; + } + break; +#if SPLASH_CMYK + case splashModeCMYK8: + for (i = 0; i < yStep; ++i) { + destPtr = destPtr0 + (i * scaledWidth + x) * nComps; + *destPtr++ = (Guchar)pix[0]; + *destPtr++ = (Guchar)pix[1]; + *destPtr++ = (Guchar)pix[2]; + *destPtr++ = (Guchar)pix[3]; + } + break; + case splashModeDeviceN8: + for (i = 0; i < yStep; ++i) { + destPtr = destPtr0 + (i * scaledWidth + x) * nComps; + for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) + *destPtr++ = (Guchar)pix[cp]; + } + break; +#endif + } + + // process alpha + if (srcAlpha) { + alpha = 0; + for (i = 0; i < xStep; ++i, ++xxa) { + alpha += alphaLineBuf[xxa]; + } + // alpha / xStep + alpha = (alpha * d) >> 23; + for (i = 0; i < yStep; ++i) { + destAlphaPtr = destAlphaPtr0 + i * scaledWidth + x; + *destAlphaPtr = (Guchar)alpha; + } + } + } + + destPtr0 += yStep * scaledWidth * nComps; + if (srcAlpha) { + destAlphaPtr0 += yStep * scaledWidth; + } + } + + gfree(alphaLineBuf); + gfree(lineBuf); +} + +void Splash::scaleImageYuXu(SplashImageSource src, void *srcData, + SplashColorMode srcMode, int nComps, + GBool srcAlpha, int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight, + SplashBitmap *dest) { + Guchar *lineBuf, *alphaLineBuf; + Guint pix[splashMaxColorComps]; + Guint alpha; + Guchar *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr; + int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx; + int i, j; + + // Bresenham parameters for y scale + yp = scaledHeight / srcHeight; + yq = scaledHeight % srcHeight; + + // Bresenham parameters for x scale + xp = scaledWidth / srcWidth; + xq = scaledWidth % srcWidth; + + // allocate buffers + lineBuf = (Guchar *)gmallocn(srcWidth, nComps); + if (srcAlpha) { + alphaLineBuf = (Guchar *)gmalloc(srcWidth); + } else { + alphaLineBuf = NULL; + } + + // init y scale Bresenham + yt = 0; + + destPtr0 = dest->data; + destAlphaPtr0 = dest->alpha; + for (y = 0; y < srcHeight; ++y) { + + // y scale Bresenham + if ((yt += yq) >= srcHeight) { + yt -= srcHeight; + yStep = yp + 1; + } else { + yStep = yp; + } + + // read row from image + (*src)(srcData, lineBuf, alphaLineBuf); + + // init x scale Bresenham + xt = 0; + + xx = 0; + for (x = 0; x < srcWidth; ++x) { + + // x scale Bresenham + if ((xt += xq) >= srcWidth) { + xt -= srcWidth; + xStep = xp + 1; + } else { + xStep = xp; + } + + // compute the final pixel + for (i = 0; i < nComps; ++i) { + pix[i] = lineBuf[x * nComps + i]; + } + + // store the pixel + switch (srcMode) { + case splashModeMono1: // mono1 is not allowed + break; + case splashModeMono8: + for (i = 0; i < yStep; ++i) { + for (j = 0; j < xStep; ++j) { + destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; + *destPtr++ = (Guchar)pix[0]; + } + } + break; + case splashModeRGB8: + for (i = 0; i < yStep; ++i) { + for (j = 0; j < xStep; ++j) { + destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; + *destPtr++ = (Guchar)pix[0]; + *destPtr++ = (Guchar)pix[1]; + *destPtr++ = (Guchar)pix[2]; + } + } + break; + case splashModeXBGR8: + for (i = 0; i < yStep; ++i) { + for (j = 0; j < xStep; ++j) { + destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; + *destPtr++ = (Guchar)pix[2]; + *destPtr++ = (Guchar)pix[1]; + *destPtr++ = (Guchar)pix[0]; + *destPtr++ = (Guchar)255; + } + } + break; + case splashModeBGR8: + for (i = 0; i < yStep; ++i) { + for (j = 0; j < xStep; ++j) { + destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; + *destPtr++ = (Guchar)pix[2]; + *destPtr++ = (Guchar)pix[1]; + *destPtr++ = (Guchar)pix[0]; + } + } + break; +#if SPLASH_CMYK + case splashModeCMYK8: + for (i = 0; i < yStep; ++i) { + for (j = 0; j < xStep; ++j) { + destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; + *destPtr++ = (Guchar)pix[0]; + *destPtr++ = (Guchar)pix[1]; + *destPtr++ = (Guchar)pix[2]; + *destPtr++ = (Guchar)pix[3]; + } + } + break; + case splashModeDeviceN8: + for (i = 0; i < yStep; ++i) { + for (j = 0; j < xStep; ++j) { + destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; + for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) + *destPtr++ = (Guchar)pix[cp]; + } + } + break; +#endif + } + + // process alpha + if (srcAlpha) { + alpha = alphaLineBuf[x]; + for (i = 0; i < yStep; ++i) { + for (j = 0; j < xStep; ++j) { + destAlphaPtr = destAlphaPtr0 + i * scaledWidth + xx + j; + *destAlphaPtr = (Guchar)alpha; + } + } + } + + xx += xStep; + } + + destPtr0 += yStep * scaledWidth * nComps; + if (srcAlpha) { + destAlphaPtr0 += yStep * scaledWidth; + } + } + + gfree(alphaLineBuf); + gfree(lineBuf); +} + +// expand source row to scaledWidth using linear interpolation +static void expandRow(Guchar *srcBuf, Guchar *dstBuf, int srcWidth, int scaledWidth, int nComps) +{ + double xStep = (double)srcWidth/scaledWidth; + double xSrc = 0.0; + double xFrac, xInt; + int p; + + // pad the source with an extra pixel equal to the last pixel + // so that when xStep is inside the last pixel we still have two + // pixels to interpolate between. + for (int i = 0; i < nComps; i++) + srcBuf[srcWidth*nComps + i] = srcBuf[(srcWidth-1)*nComps + i]; + + for (int x = 0; x < scaledWidth; x++) { + xFrac = modf(xSrc, &xInt); + p = (int)xInt; + for (int c = 0; c < nComps; c++) { + dstBuf[nComps*x + c] = srcBuf[nComps*p + c]*(1.0 - xFrac) + srcBuf[nComps*(p+1) + c]*xFrac; + } + xSrc += xStep; + } +} + +// Scale up image using bilinear interpolation +void Splash::scaleImageYuXuBilinear(SplashImageSource src, void *srcData, + SplashColorMode srcMode, int nComps, + GBool srcAlpha, int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight, + SplashBitmap *dest) { + Guchar *srcBuf, *lineBuf1, *lineBuf2, *alphaSrcBuf, *alphaLineBuf1, *alphaLineBuf2; + Guint pix[splashMaxColorComps]; + Guchar *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr; + int i; + + if (srcWidth < 1 || srcHeight < 1) + return; + + // allocate buffers + srcBuf = (Guchar *)gmallocn(srcWidth+1, nComps); // + 1 pixel of padding + lineBuf1 = (Guchar *)gmallocn(scaledWidth, nComps); + lineBuf2 = (Guchar *)gmallocn(scaledWidth, nComps); + if (srcAlpha) { + alphaSrcBuf = (Guchar *)gmalloc(srcWidth+1); // + 1 pixel of padding + alphaLineBuf1 = (Guchar *)gmalloc(scaledWidth); + alphaLineBuf2 = (Guchar *)gmalloc(scaledWidth); + } else { + alphaSrcBuf = NULL; + alphaLineBuf1 = NULL; + alphaLineBuf2 = NULL; + } + + double ySrc = 0.0; + double yStep = (double)srcHeight/scaledHeight; + double yFrac, yInt; + int currentSrcRow = -1; + (*src)(srcData, srcBuf, alphaSrcBuf); + expandRow(srcBuf, lineBuf2, srcWidth, scaledWidth, nComps); + if (srcAlpha) + expandRow(alphaSrcBuf, alphaLineBuf2, srcWidth, scaledWidth, 1); + + destPtr0 = dest->data; + destAlphaPtr0 = dest->alpha; + for (int y = 0; y < scaledHeight; y++) { + yFrac = modf(ySrc, &yInt); + if ((int)yInt > currentSrcRow) { + currentSrcRow++; + // Copy line2 data to line1 and get next line2 data. + // If line2 already contains the last source row we don't touch it. + // This effectively adds an extra row of padding for interpolating the + // last source row with. + memcpy(lineBuf1, lineBuf2, scaledWidth * nComps); + if (srcAlpha) + memcpy(alphaLineBuf1, alphaLineBuf2, scaledWidth); + if (currentSrcRow < srcHeight) { + (*src)(srcData, srcBuf, alphaSrcBuf); + expandRow(srcBuf, lineBuf2, srcWidth, scaledWidth, nComps); + if (srcAlpha) + expandRow(alphaSrcBuf, alphaLineBuf2, srcWidth, scaledWidth, 1); + } + } + + // write row y using linear interpolation on lineBuf1 and lineBuf2 + for (int x = 0; x < scaledWidth; ++x) { + // compute the final pixel + for (i = 0; i < nComps; ++i) { + pix[i] = lineBuf1[x*nComps + i]*(1.0 - yFrac) + lineBuf2[x*nComps + i]*yFrac; + } + + // store the pixel + destPtr = destPtr0 + (y * scaledWidth + x) * nComps; + switch (srcMode) { + case splashModeMono1: // mono1 is not allowed + break; + case splashModeMono8: + *destPtr++ = (Guchar)pix[0]; + break; + case splashModeRGB8: + *destPtr++ = (Guchar)pix[0]; + *destPtr++ = (Guchar)pix[1]; + *destPtr++ = (Guchar)pix[2]; + break; + case splashModeXBGR8: + *destPtr++ = (Guchar)pix[2]; + *destPtr++ = (Guchar)pix[1]; + *destPtr++ = (Guchar)pix[0]; + *destPtr++ = (Guchar)255; + break; + case splashModeBGR8: + *destPtr++ = (Guchar)pix[2]; + *destPtr++ = (Guchar)pix[1]; + *destPtr++ = (Guchar)pix[0]; + break; +#if SPLASH_CMYK + case splashModeCMYK8: + *destPtr++ = (Guchar)pix[0]; + *destPtr++ = (Guchar)pix[1]; + *destPtr++ = (Guchar)pix[2]; + *destPtr++ = (Guchar)pix[3]; + break; + case splashModeDeviceN8: + for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) + *destPtr++ = (Guchar)pix[cp]; + break; +#endif + } + + // process alpha + if (srcAlpha) { + destAlphaPtr = destAlphaPtr0 + y*scaledWidth + x; + *destAlphaPtr = alphaLineBuf1[x]*(1.0 - yFrac) + alphaLineBuf2[x]*yFrac; + } + } + + ySrc += yStep; + } + + gfree(alphaSrcBuf); + gfree(alphaLineBuf1); + gfree(alphaLineBuf2); + gfree(srcBuf); + gfree(lineBuf1); + gfree(lineBuf2); +} + +void Splash::vertFlipImage(SplashBitmap *img, int width, int height, + int nComps) { + Guchar *lineBuf; + Guchar *p0, *p1; + int w; + + if (unlikely(img->data == NULL)) { + error(errInternal, -1, "img->data is NULL in Splash::vertFlipImage"); + return; + } + + w = width * nComps; + lineBuf = (Guchar *)gmalloc(w); + for (p0 = img->data, p1 = img->data + (height - 1) * w; + p0 < p1; + p0 += w, p1 -= w) { + memcpy(lineBuf, p0, w); + memcpy(p0, p1, w); + memcpy(p1, lineBuf, w); + } + if (img->alpha) { + for (p0 = img->alpha, p1 = img->alpha + (height - 1) * width; + p0 < p1; + p0 += width, p1 -= width) { + memcpy(lineBuf, p0, width); + memcpy(p0, p1, width); + memcpy(p1, lineBuf, width); + } + } + gfree(lineBuf); +} + +void Splash::blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest) { + SplashClipResult clipRes = state->clip->testRect(xDest, yDest, xDest + src->getWidth() - 1, yDest + src->getHeight() - 1); + if (clipRes != splashClipAllOutside) { + blitImage(src, srcAlpha, xDest, yDest, clipRes); + } +} + +void Splash::blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest, + SplashClipResult clipRes) { + SplashPipe pipe; + SplashColor pixel; + Guchar *ap; + int w, h, x0, y0, x1, y1, x, y; + + // split the image into clipped and unclipped regions + w = src->getWidth(); + h = src->getHeight(); + if (clipRes == splashClipAllInside) { + x0 = 0; + y0 = 0; + x1 = w; + y1 = h; + } else { + if (state->clip->getNumPaths()) { + x0 = x1 = w; + y0 = y1 = h; + } else { + if ((x0 = splashCeil(state->clip->getXMin()) - xDest) < 0) { + x0 = 0; + } + if ((y0 = splashCeil(state->clip->getYMin()) - yDest) < 0) { + y0 = 0; + } + if ((x1 = splashFloor(state->clip->getXMax()) - xDest) > w) { + x1 = w; + } + if (x1 < x0) { + x1 = x0; + } + if ((y1 = splashFloor(state->clip->getYMax()) - yDest) > h) { + y1 = h; + } + if (y1 < y0) { + y1 = y0; + } + } + } + + // draw the unclipped region + if (x0 < w && y0 < h && x0 < x1 && y0 < y1) { + pipeInit(&pipe, xDest + x0, yDest + y0, NULL, pixel, + (Guchar)splashRound(state->fillAlpha * 255), srcAlpha, gFalse); + if (srcAlpha) { + for (y = y0; y < y1; ++y) { + pipeSetXY(&pipe, xDest + x0, yDest + y); + ap = src->getAlphaPtr() + y * w + x0; + for (x = x0; x < x1; ++x) { + src->getPixel(x, y, pixel); + pipe.shape = *ap++; + (this->*pipe.run)(&pipe); + } + } + } else { + for (y = y0; y < y1; ++y) { + pipeSetXY(&pipe, xDest + x0, yDest + y); + for (x = x0; x < x1; ++x) { + src->getPixel(x, y, pixel); + (this->*pipe.run)(&pipe); + } + } + } + updateModX(xDest + x0); + updateModX(xDest + x1 - 1); + updateModY(yDest + y0); + updateModY(yDest + y1 - 1); + } + + // draw the clipped regions + if (y0 > 0) { + blitImageClipped(src, srcAlpha, 0, 0, xDest, yDest, w, y0); + } + if (y1 < h) { + blitImageClipped(src, srcAlpha, 0, y1, xDest, yDest + y1, w, h - y1); + } + if (x0 > 0 && y0 < y1) { + blitImageClipped(src, srcAlpha, 0, y0, xDest, yDest + y0, x0, y1 - y0); + } + if (x1 < w && y0 < y1) { + blitImageClipped(src, srcAlpha, x1, y0, xDest + x1, yDest + y0, + w - x1, y1 - y0); + } +} + +void Splash::blitImageClipped(SplashBitmap *src, GBool srcAlpha, + int xSrc, int ySrc, int xDest, int yDest, + int w, int h) { + SplashPipe pipe; + SplashColor pixel; + Guchar *ap; + int x, y; + + if (vectorAntialias) { + pipeInit(&pipe, xDest, yDest, NULL, pixel, + (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); + drawAAPixelInit(); + if (srcAlpha) { + for (y = 0; y < h; ++y) { + ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc; + for (x = 0; x < w; ++x) { + src->getPixel(xSrc + x, ySrc + y, pixel); + pipe.shape = *ap++; + drawAAPixel(&pipe, xDest + x, yDest + y); + } + } + } else { + for (y = 0; y < h; ++y) { + for (x = 0; x < w; ++x) { + src->getPixel(xSrc + x, ySrc + y, pixel); + pipe.shape = 255; + drawAAPixel(&pipe, xDest + x, yDest + y); + } + } + } + } else { + pipeInit(&pipe, xDest, yDest, NULL, pixel, + (Guchar)splashRound(state->fillAlpha * 255), srcAlpha, gFalse); + if (srcAlpha) { + for (y = 0; y < h; ++y) { + ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc; + pipeSetXY(&pipe, xDest, yDest + y); + for (x = 0; x < w; ++x) { + if (state->clip->test(xDest + x, yDest + y)) { + src->getPixel(xSrc + x, ySrc + y, pixel); + pipe.shape = *ap++; + (this->*pipe.run)(&pipe); + updateModX(xDest + x); + updateModY(yDest + y); + } else { + pipeIncX(&pipe); + ++ap; + } + } + } + } else { + for (y = 0; y < h; ++y) { + pipeSetXY(&pipe, xDest, yDest + y); + for (x = 0; x < w; ++x) { + if (state->clip->test(xDest + x, yDest + y)) { + src->getPixel(xSrc + x, ySrc + y, pixel); + (this->*pipe.run)(&pipe); + updateModX(xDest + x); + updateModY(yDest + y); + } else { + pipeIncX(&pipe); + } + } + } + } + } +} + +SplashError Splash::composite(SplashBitmap *src, int xSrc, int ySrc, + int xDest, int yDest, int w, int h, + GBool noClip, GBool nonIsolated, + GBool knockout, SplashCoord knockoutOpacity) { + SplashPipe pipe; + SplashColor pixel; + Guchar alpha; + Guchar *ap; + int x, y; + + if (src->mode != bitmap->mode) { + return splashErrModeMismatch; + } + + if (unlikely(!bitmap->data)) { + return splashErrZeroImage; + } + + if(src->getSeparationList()->getLength() > bitmap->getSeparationList()->getLength()) { + for (x = bitmap->getSeparationList()->getLength(); x < src->getSeparationList()->getLength(); x++) + bitmap->getSeparationList()->append(((GfxSeparationColorSpace *)src->getSeparationList()->get(x))->copy()); + } + if (src->alpha) { + pipeInit(&pipe, xDest, yDest, NULL, pixel, + (Guchar)splashRound(state->fillAlpha * 255), gTrue, nonIsolated, + knockout, (Guchar)splashRound(knockoutOpacity * 255)); + if (noClip) { + for (y = 0; y < h; ++y) { + pipeSetXY(&pipe, xDest, yDest + y); + ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc; + for (x = 0; x < w; ++x) { + src->getPixel(xSrc + x, ySrc + y, pixel); + alpha = *ap++; + // this uses shape instead of alpha, which isn't technically + // correct, but works out the same + pipe.shape = alpha; + (this->*pipe.run)(&pipe); + } + } + updateModX(xDest); + updateModX(xDest + w - 1); + updateModY(yDest); + updateModY(yDest + h - 1); + } else { + for (y = 0; y < h; ++y) { + pipeSetXY(&pipe, xDest, yDest + y); + ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc; + for (x = 0; x < w; ++x) { + src->getPixel(xSrc + x, ySrc + y, pixel); + alpha = *ap++; + if (state->clip->test(xDest + x, yDest + y)) { + // this uses shape instead of alpha, which isn't technically + // correct, but works out the same + pipe.shape = alpha; + (this->*pipe.run)(&pipe); + updateModX(xDest + x); + updateModY(yDest + y); + } else { + pipeIncX(&pipe); + } + } + } + } + } else { + pipeInit(&pipe, xDest, yDest, NULL, pixel, + (Guchar)splashRound(state->fillAlpha * 255), gFalse, nonIsolated); + if (noClip) { + for (y = 0; y < h; ++y) { + pipeSetXY(&pipe, xDest, yDest + y); + for (x = 0; x < w; ++x) { + src->getPixel(xSrc + x, ySrc + y, pixel); + (this->*pipe.run)(&pipe); + } + } + updateModX(xDest); + updateModX(xDest + w - 1); + updateModY(yDest); + updateModY(yDest + h - 1); + } else { + for (y = 0; y < h; ++y) { + pipeSetXY(&pipe, xDest, yDest + y); + for (x = 0; x < w; ++x) { + src->getPixel(xSrc + x, ySrc + y, pixel); + if (state->clip->test(xDest + x, yDest + y)) { + (this->*pipe.run)(&pipe); + updateModX(xDest + x); + updateModY(yDest + y); + } else { + pipeIncX(&pipe); + } + } + } + } + } + + return splashOk; +} + +void Splash::compositeBackground(SplashColorPtr color) { + SplashColorPtr p; + Guchar *q; + Guchar alpha, alpha1, c, color0, color1, color2; +#if SPLASH_CMYK + Guchar color3; + Guchar colorsp[SPOT_NCOMPS+4], cp; +#endif + int x, y, mask; + + if (unlikely(bitmap->alpha == NULL)) { + error(errInternal, -1, "bitmap->alpha is NULL in Splash::compositeBackground"); + return; + } + + 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->width]; + mask = 0x80; + for (x = 0; x < bitmap->width; ++x) { + alpha = *q++; + alpha1 = 255 - alpha; + c = (*p & mask) ? 0xff : 0x00; + c = div255(alpha1 * color0 + alpha * c); + if (c & 0x80) { + *p |= mask; + } else { + *p &= ~mask; + } + if (!(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->width]; + for (x = 0; x < bitmap->width; ++x) { + alpha = *q++; + alpha1 = 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->width]; + 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 = 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; + case splashModeXBGR8: + 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->width]; + 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 = 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] = 255; + p += 4; + } + } + 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->width]; + 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 = 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; + case splashModeDeviceN8: + for (cp = 0; cp < SPOT_NCOMPS+4; cp++) + colorsp[cp] = color[cp]; + for (y = 0; y < bitmap->height; ++y) { + p = &bitmap->data[y * bitmap->rowSize]; + q = &bitmap->alpha[y * bitmap->width]; + for (x = 0; x < bitmap->width; ++x) { + alpha = *q++; + if (alpha == 0) + { + for (cp = 0; cp < SPOT_NCOMPS+4; cp++) + p[cp] = colorsp[cp]; + } + else if (alpha != 255) + { + alpha1 = 255 - alpha; + for (cp = 0; cp < SPOT_NCOMPS+4; cp++) + p[cp] = div255(alpha1 * colorsp[cp] + alpha * p[cp]); + } + p += (SPOT_NCOMPS+4); + } + } + break; +#endif + } + memset(bitmap->alpha, 255, bitmap->width * bitmap->height); +} + +GBool Splash::gouraudTriangleShadedFill(SplashGouraudColor *shading) +{ + double xdbl[3] = {0., 0., 0.}; + double ydbl[3] = {0., 0., 0.}; + int x[3] = {0, 0, 0}; + int y[3] = {0, 0, 0}; + double xt=0., xa=0., yt=0.; + double ca=0., ct=0.; + + // triangle interpolation: + // + double scanLimitMapL[2] = {0., 0.}; + double scanLimitMapR[2] = {0., 0.}; + double scanColorMapL[2] = {0., 0.}; + double scanColorMapR[2] = {0., 0.}; + double scanColorMap[2] = {0., 0.}; + int scanEdgeL[2] = { 0, 0 }; + int scanEdgeR[2] = { 0, 0 }; + GBool hasFurtherSegment = gFalse; + + int scanLineOff = 0; + int bitmapOff = 0; + int scanLimitR = 0, scanLimitL = 0; + + int bitmapWidth = bitmap->getWidth(); + SplashClip* clip = getClip(); + SplashBitmap *blitTarget = bitmap; + SplashColorPtr bitmapData = bitmap->getDataPtr(); + int bitmapOffLimit = bitmap->getHeight() * bitmap->getRowSize(); + SplashColorPtr bitmapAlpha = bitmap->getAlphaPtr(); + SplashColorPtr cur = NULL; + SplashCoord* userToCanvasMatrix = getMatrix(); + SplashColorMode bitmapMode = bitmap->getMode(); + GBool hasAlpha = (bitmapAlpha != NULL); + int rowSize = bitmap->getRowSize(); + int colorComps = 0; + switch (bitmapMode) { + case splashModeMono1: + break; + case splashModeMono8: + colorComps=1; + break; + case splashModeRGB8: + colorComps=3; + break; + case splashModeBGR8: + colorComps=3; + break; + case splashModeXBGR8: + colorComps=4; + break; +#if SPLASH_CMYK + case splashModeCMYK8: + colorComps=4; + break; + case splashModeDeviceN8: + colorComps=SPOT_NCOMPS+4; + break; +#endif + } + + SplashPipe pipe; + SplashColor cSrcVal; + + pipeInit(&pipe, 0, 0, NULL, cSrcVal, (Guchar)splashRound(state->strokeAlpha * 255), gFalse, gFalse); + + if (vectorAntialias) { + if (aaBuf == NULL) + return gFalse; // fall back to old behaviour + drawAAPixelInit(); + } + + // idea: + // 1. If pipe->noTransparency && !state->blendFunc + // -> blit directly into the drawing surface! + // -> disable alpha manually. + // 2. Otherwise: + // - blit also directly, but into an intermediate surface. + // Afterwards, blit the intermediate surface using the drawing pipeline. + // This is necessary because triangle elements can be on top of each + // other, so the complete shading needs to be drawn before opacity is + // applied. + // - the final step, is performed using a SplashPipe: + // - assign the actual color into cSrcVal: pipe uses cSrcVal by reference + // - invoke drawPixel(&pipe,X,Y,bNoClip); + GBool bDirectBlit = vectorAntialias ? gFalse : pipe.noTransparency && !state->blendFunc; + if (!bDirectBlit) { + blitTarget = new SplashBitmap(bitmap->getWidth(), + bitmap->getHeight(), + bitmap->getRowPad(), + bitmap->getMode(), + gTrue, + bitmap->getRowSize() >= 0); + bitmapData = blitTarget->getDataPtr(); + bitmapAlpha = blitTarget->getAlphaPtr(); + + // initialisation seems to be necessary: + int S = bitmap->getWidth() * bitmap->getHeight(); + for (int i = 0; i < S; ++i) + bitmapAlpha[i] = 0; + hasAlpha = gTrue; + } + + if (shading->isParameterized()) { + double color[3]; + double colorinterp; + + for (int i = 0; i < shading->getNTriangles(); ++i) { + shading->getTriangle(i, + xdbl + 0, ydbl + 0, color + 0, + xdbl + 1, ydbl + 1, color + 1, + xdbl + 2, ydbl + 2, color + 2); + for (int m = 0; m < 3; ++m) { + xt = xdbl[m] * (double)userToCanvasMatrix[0] + ydbl[m] * (double)userToCanvasMatrix[2] + (double)userToCanvasMatrix[4]; + yt = xdbl[m] * (double)userToCanvasMatrix[1] + ydbl[m] * (double)userToCanvasMatrix[3] + (double)userToCanvasMatrix[5]; + xdbl[m] = xt; + ydbl[m] = yt; + // we operate on scanlines which are integer offsets into the + // raster image. The double offsets are of no use here. + x[m] = splashRound(xt); + y[m] = splashRound(yt); + } + // sort according to y coordinate to simplify sweep through scanlines: + // INSERTION SORT. + if (y[0] > y[1]) { + Guswap(x[0], x[1]); + Guswap(y[0], y[1]); + Guswap(color[0], color[1]); + } + // first two are sorted. + assert(y[0] <= y[1]); + if (y[1] > y[2]) { + int tmpX = x[2]; + int tmpY = y[2]; + double tmpC = color[2]; + x[2] = x[1]; y[2] = y[1]; color[2] = color[1]; + + if (y[0] > tmpY) { + x[1] = x[0]; y[1] = y[0]; color[1] = color[0]; + x[0] = tmpX; y[0] = tmpY; color[0] = tmpC; + } else { + x[1] = tmpX; y[1] = tmpY; color[1] = tmpC; + } + } + // first three are sorted + assert(y[0] <= y[1]); + assert(y[1] <= y[2]); + ///// + + // this here is det( T ) == 0 + // where T is the matrix to map to barycentric coordinates. + if ((x[0] - x[2]) * (y[1] - y[2]) - (x[1] - x[2]) * (y[0] - y[2]) == 0) + continue; // degenerate triangle. + + // this here initialises the scanline generation. + // We start with low Y coordinates and sweep up to the large Y + // coordinates. + // + // scanEdgeL[m] in {0,1,2} m=0,1 + // scanEdgeR[m] in {0,1,2} m=0,1 + // + // are the two edges between which scanlines are (currently) + // sweeped. The values {0,1,2} are indices into 'x' and 'y'. + // scanEdgeL[0] = 0 means: the left scan edge has (x[0],y[0]) as vertex. + // + scanEdgeL[0] = 0; + scanEdgeR[0] = 0; + if (y[0] == y[1]) { + scanEdgeL[0] = 1; + scanEdgeL[1] = scanEdgeR[1] = 2; + + } else { + scanEdgeL[1] = 1; scanEdgeR[1] = 2; + } + assert(y[scanEdgeL[0]] < y[scanEdgeL[1]]); + assert(y[scanEdgeR[0]] < y[scanEdgeR[1]]); + + // Ok. Now prepare the linear maps which map the y coordinate of + // the current scanline to the corresponding LEFT and RIGHT x + // coordinate (which define the scanline). + scanLimitMapL[0] = double(x[scanEdgeL[1]] - x[scanEdgeL[0]]) / (y[scanEdgeL[1]] - y[scanEdgeL[0]]); + scanLimitMapL[1] = x[scanEdgeL[0]] - y[scanEdgeL[0]] * scanLimitMapL[0]; + scanLimitMapR[0] = double(x[scanEdgeR[1]] - x[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]); + scanLimitMapR[1] = x[scanEdgeR[0]] - y[scanEdgeR[0]] * scanLimitMapR[0]; + + xa = y[1] * scanLimitMapL[0] + scanLimitMapL[1]; + xt = y[1] * scanLimitMapR[0] + scanLimitMapR[1]; + if (xa > xt) { + // I have "left" is to the right of "right". + // Exchange sides! + Guswap(scanEdgeL[0], scanEdgeR[0]); + Guswap(scanEdgeL[1], scanEdgeR[1]); + Guswap(scanLimitMapL[0], scanLimitMapR[0]); + Guswap(scanLimitMapL[1], scanLimitMapR[1]); + // FIXME I'm sure there is a more efficient way to check this. + } + + // Same game: we can linearly interpolate the color based on the + // current y coordinate (that's correct for triangle + // interpolation due to linearity. We could also have done it in + // barycentric coordinates, but that's slightly more involved) + scanColorMapL[0] = (color[scanEdgeL[1]] - color[scanEdgeL[0]]) / (y[scanEdgeL[1]] - y[scanEdgeL[0]]); + scanColorMapL[1] = color[scanEdgeL[0]] - y[scanEdgeL[0]] * scanColorMapL[0]; + scanColorMapR[0] = (color[scanEdgeR[1]] - color[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]); + scanColorMapR[1] = color[scanEdgeR[0]] - y[scanEdgeR[0]] * scanColorMapR[0]; + + hasFurtherSegment = (y[1] < y[2]); + scanLineOff = y[0] * rowSize; + + for (int Y = y[0]; Y <= y[2]; ++Y, scanLineOff += rowSize) { + if (hasFurtherSegment && Y == y[1]) { + // SWEEP EVENT: we encountered the next segment. + // + // switch to next segment, either at left end or at right + // end: + if (scanEdgeL[1] == 1) { + scanEdgeL[0] = 1; + scanEdgeL[1] = 2; + scanLimitMapL[0] = double(x[scanEdgeL[1]] - x[scanEdgeL[0]]) / (y[scanEdgeL[1]] - y[scanEdgeL[0]]); + scanLimitMapL[1] = x[scanEdgeL[0]] - y[scanEdgeL[0]] * scanLimitMapL[0]; + + scanColorMapL[0] = (color[scanEdgeL[1]] - color[scanEdgeL[0]]) / (y[scanEdgeL[1]] - y[scanEdgeL[0]]); + scanColorMapL[1] = color[scanEdgeL[0]] - y[scanEdgeL[0]] * scanColorMapL[0]; + } else if (scanEdgeR[1] == 1) { + scanEdgeR[0] = 1; + scanEdgeR[1] = 2; + scanLimitMapR[0] = double(x[scanEdgeR[1]] - x[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]); + scanLimitMapR[1] = x[scanEdgeR[0]] - y[scanEdgeR[0]] * scanLimitMapR[0]; + + scanColorMapR[0] = (color[scanEdgeR[1]] - color[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]); + scanColorMapR[1] = color[scanEdgeR[0]] - y[scanEdgeR[0]] * scanColorMapR[0]; + } + assert( y[scanEdgeL[0]] < y[scanEdgeL[1]] ); + assert( y[scanEdgeR[0]] < y[scanEdgeR[1]] ); + hasFurtherSegment = gFalse; + } + + yt = Y; + + xa = yt * scanLimitMapL[0] + scanLimitMapL[1]; + xt = yt * scanLimitMapR[0] + scanLimitMapR[1]; + + ca = yt * scanColorMapL[0] + scanColorMapL[1]; + ct = yt * scanColorMapR[0] + scanColorMapR[1]; + + scanLimitL = splashRound(xa); + scanLimitR = splashRound(xt); + + // Ok. Now: init the color interpolation depending on the X + // coordinate inside of the current scanline: + scanColorMap[0] = (scanLimitR == scanLimitL) ? 0. : ((ct - ca) / (scanLimitR - scanLimitL)); + scanColorMap[1] = ca - scanLimitL * scanColorMap[0]; + + // handled by clipping: + // assert( scanLimitL >= 0 && scanLimitR < bitmap->getWidth() ); + assert(scanLimitL <= scanLimitR || abs(scanLimitL - scanLimitR) <= 2); // allow rounding inaccuracies + assert(scanLineOff == Y * rowSize); + + colorinterp = scanColorMap[0] * scanLimitL + scanColorMap[1]; + + bitmapOff = scanLineOff + scanLimitL * colorComps; + for (int X = scanLimitL; X <= scanLimitR && bitmapOff + colorComps <= bitmapOffLimit; ++X, colorinterp += scanColorMap[0], bitmapOff += colorComps) { + // FIXME : standard rectangular clipping can be done for a + // complete scanline which is faster + // --> see SplashClip and its methods + if (!clip->test(X, Y)) + continue; + + assert(fabs(colorinterp - (scanColorMap[0] * X + scanColorMap[1])) < 1e-10); + assert(bitmapOff == Y * rowSize + colorComps * X && scanLineOff == Y * rowSize); + + shading->getParameterizedColor(colorinterp, bitmapMode, &bitmapData[bitmapOff]); + + // make the shading visible. + // Note that opacity is handled by the bDirectBlit stuff, see + // above for comments and below for implementation. + if (hasAlpha) + bitmapAlpha[Y * bitmapWidth + X] = 255; + } + } + } + } else { + return gFalse; + } + + if (!bDirectBlit) { + // ok. Finalize the stuff by blitting the shading into the final + // geometry, this time respecting the rendering pipe. + int W = blitTarget->getWidth(); + int H = blitTarget->getHeight(); + cur = cSrcVal; + + for (int X = 0; X < W; ++X) { + for (int Y = 0; Y < H; ++Y) { + if (!bitmapAlpha[Y * bitmapWidth + X]) + continue; // draw only parts of the shading! + bitmapOff = Y * rowSize + colorComps * X; + + for (int m = 0; m < colorComps; ++m) + cur[m] = bitmapData[bitmapOff + m]; + if (vectorAntialias) { + drawAAPixel(&pipe, X, Y); + } else { + drawPixel(&pipe, X, Y, gTrue); // no clipping - has already been done. + } + } + } + + delete blitTarget; + blitTarget = NULL; + } + + return gTrue; +} + +SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc, + int xDest, int yDest, int w, int h) { + SplashColorPtr p, sp; + Guchar *q; + int x, y, mask, srcMask; + + if (src->mode != bitmap->mode) { + return splashErrModeMismatch; + } + + if (unlikely(!bitmap->data)) { + return splashErrZeroImage; + } + + switch (bitmap->mode) { + case splashModeMono1: + for (y = 0; y < h; ++y) { + p = &bitmap->data[(yDest + y) * bitmap->rowSize + (xDest >> 3)]; + mask = 0x80 >> (xDest & 7); + sp = &src->data[(ySrc + y) * src->rowSize + (xSrc >> 3)]; + srcMask = 0x80 >> (xSrc & 7); + for (x = 0; x < w; ++x) { + if (*sp & srcMask) { + *p |= mask; + } else { + *p &= ~mask; + } + if (!(mask >>= 1)) { + mask = 0x80; + ++p; + } + if (!(srcMask >>= 1)) { + srcMask = 0x80; + ++sp; + } + } + } + break; + case splashModeMono8: + for (y = 0; y < h; ++y) { + p = &bitmap->data[(yDest + y) * bitmap->rowSize + xDest]; + sp = &src->data[(ySrc + y) * bitmap->rowSize + xSrc]; + for (x = 0; x < w; ++x) { + *p++ = *sp++; + } + } + break; + case splashModeRGB8: + case splashModeBGR8: + for (y = 0; y < h; ++y) { + p = &bitmap->data[(yDest + y) * bitmap->rowSize + 3 * xDest]; + sp = &src->data[(ySrc + y) * src->rowSize + 3 * xSrc]; + for (x = 0; x < w; ++x) { + *p++ = *sp++; + *p++ = *sp++; + *p++ = *sp++; + } + } + break; + case splashModeXBGR8: + for (y = 0; y < h; ++y) { + p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest]; + sp = &src->data[(ySrc + y) * src->rowSize + 4 * xSrc]; + for (x = 0; x < w; ++x) { + *p++ = *sp++; + *p++ = *sp++; + *p++ = *sp++; + *p++ = 255; + sp++; + } + } + break; +#if SPLASH_CMYK + case splashModeCMYK8: + for (y = 0; y < h; ++y) { + p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest]; + sp = &src->data[(ySrc + y) * src->rowSize + 4 * xSrc]; + for (x = 0; x < w; ++x) { + *p++ = *sp++; + *p++ = *sp++; + *p++ = *sp++; + *p++ = *sp++; + } + } + break; + case splashModeDeviceN8: + for (y = 0; y < h; ++y) { + p = &bitmap->data[(yDest + y) * bitmap->rowSize + (SPOT_NCOMPS+4) * xDest]; + sp = &src->data[(ySrc + y) * src->rowSize + (SPOT_NCOMPS+4) * xSrc]; + for (x = 0; x < w; ++x) { + for (int cp=0; cp < SPOT_NCOMPS+4; cp++) + *p++ = *sp++; + } + } + break; +#endif + } + + if (bitmap->alpha) { + for (y = 0; y < h; ++y) { + q = &bitmap->alpha[(yDest + y) * bitmap->width + xDest]; + memset(q, 0x00, w); + } + } + + return splashOk; +} + +SplashPath *Splash::makeStrokePath(SplashPath *path, SplashCoord w, + GBool flatten) { +SplashPath *pathIn, *dashPath, *pathOut; + SplashCoord d, dx, dy, wdx, wdy, dxNext, dyNext, wdxNext, wdyNext; + SplashCoord crossprod, dotprod, miter, m; + 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 && state->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 + pathOut->moveTo(pathIn->pts[i0].x - wdy, pathIn->pts[i0].y + wdx); + if (i0 == subpathStart0) { + firstPt = pathOut->length - 1; + } + if (first && !closed) { + switch (state->lineCap) { + case splashLineCapButt: + pathOut->lineTo(pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx); + break; + case splashLineCapRound: + 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->lineTo(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); + pathOut->lineTo(pathIn->pts[i0].x + wdy, + pathIn->pts[i0].y - wdx); + break; + } + } else { + pathOut->lineTo(pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx); + } + + // draw the left side of the segment rectangle + left2 = pathOut->length - 1; + pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx); + + // draw the end cap + if (last && !closed) { + switch (state->lineCap) { + case splashLineCapButt: + pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); + break; + case splashLineCapRound: + 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); + pathOut->lineTo(pathIn->pts[j0].x - wdy, + pathIn->pts[j0].y + wdx); + break; + } + } else { + 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); + + // 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 (state->lineJoin == splashLineJoinRound) { + pathOut->moveTo(pathIn->pts[j0].x + (SplashCoord)0.5 * w, + pathIn->pts[j0].y); + pathOut->curveTo(pathIn->pts[j0].x + (SplashCoord)0.5 * w, + pathIn->pts[j0].y + bezierCircle2 * w, + pathIn->pts[j0].x + bezierCircle2 * w, + pathIn->pts[j0].y + (SplashCoord)0.5 * w, + pathIn->pts[j0].x, + pathIn->pts[j0].y + (SplashCoord)0.5 * w); + pathOut->curveTo(pathIn->pts[j0].x - bezierCircle2 * w, + pathIn->pts[j0].y + (SplashCoord)0.5 * w, + pathIn->pts[j0].x - (SplashCoord)0.5 * w, + pathIn->pts[j0].y + bezierCircle2 * w, + pathIn->pts[j0].x - (SplashCoord)0.5 * w, + pathIn->pts[j0].y); + pathOut->curveTo(pathIn->pts[j0].x - (SplashCoord)0.5 * w, + pathIn->pts[j0].y - bezierCircle2 * w, + pathIn->pts[j0].x - bezierCircle2 * w, + pathIn->pts[j0].y - (SplashCoord)0.5 * w, + pathIn->pts[j0].x, + pathIn->pts[j0].y - (SplashCoord)0.5 * w); + pathOut->curveTo(pathIn->pts[j0].x + bezierCircle2 * w, + pathIn->pts[j0].y - (SplashCoord)0.5 * w, + pathIn->pts[j0].x + (SplashCoord)0.5 * w, + pathIn->pts[j0].y - bezierCircle2 * w, + pathIn->pts[j0].x + (SplashCoord)0.5 * w, + pathIn->pts[j0].y); + + } else { + pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y); + + // angle < 180 + if (crossprod < 0) { + pathOut->lineTo(pathIn->pts[j0].x - wdyNext, + pathIn->pts[j0].y + wdxNext); + // miter join inside limit + if (state->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); + } + + // angle >= 180 + } else { + pathOut->lineTo(pathIn->pts[j0].x + wdy, + pathIn->pts[j0].y - wdx); + // miter join inside limit + if (state->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) { + if (seg == 0 && !closed) { + if (state->lineCap == splashLineCapButt) { + pathOut->addStrokeAdjustHint(firstPt, left2 + 1, + firstPt, firstPt + 1); + if (last) { + pathOut->addStrokeAdjustHint(firstPt, left2 + 1, + left2 + 1, left2 + 2); + } + } else if (state->lineCap == splashLineCapProjecting) { + if (last) { + pathOut->addStrokeAdjustHint(firstPt + 1, left2 + 2, + firstPt + 1, firstPt + 2); + pathOut->addStrokeAdjustHint(firstPt + 1, left2 + 2, + left2 + 2, left2 + 3); + } else { + pathOut->addStrokeAdjustHint(firstPt + 1, left2 + 1, + firstPt + 1, firstPt + 2); + } + } + } + if (seg >= 1) { + if (seg >= 2) { + pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0); + pathOut->addStrokeAdjustHint(left1, right1, join0, left2); + } else { + pathOut->addStrokeAdjustHint(left1, right1, firstPt, left2); + } + pathOut->addStrokeAdjustHint(left1, right1, right2 + 1, right2 + 1); + } + left0 = left1; + left1 = left2; + right0 = right1; + right1 = right2; + join0 = join1; + join1 = join2; + if (seg == 0) { + leftFirst = left2; + rightFirst = right2; + } + if (last) { + if (seg >= 2) { + pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0); + pathOut->addStrokeAdjustHint(left1, right1, + join0, pathOut->length - 1); + } else { + pathOut->addStrokeAdjustHint(left1, right1, + firstPt, pathOut->length - 1); + } + if (closed) { + pathOut->addStrokeAdjustHint(left1, right1, firstPt, leftFirst); + pathOut->addStrokeAdjustHint(left1, right1, + rightFirst + 1, rightFirst + 1); + pathOut->addStrokeAdjustHint(leftFirst, rightFirst, + left1 + 1, right1); + pathOut->addStrokeAdjustHint(leftFirst, rightFirst, + join1, pathOut->length - 1); + } + if (!closed && seg > 0) { + if (state->lineCap == splashLineCapButt) { + pathOut->addStrokeAdjustHint(left1 - 1, left1 + 1, + left1 + 1, left1 + 2); + } else if (state->lineCap == splashLineCapProjecting) { + pathOut->addStrokeAdjustHint(left1 - 1, left1 + 2, + left1 + 2, left1 + 3); + } + } + } + } + + i0 = j0; + i1 = j1; + ++seg; + } + + if (pathIn != path) { + delete pathIn; + } + + return pathOut; +} + +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" : ""); + } +} + +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 %s%s%s\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].flags & splashXPathHoriz) ? "H" : " ", + (path->segs[i].flags & splashXPathVert) ? "V" : " ", + (path->segs[i].flags & splashXPathFlip) ? "P" : " "); + } +} + +SplashError Splash::shadedFill(SplashPath *path, GBool hasBBox, + SplashPattern *pattern) { + SplashPipe pipe; + SplashXPath *xPath; + SplashXPathScanner *scanner; + int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y; + SplashClipResult clipRes; + + if (vectorAntialias && aaBuf == NULL) { // should not happen, but to be secure + return splashErrGeneric; + } + if (path->length == 0) { + return splashErrEmptyPath; + } + xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue); + if (vectorAntialias) { + xPath->aaScale(); + } + xPath->sort(); + yMinI = state->clip->getYMinI(); + yMaxI = state->clip->getYMaxI(); + if (vectorAntialias && !inShading) { + yMinI = yMinI * splashAASize; + yMaxI = (yMaxI + 1) * splashAASize - 1; + } + scanner = new SplashXPathScanner(xPath, gFalse, yMinI, yMaxI); + + // get the min and max x and y values + if (vectorAntialias) { + scanner->getBBoxAA(&xMinI, &yMinI, &xMaxI, &yMaxI); + } else { + scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI); + } + + // check clipping + if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI)) != splashClipAllOutside) { + // limit the y range + if (yMinI < state->clip->getYMinI()) { + yMinI = state->clip->getYMinI(); + } + if (yMaxI > state->clip->getYMaxI()) { + yMaxI = state->clip->getYMaxI(); + } + + pipeInit(&pipe, 0, yMinI, pattern, NULL, (Guchar)splashRound(state->fillAlpha * 255), vectorAntialias && !hasBBox, gFalse); + + // draw the spans + if (vectorAntialias) { + for (y = yMinI; y <= yMaxI; ++y) { + scanner->renderAALine(aaBuf, &x0, &x1, y); + if (clipRes != splashClipAllInside) { + state->clip->clipAALine(aaBuf, &x0, &x1, y); + } +#if splashAASize == 4 + if (!hasBBox && y > yMinI && y < yMaxI) { + // correct shape on left side if clip is + // vertical through the middle of shading: + Guchar *p0, *p1, *p2, *p3; + Guchar c1, c2, c3, c4; + p0 = aaBuf->getDataPtr() + (x0 >> 1); + p1 = p0 + aaBuf->getRowSize(); + p2 = p1 + aaBuf->getRowSize(); + p3 = p2 + aaBuf->getRowSize(); + if (x0 & 1) { + c1 = (*p0 & 0x0f); c2 =(*p1 & 0x0f); c3 = (*p2 & 0x0f) ; c4 = (*p3 & 0x0f); + } else { + c1 = (*p0 >> 4); c2 = (*p1 >> 4); c3 = (*p2 >> 4); c4 = (*p3 >> 4); + } + if ( (c1 & 0x03) == 0x03 && (c2 & 0x03) == 0x03 && (c3 & 0x03) == 0x03 && (c4 & 0x03) == 0x03 + && c1 == c2 && c2 == c3 && c3 == c4 && + pattern->testPosition(x0 - 1, y) ) + { + Guchar shapeCorrection = (x0 & 1) ? 0x0f : 0xf0; + *p0 |= shapeCorrection; + *p1 |= shapeCorrection; + *p2 |= shapeCorrection; + *p3 |= shapeCorrection; + } + // correct shape on right side if clip is + // through the middle of shading: + p0 = aaBuf->getDataPtr() + (x1 >> 1); + p1 = p0 + aaBuf->getRowSize(); + p2 = p1 + aaBuf->getRowSize(); + p3 = p2 + aaBuf->getRowSize(); + if (x1 & 1) { + c1 = (*p0 & 0x0f); c2 =(*p1 & 0x0f); c3 = (*p2 & 0x0f) ; c4 = (*p3 & 0x0f); + } else { + c1 = (*p0 >> 4); c2 = (*p1 >> 4); c3 = (*p2 >> 4); c4 = (*p3 >> 4); + } + + if ( (c1 & 0xc) == 0x0c && (c2 & 0x0c) == 0x0c && (c3 & 0x0c) == 0x0c && (c4 & 0x0c) == 0x0c + && c1 == c2 && c2 == c3 && c3 == c4 && + pattern->testPosition(x1 + 1, y) ) + { + Guchar shapeCorrection = (x1 & 1) ? 0x0f : 0xf0; + *p0 |= shapeCorrection; + *p1 |= shapeCorrection; + *p2 |= shapeCorrection; + *p3 |= shapeCorrection; + } + } +#endif + drawAALine(&pipe, x0, x1, y); + } + } else { + SplashClipResult clipRes2; + for (y = yMinI; y <= yMaxI; ++y) { + while (scanner->getNextSpan(y, &x0, &x1)) { + if (clipRes == splashClipAllInside) { + drawSpan(&pipe, x0, x1, y, gTrue); + } else { + // limit the x range + if (x0 < state->clip->getXMinI()) { + x0 = state->clip->getXMinI(); + } + if (x1 > state->clip->getXMaxI()) { + x1 = state->clip->getXMaxI(); + } + clipRes2 = state->clip->testSpan(x0, x1, y); + drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside); + } + } + } + } + } + opClipRes = clipRes; + + delete scanner; + delete xPath; + return splashOk; +} |