| //======================================================================== |
| // |
| // 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-2023 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> |
| // Copyright (C) 2019, 2020 Oliver Sander <oliver.sander@tu-dresden.de> |
| // Copyright (C) 2019 Marek Kasik <mkasik@redhat.com> |
| // Copyright (C) 2020 Tobias Deiminger <haxtibal@posteo.de> |
| // Copyright (C) 2021, 2024 Even Rouault <even.rouault@spatialys.com> |
| // |
| // 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 <cstdlib> |
| #include <cstring> |
| #include <climits> |
| #include <cassert> |
| #include <cmath> |
| #include "goo/gmem.h" |
| #include "goo/GooLikely.h" |
| #include "poppler/GfxState.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> |
| |
| // the MSVC math.h doesn't define this |
| #ifndef M_PI |
| # define M_PI 3.14159265358979323846 |
| #endif |
| |
| //------------------------------------------------------------------------ |
| |
| #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, splashPipeResultColorNoAlphaBlendCMYK, splashPipeResultColorNoAlphaBlendDeviceN }; |
| |
| SplashPipeResultColorCtrl Splash::pipeResultColorAlphaNoBlend[] = { splashPipeResultColorAlphaNoBlendMono, splashPipeResultColorAlphaNoBlendMono, splashPipeResultColorAlphaNoBlendRGB, splashPipeResultColorAlphaNoBlendRGB, |
| splashPipeResultColorAlphaNoBlendRGB, splashPipeResultColorAlphaNoBlendCMYK, splashPipeResultColorAlphaNoBlendDeviceN }; |
| |
| SplashPipeResultColorCtrl Splash::pipeResultColorAlphaBlend[] = { splashPipeResultColorAlphaBlendMono, splashPipeResultColorAlphaBlendMono, splashPipeResultColorAlphaBlendRGB, splashPipeResultColorAlphaBlendRGB, |
| splashPipeResultColorAlphaBlendRGB, splashPipeResultColorAlphaBlendCMYK, splashPipeResultColorAlphaBlendDeviceN }; |
| |
| //------------------------------------------------------------------------ |
| |
| 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]; |
| } |
| } |
| |
| //------------------------------------------------------------------------ |
| // 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; |
| } else if (bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr) { |
| pipe->run = &Splash::pipeRunSimpleCMYK8; |
| } else if (bitmap->mode == splashModeDeviceN8 && pipe->destAlphaPtr) { |
| pipe->run = &Splash::pipeRunSimpleDeviceN8; |
| } |
| } 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; |
| } else if (bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr) { |
| pipe->run = &Splash::pipeRunAACMYK8; |
| } else if (bitmap->mode == splashModeDeviceN8 && pipe->destAlphaPtr) { |
| pipe->run = &Splash::pipeRunAADeviceN8; |
| } |
| } |
| } |
| |
| // 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; |
| int cp, mask; |
| unsigned char cResult[SPOT_NCOMPS + 4]; |
| |
| //----- source color |
| |
| // static pattern: handled in pipeInit |
| // fixed color: handled in pipeInit |
| |
| // dynamic pattern |
| if (pipe->pattern) { |
| if (!pipe->pattern->getColor(pipe->x, pipe->y, pipe->cSrcVal)) { |
| pipeIncX(pipe); |
| return; |
| } |
| if (bitmap->mode == splashModeCMYK8 || bitmap->mode == splashModeDeviceN8) { |
| if (state->fillOverprint && state->overprintMode && pipe->pattern->isCMYK()) { |
| unsigned int overprintMask = 15; |
| if (pipe->cSrcVal[0] == 0) { |
| overprintMask &= ~1; |
| } |
| if (pipe->cSrcVal[1] == 0) { |
| overprintMask &= ~2; |
| } |
| if (pipe->cSrcVal[2] == 0) { |
| overprintMask &= ~4; |
| } |
| if (pipe->cSrcVal[3] == 0) { |
| overprintMask &= ~8; |
| } |
| state->overprintMask = overprintMask; |
| } |
| } |
| } |
| |
| 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; |
| 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; |
| } |
| 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: |
| case splashModeCMYK8: |
| destColorPtr += (alpha0X + pipe->x) * 4; |
| break; |
| case splashModeDeviceN8: |
| destColorPtr += (alpha0X + pipe->x) * (SPOT_NCOMPS + 4); |
| break; |
| } |
| } 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; |
| 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; |
| } |
| 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) { |
| 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; |
| 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) { |
| if (bitmap->mode == splashModeDeviceN8) { |
| for (int k = 4; k < 4 + SPOT_NCOMPS; k++) { |
| cBlend[k] = 0; |
| } |
| } |
| (*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; |
| 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; |
| |
| 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; |
| 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; |
| |
| 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; |
| 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; |
| } |
| |
| //----- 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; |
| 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; |
| } |
| 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; |
| } |
| |
| // 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; |
| } |
| |
| // 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; |
| } |
| |
| // 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; |
| } |
| |
| 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; |
| 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; |
| } |
| 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; |
| case splashModeCMYK8: |
| pipe->destColorPtr += 4; |
| break; |
| case splashModeDeviceN8: |
| pipe->destColorPtr += (SPOT_NCOMPS + 4); |
| break; |
| } |
| 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); |
| } |
| } |
| |
| inline void Splash::drawAAPixelInit() |
| { |
| aaBufY = -1; |
| } |
| |
| inline void Splash::drawAAPixel(SplashPipe *pipe, int x, int y) |
| { |
| #if splashAASize == 4 |
| static const 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(static_cast<int>(aaGamma[t] * pipe->shape)); |
| (this->*pipe->run)(pipe); |
| } |
| } |
| |
| 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); |
| } |
| } 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); |
| } else { |
| pipeIncX(pipe); |
| } |
| } |
| } |
| } |
| |
| inline void Splash::drawAALine(SplashPipe *pipe, int x0, int x1, int y, bool adjustLine, unsigned char lineOpacity) |
| { |
| #if splashAASize == 4 |
| static const 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(static_cast<int>((int)lineOpacity * (double)aaGamma[t])) : (int)aaGamma[t]; |
| (this->*pipe->run)(pipe); |
| } else { |
| pipeIncX(pipe); |
| } |
| } |
| } |
| |
| //------------------------------------------------------------------------ |
| |
| // Transform a point from user space to device space. |
| inline void Splash::transform(const 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; |
| 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; |
| 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::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 sop) |
| { |
| state->strokeOverprint = sop; |
| } |
| |
| 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(std::vector<SplashCoord> &&lineDash, SplashCoord lineDashPhase) |
| { |
| state->setLineDash(std::move(lineDash), 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; |
| 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; |
| } |
| |
| if (bitmap->alpha) { |
| memset(bitmap->alpha, alpha, bitmap->width * bitmap->height); |
| } |
| } |
| |
| SplashError Splash::stroke(SplashPath *path) |
| { |
| SplashPath *path2, *dPath; |
| SplashCoord d1, d2, t1, t2, w; |
| |
| if (debugMode) { |
| printf("stroke [dash:%zu] [width:%.2f]:\n", state->lineDash.size(), (double)state->lineWidth); |
| dumpPath(path); |
| } |
| opClipRes = splashClipAllOutside; |
| if (path->length == 0) { |
| return splashErrEmptyPath; |
| } |
| path2 = flattenPath(path, state->matrix, state->flatness); |
| if (!state->lineDash.empty()) { |
| 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 + (state->clip->getYMin() - seg->y0) * dxdy); |
| } |
| if (y1 > state->clip->getYMaxI()) { |
| y1 = state->clip->getYMaxI(); |
| x1 = splashFloor(seg->x0 + (state->clip->getYMax() - 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(); |
| flatness2 = flatness * flatness; |
| 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); |
| dx = tx - mx; |
| dy = ty - my; |
| d1 = dx * dx + dy * dy; |
| transform(matrix, xx2, yy2, &tx, &ty); |
| dx = tx - mx; |
| dy = ty - my; |
| d2 = dx * dx + dy * dy; |
| |
| // 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 i, j, k; |
| |
| lineDashTotal = 0; |
| for (SplashCoord dash : state->lineDash) { |
| lineDashTotal += dash; |
| } |
| // 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; |
| size_t lineDashStartIdx = 0; |
| if (lineDashStartPhase > 0) { |
| while (lineDashStartIdx < state->lineDash.size() && lineDashStartPhase >= state->lineDash[lineDashStartIdx]) { |
| lineDashStartOn = !lineDashStartOn; |
| lineDashStartPhase -= state->lineDash[lineDashStartIdx]; |
| ++lineDashStartIdx; |
| } |
| if (unlikely(lineDashStartIdx == state->lineDash.size())) { |
| 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; |
| size_t 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->lineDash.size()) { |
| lineDashIdx = 0; |
| } |
| lineDashDist = state->lineDash[lineDashIdx]; |
| newPath = true; |
| } |
| } |
| } |
| i = j + 1; |
| } |
| |
| if (dPath->length == 0) { |
| bool allSame = true; |
| for (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 doAdjustLine = 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); |
| doAdjustLine = true; |
| lineShape = clip255(static_cast<int>((delta - mx) * 255)); |
| } |
| drawAALine(&pipe, x0, x1, y, doAdjustLine, 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); |
|