| //======================================================================== |
| // |
| // 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-2018 Albert Astals Cid <aacid@kde.org> |
| // Copyright (C) 2005 Marco Pesenti Gritti <mpg@redhat.com> |
| // Copyright (C) 2010-2016 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, 2017 Adrian Johnson <ajohnson@redneon.com> |
| // Copyright (C) 2012 Matthias Kramm <kramm@quiss.org> |
| // Copyright (C) 2018, 2019 Stefan Brüns <stefan.bruens@rwth-aachen.de> |
| // Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de> |
| // |
| // 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> |
| |
| #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 unsigned char div255(int x) { |
| return (unsigned char)((x + (x >> 8) + 0x80) >> 8); |
| } |
| |
| // Clip x to lie in [0, 255]. |
| static inline unsigned char 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, bool glyphMode) { |
| return glyphMode ? (splashCeil(x + 0.5) - 1) : splashFloor(x); |
| } |
| static inline int imgCoordMungeUpperC(SplashCoord x, bool 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 |
| unsigned char aInput; |
| bool usesShape; |
| SplashColorPtr cSrc; |
| SplashColor cSrcVal = {}; |
| |
| // non-isolated group alpha0 |
| unsigned char *alpha0Ptr; |
| |
| // knockout groups |
| bool knockout; |
| unsigned char knockoutOpacity; |
| |
| // soft mask |
| SplashColorPtr softMaskPtr; |
| |
| // destination alpha and color |
| SplashColorPtr destColorPtr; |
| int destColorMask; |
| unsigned char *destAlphaPtr; |
| |
| // shape |
| unsigned char shape; |
| |
| // result alpha and color |
| bool noTransparency; |
| SplashPipeResultColorCtrl resultColorCtrl; |
| |
| // non-isolated group correction |
| bool nonIsolatedGroup; |
| |
| // the "run" function |
| void (Splash::*run)(SplashPipe *pipe); |
| }; |
| |
| SplashPipeResultColorCtrl Splash::pipeResultColorNoAlphaBlend[] = { |
| splashPipeResultColorNoAlphaBlendMono, |
| splashPipeResultColorNoAlphaBlendMono, |
| splashPipeResultColorNoAlphaBlendRGB, |
| splashPipeResultColorNoAlphaBlendRGB, |
| splashPipeResultColorNoAlphaBlendRGB |
| #ifdef SPLASH_CMYK |
| , |
| splashPipeResultColorNoAlphaBlendCMYK, |
| splashPipeResultColorNoAlphaBlendDeviceN |
| #endif |
| }; |
| |
| SplashPipeResultColorCtrl Splash::pipeResultColorAlphaNoBlend[] = { |
| splashPipeResultColorAlphaNoBlendMono, |
| splashPipeResultColorAlphaNoBlendMono, |
| splashPipeResultColorAlphaNoBlendRGB, |
| splashPipeResultColorAlphaNoBlendRGB, |
| splashPipeResultColorAlphaNoBlendRGB |
| #ifdef SPLASH_CMYK |
| , |
| splashPipeResultColorAlphaNoBlendCMYK, |
| splashPipeResultColorAlphaNoBlendDeviceN |
| #endif |
| }; |
| |
| SplashPipeResultColorCtrl Splash::pipeResultColorAlphaBlend[] = { |
| splashPipeResultColorAlphaBlendMono, |
| splashPipeResultColorAlphaBlendMono, |
| splashPipeResultColorAlphaBlendRGB, |
| splashPipeResultColorAlphaBlendRGB, |
| splashPipeResultColorAlphaBlendRGB |
| #ifdef 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, |
| unsigned char aInput, bool usesShape, |
| bool nonIsolatedGroup, |
| bool knockout, unsigned char knockoutOpacity) { |
| pipeSetXY(pipe, x, y); |
| pipe->pattern = nullptr; |
| |
| // 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 = true; |
| } else { |
| pipe->noTransparency = false; |
| } |
| |
| // 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; |
| #ifdef 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; |
| #ifdef 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) { |
| unsigned char aSrc, aDest, alphaI, alphaIm1, alpha0, aResult; |
| SplashColor cSrcNonIso, cDest, cBlend; |
| SplashColorPtr cSrc; |
| unsigned char cResult0, cResult1, cResult2, cResult3; |
| int t; |
| #ifdef SPLASH_CMYK |
| int cp, mask; |
| unsigned char 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; |
| } |
| #ifdef SPLASH_CMYK |
| if (bitmap->mode == splashModeCMYK8 || bitmap->mode == splashModeDeviceN8) { |
| if (state->fillOverprint && state->overprintMode && pipe->pattern->isCMYK()) { |
| unsigned int 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; |
| #ifdef 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 |
| |
| unsigned char *destColorPtr; |
| if (pipe->shape && state->blendFunc && pipe->knockout && alpha0Bitmap != nullptr) { |
| 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: |
| #ifdef SPLASH_CMYK |
| case splashModeCMYK8: |
| #endif |
| destColorPtr += (alpha0X+pipe->x) * 4; |
| break; |
| #ifdef 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; |
| #ifdef 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) { |
| #ifdef 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; |
| // fallthrough |
| 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); |
| // fallthrough |
| 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) { |
| #ifdef 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; |
| #ifdef 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; |
| #ifdef 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; |
| #ifdef 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; |
| #ifdef 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) { |
| unsigned char 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; |
| } |
| |
| #ifdef 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) { |
| unsigned char aSrc; |
| SplashColor cDest; |
| unsigned char 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[(unsigned char)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) { |
| unsigned char aSrc, aDest, alpha2, aResult; |
| SplashColor cDest; |
| unsigned char 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[(unsigned char)(((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) { |
| unsigned char aSrc, aDest, alpha2, aResult; |
| SplashColor cDest; |
| unsigned char cResult0, cResult1, cResult2; |
| |
| //----- read destination alpha |
| aDest = *pipe->destAlphaPtr; |
| |
| //----- source alpha |
| aSrc = div255(pipe->aInput * pipe->shape); |
| |
| //----- result color |
| if (aSrc == 255) { |
| cResult0 = state->rgbTransferR[pipe->cSrc[0]]; |
| cResult1 = state->rgbTransferG[pipe->cSrc[1]]; |
| cResult2 = state->rgbTransferB[pipe->cSrc[2]]; |
| aResult = 255; |
| |
| } else if (aSrc == 0 && aDest == 0) { |
| cResult0 = 0; |
| cResult1 = 0; |
| cResult2 = 0; |
| aResult = 0; |
| |
| } else { |
| //----- read destination pixel |
| cDest[0] = pipe->destColorPtr[0]; |
| cDest[1] = pipe->destColorPtr[1]; |
| cDest[2] = pipe->destColorPtr[2]; |
| |
| //----- result alpha and non-isolated group element correction |
| aResult = aSrc + aDest - div255(aSrc * aDest); |
| alpha2 = aResult; |
| |
| cResult0 = state->rgbTransferR[(unsigned char)(((alpha2 - aSrc) * cDest[0] + |
| aSrc * pipe->cSrc[0]) / alpha2)]; |
| cResult1 = state->rgbTransferG[(unsigned char)(((alpha2 - aSrc) * cDest[1] + |
| aSrc * pipe->cSrc[1]) / alpha2)]; |
| cResult2 = state->rgbTransferB[(unsigned char)(((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) { |
| unsigned char aSrc, aDest, alpha2, aResult; |
| SplashColor cDest; |
| unsigned char cResult0, cResult1, cResult2; |
| |
| //----- read destination alpha |
| aDest = *pipe->destAlphaPtr; |
| |
| //----- source alpha |
| aSrc = div255(pipe->aInput * pipe->shape); |
| |
| //----- result color |
| if (aSrc == 255) { |
| cResult0 = state->rgbTransferR[pipe->cSrc[0]]; |
| cResult1 = state->rgbTransferG[pipe->cSrc[1]]; |
| cResult2 = state->rgbTransferB[pipe->cSrc[2]]; |
| aResult = 255; |
| |
| } else if (aSrc == 0 && aDest == 0) { |
| cResult0 = 0; |
| cResult1 = 0; |
| cResult2 = 0; |
| aResult = 0; |
| |
| } else { |
| //----- read destination color |
| cDest[0] = pipe->destColorPtr[2]; |
| cDest[1] = pipe->destColorPtr[1]; |
| cDest[2] = pipe->destColorPtr[0]; |
| |
| //----- result alpha and non-isolated group element correction |
| aResult = aSrc + aDest - div255(aSrc * aDest); |
| alpha2 = aResult; |
| |
| cResult0 = state->rgbTransferR[(unsigned char)(((alpha2 - aSrc) * cDest[0] + |
| aSrc * pipe->cSrc[0]) / alpha2)]; |
| cResult1 = state->rgbTransferG[(unsigned char)(((alpha2 - aSrc) * cDest[1] + |
| aSrc * pipe->cSrc[1]) / alpha2)]; |
| cResult2 = state->rgbTransferB[(unsigned char)(((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) { |
| unsigned char aSrc, aDest, alpha2, aResult; |
| SplashColor cDest; |
| unsigned char cResult0, cResult1, cResult2; |
| |
| //----- read destination alpha |
| aDest = *pipe->destAlphaPtr; |
| |
| //----- source alpha |
| aSrc = div255(pipe->aInput * pipe->shape); |
| |
| //----- result color |
| if (aSrc == 255) { |
| cResult0 = state->rgbTransferR[pipe->cSrc[0]]; |
| cResult1 = state->rgbTransferG[pipe->cSrc[1]]; |
| cResult2 = state->rgbTransferB[pipe->cSrc[2]]; |
| aResult = 255; |
| |
| } else if (aSrc == 0 && aDest == 0) { |
| cResult0 = 0; |
| cResult1 = 0; |
| cResult2 = 0; |
| aResult = 0; |
| |
| } else { |
| //----- read destination color |
| cDest[0] = pipe->destColorPtr[2]; |
| cDest[1] = pipe->destColorPtr[1]; |
| cDest[2] = pipe->destColorPtr[0]; |
| |
| //----- result alpha and non-isolated group element correction |
| aResult = aSrc + aDest - div255(aSrc * aDest); |
| alpha2 = aResult; |
| |
| cResult0 = state->rgbTransferR[(unsigned char)(((alpha2 - aSrc) * cDest[0] + |
| aSrc * pipe->cSrc[0]) / alpha2)]; |
| cResult1 = state->rgbTransferG[(unsigned char)(((alpha2 - aSrc) * cDest[1] + |
| aSrc * pipe->cSrc[1]) / alpha2)]; |
| cResult2 = state->rgbTransferB[(unsigned char)(((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; |
| } |
| |
| #ifdef 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) { |
| unsigned char aSrc, aDest, alpha2, aResult; |
| SplashColor cDest; |
| unsigned char 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[(unsigned char)(((alpha2 - aSrc) * cDest[0] + |
| aSrc * pipe->cSrc[0]) / alpha2)]; |
| cResult1 = state->cmykTransferM[(unsigned char)(((alpha2 - aSrc) * cDest[1] + |
| aSrc * pipe->cSrc[1]) / alpha2)]; |
| cResult2 = state->cmykTransferY[(unsigned char)(((alpha2 - aSrc) * cDest[2] + |
| aSrc * pipe->cSrc[2]) / alpha2)]; |
| cResult3 = state->cmykTransferK[(unsigned char)(((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) { |
| unsigned char aSrc, aDest, alpha2, aResult; |
| SplashColor cDest; |
| unsigned char 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][(unsigned char)(((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; |
| #ifdef 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 = nullptr; |
| } |
| if (state->inNonIsolatedGroup && alpha0Bitmap->alpha) { |
| pipe->alpha0Ptr = |
| &alpha0Bitmap->alpha[(alpha0Y + y) * alpha0Bitmap->width + |
| (alpha0X + x)]; |
| } else { |
| pipe->alpha0Ptr = nullptr; |
| } |
| } |
| |
| 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; |
| #ifdef 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, bool 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, |
| bool 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, bool adjustLine, unsigned char 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, bool vectorAntialiasA, |
| SplashScreenParams *screenParams) { |
| int i; |
| |
| bitmap = bitmapA; |
| vectorAntialias = vectorAntialiasA; |
| inShading = false; |
| state = new SplashState(bitmap->width, bitmap->height, vectorAntialias, |
| screenParams); |
| if (vectorAntialias) { |
| aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize, |
| 1, splashModeMono1, false); |
| for (i = 0; i <= splashAASize * splashAASize; ++i) { |
| aaGamma[i] = (unsigned char)splashRound( |
| splashPow((SplashCoord)i / |
| (SplashCoord)(splashAASize * splashAASize), |
| splashAAGamma) * 255); |
| } |
| } else { |
| aaBuf = nullptr; |
| } |
| minLineWidth = 0; |
| thinLineMode = splashThinLineDefault; |
| clearModRegion(); |
| debugMode = false; |
| alpha0Bitmap = nullptr; |
| } |
| |
| Splash::Splash(SplashBitmap *bitmapA, bool vectorAntialiasA, |
| SplashScreen *screenA) { |
| int i; |
| |
| bitmap = bitmapA; |
| inShading = false; |
| vectorAntialias = vectorAntialiasA; |
| state = new SplashState(bitmap->width, bitmap->height, vectorAntialias, |
| screenA); |
| if (vectorAntialias) { |
| aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize, |
| 1, splashModeMono1, false); |
| for (i = 0; i <= splashAASize * splashAASize; ++i) { |
| aaGamma[i] = (unsigned char)splashRound( |
| splashPow((SplashCoord)i / |
| (SplashCoord)(splashAASize * splashAASize), |
| splashAAGamma) * 255); |
| } |
| } else { |
| aaBuf = nullptr; |
| } |
| minLineWidth = 0; |
| thinLineMode = splashThinLineDefault; |
| clearModRegion(); |
| debugMode = false; |
| alpha0Bitmap = nullptr; |
| } |
| |
| Splash::~Splash() { |
| while (state->next) { |
| restoreState(); |
| } |
| delete state; |
| 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; |
| } |
| |
| bool Splash::getStrokeAdjust() { |
| return state->strokeAdjust; |
| } |
| |
| SplashClip *Splash::getClip() { |
| return state->clip; |
| } |
| |
| SplashBitmap *Splash::getSoftMask() { |
| return state->softMask; |
| } |
| |
| bool 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 = (state->multiplyPatternAlpha) ? alpha * state->patternStrokeAlpha : alpha; |
| } |
| |
| void Splash::setFillAlpha(SplashCoord alpha) { |
| state->fillAlpha = (state->multiplyPatternAlpha) ? alpha * state->patternFillAlpha : alpha; |
| } |
| |
| void Splash::setPatternAlpha(SplashCoord strokeAlpha, SplashCoord fillAlpha) { |
| state->patternStrokeAlpha = strokeAlpha; |
| state->patternFillAlpha = fillAlpha; |
| state->multiplyPatternAlpha = true; |
| } |
| |
| void Splash::clearPatternAlpha() { |
| state->patternStrokeAlpha = 1; |
| state->patternFillAlpha = 1; |
| state->multiplyPatternAlpha = false; |
| } |
| |
| void Splash::setFillOverprint(bool fop) { |
| state->fillOverprint = fop; |
| } |
| |
| void Splash::setStrokeOverprint(bool 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(bool 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, bool 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 = true; |
| } |
| |
| void Splash::setTransfer(unsigned char *red, unsigned char *green, unsigned char *blue, |
| unsigned char *gray) { |
| state->setTransfer(red, green, blue, gray); |
| } |
| |
| void Splash::setOverprintMask(unsigned int overprintMask, bool 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, unsigned char alpha) { |
| SplashColorPtr row, p; |
| unsigned char 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; |
| #ifdef 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; |
| 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; |
| |
| SplashXPath xPath(path, state->matrix, state->flatness, false); |
| |
| pipeInit(&pipe, 0, 0, state->strokePattern, nullptr, |
| (unsigned char)splashRound(state->strokeAlpha * 255), |
| false, false); |
| |
| 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; |
| } |
| } |
| |
| void Splash::strokeWide(SplashPath *path, SplashCoord w) { |
| SplashPath *path2; |
| |
| path2 = makeStrokePath(path, w, false); |
| fillWithPattern(path2, false, state->strokePattern, state->strokeAlpha); |
| delete path2; |
| } |
| |
| SplashPath *Splash::flattenPath(SplashPath *path, SplashCoord *matrix, |
| SplashCoord flatness) { |
| SplashPath *fPath; |
| SplashCoord flatness2; |
| unsigned char flag; |
| int i; |
| |
| fPath = new SplashPath(); |
| #ifdef 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); |
| #ifdef 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); |
| #ifdef 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; |
| bool 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 = true; |
| 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 = true; |
| 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 = false; |
| } |
| 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 = false; |
| } |
| 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 = true; |
| } |
| } |
| } |
| i = j + 1; |
| } |
| |
| if (dPath->length == 0) { |
| bool allSame = true; |
| 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, bool 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, bool eo, |
| SplashPattern *pattern, |
| SplashCoord alpha) { |
| SplashPipe pipe = {}; |
| int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y; |
| SplashClipResult clipRes, clipRes2; |
| bool adjustLine = false; |
| 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(true); |
| 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 = true; |
| } else if (state->clip->getXMinI() == state->clip->getXMaxI() - 1) { |
| adjustLine = true; |
| linePosI = splashFloor(state->clip->getXMin() + state->lineWidth); |
| } else if (state->clip->getYMinI() == state->clip->getYMaxI()) { |
| linePosI = state->clip->getYMinI(); |
| adjustLine = true; |
| } else if (state->clip->getYMinI() == state->clip->getYMaxI() - 1) { |
| adjustLine = true; |
| linePosI = splashFloor(state->clip->getYMin() + state->lineWidth); |
| } |
| } |
| |
| SplashXPath xPath(path, state->matrix, state->flatness, true, |
| 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; |
| } |
| SplashXPathScanner scanner(&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; |
| return splashOk; |
| } |
| } |
| |
| // check clipping |
| if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI)) |
| != splashClipAllOutside) { |
| if (scanner.hasPartialClip()) { |
| clipRes = splashClipPartial; |
| } |
| |
| pipeInit(&pipe, 0, yMinI, pattern, nullptr, (unsigned char)splashRound(alpha * 255), |
| vectorAntialias && !inShading, false); |
| |
| // 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); |
| } |
| unsigned char lineShape = 255; |
| bool adjustLine = false; |
| 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 = true; |
| lineShape = clip255((delta - mx) * 255); |
| } |
| drawAALine(&pipe, x0, x1, y, adjustLine, lineShape); |
| } |
| } else { |
| for (y = yMinI; y <= yMaxI; ++y) { |
| SplashXPathScanIterator iterator(scanner, y); |
| while (iterator.getNextSpan(&x0, &x1)) { |
| if (clipRes == splashClipAllInside) { |
| drawSpan(&pipe, x0, x1, y, true); |
| } 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; |
| |
| return splashOk; |
| } |
| |
| bool 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, bool eo) { |
| SplashPipe pipe; |
| int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y; |
| SplashClipResult clipRes, clipRes2; |
| SplashBlendFunc origBlendFunc; |
| |
| if (path->length == 0) { |
| return splashErrEmptyPath; |
| } |
| SplashXPath xPath(path, state->matrix, state->flatness, true); |
| xPath.sort(); |
| SplashXPathScanner scanner(&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, nullptr, 255, false, false); |
| |
| // draw the spans |
| for (y = yMinI; y <= yMaxI; ++y) { |
| SplashXPathScanIterator iterator(scanner, y); |
| while (iterator.getNextSpan(&x0, &x1)) { |
| if (clipRes == splashClipAllInside) { |
| drawSpan(&pipe, x0, x1, y, true); |
| } 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; |
| |
| 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, bool noClip) { |
| SplashPipe pipe; |
| int alpha0; |
| unsigned char alpha; |
| unsigned char *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, nullptr, (unsigned char)splashRound(state->fillAlpha * 255), true, false); |
| 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, nullptr, (unsigned char)splashRound(state->fillAlpha * 255), false, false); |
| 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 && xx < xxLimit - 8 ? (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, nullptr, (unsigned char)splashRound(state->fillAlpha * 255), true, false); |
| 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, nullptr, (unsigned char)splashRound(state->fillAlpha * 255), false, false); |
| 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 && xx < xxLimit - 8 ? (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, |
| bool glyphMode) { |
| SplashBitmap *scaledMask; |
| SplashClipResult clipRes; |
| bool 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, bool 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 == nullptr) { |
| 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, nullptr, |
| (unsigned char)splashRound(state->fillAlpha * 255), true, false); |
| 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 (unlikely(xx < 0)) { |
| xx = 0; |
| clipRes2 = splashClipPartial; |
| } else if (unlikely(xx >= scaledWidth)) { |
| xx = scaledWidth - 1; |
| clipRes2 = splashClipPartial; |
| } |
| if (unlikely(yy < 0)) { |
| yy = 0; |
| clipRes2 = splashClipPartial; |
| } else if (unlikely(yy >= scaledHeight)) { |
| yy = scaledHeight - 1; |
| clipRes2 = splashClipPartial; |
| } |
| 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, |
| false); |
| 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) { |
| unsigned char *lineBuf; |
| unsigned int *pixBuf; |
| unsigned int pix; |
| unsigned char *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 = (unsigned char *)gmalloc(srcWidth); |
| pixBuf = (unsigned int *)gmallocn_checkoverflow(srcWidth, sizeof(int)); |
| if (unlikely(!pixBuf)) { |
| error(errInternal, -1, "Couldn't allocate memory for pixBux in Splash::scaleMaskYdXd"); |
| gfree(lineBuf); |
| return; |
| } |
| |
| // 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++ = (unsigned char)pix; |
| } |
| } |
| |
| gfree(pixBuf); |
| gfree(lineBuf); |
| } |
| |
| void Splash::scaleMaskYdXu(SplashImageMaskSource src, void *srcData, |
| int srcWidth, int srcHeight, |
| int scaledWidth, int scaledHeight, |
| SplashBitmap *dest) { |
| unsigned char *lineBuf; |
| unsigned int *pixBuf; |
| unsigned int pix; |
| unsigned char *destPtr; |
| int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, d; |
| int i, j; |
| |
| destPtr = dest->data; |
| if (destPtr == nullptr) { |
| 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 = (unsigned char *)gmalloc(srcWidth); |
| pixBuf = (unsigned int *)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++ = (unsigned char)pix; |
| } |
| } |
| } |
| |
| gfree(pixBuf); |
| gfree(lineBuf); |
| } |
| |
| void Splash::scaleMaskYuXd(SplashImageMaskSource src, void *srcData, |
| int srcWidth, int srcHeight, |
| int scaledWidth, int scaledHeight, |
| SplashBitmap *dest) { |
| unsigned char *lineBuf; |
| unsigned int pix; |
| unsigned char *destPtr0, *destPtr; |
| int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, d, d0, d1; |
| int i; |
| |
| destPtr0 = dest->data; |
| if (destPtr0 == nullptr) { |
| 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 = (unsigned char *)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 = (unsigned char)pix; |
| } |
| } |
| |
| destPtr0 += yStep * scaledWidth; |
| } |
| |
| gfree(lineBuf); |
| } |
| |
| void Splash::scaleMaskYuXu(SplashImageMaskSource src, void *srcData, |
| int srcWidth, int srcHeight, |
| int scaledWidth, int scaledHeight, |
| SplashBitmap *dest) { |
| unsigned char *lineBuf; |
| unsigned int pix; |
| unsigned char *destPtr0, *destPtr; |
| int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx; |
| int i, j; |
| |
| destPtr0 = dest->data; |
| if (destPtr0 == nullptr) { |
| error(errInternal, -1, "dest->data is NULL in Splash::scaleMaskYuXu"); |
| return; |
| } |
| |
| if (unlikely(srcWidth <= 0 || srcHeight <= 0)) { |
| error(errSyntaxError, -1, "srcWidth <= 0 || srcHeight <= 0 in Splash::scaleMaskYuXu"); |
| gfree(dest->takeData()); |
| 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 = (unsigned char *)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++ = (unsigned char)pix; |
| } |
| } |
| |
| xx += xStep; |
| } |
| |
| destPtr0 += yStep * scaledWidth; |
| } |
| |
| gfree(lineBuf); |
| } |
| |
| void Splash::blitMask(SplashBitmap *src, int xDest, int yDest, |
| SplashClipResult clipRes) { |
| SplashPipe pipe; |
| unsigned char *p; |
| int w, h, x, y; |
| |
| w = src->getWidth(); |
| h = src->getHeight(); |
| p = src->getDataPtr(); |
| if (p == nullptr) { |
| error(errInternal, -1, "src->getDataPtr() is NULL in Splash::blitMask"); |
| return; |
| } |
| if (vectorAntialias && clipRes != splashClipAllInside) { |
| pipeInit(&pipe, xDest, yDest, state->fillPattern, nullptr, |
| (unsigned char)splashRound(state->fillAlpha * 255), true, false); |
| 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, nullptr, |
| (unsigned char)splashRound(state->fillAlpha * 255), true, false); |
| 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, bool srcAlpha, |
| int w, int h, SplashCoord *mat, bool interpolate, |
| bool tilingPattern) { |
| bool ok; |
| SplashBitmap *scaledImg; |
| SplashClipResult clipRes; |
| bool 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 = false; // 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; |
| #ifdef SPLASH_CMYK |
| case splashModeCMYK8: |
| ok = srcMode == splashModeCMYK8; |
| nComps = 4; |
| break; |
| case splashModeDeviceN8: |
| ok = srcMode == splashModeDeviceN8; |
| nComps = SPOT_NCOMPS+4; |
| break; |
| #endif |
| default: |
| ok = false; |
| 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 == nullptr) { |
| return splashErrBadArg; |
| } |
| if (tf != nullptr) { |
| (*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 == nullptr) { |
| return splashErrBadArg; |
| } |
| if (tf != nullptr) { |
| (*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, |
| bool srcAlpha, |
| int srcWidth, int srcHeight, |
| SplashCoord *mat, bool interpolate, |
| bool 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 == nullptr) { |
| return splashErrBadArg; |
| } |
| |
| if (tf != nullptr) { |
| (*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, nullptr, pixel, |
| (unsigned char)splashRound(state->fillAlpha * 255), |
| srcAlpha || (vectorAntialias && clipRes != splashClipAllInside), |
| false); |
| 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 bool isImageInterpolationRequired(int srcWidth, int srcHeight, |
| int scaledWidth, int scaledHeight, |
| bool interpolate) { |
| if (interpolate || srcWidth == 0 || srcHeight == 0) |
| return true; |
| |
| /* When scale factor is >= 400% we don't interpolate. See bugs #25268, #9860 */ |
| if (scaledWidth / srcWidth >= 4 || scaledHeight / srcHeight >= 4) |
| return false; |
| |
| return true; |
| } |
| |
| // Scale an image into a SplashBitmap. |
| SplashBitmap *Splash::scaleImage(SplashImageSource src, void *srcData, |
| SplashColorMode srcMode, int nComps, |
| bool srcAlpha, int srcWidth, int srcHeight, |
| int scaledWidth, int scaledHeight, bool interpolate, bool tilingPattern) { |
| SplashBitmap *dest; |
| |
| dest = new SplashBitmap(scaledWidth, scaledHeight, 1, srcMode, srcAlpha, true, bitmap->getSeparationList()); |
| if (dest->getDataPtr() != nullptr && srcHeight > 0 && srcWidth > 0) { |
| 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 = nullptr; |
| } |
| return dest; |
| } |
| |
| void Splash::scaleImageYdXd(SplashImageSource src, void *srcData, |
| SplashColorMode srcMode, int nComps, |
| bool srcAlpha, int srcWidth, int srcHeight, |
| int scaledWidth, int scaledHeight, |
| SplashBitmap *dest) { |
| unsigned char *lineBuf, *alphaLineBuf; |
| unsigned int *pixBuf, *alphaPixBuf; |
| unsigned int pix0, pix1, pix2; |
| #ifdef SPLASH_CMYK |
| unsigned int pix3; |
| unsigned int pix[SPOT_NCOMPS+4], cp; |
| #endif |
| unsigned int alpha; |
| unsigned char *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 = (unsigned char *)gmallocn_checkoverflow(srcWidth, nComps); |
| if (unlikely(!lineBuf)) { |
| return; |
| } |
| pixBuf = (unsigned int *)gmallocn_checkoverflow(srcWidth, nComps * sizeof(int)); |
| if (unlikely(!pixBuf)) { |
| gfree(lineBuf); |
| return; |
| } |
| if (srcAlpha) { |
| alphaLineBuf = (unsigned char *)gmalloc(srcWidth); |
| alphaPixBuf = (unsigned int *)gmallocn(srcWidth, sizeof(int)); |
| } else { |
| alphaLineBuf = nullptr; |
| alphaPixBuf = nullptr; |
| } |
| |
| // 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++ = (unsigned char)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++ = (unsigned char)pix0; |
| *destPtr++ = (unsigned char)pix1; |
| *destPtr++ = (unsigned char)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++ = (unsigned char)pix2; |
| *destPtr++ = (unsigned char)pix1; |
| *destPtr++ = (unsigned char)pix0; |
| *destPtr++ = (unsigned char)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++ = (unsigned char)pix2; |
| *destPtr++ = (unsigned char)pix1; |
| *destPtr++ = (unsigned char)pix0; |
| break; |
| |
| #ifdef 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++ = (unsigned char)pix0; |
| *destPtr++ = (unsigned char)pix1; |
| *destPtr++ = (unsigned char)pix2; |
| *destPtr++ = (unsigned char)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++ = (unsigned char)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++ = (unsigned char)alpha; |
| } |
| } |
| } |
| |
| gfree(alphaPixBuf); |
| gfree(alphaLineBuf); |
| gfree(pixBuf); |
| gfree(lineBuf); |
| } |
| |
| void Splash::scaleImageYdXu(SplashImageSource src, void *srcData, |
| SplashColorMode srcMode, int nComps, |
| bool srcAlpha, int srcWidth, int srcHeight, |
| int scaledWidth, int scaledHeight, |
| SplashBitmap *dest) { |
| unsigned char *lineBuf, *alphaLineBuf; |
| unsigned int *pixBuf, *alphaPixBuf; |
| unsigned int pix[splashMaxColorComps]; |
| unsigned int alpha; |
| unsigned char *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 = (unsigned char *)gmallocn(srcWidth, nComps); |
| pixBuf = (unsigned int *)gmallocn(srcWidth, nComps * sizeof(int)); |
| if (srcAlpha) { |
| alphaLineBuf = (unsigned char *)gmalloc(srcWidth); |
| alphaPixBuf = (unsigned int *)gmallocn(srcWidth, sizeof(int)); |
| } else { |
| alphaLineBuf = nullptr; |
| alphaPixBuf = nullptr; |
| } |
| |
| // 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++ = (unsigned char)pix[0]; |
| } |
| break; |
| case splashModeRGB8: |
| for (i = 0; i < xStep; ++i) { |
| *destPtr++ = (unsigned char)pix[0]; |
| *destPtr++ = (unsigned char)pix[1]; |
| *destPtr++ = (unsigned char)pix[2]; |
| } |
| break; |
| case splashModeXBGR8: |
| for (i = 0; i < xStep; ++i) { |
| *destPtr++ = (unsigned char)pix[2]; |
| *destPtr++ = (unsigned char)pix[1]; |
| *destPtr++ = (unsigned char)pix[0]; |
| *destPtr++ = (unsigned char)255; |
| } |
| break; |
| case splashModeBGR8: |
| for (i = 0; i < xStep; ++i) { |
| *destPtr++ = (unsigned char)pix[2]; |
| *destPtr++ = (unsigned char)pix[1]; |
| *destPtr++ = (unsigned char)pix[0]; |
| } |
| break; |
| #ifdef SPLASH_CMYK |
| case splashModeCMYK8: |
| for (i = 0; i < xStep; ++i) { |
| *destPtr++ = (unsigned char)pix[0]; |
| *destPtr++ = (unsigned char)pix[1]; |
| *destPtr++ = (unsigned char)pix[2]; |
| *destPtr++ = (unsigned char)pix[3]; |
| } |
| break; |
| case splashModeDeviceN8: |
| for (i = 0; i < xStep; ++i) { |
| for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) |
| *destPtr++ = (unsigned char)pix[cp]; |
| } |
| break; |
| #endif |
| } |
| |
| // process alpha |
| if (srcAlpha) { |
| // alphaPixBuf[] / yStep |
| alpha = (alphaPixBuf[x] * d) >> 23; |
| for (i = 0; i < xStep; ++i) { |
| *destAlphaPtr++ = (unsigned char)alpha; |
| } |
| } |
| } |
| } |
| |
| gfree(alphaPixBuf); |
| gfree(alphaLineBuf); |
| gfree(pixBuf); |
| gfree(lineBuf); |
| } |
| |
| void Splash::scaleImageYuXd(SplashImageSource src, void *srcData, |
| SplashColorMode srcMode, int nComps, |
| bool srcAlpha, int srcWidth, int srcHeight, |
| int scaledWidth, int scaledHeight, |
| SplashBitmap *dest) { |
| unsigned char *lineBuf, *alphaLineBuf; |
| unsigned int pix[splashMaxColorComps]; |
| unsigned int alpha; |
| unsigned char *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 = (unsigned char *)gmallocn_checkoverflow(srcWidth, nComps); |
| if (unlikely(!lineBuf)) { |
| gfree(dest->takeData()); |
| return; |
| } |
| if (srcAlpha) { |
| alphaLineBuf = (unsigned char *)gmalloc(srcWidth); |
| } else { |
| alphaLineBuf = nullptr; |
| } |
| |
| // 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++ = (unsigned char)pix[0]; |
| } |
| break; |
| case splashModeRGB8: |
| for (i = 0; i < yStep; ++i) { |
| destPtr = destPtr0 + (i * scaledWidth + x) * nComps; |
| *destPtr++ = (unsigned char)pix[0]; |
| *destPtr++ = (unsigned char)pix[1]; |
| *destPtr++ = (unsigned char)pix[2]; |
| } |
| break; |
| case splashModeXBGR8: |
| for (i = 0; i < yStep; ++i) { |
| destPtr = destPtr0 + (i * scaledWidth + x) * nComps; |
| *destPtr++ = (unsigned char)pix[2]; |
| *destPtr++ = (unsigned char)pix[1]; |
| *destPtr++ = (unsigned char)pix[0]; |
| *destPtr++ = (unsigned char)255; |
| } |
| break; |
| case splashModeBGR8: |
| for (i = 0; i < yStep; ++i) { |
| destPtr = destPtr0 + (i * scaledWidth + x) * nComps; |
| *destPtr++ = (unsigned char)pix[2]; |
| *destPtr++ = (unsigned char)pix[1]; |
| *destPtr++ = (unsigned char)pix[0]; |
| } |
| break; |
| #ifdef SPLASH_CMYK |
| case splashModeCMYK8: |
| for (i = 0; i < yStep; ++i) { |
| destPtr = destPtr0 + (i * scaledWidth + x) * nComps; |
| *destPtr++ = (unsigned char)pix[0]; |
| *destPtr++ = (unsigned char)pix[1]; |
| *destPtr++ = (unsigned char)pix[2]; |
| *destPtr++ = (unsigned char)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++ = (unsigned char)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 = (unsigned char)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, |
| bool srcAlpha, int srcWidth, int srcHeight, |
| int scaledWidth, int scaledHeight, |
| SplashBitmap *dest) { |
| unsigned char *lineBuf, *alphaLineBuf; |
| unsigned int pix[splashMaxColorComps]; |
| unsigned int alpha; |
| unsigned char *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 = (unsigned char *)gmallocn(srcWidth, nComps); |
| if (srcAlpha) { |
| alphaLineBuf = (unsigned char *)gmalloc(srcWidth); |
| } else { |
| alphaLineBuf = nullptr; |
| } |
| |
| // 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++ = (unsigned char)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++ = (unsigned char)pix[0]; |
| *destPtr++ = (unsigned char)pix[1]; |
| *destPtr++ = (unsigned char)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++ = (unsigned char)pix[2]; |
| *destPtr++ = (unsigned char)pix[1]; |
| *destPtr++ = (unsigned char)pix[0]; |
| *destPtr++ = (unsigned char)255; |
| } |
| } |
| break; |
| case splashModeBGR8: |
| for (i = 0; i < yStep; ++i) { |
| for (j = 0; j < xStep; ++j) { |
| destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; |
| *destPtr++ = (unsigned char)pix[2]; |
| *destPtr++ = (unsigned char)pix[1]; |
| *destPtr++ = (unsigned char)pix[0]; |
| } |
| } |
| break; |
| #ifdef SPLASH_CMYK |
| case splashModeCMYK8: |
| for (i = 0; i < yStep; ++i) { |
| for (j = 0; j < xStep; ++j) { |
| destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; |
| *destPtr++ = (unsigned char)pix[0]; |
| *destPtr++ = (unsigned char)pix[1]; |
| *destPtr++ = (unsigned char)pix[2]; |
| *destPtr++ = (unsigned char)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++ = (unsigned char)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 = (unsigned char)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(unsigned char *srcBuf, unsigned char *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, |
| bool srcAlpha, int srcWidth, int srcHeight, |
| int scaledWidth, int scaledHeight, |
| SplashBitmap *dest) { |
| unsigned char *srcBuf, *lineBuf1, *lineBuf2, *alphaSrcBuf, *alphaLineBuf1, *alphaLineBuf2; |
| unsigned int pix[splashMaxColorComps]; |
| unsigned char *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr; |
| int i; |
| |
| if (srcWidth < 1 || srcHeight < 1) |
| return; |
| |
| // allocate buffers |
| srcBuf = (unsigned char *)gmallocn(srcWidth+1, nComps); // + 1 pixel of padding |
| lineBuf1 = (unsigned char *)gmallocn(scaledWidth, nComps); |
| lineBuf2 = (unsigned char *)gmallocn(scaledWidth, nComps); |
| if (srcAlpha) { |
| alphaSrcBuf = (unsigned char *)gmalloc(srcWidth+1); // + 1 pixel of padding |
| alphaLineBuf1 = (unsigned char *)gmalloc(scaledWidth); |
| alphaLineBuf2 = (unsigned char *)gmalloc(scaledWidth); |
| } else { |
| alphaSrcBuf = nullptr; |
| alphaLineBuf1 = nullptr; |
| alphaLineBuf2 = nullptr; |
| } |
| |
| 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++ = (unsigned char)pix[0]; |
| break; |
| case splashModeRGB8: |
| *destPtr++ = (unsigned char)pix[0]; |
| *destPtr++ = (unsigned char)pix[1]; |
| *destPtr++ = (unsigned char)pix[2]; |
| break; |
| case splashModeXBGR8: |
| *destPtr++ = (unsigned char)pix[2]; |
| *destPtr++ = (unsigned char)pix[1]; |
| *destPtr++ = (unsigned char)pix[0]; |
| *destPtr++ = (unsigned char)255; |
| break; |
| case splashModeBGR8: |
| *destPtr++ = (unsigned char)pix[2]; |
| *destPtr++ = (unsigned char)pix[1]; |
| *destPtr++ = (unsigned char)pix[0]; |
| break; |
| #ifdef SPLASH_CMYK |
| case splashModeCMYK8: |
| *destPtr++ = (unsigned char)pix[0]; |
| *destPtr++ = (unsigned char)pix[1]; |
| *destPtr++ = (unsigned char)pix[2]; |
| *destPtr++ = (unsigned char)pix[3]; |
| break; |
| case splashModeDeviceN8: |
| for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) |
| *destPtr++ = (unsigned char)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) { |
| unsigned char *lineBuf; |
| unsigned char *p0, *p1; |
| int w; |
| |
| if (unlikely(img->data == nullptr)) { |
| error(errInternal, -1, "img->data is NULL in Splash::vertFlipImage"); |
| return; |
| } |
| |
| w = width * nComps; |
| lineBuf = (unsigned char *)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, bool 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, bool srcAlpha, int xDest, int yDest, |
| SplashClipResult clipRes) { |
| SplashPipe pipe; |
| SplashColor pixel = {}; |
| unsigned char *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, nullptr, pixel, |
| (unsigned char)splashRound(state->fillAlpha * 255), srcAlpha, false); |
| 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, bool srcAlpha, |
| int xSrc, int ySrc, int xDest, int yDest, |
| int w, int h) { |
| SplashPipe pipe; |
| SplashColor pixel = {}; |
| unsigned char *ap; |
| int x, y; |
| |
| if (vectorAntialias) { |
| pipeInit(&pipe, xDest, yDest, nullptr, pixel, |
| (unsigned char)splashRound(state->fillAlpha * 255), true, false); |
| 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, nullptr, pixel, |
| (unsigned char)splashRound(state->fillAlpha * 255), srcAlpha, false); |
| 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, |
| bool noClip, bool nonIsolated, |
| bool knockout, SplashCoord knockoutOpacity) { |
| SplashPipe pipe; |
| SplashColor pixel; |
| unsigned char alpha; |
| unsigned char *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()->push_back(((GfxSeparationColorSpace *)src->getSeparationList()->get(x))->copy()); |
| } |
| if (src->alpha) { |
| pipeInit(&pipe, xDest, yDest, nullptr, pixel, |
| (unsigned char)splashRound(state->fillAlpha * 255), true, nonIsolated, |
| knockout, (unsigned char)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, nullptr, pixel, |
| (unsigned char)splashRound(state->fillAlpha * 255), false, 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; |
| unsigned char *q; |
| unsigned char alpha, alpha1, c, color0, color1, color2; |
| #ifdef SPLASH_CMYK |
| unsigned char color3; |
| unsigned char colorsp[SPOT_NCOMPS+4], cp; |
| #endif |
| int x, y, mask; |
| |
| if (unlikely(bitmap->alpha == nullptr)) { |
| 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; |
| #ifdef 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); |
| } |
| |
| bool 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 }; |
| bool hasFurtherSegment = false; |
| |
| 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 = nullptr; |
| SplashCoord* userToCanvasMatrix = getMatrix(); |
| SplashColorMode bitmapMode = bitmap->getMode(); |
| bool hasAlpha = (bitmapAlpha != nullptr); |
| 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; |
| #ifdef SPLASH_CMYK |
| case splashModeCMYK8: |
| colorComps=4; |
| break; |
| case splashModeDeviceN8: |
| colorComps=SPOT_NCOMPS+4; |
| break; |
| #endif |
| } |
| |
| SplashPipe pipe; |
| SplashColor cSrcVal; |
| |
| pipeInit(&pipe, 0, 0, nullptr, cSrcVal, (unsigned char)splashRound(state->strokeAlpha * 255), false, false); |
| |
| if (vectorAntialias) { |
| if (aaBuf == nullptr) |
| return false; // 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); |
| bool bDirectBlit = vectorAntialias ? false : pipe.noTransparency && !state->blendFunc; |
| if (!bDirectBlit) { |
| blitTarget = new SplashBitmap(bitmap->getWidth(), |
| bitmap->getHeight(), |
| bitmap->getRowPad(), |
| bitmap->getMode(), |
| true, |
| 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 = true; |
| } |
| |
| 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 = false; |
| } |
| |
| 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; |
| if (likely(bitmapOff >= 0)) { |
| 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 { |
| if (!bDirectBlit) { |
| delete blitTarget; |
| } |
| return false; |
| } |
| |
| 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, true); // no clipping - has already been done. |
| } |
| } |
| } |
| |
| delete blitTarget; |
| blitTarget = nullptr; |
| } |
| |
| return true; |
| } |
| |
| SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc, |
| int xDest, int yDest, int w, int h) { |
| SplashColorPtr p, sp; |
| unsigned char *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; |
| #ifdef 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, |
| bool flatten) { |
| SplashPath *pathIn, *dashPath, *pathOut; |
| SplashCoord d, dx, dy, wdx, wdy, dxNext, dyNext, wdxNext, wdyNext; |
| SplashCoord crossprod, dotprod, miter, m; |
| bool first, last, closed, hasangle; |
| 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 = false; // 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) |
| #ifdef 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) |
| #ifdef 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); |
| hasangle = crossprod != 0 || dx * dxNext < 0 || dy * dyNext < 0; |
| 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 (hasangle && 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 if (hasangle) { |
| 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, bool hasBBox, |
| SplashPattern *pattern) { |
| SplashPipe pipe; |
| int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y; |
| SplashClipResult clipRes; |
| |
| if (vectorAntialias && aaBuf == nullptr) { // should not happen, but to be secure |
| return splashErrGeneric; |
| } |
| if (path->length == 0) { |
| return splashErrEmptyPath; |
| } |
| SplashXPath xPath(path, state->matrix, state->flatness, true); |
| 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; |
| } |
| SplashXPathScanner scanner(&xPath, false, 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, nullptr, (unsigned char)splashRound(state->fillAlpha * 255), vectorAntialias && !hasBBox, false); |
| |
| // 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: |
| unsigned char *p0, *p1, *p2, *p3; |
| unsigned char 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) ) |
| { |
| unsigned char 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) ) |
| { |
| unsigned char 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) { |
| SplashXPathScanIterator iterator(scanner, y); |
| while (iterator.getNextSpan(&x0, &x1)) { |
| if (clipRes == splashClipAllInside) { |
| drawSpan(&pipe, x0, x1, y, true); |
| } 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; |
| |
| return splashOk; |
| } |