diff options
Diffstat (limited to 'kpdf/xpdf/splash/Splash.cc')
-rw-r--r-- | kpdf/xpdf/splash/Splash.cc | 3335 |
1 files changed, 3335 insertions, 0 deletions
diff --git a/kpdf/xpdf/splash/Splash.cc b/kpdf/xpdf/splash/Splash.cc new file mode 100644 index 00000000..30179fda --- /dev/null +++ b/kpdf/xpdf/splash/Splash.cc @@ -0,0 +1,3335 @@ +//======================================================================== +// +// Splash.cc +// +//======================================================================== + +#include <aconf.h> + +#ifdef USE_GCC_PRAGMAS +#pragma implementation +#endif + +#include <stdlib.h> +#include <string.h> +#include "gmem.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" + +//------------------------------------------------------------------------ + +// distance of Bezier control point from center for circle approximation +// = (4 * (sqrt(2) - 1) / 3) * r +#define bezierCircle ((SplashCoord)0.55228475) +#define bezierCircle2 ((SplashCoord)(0.5 * 0.55228475)) + +// Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result. +static inline Guchar div255(int x) { + return (Guchar)((x + (x >> 8) + 0x80) >> 8); +} + +//------------------------------------------------------------------------ +// SplashPipe +//------------------------------------------------------------------------ + +#define splashPipeMaxStages 9 + +struct SplashPipe { + // pixel coordinates + int x, y; + + // source pattern + SplashPattern *pattern; + + // source alpha and color + SplashCoord aInput; + GBool usesShape; + Guchar aSrc; + SplashColorPtr cSrc; + SplashColor cSrcVal; + + // non-isolated group alpha0 + Guchar *alpha0Ptr; + + // soft mask + SplashColorPtr softMaskPtr; + + // destination alpha and color + SplashColorPtr destColorPtr; + int destColorMask; + Guchar *destAlphaPtr; + + // shape + SplashCoord shape; + + // result alpha and color + GBool noTransparency; + SplashPipeResultColorCtrl resultColorCtrl; + + // non-isolated group correction + int nonIsolatedGroup; +}; + +SplashPipeResultColorCtrl Splash::pipeResultColorNoAlphaBlend[] = { + splashPipeResultColorNoAlphaBlendMono, + splashPipeResultColorNoAlphaBlendMono, + splashPipeResultColorNoAlphaBlendRGB, + splashPipeResultColorNoAlphaBlendRGB +#if SPLASH_CMYK + , + splashPipeResultColorNoAlphaBlendCMYK +#endif +}; + +SplashPipeResultColorCtrl Splash::pipeResultColorAlphaNoBlend[] = { + splashPipeResultColorAlphaNoBlendMono, + splashPipeResultColorAlphaNoBlendMono, + splashPipeResultColorAlphaNoBlendRGB, + splashPipeResultColorAlphaNoBlendRGB +#if SPLASH_CMYK + , + splashPipeResultColorAlphaNoBlendCMYK +#endif +}; + +SplashPipeResultColorCtrl Splash::pipeResultColorAlphaBlend[] = { + splashPipeResultColorAlphaBlendMono, + splashPipeResultColorAlphaBlendMono, + splashPipeResultColorAlphaBlendRGB, + splashPipeResultColorAlphaBlendRGB +#if SPLASH_CMYK + , + splashPipeResultColorAlphaBlendCMYK +#endif +}; + +//------------------------------------------------------------------------ + +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, + SplashCoord aInput, GBool usesShape, + GBool nonIsolatedGroup) { + pipeSetXY(pipe, x, y); + pipe->pattern = NULL; + + // source color + if (pattern) { + if (pattern->isStatic()) { + pattern->getColor(x, y, pipe->cSrcVal); + } else { + pipe->pattern = pattern; + } + pipe->cSrc = pipe->cSrcVal; + } else { + pipe->cSrc = cSrc; + } + + // source alpha + pipe->aInput = aInput; + if (!state->softMask) { + if (usesShape) { + pipe->aInput *= 255; + } else { + pipe->aSrc = (Guchar)splashRound(pipe->aInput * 255); + } + } + pipe->usesShape = usesShape; + + // result alpha + if (aInput == 1 && !state->softMask && !usesShape && + !state->inNonIsolatedGroup) { + pipe->noTransparency = gTrue; + } else { + pipe->noTransparency = gFalse; + } + + // result color + if (pipe->noTransparency) { + // the !state->blendFunc case is handled separately in pipeRun + pipe->resultColorCtrl = pipeResultColorNoAlphaBlend[bitmap->mode]; + } else if (!state->blendFunc) { + pipe->resultColorCtrl = pipeResultColorAlphaNoBlend[bitmap->mode]; + } else { + pipe->resultColorCtrl = pipeResultColorAlphaBlend[bitmap->mode]; + } + + // non-isolated group correction + if (nonIsolatedGroup) { + pipe->nonIsolatedGroup = splashColorModeNComps[bitmap->mode]; + } else { + pipe->nonIsolatedGroup = 0; + } +} + +inline void Splash::pipeRun(SplashPipe *pipe) { + Guchar aSrc, aDest, alpha2, alpha0, aResult; + SplashColor cDest, cBlend; + Guchar cResult0, cResult1, cResult2, cResult3; + + //----- source color + + // static pattern: handled in pipeInit + // fixed color: handled in pipeInit + + // dynamic pattern + if (pipe->pattern) { + pipe->pattern->getColor(pipe->x, pipe->y, pipe->cSrcVal); + } + + if (pipe->noTransparency && !state->blendFunc) { + + //----- write destination pixel + + switch (bitmap->mode) { + case splashModeMono1: + cResult0 = 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++ = pipe->cSrc[0]; + break; + case splashModeRGB8: + *pipe->destColorPtr++ = pipe->cSrc[0]; + *pipe->destColorPtr++ = pipe->cSrc[1]; + *pipe->destColorPtr++ = pipe->cSrc[2]; + break; + case splashModeBGR8: + *pipe->destColorPtr++ = pipe->cSrc[2]; + *pipe->destColorPtr++ = pipe->cSrc[1]; + *pipe->destColorPtr++ = pipe->cSrc[0]; + break; +#if SPLASH_CMYK + case splashModeCMYK8: + *pipe->destColorPtr++ = pipe->cSrc[0]; + *pipe->destColorPtr++ = pipe->cSrc[1]; + *pipe->destColorPtr++ = pipe->cSrc[2]; + *pipe->destColorPtr++ = pipe->cSrc[3]; + break; +#endif + } + if (pipe->destAlphaPtr) { + *pipe->destAlphaPtr++ = 255; + } + + } else { + + //----- read destination pixel + + switch (bitmap->mode) { + case splashModeMono1: + cDest[0] = (*pipe->destColorPtr & pipe->destColorMask) ? 0xff : 0x00; + break; + case splashModeMono8: + cDest[0] = *pipe->destColorPtr; + break; + case splashModeRGB8: + cDest[0] = pipe->destColorPtr[0]; + cDest[1] = pipe->destColorPtr[1]; + cDest[2] = pipe->destColorPtr[2]; + break; + case splashModeBGR8: + cDest[0] = pipe->destColorPtr[2]; + cDest[1] = pipe->destColorPtr[1]; + cDest[2] = pipe->destColorPtr[0]; + break; +#if SPLASH_CMYK + case splashModeCMYK8: + cDest[0] = pipe->destColorPtr[0]; + cDest[1] = pipe->destColorPtr[1]; + cDest[2] = pipe->destColorPtr[2]; + cDest[3] = pipe->destColorPtr[3]; + break; +#endif + } + if (pipe->destAlphaPtr) { + aDest = *pipe->destAlphaPtr; + } else { + aDest = 0xff; + } + + //----- blend function + + if (state->blendFunc) { + (*state->blendFunc)(pipe->cSrc, cDest, cBlend, bitmap->mode); + } + + //----- source alpha + + if (state->softMask) { + if (pipe->usesShape) { + aSrc = (Guchar)splashRound(pipe->aInput * *pipe->softMaskPtr++ + * pipe->shape); + } else { + aSrc = (Guchar)splashRound(pipe->aInput * *pipe->softMaskPtr++); + } + } else if (pipe->usesShape) { + // pipe->aInput is premultiplied by 255 in pipeInit + aSrc = (Guchar)splashRound(pipe->aInput * pipe->shape); + } else { + // precomputed in pipeInit + aSrc = pipe->aSrc; + } + + //----- result alpha and non-isolated group element correction + + if (pipe->noTransparency) { + alpha2 = aResult = 255; + } else { + aResult = aSrc + aDest - div255(aSrc * aDest); + + if (pipe->alpha0Ptr) { + alpha0 = *pipe->alpha0Ptr++; + alpha2 = aResult + alpha0 - div255(aResult * alpha0); + } else { + alpha2 = aResult; + } + } + + //----- result color + + cResult0 = cResult1 = cResult2 = cResult3 = 0; // make gcc happy + + switch (pipe->resultColorCtrl) { + +#if SPLASH_CMYK + case splashPipeResultColorNoAlphaBlendCMYK: + cResult3 = div255((255 - aDest) * pipe->cSrc[3] + aDest * cBlend[3]); +#endif + case splashPipeResultColorNoAlphaBlendRGB: + cResult2 = div255((255 - aDest) * pipe->cSrc[2] + aDest * cBlend[2]); + cResult1 = div255((255 - aDest) * pipe->cSrc[1] + aDest * cBlend[1]); + case splashPipeResultColorNoAlphaBlendMono: + cResult0 = div255((255 - aDest) * pipe->cSrc[0] + aDest * cBlend[0]); + break; + + case splashPipeResultColorAlphaNoBlendMono: + if (alpha2 == 0) { + cResult0 = 0; + } else { + cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] + + aSrc * pipe->cSrc[0]) / alpha2); + } + break; + case splashPipeResultColorAlphaNoBlendRGB: + if (alpha2 == 0) { + cResult0 = 0; + cResult1 = 0; + cResult2 = 0; + } else { + cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] + + aSrc * pipe->cSrc[0]) / alpha2); + cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] + + aSrc * pipe->cSrc[1]) / alpha2); + cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] + + aSrc * pipe->cSrc[2]) / alpha2); + } + break; +#if SPLASH_CMYK + case splashPipeResultColorAlphaNoBlendCMYK: + if (alpha2 == 0) { + cResult0 = 0; + cResult1 = 0; + cResult2 = 0; + cResult3 = 0; + } else { + cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] + + aSrc * pipe->cSrc[0]) / alpha2); + cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] + + aSrc * pipe->cSrc[1]) / alpha2); + cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] + + aSrc * pipe->cSrc[2]) / alpha2); + cResult3 = (Guchar)(((alpha2 - aSrc) * cDest[3] + + aSrc * pipe->cSrc[3]) / alpha2); + } + break; +#endif + + case splashPipeResultColorAlphaBlendMono: + if (alpha2 == 0) { + cResult0 = 0; + } else { + cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] + + aSrc * ((255 - aDest) * pipe->cSrc[0] + + aDest * cBlend[0]) / 255) / + alpha2); + } + break; + case splashPipeResultColorAlphaBlendRGB: + if (alpha2 == 0) { + cResult0 = 0; + cResult1 = 0; + cResult2 = 0; + } else { + cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] + + aSrc * ((255 - aDest) * pipe->cSrc[0] + + aDest * cBlend[0]) / 255) / + alpha2); + cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] + + aSrc * ((255 - aDest) * pipe->cSrc[1] + + aDest * cBlend[1]) / 255) / + alpha2); + cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] + + aSrc * ((255 - aDest) * pipe->cSrc[2] + + aDest * cBlend[2]) / 255) / + alpha2); + } + break; +#if SPLASH_CMYK + case splashPipeResultColorAlphaBlendCMYK: + if (alpha2 == 0) { + cResult0 = 0; + cResult1 = 0; + cResult2 = 0; + cResult3 = 0; + } else { + cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] + + aSrc * ((255 - aDest) * pipe->cSrc[0] + + aDest * cBlend[0]) / 255) / + alpha2); + cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] + + aSrc * ((255 - aDest) * pipe->cSrc[1] + + aDest * cBlend[1]) / 255) / + alpha2); + cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] + + aSrc * ((255 - aDest) * pipe->cSrc[2] + + aDest * cBlend[2]) / 255) / + alpha2); + cResult3 = (Guchar)(((alpha2 - aSrc) * cDest[3] + + aSrc * ((255 - aDest) * pipe->cSrc[3] + + aDest * cBlend[3]) / 255) / + alpha2); + } + break; +#endif + } + + //----- non-isolated group correction + + if (aResult != 0) { + switch (pipe->nonIsolatedGroup) { +#if SPLASH_CMYK + case 4: + cResult3 += (cResult3 - cDest[3]) * aDest * + (255 - aResult) / (255 * aResult); +#endif + case 3: + cResult2 += (cResult2 - cDest[2]) * aDest * + (255 - aResult) / (255 * aResult); + cResult1 += (cResult1 - cDest[1]) * aDest * + (255 - aResult) / (255 * aResult); + case 1: + cResult0 += (cResult0 - cDest[0]) * aDest * + (255 - aResult) / (255 * aResult); + case 0: + 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 splashModeBGR8: + *pipe->destColorPtr++ = cResult2; + *pipe->destColorPtr++ = cResult1; + *pipe->destColorPtr++ = cResult0; + break; +#if SPLASH_CMYK + case splashModeCMYK8: + *pipe->destColorPtr++ = cResult0; + *pipe->destColorPtr++ = cResult1; + *pipe->destColorPtr++ = cResult2; + *pipe->destColorPtr++ = cResult3; + break; +#endif + } + if (pipe->destAlphaPtr) { + *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; +#if SPLASH_CMYK + case splashModeCMYK8: + pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x]; + break; +#endif + } + if (bitmap->alpha) { + pipe->destAlphaPtr = &bitmap->alpha[y * bitmap->width + x]; + } else { + pipe->destAlphaPtr = NULL; + } + if (state->inNonIsolatedGroup && alpha0Bitmap->alpha) { + pipe->alpha0Ptr = + &alpha0Bitmap->alpha[(alpha0Y + y) * alpha0Bitmap->width + + (alpha0X + x)]; + } else { + pipe->alpha0Ptr = NULL; + } +} + +inline void Splash::pipeIncX(SplashPipe *pipe) { + ++pipe->x; + if (state->softMask) { + ++pipe->softMaskPtr; + } + switch (bitmap->mode) { + case splashModeMono1: + if (!(pipe->destColorMask >>= 1)) { + pipe->destColorMask = 0x80; + ++pipe->destColorPtr; + } + break; + case splashModeMono8: + ++pipe->destColorPtr; + break; + case splashModeRGB8: + case splashModeBGR8: + pipe->destColorPtr += 3; + break; +#if SPLASH_CMYK + case splashModeCMYK8: + pipe->destColorPtr += 4; + break; +#endif + } + if (pipe->destAlphaPtr) { + ++pipe->destAlphaPtr; + } + if (pipe->alpha0Ptr) { + ++pipe->alpha0Ptr; + } +} + +inline void Splash::drawPixel(SplashPipe *pipe, int x, int y, GBool noClip) { + if (noClip || state->clip->test(x, y)) { + pipeSetXY(pipe, x, y); + pipeRun(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 *= aaGamma[t]; + pipeRun(pipe); + updateModX(x); + updateModY(y); + } +} + +inline void Splash::drawSpan(SplashPipe *pipe, int x0, int x1, int y, + GBool noClip) { + int x; + + pipeSetXY(pipe, x0, y); + if (noClip) { + for (x = x0; x <= x1; ++x) { + pipeRun(pipe); + } + updateModX(x0); + updateModX(x1); + updateModY(y); + } else { + for (x = x0; x <= x1; ++x) { + if (state->clip->test(x, y)) { + pipeRun(pipe); + updateModX(x); + updateModY(y); + } else { + pipeIncX(pipe); + } + } + } +} + +inline void Splash::drawAALine(SplashPipe *pipe, int x0, int x1, 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 }; + 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 = aaGamma[t]; + pipeRun(pipe); + updateModX(x); + updateModY(y); + } else { + pipeIncX(pipe); + } + } +} + +//------------------------------------------------------------------------ + +// Transform a point from user space to device space. +inline void Splash::transform(SplashCoord *matrix, + SplashCoord xi, SplashCoord yi, + SplashCoord *xo, SplashCoord *yo) { + // [ m[0] m[1] 0 ] + // [xo yo 1] = [xi yi 1] * [ m[2] m[3] 0 ] + // [ m[4] m[5] 1 ] + *xo = xi * matrix[0] + yi * matrix[2] + matrix[4]; + *yo = xi * matrix[1] + yi * matrix[3] + matrix[5]; +} + +//------------------------------------------------------------------------ +// Splash +//------------------------------------------------------------------------ + +Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, + SplashScreenParams *screenParams) { + int i; + + bitmap = bitmapA; + vectorAntialias = vectorAntialiasA; + state = new SplashState(bitmap->width, bitmap->height, vectorAntialias, + screenParams); + if (vectorAntialias) { + aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize, + 1, splashModeMono1, gFalse); + for (i = 0; i <= splashAASize * splashAASize; ++i) { + aaGamma[i] = splashPow((SplashCoord)i / + (SplashCoord)(splashAASize * splashAASize), + 1.5); + } + } else { + aaBuf = NULL; + } + clearModRegion(); + debugMode = gFalse; +} + +Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, + SplashScreen *screenA) { + int i; + + bitmap = bitmapA; + vectorAntialias = vectorAntialiasA; + state = new SplashState(bitmap->width, bitmap->height, vectorAntialias, + screenA); + if (vectorAntialias) { + aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize, + 1, splashModeMono1, gFalse); + for (i = 0; i <= splashAASize * splashAASize; ++i) { + aaGamma[i] = splashPow((SplashCoord)i / + (SplashCoord)(splashAASize * splashAASize), + 1.5); + } + } else { + aaBuf = NULL; + } + clearModRegion(); + debugMode = gFalse; +} + +Splash::~Splash() { + while (state->next) { + restoreState(); + } + delete state; + if (vectorAntialias) { + delete aaBuf; + } +} + +//------------------------------------------------------------------------ +// state read +//------------------------------------------------------------------------ + +SplashCoord *Splash::getMatrix() { + return state->matrix; +} + +SplashPattern *Splash::getStrokePattern() { + return state->strokePattern; +} + +SplashPattern *Splash::getFillPattern() { + return state->fillPattern; +} + +SplashScreen *Splash::getScreen() { + return state->screen; +} + +SplashBlendFunc Splash::getBlendFunc() { + return state->blendFunc; +} + +SplashCoord Splash::getStrokeAlpha() { + return state->strokeAlpha; +} + +SplashCoord Splash::getFillAlpha() { + return state->fillAlpha; +} + +SplashCoord Splash::getLineWidth() { + return state->lineWidth; +} + +int Splash::getLineCap() { + return state->lineCap; +} + +int Splash::getLineJoin() { + return state->lineJoin; +} + +SplashCoord Splash::getMiterLimit() { + return state->miterLimit; +} + +SplashCoord Splash::getFlatness() { + return state->flatness; +} + +SplashCoord *Splash::getLineDash() { + return state->lineDash; +} + +int Splash::getLineDashLength() { + return state->lineDashLength; +} + +SplashCoord Splash::getLineDashPhase() { + return state->lineDashPhase; +} + +SplashClip *Splash::getClip() { + return state->clip; +} + +SplashBitmap *Splash::getSoftMask() { + return state->softMask; +} + +GBool Splash::getInNonIsolatedGroup() { + return state->inNonIsolatedGroup; +} + +//------------------------------------------------------------------------ +// state write +//------------------------------------------------------------------------ + +void Splash::setMatrix(SplashCoord *matrix) { + memcpy(state->matrix, matrix, 6 * sizeof(SplashCoord)); +} + +void Splash::setStrokePattern(SplashPattern *strokePattern) { + state->setStrokePattern(strokePattern); +} + +void Splash::setFillPattern(SplashPattern *fillPattern) { + state->setFillPattern(fillPattern); +} + +void Splash::setScreen(SplashScreen *screen) { + state->setScreen(screen); +} + +void Splash::setBlendFunc(SplashBlendFunc func) { + state->blendFunc = func; +} + +void Splash::setStrokeAlpha(SplashCoord alpha) { + state->strokeAlpha = alpha; +} + +void Splash::setFillAlpha(SplashCoord alpha) { + state->fillAlpha = alpha; +} + +void Splash::setLineWidth(SplashCoord lineWidth) { + state->lineWidth = lineWidth; +} + +void Splash::setLineCap(int lineCap) { + state->lineCap = lineCap; +} + +void Splash::setLineJoin(int lineJoin) { + state->lineJoin = lineJoin; +} + +void Splash::setMiterLimit(SplashCoord miterLimit) { + state->miterLimit = miterLimit; +} + +void Splash::setFlatness(SplashCoord flatness) { + if (flatness < 1) { + state->flatness = 1; + } else { + state->flatness = flatness; + } +} + +void Splash::setLineDash(SplashCoord *lineDash, int lineDashLength, + SplashCoord lineDashPhase) { + state->setLineDash(lineDash, lineDashLength, lineDashPhase); +} + +void Splash::setStrokeAdjust(GBool strokeAdjust) { + state->strokeAdjust = strokeAdjust; +} + +void Splash::clipResetToRect(SplashCoord x0, SplashCoord y0, + SplashCoord x1, SplashCoord y1) { + state->clip->resetToRect(x0, y0, x1, y1); +} + +SplashError Splash::clipToRect(SplashCoord x0, SplashCoord y0, + SplashCoord x1, SplashCoord y1) { + return state->clip->clipToRect(x0, y0, x1, y1); +} + +SplashError Splash::clipToPath(SplashPath *path, GBool eo) { + return state->clip->clipToPath(path, state->matrix, state->flatness, eo); +} + +void Splash::setSoftMask(SplashBitmap *softMask) { + state->setSoftMask(softMask); +} + +void Splash::setInNonIsolatedGroup(SplashBitmap *alpha0BitmapA, + int alpha0XA, int alpha0YA) { + alpha0Bitmap = alpha0BitmapA; + alpha0X = alpha0XA; + alpha0Y = alpha0YA; + state->inNonIsolatedGroup = gTrue; +} + +//------------------------------------------------------------------------ +// state save/restore +//------------------------------------------------------------------------ + +void Splash::saveState() { + SplashState *newState; + + newState = state->copy(); + newState->next = state; + state = newState; +} + +SplashError Splash::restoreState() { + SplashState *oldState; + + if (!state->next) { + return splashErrNoSave; + } + oldState = state; + state = state->next; + delete oldState; + return splashOk; +} + +//------------------------------------------------------------------------ +// drawing operations +//------------------------------------------------------------------------ + +void Splash::clear(SplashColorPtr color, Guchar alpha) { + SplashColorPtr row, p; + Guchar mono; + int x, y; + + switch (bitmap->mode) { + case splashModeMono1: + mono = (color[0] & 0x80) ? 0xff : 0x00; + if (bitmap->rowSize < 0) { + memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), + mono, -bitmap->rowSize * bitmap->height); + } else { + memset(bitmap->data, mono, bitmap->rowSize * bitmap->height); + } + break; + case splashModeMono8: + if (bitmap->rowSize < 0) { + memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), + color[0], -bitmap->rowSize * bitmap->height); + } else { + memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); + } + break; + case splashModeRGB8: + if (color[0] == color[1] && color[1] == color[2]) { + if (bitmap->rowSize < 0) { + memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), + color[0], -bitmap->rowSize * bitmap->height); + } else { + memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); + } + } else { + row = bitmap->data; + for (y = 0; y < bitmap->height; ++y) { + p = row; + for (x = 0; x < bitmap->width; ++x) { + *p++ = color[2]; + *p++ = color[1]; + *p++ = color[0]; + } + row += bitmap->rowSize; + } + } + break; + case splashModeBGR8: + if (color[0] == color[1] && color[1] == color[2]) { + if (bitmap->rowSize < 0) { + memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), + color[0], -bitmap->rowSize * bitmap->height); + } else { + memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); + } + } else { + row = bitmap->data; + for (y = 0; y < bitmap->height; ++y) { + p = row; + for (x = 0; x < bitmap->width; ++x) { + *p++ = color[0]; + *p++ = color[1]; + *p++ = color[2]; + } + row += bitmap->rowSize; + } + } + break; +#if SPLASH_CMYK + case splashModeCMYK8: + if (color[0] == color[1] && color[1] == color[2] && color[2] == color[3]) { + if (bitmap->rowSize < 0) { + memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), + color[0], -bitmap->rowSize * bitmap->height); + } else { + memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); + } + } else { + row = bitmap->data; + for (y = 0; y < bitmap->height; ++y) { + p = row; + for (x = 0; x < bitmap->width; ++x) { + *p++ = color[0]; + *p++ = color[1]; + *p++ = color[2]; + *p++ = color[3]; + } + row += bitmap->rowSize; + } + } + break; +#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; + + 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 (state->lineWidth == 0) { + strokeNarrow(path2); + } else { + strokeWide(path2); + } + delete path2; + return splashOk; +} + +void Splash::strokeNarrow(SplashPath *path) { + SplashPipe pipe; + SplashXPath *xPath; + SplashXPathSeg *seg; + int x0, x1, x2, x3, y0, y1, x, y, t; + SplashCoord dx, dy, dxdy; + SplashClipResult clipRes; + int nClipRes[3]; + int i; + + nClipRes[0] = nClipRes[1] = nClipRes[2] = 0; + + xPath = new SplashXPath(path, state->matrix, state->flatness, gFalse); + + pipeInit(&pipe, 0, 0, state->strokePattern, NULL, state->strokeAlpha, + gFalse, gFalse); + + for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) { + + x0 = splashFloor(seg->x0); + x1 = splashFloor(seg->x1); + y0 = splashFloor(seg->y0); + y1 = splashFloor(seg->y1); + + // horizontal segment + if (y0 == y1) { + if (x0 > x1) { + t = x0; x0 = x1; x1 = t; + } + if ((clipRes = state->clip->testSpan(x0, x1, y0)) + != splashClipAllOutside) { + drawSpan(&pipe, x0, x1, y0, clipRes == splashClipAllInside); + } + + // segment with |dx| > |dy| + } else if (splashAbs(seg->dxdy) > 1) { + dx = seg->x1 - seg->x0; + dy = seg->y1 - seg->y0; + dxdy = seg->dxdy; + if (y0 > y1) { + t = y0; y0 = y1; y1 = t; + t = x0; x0 = x1; x1 = t; + dx = -dx; + dy = -dy; + } + if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0, + x0 <= x1 ? x1 : x0, y1)) + != splashClipAllOutside) { + if (dx > 0) { + x2 = x0; + x3 = splashFloor(seg->x0 + ((SplashCoord)y0 + 1 - seg->y0) * dxdy); + drawSpan(&pipe, x2, (x2 <= x3 - 1) ? x3 - 1 : x2, y0, + clipRes == splashClipAllInside); + x2 = x3; + for (y = y0 + 1; y <= y1 - 1; ++y) { + x3 = splashFloor(seg->x0 + ((SplashCoord)y + 1 - seg->y0) * dxdy); + drawSpan(&pipe, x2, x3 - 1, y, clipRes == splashClipAllInside); + x2 = x3; + } + drawSpan(&pipe, x2, x2 <= x1 ? x1 : x2, y1, + clipRes == splashClipAllInside); + } else { + x2 = x0; + x3 = splashFloor(seg->x0 + ((SplashCoord)y0 + 1 - seg->y0) * dxdy); + drawSpan(&pipe, (x3 + 1 <= x2) ? x3 + 1 : x2, x2, y0, + clipRes == splashClipAllInside); + x2 = x3; + for (y = y0 + 1; y <= y1 - 1; ++y) { + x3 = splashFloor(seg->x0 + ((SplashCoord)y + 1 - seg->y0) * dxdy); + drawSpan(&pipe, x3 + 1, x2, y, clipRes == splashClipAllInside); + x2 = x3; + } + drawSpan(&pipe, x1, (x1 <= x2) ? x2 : x1, y1, + clipRes == splashClipAllInside); + } + } + + // segment with |dy| > |dx| + } else { + dxdy = seg->dxdy; + if (y0 > y1) { + t = x0; x0 = x1; x1 = t; + t = y0; y0 = y1; y1 = t; + } + if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0, + x0 <= x1 ? x1 : x0, y1)) + != splashClipAllOutside) { + drawPixel(&pipe, x0, y0, clipRes == splashClipAllInside); + for (y = y0 + 1; y <= y1 - 1; ++y) { + x = splashFloor(seg->x0 + ((SplashCoord)y - seg->y0) * dxdy); + drawPixel(&pipe, x, y, clipRes == splashClipAllInside); + } + drawPixel(&pipe, x1, y1, clipRes == splashClipAllInside); + } + } + ++nClipRes[clipRes]; + } + if (nClipRes[splashClipPartial] || + (nClipRes[splashClipAllInside] && nClipRes[splashClipAllOutside])) { + opClipRes = splashClipPartial; + } else if (nClipRes[splashClipAllInside]) { + opClipRes = splashClipAllInside; + } else { + opClipRes = splashClipAllOutside; + } + + delete xPath; +} + +void Splash::strokeWide(SplashPath *path) { + SplashPath *path2; + + path2 = makeStrokePath(path, gFalse); + fillWithPattern(path2, gFalse, state->strokePattern, state->strokeAlpha); + delete path2; +} + +SplashPath *Splash::flattenPath(SplashPath *path, SplashCoord *matrix, + SplashCoord flatness) { + SplashPath *fPath; + SplashCoord flatness2; + Guchar flag; + int i; + + fPath = new SplashPath(); + 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 = (xl0 + xx1) * 0.5; + yl1 = (yl0 + yy1) * 0.5; + xh = (xx1 + xx2) * 0.5; + yh = (yy1 + yy2) * 0.5; + xl2 = (xl1 + xh) * 0.5; + yl2 = (yl1 + yh) * 0.5; + xr2 = (xx2 + xr3) * 0.5; + yr2 = (yy2 + yr3) * 0.5; + xr1 = (xh + xr2) * 0.5; + yr1 = (yh + yr2) * 0.5; + xr0 = (xl2 + xr1) * 0.5; + yr0 = (yl2 + yr1) * 0.5; + // add the new subdivision points + p3 = (p1 + p2) / 2; + cx[p1][1] = xl1; cy[p1][1] = yl1; + cx[p1][2] = xl2; cy[p1][2] = yl2; + cNext[p1] = p3; + cx[p3][0] = xr0; cy[p3][0] = yr0; + cx[p3][1] = xr1; cy[p3][1] = yr1; + cx[p3][2] = xr2; cy[p3][2] = yr2; + cNext[p3] = p2; + } + } +} + +SplashPath *Splash::makeDashedPath(SplashPath *path) { + SplashPath *dPath; + SplashCoord lineDashTotal; + SplashCoord lineDashStartPhase, lineDashDist, segLen; + SplashCoord x0, y0, x1, y1, xa, ya; + GBool lineDashStartOn, lineDashOn, newPath; + int lineDashStartIdx, lineDashIdx; + int i, j, k; + + lineDashTotal = 0; + for (i = 0; i < state->lineDashLength; ++i) { + lineDashTotal += state->lineDash[i]; + } + lineDashStartPhase = state->lineDashPhase; + i = splashFloor(lineDashStartPhase / lineDashTotal); + lineDashStartPhase -= (SplashCoord)i * lineDashTotal; + lineDashStartOn = gTrue; + lineDashStartIdx = 0; + while (lineDashStartPhase >= state->lineDash[lineDashStartIdx]) { + lineDashStartOn = !lineDashStartOn; + lineDashStartPhase -= state->lineDash[lineDashStartIdx]; + ++lineDashStartIdx; + } + + dPath = new SplashPath(); + + // process each subpath + i = 0; + while (i < path->length) { + + // find the end of the subpath + for (j = i; + j < path->length - 1 && !(path->flags[j] & splashPathLast); + ++j) ; + + // initialize the dash parameters + lineDashOn = lineDashStartOn; + lineDashIdx = lineDashStartIdx; + lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase; + + // process each segment of the subpath + newPath = gTrue; + for (k = i; k < j; ++k) { + + // grab the segment + x0 = path->pts[k].x; + y0 = path->pts[k].y; + x1 = path->pts[k+1].x; + y1 = path->pts[k+1].y; + segLen = splashDist(x0, y0, x1, y1); + + // process the segment + while (segLen > 0) { + + if (lineDashDist >= segLen) { + if (lineDashOn) { + if (newPath) { + dPath->moveTo(x0, y0); + newPath = gFalse; + } + dPath->lineTo(x1, y1); + } + lineDashDist -= segLen; + segLen = 0; + + } else { + xa = x0 + (lineDashDist / segLen) * (x1 - x0); + ya = y0 + (lineDashDist / segLen) * (y1 - y0); + if (lineDashOn) { + if (newPath) { + dPath->moveTo(x0, y0); + newPath = gFalse; + } + dPath->lineTo(xa, ya); + } + x0 = xa; + y0 = ya; + segLen -= lineDashDist; + lineDashDist = 0; + } + + // get the next entry in the dash array + if (lineDashDist <= 0) { + lineDashOn = !lineDashOn; + if (++lineDashIdx == state->lineDashLength) { + lineDashIdx = 0; + } + lineDashDist = state->lineDash[lineDashIdx]; + newPath = gTrue; + } + } + } + i = j + 1; + } + + return dPath; +} + +SplashError Splash::fill(SplashPath *path, GBool eo) { + if (debugMode) { + printf("fill [eo:%d]:\n", eo); + dumpPath(path); + } + return fillWithPattern(path, eo, state->fillPattern, state->fillAlpha); +} + +SplashError Splash::fillWithPattern(SplashPath *path, GBool eo, + SplashPattern *pattern, + SplashCoord alpha) { + SplashPipe pipe; + SplashXPath *xPath; + SplashXPathScanner *scanner; + int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y; + SplashClipResult clipRes, clipRes2; + + if (path->length == 0) { + return splashErrEmptyPath; + } + xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue); + if (vectorAntialias) { + xPath->aaScale(); + } + xPath->sort(); + scanner = new SplashXPathScanner(xPath, eo); + + // get the min and max x and y values + if (vectorAntialias) { + scanner->getBBoxAA(&xMinI, &yMinI, &xMaxI, &yMaxI); + } else { + scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI); + } + + // check clipping + if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI)) + != splashClipAllOutside) { + + // limit the y range + if (yMinI < state->clip->getYMinI()) { + yMinI = state->clip->getYMinI(); + } + if (yMaxI > state->clip->getYMaxI()) { + yMaxI = state->clip->getYMaxI(); + } + + pipeInit(&pipe, 0, yMinI, pattern, NULL, alpha, vectorAntialias, gFalse); + + // draw the spans + if (vectorAntialias) { + for (y = yMinI; y <= yMaxI; ++y) { + scanner->renderAALine(aaBuf, &x0, &x1, y); + if (clipRes != splashClipAllInside) { + state->clip->clipAALine(aaBuf, &x0, &x1, y); + } + drawAALine(&pipe, x0, x1, y); + } + } else { + for (y = yMinI; y <= yMaxI; ++y) { + while (scanner->getNextSpan(y, &x0, &x1)) { + if (clipRes == splashClipAllInside) { + drawSpan(&pipe, x0, x1, y, gTrue); + } else { + // limit the x range + if (x0 < state->clip->getXMinI()) { + x0 = state->clip->getXMinI(); + } + if (x1 > state->clip->getXMaxI()) { + x1 = state->clip->getXMaxI(); + } + clipRes2 = state->clip->testSpan(x0, x1, y); + drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside); + } + } + } + } + } + opClipRes = clipRes; + + delete scanner; + delete xPath; + return splashOk; +} + +SplashError Splash::xorFill(SplashPath *path, GBool eo) { + SplashPipe pipe; + SplashXPath *xPath; + SplashXPathScanner *scanner; + int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y; + SplashClipResult clipRes, clipRes2; + SplashBlendFunc origBlendFunc; + + if (path->length == 0) { + return splashErrEmptyPath; + } + xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue); + xPath->sort(); + scanner = new SplashXPathScanner(xPath, eo); + + // 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) { + + // limit the y range + if (yMinI < state->clip->getYMinI()) { + yMinI = state->clip->getYMinI(); + } + if (yMaxI > state->clip->getYMaxI()) { + yMaxI = state->clip->getYMaxI(); + } + + origBlendFunc = state->blendFunc; + state->blendFunc = &blendXor; + pipeInit(&pipe, 0, yMinI, state->fillPattern, NULL, 1, gFalse, gFalse); + + // draw the spans + for (y = yMinI; y <= yMaxI; ++y) { + while (scanner->getNextSpan(y, &x0, &x1)) { + if (clipRes == splashClipAllInside) { + drawSpan(&pipe, x0, x1, y, gTrue); + } else { + // limit the x range + if (x0 < state->clip->getXMinI()) { + x0 = state->clip->getXMinI(); + } + if (x1 > state->clip->getXMaxI()) { + x1 = state->clip->getXMaxI(); + } + clipRes2 = state->clip->testSpan(x0, x1, y); + drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside); + } + } + } + state->blendFunc = origBlendFunc; + } + opClipRes = clipRes; + + delete scanner; + delete xPath; + return splashOk; +} + +SplashError Splash::fillChar(SplashCoord x, SplashCoord y, + int c, SplashFont *font) { + SplashGlyphBitmap glyph; + SplashCoord xt, yt; + int x0, y0, xFrac, yFrac; + SplashClipResult clipRes; + + if (debugMode) { + printf("fillChar: x=%.2f y=%.2f c=%3d=0x%02x='%c'\n", + (double)x, (double)y, c, c, c); + } + transform(state->matrix, x, y, &xt, &yt); + x0 = splashFloor(xt); + xFrac = splashFloor((xt - x0) * splashFontFraction); + y0 = splashFloor(yt); + yFrac = splashFloor((yt - y0) * splashFontFraction); + if (!font->getGlyph(c, xFrac, yFrac, &glyph, x0, y0, state->clip, &clipRes)) { + return splashErrNoGlyph; + } + if (clipRes != splashClipAllOutside) { + fillGlyph2(x0, y0, &glyph, clipRes == splashClipAllInside); + } + opClipRes = clipRes; + if (glyph.freeData) { + gfree(glyph.data); + } + return splashOk; +} + +void Splash::fillGlyph(SplashCoord x, SplashCoord y, + SplashGlyphBitmap *glyph) { + SplashCoord xt, yt; + int x0, y0; + + transform(state->matrix, x, y, &xt, &yt); + x0 = splashFloor(xt); + y0 = splashFloor(yt); + SplashClipResult clipRes = state->clip->testRect(x0 - glyph->x, + y0 - glyph->y, + x0 - glyph->x + glyph->w - 1, + y0 - glyph->y + glyph->h - 1); + if (clipRes != splashClipAllOutside) { + fillGlyph2(x0, y0, glyph, clipRes == splashClipAllInside); + } + opClipRes = clipRes; +} + +void Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph, GBool noClip) { + SplashPipe pipe; + int alpha0, alpha; + Guchar *p; + int x1, y1, xx, xx1, yy; + + p = glyph->data; + int xStart = x0 - glyph->x; + int yStart = y0 - glyph->y; + int xxLimit = glyph->w; + int yyLimit = glyph->h; + + if (yStart < 0) + { + p += glyph->w * -yStart; // move p to the beginning of the first painted row + yyLimit += yStart; + yStart = 0; + } + + if (xStart < 0) + { + p += -xStart; // move p to the first painted pixel + xxLimit += xStart; + xStart = 0; + } + + if (xxLimit + xStart >= bitmap->width) xxLimit = bitmap->width - xStart; + if (yyLimit + yStart >= bitmap->height) yyLimit = bitmap->height - yStart; + + if (noClip) { + if (glyph->aa) { + pipeInit(&pipe, xStart, yStart, + state->fillPattern, NULL, state->fillAlpha, gTrue, gFalse); + for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) { + pipeSetXY(&pipe, xStart, y1); + for (xx = 0, x1 = xStart; xx < xxLimit; ++xx, ++x1) { + alpha = p[xx]; + if (alpha != 0) { + pipe.shape = (SplashCoord)(alpha / 255.0); + pipeRun(&pipe); + updateModX(x1); + updateModY(y1); + } else { + pipeIncX(&pipe); + } + } + p += glyph->w; + } + } else { + const int widthEight = (int)ceil(glyph->w / 8.0); + + pipeInit(&pipe, xStart, yStart, + state->fillPattern, NULL, state->fillAlpha, gFalse, gFalse); + for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) { + pipeSetXY(&pipe, xStart, y1); + for (xx = 0, x1 = xStart; xx < xxLimit; xx += 8) { + alpha0 = p[xx / 8]; + for (xx1 = 0; xx1 < 8 && xx + xx1 < xxLimit; ++xx1, ++x1) { + if (alpha0 & 0x80) { + pipeRun(&pipe); + updateModX(x1); + updateModY(y1); + } else { + pipeIncX(&pipe); + } + alpha0 <<= 1; + } + } + p += widthEight; + } + } + } else { + if (glyph->aa) { + pipeInit(&pipe, xStart, yStart, + state->fillPattern, NULL, state->fillAlpha, gTrue, gFalse); + for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) { + pipeSetXY(&pipe, xStart, y1); + for (xx = 0, x1 = xStart; xx < xxLimit; ++xx, ++x1) { + if (state->clip->test(x1, y1)) { + alpha = p[xx]; + if (alpha != 0) { + pipe.shape = (SplashCoord)(alpha / 255.0); + pipeRun(&pipe); + updateModX(x1); + updateModY(y1); + } else { + pipeIncX(&pipe); + } + } else { + pipeIncX(&pipe); + } + } + p += glyph->w; + } + } else { + const int widthEight = (int)ceil(glyph->w / 8.0); + + pipeInit(&pipe, xStart, yStart, + state->fillPattern, NULL, state->fillAlpha, gFalse, gFalse); + for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) { + pipeSetXY(&pipe, xStart, y1); + for (xx = 0, x1 = xStart; xx < xxLimit; xx += 8) { + alpha0 = p[xx / 8]; + for (xx1 = 0; xx1 < 8 && xx + xx1 < xxLimit; ++xx1, ++x1) { + if (state->clip->test(x1, y1)) { + if (alpha0 & 0x80) { + pipeRun(&pipe); + updateModX(x1); + updateModY(y1); + } else { + pipeIncX(&pipe); + } + } else { + pipeIncX(&pipe); + } + alpha0 <<= 1; + } + } + p += widthEight; + } + } + } +} + +SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData, + int w, int h, SplashCoord *mat, + GBool glyphMode) { + SplashPipe pipe; + GBool rot; + SplashCoord xScale, yScale, xShear, yShear, yShear1; + int tx, tx2, ty, ty2, scaledWidth, scaledHeight, xSign, ySign; + int ulx, uly, llx, lly, urx, ury, lrx, lry; + int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1; + int xMin, xMax, yMin, yMax; + SplashClipResult clipRes, clipRes2; + int yp, yq, yt, yStep, lastYStep; + int xp, xq, xt, xStep, xSrc; + int k1, spanXMin, spanXMax, spanY; + SplashColorPtr pixBuf, p; + int pixAcc; + int x, y, x1, x2, y2; + SplashCoord y1; + int n, m, i, j; + + 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 (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.000001) { + return splashErrSingularMatrix; + } + + // compute scale, shear, rotation, translation parameters + rot = splashAbs(mat[1]) > splashAbs(mat[0]); + if (rot) { + xScale = -mat[1]; + yScale = mat[2] - (mat[0] * mat[3]) / mat[1]; + xShear = -mat[3] / yScale; + yShear = -mat[0] / mat[1]; + } else { + xScale = mat[0]; + yScale = mat[3] - (mat[1] * mat[2]) / mat[0]; + xShear = mat[2] / yScale; + yShear = mat[1] / mat[0]; + } + // Note 1: The PDF spec says that all pixels whose *centers* lie + // within the region get painted -- but that doesn't seem to match + // up with what Acrobat actually does: it ends up leaving gaps + // between image stripes. So we use the same rule here as for + // fills: any pixel that overlaps the region gets painted. + // Note 2: The "glyphMode" flag is a kludge: it switches back to + // "correct" behavior (matching the spec), for use in rendering Type + // 3 fonts. + // Note 3: The +/-0.01 in these computations is to avoid floating + // point precision problems which can lead to gaps between image + // stripes (it can cause image stripes to overlap, but that's a much + // less visible problem). + if (glyphMode) { + if (xScale >= 0) { + tx = splashRound(mat[4]); + tx2 = splashRound(mat[4] + xScale) - 1; + } else { + tx = splashRound(mat[4]) - 1; + tx2 = splashRound(mat[4] + xScale); + } + } else { + if (xScale >= 0) { + tx = splashFloor(mat[4] - 0.01); + tx2 = splashFloor(mat[4] + xScale + 0.01); + } else { + tx = splashFloor(mat[4] + 0.01); + tx2 = splashFloor(mat[4] + xScale - 0.01); + } + } + scaledWidth = abs(tx2 - tx) + 1; + if (glyphMode) { + if (yScale >= 0) { + ty = splashRound(mat[5]); + ty2 = splashRound(mat[5] + yScale) - 1; + } else { + ty = splashRound(mat[5]) - 1; + ty2 = splashRound(mat[5] + yScale); + } + } else { + if (yScale >= 0) { + ty = splashFloor(mat[5] - 0.01); + ty2 = splashFloor(mat[5] + yScale + 0.01); + } else { + ty = splashFloor(mat[5] + 0.01); + ty2 = splashFloor(mat[5] + yScale - 0.01); + } + } + scaledHeight = abs(ty2 - ty) + 1; + xSign = (xScale < 0) ? -1 : 1; + ySign = (yScale < 0) ? -1 : 1; + yShear1 = (SplashCoord)xSign * yShear; + + // clipping + ulx1 = 0; + uly1 = 0; + urx1 = xSign * (scaledWidth - 1); + ury1 = (int)(yShear * urx1); + llx1 = splashRound(xShear * ySign * (scaledHeight - 1)); + lly1 = ySign * (scaledHeight - 1) + (int)(yShear * llx1); + lrx1 = xSign * (scaledWidth - 1) + + splashRound(xShear * ySign * (scaledHeight - 1)); + lry1 = ySign * (scaledHeight - 1) + (int)(yShear * lrx1); + if (rot) { + ulx = tx + uly1; uly = ty - ulx1; + urx = tx + ury1; ury = ty - urx1; + llx = tx + lly1; lly = ty - llx1; + lrx = tx + lry1; lry = ty - lrx1; + } else { + ulx = tx + ulx1; uly = ty + uly1; + urx = tx + urx1; ury = ty + ury1; + llx = tx + llx1; lly = ty + lly1; + lrx = tx + lrx1; lry = ty + lry1; + } + xMin = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx + : (llx < lrx) ? llx : lrx + : (urx < llx) ? (urx < lrx) ? urx : lrx + : (llx < lrx) ? llx : lrx; + xMax = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx + : (llx > lrx) ? llx : lrx + : (urx > llx) ? (urx > lrx) ? urx : lrx + : (llx > lrx) ? llx : lrx; + yMin = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry + : (lly < lry) ? lly : lry + : (ury < lly) ? (ury < lry) ? ury : lry + : (lly < lry) ? lly : lry; + yMax = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry + : (lly > lry) ? lly : lry + : (ury > lly) ? (ury > lry) ? ury : lry + : (lly > lry) ? lly : lry; + clipRes = state->clip->testRect(xMin, yMin, xMax, yMax); + opClipRes = clipRes; + + // compute Bresenham parameters for x and y scaling + yp = h / scaledHeight; + yq = h % scaledHeight; + xp = w / scaledWidth; + xq = w % scaledWidth; + + // allocate pixel buffer + pixBuf = (SplashColorPtr)gmalloc((yp + 1) * w); + + // initialize the pixel pipe + pipeInit(&pipe, 0, 0, state->fillPattern, NULL, state->fillAlpha, + gTrue, gFalse); + if (vectorAntialias) { + drawAAPixelInit(); + } + + // init y scale Bresenham + yt = 0; + lastYStep = 1; + + for (y = 0; y < scaledHeight; ++y) { + + // y scale Bresenham + yStep = yp; + yt += yq; + if (yt >= scaledHeight) { + yt -= scaledHeight; + ++yStep; + } + + // read row(s) from image + n = (yp > 0) ? yStep : lastYStep; + if (n > 0) { + p = pixBuf; + for (i = 0; i < n; ++i) { + (*src)(srcData, p); + p += w; + } + } + lastYStep = yStep; + + // loop-invariant constants + k1 = splashRound(xShear * ySign * y); + + // clipping test + if (clipRes != splashClipAllInside && + !rot && + (int)(yShear * k1) == + (int)(yShear * (xSign * (scaledWidth - 1) + k1))) { + if (xSign > 0) { + spanXMin = tx + k1; + spanXMax = spanXMin + (scaledWidth - 1); + } else { + spanXMax = tx + k1; + spanXMin = spanXMax - (scaledWidth - 1); + } + spanY = ty + ySign * y + (int)(yShear * k1); + clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY); + if (clipRes2 == splashClipAllOutside) { + continue; + } + } else { + clipRes2 = clipRes; + } + + // init x scale Bresenham + xt = 0; + xSrc = 0; + + // x shear + x1 = k1; + + // y shear + y1 = (SplashCoord)ySign * y + yShear * x1; + // this is a kludge: if yShear1 is negative, then (int)y1 would + // change immediately after the first pixel, which is not what we + // want + if (yShear1 < 0) { + y1 += 0.999; + } + + // loop-invariant constants + n = yStep > 0 ? yStep : 1; + + for (x = 0; x < scaledWidth; ++x) { + + // x scale Bresenham + xStep = xp; + xt += xq; + if (xt >= scaledWidth) { + xt -= scaledWidth; + ++xStep; + } + + // rotation + if (rot) { + x2 = (int)y1; + y2 = -x1; + } else { + x2 = x1; + y2 = (int)y1; + } + + // compute the alpha value for (x,y) after the x and y scaling + // operations + m = xStep > 0 ? xStep : 1; + p = pixBuf + xSrc; + pixAcc = 0; + for (i = 0; i < n; ++i) { + for (j = 0; j < m; ++j) { + pixAcc += *p++; + } + p += w - m; + } + + // blend fill color with background + if (pixAcc != 0) { + pipe.shape = (pixAcc == n * m) + ? (SplashCoord)1 + : (SplashCoord)pixAcc / (SplashCoord)(n * m); + if (vectorAntialias && clipRes2 != splashClipAllInside) { + drawAAPixel(&pipe, tx + x2, ty + y2); + } else { + drawPixel(&pipe, tx + x2, ty + y2, clipRes2 == splashClipAllInside); + } + } + + // x scale Bresenham + xSrc += xStep; + + // x shear + x1 += xSign; + + // y shear + y1 += yShear1; + } + } + + // free memory + gfree(pixBuf); + + return splashOk; +} + +SplashError Splash::drawImage(SplashImageSource src, void *srcData, + SplashColorMode srcMode, GBool srcAlpha, + int w, int h, SplashCoord *mat) { + SplashPipe pipe; + GBool ok, rot; + SplashCoord xScale, yScale, xShear, yShear, yShear1; + int tx, tx2, ty, ty2, scaledWidth, scaledHeight, xSign, ySign; + int ulx, uly, llx, lly, urx, ury, lrx, lry; + int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1; + int xMin, xMax, yMin, yMax; + SplashClipResult clipRes, clipRes2; + int yp, yq, yt, yStep, lastYStep; + int xp, xq, xt, xStep, xSrc; + int k1, spanXMin, spanXMax, spanY; + SplashColorPtr colorBuf, p; + SplashColor pix; + Guchar *alphaBuf, *q; +#if SPLASH_CMYK + int pixAcc0, pixAcc1, pixAcc2, pixAcc3; +#else + int pixAcc0, pixAcc1, pixAcc2; +#endif + int alphaAcc; + SplashCoord pixMul, alphaMul, alpha; + int x, y, x1, x2, y2; + SplashCoord y1; + int nComps, n, m, i, j; + + if (debugMode) { + printf("drawImage: srcMode=%d srcAlpha=%d w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n", + srcMode, srcAlpha, w, h, (double)mat[0], (double)mat[1], (double)mat[2], + (double)mat[3], (double)mat[4], (double)mat[5]); + } + + // check color modes + ok = gFalse; // make gcc happy + nComps = 0; // make gcc happy + switch (bitmap->mode) { + case splashModeMono1: + case splashModeMono8: + ok = srcMode == splashModeMono8; + nComps = 1; + break; + case splashModeRGB8: + ok = srcMode == splashModeRGB8; + nComps = 3; + break; + case splashModeBGR8: + ok = srcMode == splashModeBGR8; + nComps = 3; + break; +#if SPLASH_CMYK + case splashModeCMYK8: + ok = srcMode == splashModeCMYK8; + nComps = 4; + break; +#endif + } + if (!ok) { + return splashErrModeMismatch; + } + + // check for singular matrix + if (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.000001) { + return splashErrSingularMatrix; + } + + // compute scale, shear, rotation, translation parameters + rot = splashAbs(mat[1]) > splashAbs(mat[0]); + if (rot) { + xScale = -mat[1]; + yScale = mat[2] - (mat[0] * mat[3]) / mat[1]; + xShear = -mat[3] / yScale; + yShear = -mat[0] / mat[1]; + } else { + xScale = mat[0]; + yScale = mat[3] - (mat[1] * mat[2]) / mat[0]; + xShear = mat[2] / yScale; + yShear = mat[1] / mat[0]; + } + // Note 1: The PDF spec says that all pixels whose *centers* lie + // within the region get painted -- but that doesn't seem to match + // up with what Acrobat actually does: it ends up leaving gaps + // between image stripes. So we use the same rule here as for + // fills: any pixel that overlaps the region gets painted. + // Note 2: The +/-0.01 in these computations is to avoid floating + // point precision problems which can lead to gaps between image + // stripes (it can cause image stripes to overlap, but that's a much + // less visible problem). + if (xScale >= 0) { + tx = splashFloor(mat[4] - 0.01); + tx2 = splashFloor(mat[4] + xScale + 0.01); + } else { + tx = splashFloor(mat[4] + 0.01); + tx2 = splashFloor(mat[4] + xScale - 0.01); + } + scaledWidth = abs(tx2 - tx) + 1; + if (yScale >= 0) { + ty = splashFloor(mat[5] - 0.01); + ty2 = splashFloor(mat[5] + yScale + 0.01); + } else { + ty = splashFloor(mat[5] + 0.01); + ty2 = splashFloor(mat[5] + yScale - 0.01); + } + scaledHeight = abs(ty2 - ty) + 1; + xSign = (xScale < 0) ? -1 : 1; + ySign = (yScale < 0) ? -1 : 1; + yShear1 = (SplashCoord)xSign * yShear; + + // clipping + ulx1 = 0; + uly1 = 0; + urx1 = xSign * (scaledWidth - 1); + ury1 = (int)(yShear * urx1); + llx1 = splashRound(xShear * ySign * (scaledHeight - 1)); + lly1 = ySign * (scaledHeight - 1) + (int)(yShear * llx1); + lrx1 = xSign * (scaledWidth - 1) + + splashRound(xShear * ySign * (scaledHeight - 1)); + lry1 = ySign * (scaledHeight - 1) + (int)(yShear * lrx1); + if (rot) { + ulx = tx + uly1; uly = ty - ulx1; + urx = tx + ury1; ury = ty - urx1; + llx = tx + lly1; lly = ty - llx1; + lrx = tx + lry1; lry = ty - lrx1; + } else { + ulx = tx + ulx1; uly = ty + uly1; + urx = tx + urx1; ury = ty + ury1; + llx = tx + llx1; lly = ty + lly1; + lrx = tx + lrx1; lry = ty + lry1; + } + xMin = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx + : (llx < lrx) ? llx : lrx + : (urx < llx) ? (urx < lrx) ? urx : lrx + : (llx < lrx) ? llx : lrx; + xMax = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx + : (llx > lrx) ? llx : lrx + : (urx > llx) ? (urx > lrx) ? urx : lrx + : (llx > lrx) ? llx : lrx; + yMin = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry + : (lly < lry) ? lly : lry + : (ury < lly) ? (ury < lry) ? ury : lry + : (lly < lry) ? lly : lry; + yMax = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry + : (lly > lry) ? lly : lry + : (ury > lly) ? (ury > lry) ? ury : lry + : (lly > lry) ? lly : lry; + clipRes = state->clip->testRect(xMin, yMin, xMax, yMax); + opClipRes = clipRes; + if (clipRes == splashClipAllOutside) { + return splashOk; + } + + // compute Bresenham parameters for x and y scaling + yp = h / scaledHeight; + yq = h % scaledHeight; + xp = w / scaledWidth; + xq = w % scaledWidth; + + // allocate pixel buffers + colorBuf = (SplashColorPtr)gmalloc((yp + 1) * w * nComps); + if (srcAlpha) { + alphaBuf = (Guchar *)gmalloc((yp + 1) * w); + } else { + alphaBuf = NULL; + } + + pixAcc0 = pixAcc1 = pixAcc2 = 0; // make gcc happy +#if SPLASH_CMYK + pixAcc3 = 0; // make gcc happy +#endif + + // initialize the pixel pipe + pipeInit(&pipe, 0, 0, NULL, pix, state->fillAlpha, + srcAlpha || (vectorAntialias && clipRes != splashClipAllInside), + gFalse); + if (vectorAntialias) { + drawAAPixelInit(); + } + + if (srcAlpha) { + + // init y scale Bresenham + yt = 0; + lastYStep = 1; + + for (y = 0; y < scaledHeight; ++y) { + + // y scale Bresenham + yStep = yp; + yt += yq; + if (yt >= scaledHeight) { + yt -= scaledHeight; + ++yStep; + } + + // read row(s) from image + n = (yp > 0) ? yStep : lastYStep; + if (n > 0) { + p = colorBuf; + q = alphaBuf; + for (i = 0; i < n; ++i) { + (*src)(srcData, p, q); + p += w * nComps; + q += w; + } + } + lastYStep = yStep; + + // loop-invariant constants + k1 = splashRound(xShear * ySign * y); + + // clipping test + if (clipRes != splashClipAllInside && + !rot && + (int)(yShear * k1) == + (int)(yShear * (xSign * (scaledWidth - 1) + k1))) { + if (xSign > 0) { + spanXMin = tx + k1; + spanXMax = spanXMin + (scaledWidth - 1); + } else { + spanXMax = tx + k1; + spanXMin = spanXMax - (scaledWidth - 1); + } + spanY = ty + ySign * y + (int)(yShear * k1); + clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY); + if (clipRes2 == splashClipAllOutside) { + continue; + } + } else { + clipRes2 = clipRes; + } + + // init x scale Bresenham + xt = 0; + xSrc = 0; + + // x shear + x1 = k1; + + // y shear + y1 = (SplashCoord)ySign * y + yShear * x1; + // this is a kludge: if yShear1 is negative, then (int)y1 would + // change immediately after the first pixel, which is not what + // we want + if (yShear1 < 0) { + y1 += 0.999; + } + + // loop-invariant constants + n = yStep > 0 ? yStep : 1; + + switch (srcMode) { + + case splashModeMono1: + case splashModeMono8: + for (x = 0; x < scaledWidth; ++x) { + + // x scale Bresenham + xStep = xp; + xt += xq; + if (xt >= scaledWidth) { + xt -= scaledWidth; + ++xStep; + } + + // rotation + if (rot) { + x2 = (int)y1; + y2 = -x1; + } else { + x2 = x1; + y2 = (int)y1; + } + + // compute the filtered pixel at (x,y) after the x and y scaling + // operations + m = xStep > 0 ? xStep : 1; + alphaAcc = 0; + p = colorBuf + xSrc; + q = alphaBuf + xSrc; + pixAcc0 = 0; + for (i = 0; i < n; ++i) { + for (j = 0; j < m; ++j) { + pixAcc0 += *p++; + alphaAcc += *q++; + } + p += w - m; + q += w - m; + } + pixMul = (SplashCoord)1 / (SplashCoord)(n * m); + alphaMul = pixMul * (1.0 / 255.0); + alpha = (SplashCoord)alphaAcc * alphaMul; + + if (alpha > 0) { + pix[0] = (int)((SplashCoord)pixAcc0 * pixMul); + + // set pixel + pipe.shape = alpha; + if (vectorAntialias && clipRes != splashClipAllInside) { + drawAAPixel(&pipe, tx + x2, ty + y2); + } else { + drawPixel(&pipe, tx + x2, ty + y2, + clipRes2 == splashClipAllInside); + } + } + + // x scale Bresenham + xSrc += xStep; + + // x shear + x1 += xSign; + + // y shear + y1 += yShear1; + } + break; + + case splashModeRGB8: + case splashModeBGR8: + for (x = 0; x < scaledWidth; ++x) { + + // x scale Bresenham + xStep = xp; + xt += xq; + if (xt >= scaledWidth) { + xt -= scaledWidth; + ++xStep; + } + + // rotation + if (rot) { + x2 = (int)y1; + y2 = -x1; + } else { + x2 = x1; + y2 = (int)y1; + } + + // compute the filtered pixel at (x,y) after the x and y scaling + // operations + m = xStep > 0 ? xStep : 1; + alphaAcc = 0; + p = colorBuf + xSrc * 3; + q = alphaBuf + xSrc; + pixAcc0 = pixAcc1 = pixAcc2 = 0; + for (i = 0; i < n; ++i) { + for (j = 0; j < m; ++j) { + pixAcc0 += *p++; + pixAcc1 += *p++; + pixAcc2 += *p++; + alphaAcc += *q++; + } + p += 3 * (w - m); + q += w - m; + } + pixMul = (SplashCoord)1 / (SplashCoord)(n * m); + alphaMul = pixMul * (1.0 / 255.0); + alpha = (SplashCoord)alphaAcc * alphaMul; + + if (alpha > 0) { + pix[0] = (int)((SplashCoord)pixAcc0 * pixMul); + pix[1] = (int)((SplashCoord)pixAcc1 * pixMul); + pix[2] = (int)((SplashCoord)pixAcc2 * pixMul); + + // set pixel + pipe.shape = alpha; + if (vectorAntialias && clipRes != splashClipAllInside) { + drawAAPixel(&pipe, tx + x2, ty + y2); + } else { + drawPixel(&pipe, tx + x2, ty + y2, + clipRes2 == splashClipAllInside); + } + } + + // x scale Bresenham + xSrc += xStep; + + // x shear + x1 += xSign; + + // y shear + y1 += yShear1; + } + break; + +#if SPLASH_CMYK + case splashModeCMYK8: + for (x = 0; x < scaledWidth; ++x) { + + // x scale Bresenham + xStep = xp; + xt += xq; + if (xt >= scaledWidth) { + xt -= scaledWidth; + ++xStep; + } + + // rotation + if (rot) { + x2 = (int)y1; + y2 = -x1; + } else { + x2 = x1; + y2 = (int)y1; + } + + // compute the filtered pixel at (x,y) after the x and y scaling + // operations + m = xStep > 0 ? xStep : 1; + alphaAcc = 0; + p = colorBuf + xSrc * 4; + q = alphaBuf + xSrc; + pixAcc0 = pixAcc1 = pixAcc2 = pixAcc3 = 0; + for (i = 0; i < n; ++i) { + for (j = 0; j < m; ++j) { + pixAcc0 += *p++; + pixAcc1 += *p++; + pixAcc2 += *p++; + pixAcc3 += *p++; + alphaAcc += *q++; + } + p += 4 * (w - m); + q += w - m; + } + pixMul = (SplashCoord)1 / (SplashCoord)(n * m); + alphaMul = pixMul * (1.0 / 255.0); + alpha = (SplashCoord)alphaAcc * alphaMul; + + if (alpha > 0) { + pix[0] = (int)((SplashCoord)pixAcc0 * pixMul); + pix[1] = (int)((SplashCoord)pixAcc1 * pixMul); + pix[2] = (int)((SplashCoord)pixAcc2 * pixMul); + pix[3] = (int)((SplashCoord)pixAcc3 * pixMul); + + // set pixel + pipe.shape = alpha; + if (vectorAntialias && clipRes != splashClipAllInside) { + drawAAPixel(&pipe, tx + x2, ty + y2); + } else { + drawPixel(&pipe, tx + x2, ty + y2, + clipRes2 == splashClipAllInside); + } + } + + // x scale Bresenham + xSrc += xStep; + + // x shear + x1 += xSign; + + // y shear + y1 += yShear1; + } + break; +#endif // SPLASH_CMYK + } + } + + } else { + + // init y scale Bresenham + yt = 0; + lastYStep = 1; + + for (y = 0; y < scaledHeight; ++y) { + + // y scale Bresenham + yStep = yp; + yt += yq; + if (yt >= scaledHeight) { + yt -= scaledHeight; + ++yStep; + } + + // read row(s) from image + n = (yp > 0) ? yStep : lastYStep; + if (n > 0) { + p = colorBuf; + for (i = 0; i < n; ++i) { + (*src)(srcData, p, NULL); + p += w * nComps; + } + } + lastYStep = yStep; + + // loop-invariant constants + k1 = splashRound(xShear * ySign * y); + + // clipping test + if (clipRes != splashClipAllInside && + !rot && + (int)(yShear * k1) == + (int)(yShear * (xSign * (scaledWidth - 1) + k1))) { + if (xSign > 0) { + spanXMin = tx + k1; + spanXMax = spanXMin + (scaledWidth - 1); + } else { + spanXMax = tx + k1; + spanXMin = spanXMax - (scaledWidth - 1); + } + spanY = ty + ySign * y + (int)(yShear * k1); + clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY); + if (clipRes2 == splashClipAllOutside) { + continue; + } + } else { + clipRes2 = clipRes; + } + + // init x scale Bresenham + xt = 0; + xSrc = 0; + + // x shear + x1 = k1; + + // y shear + y1 = (SplashCoord)ySign * y + yShear * x1; + // this is a kludge: if yShear1 is negative, then (int)y1 would + // change immediately after the first pixel, which is not what + // we want + if (yShear1 < 0) { + y1 += 0.999; + } + + // loop-invariant constants + n = yStep > 0 ? yStep : 1; + + switch (srcMode) { + + case splashModeMono1: + case splashModeMono8: + for (x = 0; x < scaledWidth; ++x) { + + // x scale Bresenham + xStep = xp; + xt += xq; + if (xt >= scaledWidth) { + xt -= scaledWidth; + ++xStep; + } + + // rotation + if (rot) { + x2 = (int)y1; + y2 = -x1; + } else { + x2 = x1; + y2 = (int)y1; + } + + // compute the filtered pixel at (x,y) after the x and y scaling + // operations + m = xStep > 0 ? xStep : 1; + p = colorBuf + xSrc; + pixAcc0 = 0; + for (i = 0; i < n; ++i) { + for (j = 0; j < m; ++j) { + pixAcc0 += *p++; + } + p += w - m; + } + pixMul = (SplashCoord)1 / (SplashCoord)(n * m); + + pix[0] = (int)((SplashCoord)pixAcc0 * pixMul); + + // set pixel + if (vectorAntialias && clipRes != splashClipAllInside) { + pipe.shape = (SplashCoord)1; + drawAAPixel(&pipe, tx + x2, ty + y2); + } else { + drawPixel(&pipe, tx + x2, ty + y2, + clipRes2 == splashClipAllInside); + } + + // x scale Bresenham + xSrc += xStep; + + // x shear + x1 += xSign; + + // y shear + y1 += yShear1; + } + break; + + case splashModeRGB8: + case splashModeBGR8: + for (x = 0; x < scaledWidth; ++x) { + + // x scale Bresenham + xStep = xp; + xt += xq; + if (xt >= scaledWidth) { + xt -= scaledWidth; + ++xStep; + } + + // rotation + if (rot) { + x2 = (int)y1; + y2 = -x1; + } else { + x2 = x1; + y2 = (int)y1; + } + + // compute the filtered pixel at (x,y) after the x and y scaling + // operations + m = xStep > 0 ? xStep : 1; + p = colorBuf + xSrc * 3; + pixAcc0 = pixAcc1 = pixAcc2 = 0; + for (i = 0; i < n; ++i) { + for (j = 0; j < m; ++j) { + pixAcc0 += *p++; + pixAcc1 += *p++; + pixAcc2 += *p++; + } + p += 3 * (w - m); + } + pixMul = (SplashCoord)1 / (SplashCoord)(n * m); + + pix[0] = (int)((SplashCoord)pixAcc0 * pixMul); + pix[1] = (int)((SplashCoord)pixAcc1 * pixMul); + pix[2] = (int)((SplashCoord)pixAcc2 * pixMul); + + // set pixel + if (vectorAntialias && clipRes != splashClipAllInside) { + pipe.shape = (SplashCoord)1; + drawAAPixel(&pipe, tx + x2, ty + y2); + } else { + drawPixel(&pipe, tx + x2, ty + y2, + clipRes2 == splashClipAllInside); + } + + // x scale Bresenham + xSrc += xStep; + + // x shear + x1 += xSign; + + // y shear + y1 += yShear1; + } + break; + +#if SPLASH_CMYK + case splashModeCMYK8: + for (x = 0; x < scaledWidth; ++x) { + + // x scale Bresenham + xStep = xp; + xt += xq; + if (xt >= scaledWidth) { + xt -= scaledWidth; + ++xStep; + } + + // rotation + if (rot) { + x2 = (int)y1; + y2 = -x1; + } else { + x2 = x1; + y2 = (int)y1; + } + + // compute the filtered pixel at (x,y) after the x and y scaling + // operations + m = xStep > 0 ? xStep : 1; + p = colorBuf + xSrc * 4; + pixAcc0 = pixAcc1 = pixAcc2 = pixAcc3 = 0; + for (i = 0; i < n; ++i) { + for (j = 0; j < m; ++j) { + pixAcc0 += *p++; + pixAcc1 += *p++; + pixAcc2 += *p++; + pixAcc3 += *p++; + } + p += 4 * (w - m); + } + pixMul = (SplashCoord)1 / (SplashCoord)(n * m); + + pix[0] = (int)((SplashCoord)pixAcc0 * pixMul); + pix[1] = (int)((SplashCoord)pixAcc1 * pixMul); + pix[2] = (int)((SplashCoord)pixAcc2 * pixMul); + pix[3] = (int)((SplashCoord)pixAcc3 * pixMul); + + // set pixel + if (vectorAntialias && clipRes != splashClipAllInside) { + pipe.shape = (SplashCoord)1; + drawAAPixel(&pipe, tx + x2, ty + y2); + } else { + drawPixel(&pipe, tx + x2, ty + y2, + clipRes2 == splashClipAllInside); + } + + // x scale Bresenham + xSrc += xStep; + + // x shear + x1 += xSign; + + // y shear + y1 += yShear1; + } + break; +#endif // SPLASH_CMYK + } + } + + } + + gfree(colorBuf); + gfree(alphaBuf); + + return splashOk; +} + +SplashError Splash::composite(SplashBitmap *src, int xSrc, int ySrc, + int xDest, int yDest, int w, int h, + GBool noClip, GBool nonIsolated) { + SplashPipe pipe; + SplashColor pixel; + Guchar alpha; + Guchar *ap; + int x, y; + + if (src->mode != bitmap->mode) { + return splashErrModeMismatch; + } + + if (src->alpha) { + pipeInit(&pipe, xDest, yDest, NULL, pixel, state->fillAlpha, + gTrue, nonIsolated); + 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 (noClip || 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 = (SplashCoord)(alpha / 255.0); + pipeRun(&pipe); + updateModX(xDest + x); + updateModY(yDest + y); + } else { + pipeIncX(&pipe); + } + } + } + } else { + pipeInit(&pipe, xDest, yDest, NULL, pixel, state->fillAlpha, + gFalse, nonIsolated); + 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 (noClip || state->clip->test(xDest + x, yDest + y)) { + pipeRun(&pipe); + updateModX(xDest + x); + updateModY(yDest + y); + } else { + pipeIncX(&pipe); + } + } + } + } + + return splashOk; +} + +void Splash::compositeBackground(SplashColorPtr color) { + SplashColorPtr p; + Guchar *q; + Guchar alpha, alpha1, c, color0, color1, color2, color3; + int x, y, mask; + + 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++; + 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; +#if SPLASH_CMYK + case splashModeCMYK8: + color0 = color[0]; + color1 = color[1]; + color2 = color[2]; + color3 = color[3]; + for (y = 0; y < bitmap->height; ++y) { + p = &bitmap->data[y * bitmap->rowSize]; + q = &bitmap->alpha[y * bitmap->width]; + for (x = 0; x < bitmap->width; ++x) { + alpha = *q++; + 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; +#endif + } + memset(bitmap->alpha, 255, bitmap->width * bitmap->height); +} + +SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc, + int xDest, int yDest, int w, int h) { + SplashColor pixel; + SplashColorPtr p; + Guchar *q; + int x, y, mask; + + if (src->mode != bitmap->mode) { + return splashErrModeMismatch; + } + + switch (bitmap->mode) { + case splashModeMono1: + for (y = 0; y < h; ++y) { + p = &bitmap->data[(yDest + y) * bitmap->rowSize + (xDest >> 3)]; + mask = 0x80 >> (xDest & 7); + for (x = 0; x < w; ++x) { + src->getPixel(xSrc + x, ySrc + y, pixel); + if (pixel[0]) { + *p |= mask; + } else { + *p &= ~mask; + } + if (!(mask >>= 1)) { + mask = 0x80; + ++p; + } + } + } + break; + case splashModeMono8: + for (y = 0; y < h; ++y) { + p = &bitmap->data[(yDest + y) * bitmap->rowSize + xDest]; + for (x = 0; x < w; ++x) { + src->getPixel(xSrc + x, ySrc + y, pixel); + *p++ = pixel[0]; + } + } + break; + case splashModeRGB8: + case splashModeBGR8: + for (y = 0; y < h; ++y) { + p = &bitmap->data[(yDest + y) * bitmap->rowSize + 3 * xDest]; + for (x = 0; x < w; ++x) { + src->getPixel(xSrc + x, ySrc + y, pixel); + *p++ = pixel[0]; + *p++ = pixel[1]; + *p++ = pixel[2]; + } + } + break; +#if SPLASH_CMYK + case splashModeCMYK8: + for (y = 0; y < h; ++y) { + p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest]; + for (x = 0; x < w; ++x) { + src->getPixel(xSrc + x, ySrc + y, pixel); + *p++ = pixel[0]; + *p++ = pixel[1]; + *p++ = pixel[2]; + *p++ = pixel[3]; + } + } + break; +#endif + } + + if (bitmap->alpha) { + for (y = 0; y < h; ++y) { + q = &bitmap->alpha[(yDest + y) * bitmap->width + xDest]; + for (x = 0; x < w; ++x) { + *q++ = 0x00; + } + } + } + + return splashOk; +} + +SplashPath *Splash::makeStrokePath(SplashPath *path, GBool flatten) { + SplashPath *pathIn, *pathOut; + SplashCoord w, d, dx, dy, wdx, wdy, dxNext, dyNext, wdxNext, wdyNext; + SplashCoord crossprod, dotprod, miter, m; + GBool first, last, closed; + int subpathStart, next, i; + int left0, left1, left2, right0, right1, right2, join0, join1, join2; + int leftFirst, rightFirst, firstPt; + + if (flatten) { + pathIn = flattenPath(path, state->matrix, state->flatness); + if (state->lineDashLength > 0) { + pathOut = makeDashedPath(pathIn); + delete pathIn; + pathIn = pathOut; + } + } else { + pathIn = path; + } + + subpathStart = 0; // make gcc happy + closed = gFalse; // make gcc happy + left0 = left1 = right0 = right1 = join0 = join1 = 0; // make gcc happy + leftFirst = rightFirst = firstPt = 0; // make gcc happy + + pathOut = new SplashPath(); + w = state->lineWidth; + + for (i = 0; i < pathIn->length - 1; ++i) { + if (pathIn->flags[i] & splashPathLast) { + continue; + } + if ((first = pathIn->flags[i] & splashPathFirst)) { + subpathStart = i; + closed = pathIn->flags[i] & splashPathClosed; + } + last = pathIn->flags[i+1] & splashPathLast; + + // compute the deltas for segment (i, i+1) + d = splashDist(pathIn->pts[i].x, pathIn->pts[i].y, + pathIn->pts[i+1].x, pathIn->pts[i+1].y); + if (d == 0) { + // we need to draw end caps on zero-length lines + //~ not clear what the behavior should be for splashLineCapButt + //~ with d==0 + dx = 0; + dy = 1; + } else { + d = (SplashCoord)1 / d; + dx = d * (pathIn->pts[i+1].x - pathIn->pts[i].x); + dy = d * (pathIn->pts[i+1].y - pathIn->pts[i].y); + } + wdx = (SplashCoord)0.5 * w * dx; + wdy = (SplashCoord)0.5 * w * dy; + + // compute the deltas for segment (i+1, next) + next = last ? subpathStart + 1 : i + 2; + d = splashDist(pathIn->pts[i+1].x, pathIn->pts[i+1].y, + pathIn->pts[next].x, pathIn->pts[next].y); + if (d == 0) { + // we need to draw end caps on zero-length lines + //~ not clear what the behavior should be for splashLineCapButt + //~ with d==0 + dxNext = 0; + dyNext = 1; + } else { + d = (SplashCoord)1 / d; + dxNext = d * (pathIn->pts[next].x - pathIn->pts[i+1].x); + dyNext = d * (pathIn->pts[next].y - pathIn->pts[i+1].y); + } + wdxNext = (SplashCoord)0.5 * w * dxNext; + wdyNext = (SplashCoord)0.5 * w * dyNext; + + // draw the start cap + pathOut->moveTo(pathIn->pts[i].x - wdy, pathIn->pts[i].y + wdx); + if (i == subpathStart) { + firstPt = pathOut->length - 1; + } + if (first && !closed) { + switch (state->lineCap) { + case splashLineCapButt: + pathOut->lineTo(pathIn->pts[i].x + wdy, pathIn->pts[i].y - wdx); + break; + case splashLineCapRound: + pathOut->curveTo(pathIn->pts[i].x - wdy - bezierCircle * wdx, + pathIn->pts[i].y + wdx - bezierCircle * wdy, + pathIn->pts[i].x - wdx - bezierCircle * wdy, + pathIn->pts[i].y - wdy + bezierCircle * wdx, + pathIn->pts[i].x - wdx, + pathIn->pts[i].y - wdy); + pathOut->curveTo(pathIn->pts[i].x - wdx + bezierCircle * wdy, + pathIn->pts[i].y - wdy - bezierCircle * wdx, + pathIn->pts[i].x + wdy - bezierCircle * wdx, + pathIn->pts[i].y - wdx - bezierCircle * wdy, + pathIn->pts[i].x + wdy, + pathIn->pts[i].y - wdx); + break; + case splashLineCapProjecting: + pathOut->lineTo(pathIn->pts[i].x - wdx - wdy, + pathIn->pts[i].y + wdx - wdy); + pathOut->lineTo(pathIn->pts[i].x - wdx + wdy, + pathIn->pts[i].y - wdx - wdy); + pathOut->lineTo(pathIn->pts[i].x + wdy, + pathIn->pts[i].y - wdx); + break; + } + } else { + pathOut->lineTo(pathIn->pts[i].x + wdy, pathIn->pts[i].y - wdx); + } + + // draw the left side of the segment rectangle + left2 = pathOut->length - 1; + pathOut->lineTo(pathIn->pts[i+1].x + wdy, pathIn->pts[i+1].y - wdx); + + // draw the end cap + if (last && !closed) { + switch (state->lineCap) { + case splashLineCapButt: + pathOut->lineTo(pathIn->pts[i+1].x - wdy, pathIn->pts[i+1].y + wdx); + break; + case splashLineCapRound: + pathOut->curveTo(pathIn->pts[i+1].x + wdy + bezierCircle * wdx, + pathIn->pts[i+1].y - wdx + bezierCircle * wdy, + pathIn->pts[i+1].x + wdx + bezierCircle * wdy, + pathIn->pts[i+1].y + wdy - bezierCircle * wdx, + pathIn->pts[i+1].x + wdx, + pathIn->pts[i+1].y + wdy); + pathOut->curveTo(pathIn->pts[i+1].x + wdx - bezierCircle * wdy, + pathIn->pts[i+1].y + wdy + bezierCircle * wdx, + pathIn->pts[i+1].x - wdy + bezierCircle * wdx, + pathIn->pts[i+1].y + wdx + bezierCircle * wdy, + pathIn->pts[i+1].x - wdy, + pathIn->pts[i+1].y + wdx); + break; + case splashLineCapProjecting: + pathOut->lineTo(pathIn->pts[i+1].x + wdy + wdx, + pathIn->pts[i+1].y - wdx + wdy); + pathOut->lineTo(pathIn->pts[i+1].x - wdy + wdx, + pathIn->pts[i+1].y + wdx + wdy); + pathOut->lineTo(pathIn->pts[i+1].x - wdy, + pathIn->pts[i+1].y + wdx); + break; + } + } else { + pathOut->lineTo(pathIn->pts[i+1].x - wdy, pathIn->pts[i+1].y + wdx); + } + + // draw the right side of the segment rectangle + right2 = pathOut->length - 1; + pathOut->close(); + + // draw the join + join2 = pathOut->length; + if (!last || closed) { + crossprod = dx * dyNext - dy * dxNext; + dotprod = -(dx * dxNext + dy * dyNext); + if (dotprod > 0.99999) { + // 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) + miter = (state->miterLimit + 1) * (state->miterLimit + 1); + m = 0; + } else { + miter = (SplashCoord)2 / ((SplashCoord)1 - dotprod); + if (miter < 1) { + // this can happen because of floating point inaccuracies + miter = 1; + } + m = splashSqrt(miter - 1); + } + + // round join + if (state->lineJoin == splashLineJoinRound) { + pathOut->moveTo(pathIn->pts[i+1].x + (SplashCoord)0.5 * w, + pathIn->pts[i+1].y); + pathOut->curveTo(pathIn->pts[i+1].x + (SplashCoord)0.5 * w, + pathIn->pts[i+1].y + bezierCircle2 * w, + pathIn->pts[i+1].x + bezierCircle2 * w, + pathIn->pts[i+1].y + (SplashCoord)0.5 * w, + pathIn->pts[i+1].x, + pathIn->pts[i+1].y + (SplashCoord)0.5 * w); + pathOut->curveTo(pathIn->pts[i+1].x - bezierCircle2 * w, + pathIn->pts[i+1].y + (SplashCoord)0.5 * w, + pathIn->pts[i+1].x - (SplashCoord)0.5 * w, + pathIn->pts[i+1].y + bezierCircle2 * w, + pathIn->pts[i+1].x - (SplashCoord)0.5 * w, + pathIn->pts[i+1].y); + pathOut->curveTo(pathIn->pts[i+1].x - (SplashCoord)0.5 * w, + pathIn->pts[i+1].y - bezierCircle2 * w, + pathIn->pts[i+1].x - bezierCircle2 * w, + pathIn->pts[i+1].y - (SplashCoord)0.5 * w, + pathIn->pts[i+1].x, + pathIn->pts[i+1].y - (SplashCoord)0.5 * w); + pathOut->curveTo(pathIn->pts[i+1].x + bezierCircle2 * w, + pathIn->pts[i+1].y - (SplashCoord)0.5 * w, + pathIn->pts[i+1].x + (SplashCoord)0.5 * w, + pathIn->pts[i+1].y - bezierCircle2 * w, + pathIn->pts[i+1].x + (SplashCoord)0.5 * w, + pathIn->pts[i+1].y); + + } else { + pathOut->moveTo(pathIn->pts[i+1].x, pathIn->pts[i+1].y); + + // angle < 180 + if (crossprod < 0) { + pathOut->lineTo(pathIn->pts[i+1].x - wdyNext, + pathIn->pts[i+1].y + wdxNext); + // miter join inside limit + if (state->lineJoin == splashLineJoinMiter && + splashSqrt(miter) <= state->miterLimit) { + pathOut->lineTo(pathIn->pts[i+1].x - wdy + wdx * m, + pathIn->pts[i+1].y + wdx + wdy * m); + pathOut->lineTo(pathIn->pts[i+1].x - wdy, + pathIn->pts[i+1].y + wdx); + // bevel join or miter join outside limit + } else { + pathOut->lineTo(pathIn->pts[i+1].x - wdy, pathIn->pts[i+1].y + wdx); + } + + // angle >= 180 + } else { + pathOut->lineTo(pathIn->pts[i+1].x + wdy, + pathIn->pts[i+1].y - wdx); + // miter join inside limit + if (state->lineJoin == splashLineJoinMiter && + splashSqrt(miter) <= state->miterLimit) { + pathOut->lineTo(pathIn->pts[i+1].x + wdy + wdx * m, + pathIn->pts[i+1].y - wdx + wdy * m); + pathOut->lineTo(pathIn->pts[i+1].x + wdyNext, + pathIn->pts[i+1].y - wdxNext); + // bevel join or miter join outside limit + } else { + pathOut->lineTo(pathIn->pts[i+1].x + wdyNext, + pathIn->pts[i+1].y - wdxNext); + } + } + } + + pathOut->close(); + } + + // add stroke adjustment hints + if (state->strokeAdjust) { + if (i >= subpathStart + 1) { + if (i >= subpathStart + 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 (i == subpathStart) { + leftFirst = left2; + rightFirst = right2; + } + if (last) { + if (i >= subpathStart + 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 (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%s%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 & splashXPathFirst) ? "F" : " ", + (path->segs[i].flags & splashXPathLast) ? "L" : " ", + (path->segs[i].flags & splashXPathEnd0) ? "0" : " ", + (path->segs[i].flags & splashXPathEnd1) ? "1" : " ", + (path->segs[i].flags & splashXPathHoriz) ? "H" : " ", + (path->segs[i].flags & splashXPathVert) ? "V" : " ", + (path->segs[i].flags & splashXPathFlip) ? "P" : " "); + } +} |