diff options
Diffstat (limited to 'splash')
32 files changed, 4073 insertions, 1919 deletions
diff --git a/splash/Splash.cc b/splash/Splash.cc index 9deec105..fcab28c3 100644 --- a/splash/Splash.cc +++ b/splash/Splash.cc @@ -51,6 +51,8 @@ //------------------------------------------------------------------------ +#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) @@ -61,9 +63,55 @@ static inline Guchar div255(int x) { return (Guchar)((x + (x >> 8) + 0x80) >> 8); } +// Clip x to lie in [0, 255]. +static inline Guchar clip255(int x) { + return x < 0 ? 0 : x > 255 ? 255 : x; +} + template<typename T> inline void Guswap( T&a, T&b ) { T tmp = a; a=b; b=tmp; } +// The PDF spec says that all pixels whose *centers* lie within the +// image target region get painted, so we want to round n+0.5 down to +// n. But this causes problems, e.g., with PDF files that fill a +// rectangle with black and then draw an image to the exact same +// rectangle, so we instead use the fill scan conversion rule. +// However, the correct rule works better for glyphs, so we also +// provide that option in fillImageMask. +#if 0 +static inline int imgCoordMungeLower(SplashCoord x) { + return splashCeil(x + 0.5) - 1; +} +static inline int imgCoordMungeUpper(SplashCoord x) { + return splashCeil(x + 0.5) - 1; +} +#else +static inline int imgCoordMungeLower(SplashCoord x) { + return splashFloor(x); +} +static inline int imgCoordMungeUpper(SplashCoord x) { + return splashFloor(x) + 1; +} +static inline int imgCoordMungeLowerC(SplashCoord x, GBool glyphMode) { + return glyphMode ? (splashCeil(x + 0.5) - 1) : splashFloor(x); +} +static inline int imgCoordMungeUpperC(SplashCoord x, GBool glyphMode) { + return glyphMode ? (splashCeil(x + 0.5) - 1) : (splashFloor(x) + 1); +} +#endif + +// Used by drawImage and fillImageMask to divide the target +// quadrilateral into sections. +struct ImageSection { + int y0, y1; // actual y range + int ia0, ia1; // vertex indices for edge A + int ib0, ib1; // vertex indices for edge A + SplashCoord xa0, ya0, xa1, ya1; // edge A + SplashCoord dxdya; // slope of edge A + SplashCoord xb0, yb0, xb1, yb1; // edge B + SplashCoord dxdyb; // slope of edge B +}; + //------------------------------------------------------------------------ // SplashPipe //------------------------------------------------------------------------ @@ -78,9 +126,8 @@ struct SplashPipe { SplashPattern *pattern; // source alpha and color - SplashCoord aInput; + Guchar aInput; GBool usesShape; - Guchar aSrc; SplashColorPtr cSrc; SplashColor cSrcVal; @@ -96,18 +143,17 @@ struct SplashPipe { Guchar *destAlphaPtr; // shape - SplashCoord shape; + Guchar shape; // result alpha and color GBool noTransparency; SplashPipeResultColorCtrl resultColorCtrl; // non-isolated group correction - int nonIsolatedGroup; + GBool nonIsolatedGroup; - // stroke / fill operation and pattern for calculate overprint - GBool stroke; - SplashPattern *overprintPattern; + // the "run" function + void (Splash::*run)(SplashPipe *pipe); }; SplashPipeResultColorCtrl Splash::pipeResultColorNoAlphaBlend[] = { @@ -192,8 +238,8 @@ inline void Splash::updateModY(int y) { inline void Splash::pipeInit(SplashPipe *pipe, int x, int y, SplashPattern *pattern, SplashColorPtr cSrc, - SplashCoord aInput, GBool usesShape, - GBool nonIsolatedGroup, SplashPattern *opPattern, GBool strokeA) { + Guchar aInput, GBool usesShape, + GBool nonIsolatedGroup) { pipeSetXY(pipe, x, y); pipe->pattern = NULL; @@ -211,18 +257,11 @@ inline void Splash::pipeInit(SplashPipe *pipe, int x, int y, // 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) { + if (aInput == 255 && !state->softMask && !usesShape && + !state->inNonIsolatedGroup && !nonIsolatedGroup) { pipe->noTransparency = gTrue; } else { pipe->noTransparency = gFalse; @@ -239,19 +278,58 @@ inline void Splash::pipeInit(SplashPipe *pipe, int x, int y, } // non-isolated group correction - if (nonIsolatedGroup) { - pipe->nonIsolatedGroup = splashColorModeNComps[bitmap->mode]; - } else { - pipe->nonIsolatedGroup = 0; + pipe->nonIsolatedGroup = nonIsolatedGroup; + + // select the 'run' function + pipe->run = &Splash::pipeRun; + if (!pipe->pattern && pipe->noTransparency && !state->blendFunc) { + if (bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr) { + pipe->run = &Splash::pipeRunSimpleMono1; + } else if (bitmap->mode == splashModeMono8 && pipe->destAlphaPtr) { + pipe->run = &Splash::pipeRunSimpleMono8; + } else if (bitmap->mode == splashModeRGB8 && pipe->destAlphaPtr) { + pipe->run = &Splash::pipeRunSimpleRGB8; + } else if (bitmap->mode == splashModeXBGR8 && pipe->destAlphaPtr) { + pipe->run = &Splash::pipeRunSimpleXBGR8; + } else if (bitmap->mode == splashModeBGR8 && pipe->destAlphaPtr) { + pipe->run = &Splash::pipeRunSimpleBGR8; +#if SPLASH_CMYK + } else if (bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr) { + pipe->run = &Splash::pipeRunSimpleCMYK8; +#endif + } + } else if (!pipe->pattern && !pipe->noTransparency && !state->softMask && + pipe->usesShape && + !(state->inNonIsolatedGroup && alpha0Bitmap->alpha) && + !state->blendFunc && !pipe->nonIsolatedGroup) { + if (bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr) { + pipe->run = &Splash::pipeRunAAMono1; + } else if (bitmap->mode == splashModeMono8 && pipe->destAlphaPtr) { + pipe->run = &Splash::pipeRunAAMono8; + } else if (bitmap->mode == splashModeRGB8 && pipe->destAlphaPtr) { + pipe->run = &Splash::pipeRunAARGB8; + } else if (bitmap->mode == splashModeXBGR8 && pipe->destAlphaPtr) { + pipe->run = &Splash::pipeRunAAXBGR8; + } else if (bitmap->mode == splashModeBGR8 && pipe->destAlphaPtr) { + pipe->run = &Splash::pipeRunAABGR8; +#if SPLASH_CMYK + } else if (bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr) { + pipe->run = &Splash::pipeRunAACMYK8; +#endif + } } - pipe->stroke = strokeA; - pipe->overprintPattern = opPattern; } -inline void Splash::pipeRun(SplashPipe *pipe) { - Guchar aSrc, aDest, alpha2, alpha0, aResult; - SplashColor cDest, cBlend; +// general case +void Splash::pipeRun(SplashPipe *pipe) { + Guchar aSrc, aDest, alphaI, alphaIm1, alpha0, aResult; + SplashColor cSrcNonIso, cDest, cBlend; + SplashColorPtr cSrc; Guchar cResult0, cResult1, cResult2, cResult3; + int t; +#if SPLASH_CMYK + SplashColor cSrc2, cDest2; +#endif //----- source color @@ -272,7 +350,7 @@ inline void Splash::pipeRun(SplashPipe *pipe) { switch (bitmap->mode) { case splashModeMono1: - cResult0 = pipe->cSrc[0]; + cResult0 = state->grayTransfer[pipe->cSrc[0]]; if (state->screen->test(pipe->x, pipe->y, cResult0)) { *pipe->destColorPtr |= pipe->destColorMask; } else { @@ -284,45 +362,39 @@ inline void Splash::pipeRun(SplashPipe *pipe) { } break; case splashModeMono8: - *pipe->destColorPtr++ = pipe->cSrc[0]; + *pipe->destColorPtr++ = state->grayTransfer[pipe->cSrc[0]]; break; case splashModeRGB8: - *pipe->destColorPtr++ = pipe->cSrc[0]; - *pipe->destColorPtr++ = pipe->cSrc[1]; - *pipe->destColorPtr++ = pipe->cSrc[2]; + *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++ = pipe->cSrc[2]; - *pipe->destColorPtr++ = pipe->cSrc[1]; - *pipe->destColorPtr++ = pipe->cSrc[0]; + *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++ = pipe->cSrc[2]; - *pipe->destColorPtr++ = pipe->cSrc[1]; - *pipe->destColorPtr++ = pipe->cSrc[0]; + *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]]; + *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]]; + *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]]; break; #if SPLASH_CMYK case splashModeCMYK8: - if (pipe->overprintPattern != NULL && - ((pipe->stroke && state->strokeOverprint) || - (!pipe->stroke && state->fillOverprint))) { - SplashColor cResult; - cDest[0] = pipe->destColorPtr[0]; - cDest[1] = pipe->destColorPtr[1]; - cDest[2] = pipe->destColorPtr[2]; - cDest[3] = pipe->destColorPtr[3]; - pipe->overprintPattern->overprint(state->overprintMode == 1, pipe->aSrc, pipe->cSrc, 255, cDest, cResult); - *pipe->destColorPtr++ = cResult[0]; - *pipe->destColorPtr++ = cResult[1]; - *pipe->destColorPtr++ = cResult[2]; - *pipe->destColorPtr++ = cResult[3]; - } else { - *pipe->destColorPtr++ = pipe->cSrc[0]; - *pipe->destColorPtr++ = pipe->cSrc[1]; - *pipe->destColorPtr++ = pipe->cSrc[2]; - *pipe->destColorPtr++ = pipe->cSrc[3]; + if (state->overprintMask & 1) { + pipe->destColorPtr[0] = state->cmykTransferC[pipe->cSrc[0]]; + } + if (state->overprintMask & 2) { + pipe->destColorPtr[1] = state->cmykTransferM[pipe->cSrc[1]]; } + if (state->overprintMask & 4) { + pipe->destColorPtr[2] = state->cmykTransferY[pipe->cSrc[2]]; + } + if (state->overprintMask & 8) { + pipe->destColorPtr[3] = state->cmykTransferK[pipe->cSrc[3]]; + } + pipe->destColorPtr += 4; break; #endif } @@ -372,41 +444,99 @@ inline void Splash::pipeRun(SplashPipe *pipe) { 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); + aSrc = div255(div255(pipe->aInput * *pipe->softMaskPtr++) * + pipe->shape); } else { - aSrc = (Guchar)splashRound(pipe->aInput * *pipe->softMaskPtr++); + aSrc = div255(pipe->aInput * *pipe->softMaskPtr++); } } else if (pipe->usesShape) { - // pipe->aInput is premultiplied by 255 in pipeInit - aSrc = (Guchar)splashRound(pipe->aInput * pipe->shape); + aSrc = div255(pipe->aInput * pipe->shape); + } else { + aSrc = pipe->aInput; + } + + //----- non-isolated group correction + + if (pipe->nonIsolatedGroup) { + // This path is only used when Splash::composite() is called to + // composite a non-isolated group onto the backdrop. In this + // case, pipe->shape is the source (group) alpha. + if (pipe->shape == 0) { + // this value will be multiplied by zero later, so it doesn't + // matter what we use + cSrc = pipe->cSrc; + } else { + t = (aDest * 255) / pipe->shape - aDest; + switch (bitmap->mode) { +#if SPLASH_CMYK + case splashModeCMYK8: + cSrcNonIso[3] = clip255(pipe->cSrc[3] + + ((pipe->cSrc[3] - cDest[3]) * t) / 255); +#endif + case splashModeRGB8: + case splashModeXBGR8: + case splashModeBGR8: + cSrcNonIso[2] = clip255(pipe->cSrc[2] + + ((pipe->cSrc[2] - cDest[2]) * t) / 255); + cSrcNonIso[1] = clip255(pipe->cSrc[1] + + ((pipe->cSrc[1] - cDest[1]) * t) / 255); + case splashModeMono1: + case splashModeMono8: + cSrcNonIso[0] = clip255(pipe->cSrc[0] + + ((pipe->cSrc[0] - cDest[0]) * t) / 255); + break; + } + cSrc = cSrcNonIso; + } } else { - // precomputed in pipeInit - aSrc = pipe->aSrc; + cSrc = pipe->cSrc; + } + + //----- blend function + + if (state->blendFunc) { +#if SPLASH_CMYK + if (bitmap->mode == splashModeCMYK8) { + // convert colors to additive + cSrc2[0] = 0xff - cSrc[0]; + cSrc2[1] = 0xff - cSrc[1]; + cSrc2[2] = 0xff - cSrc[2]; + cSrc2[3] = 0xff - cSrc[3]; + cDest2[0] = 0xff - cDest[0]; + cDest2[1] = 0xff - cDest[1]; + cDest2[2] = 0xff - cDest[2]; + cDest2[3] = 0xff - cDest[3]; + (*state->blendFunc)(cSrc2, cDest2, cBlend, bitmap->mode); + // convert result back to subtractive + cBlend[0] = 0xff - cBlend[0]; + cBlend[1] = 0xff - cBlend[1]; + cBlend[2] = 0xff - cBlend[2]; + cBlend[3] = 0xff - cBlend[3]; + } else +#endif + (*state->blendFunc)(cSrc, cDest, cBlend, bitmap->mode); } //----- result alpha and non-isolated group element correction if (pipe->noTransparency) { - alpha2 = aResult = 255; + alphaI = alphaIm1 = aResult = 255; } else { aResult = aSrc + aDest - div255(aSrc * aDest); + // alphaI = alpha_i + // alphaIm1 = alpha_(i-1) if (pipe->alpha0Ptr) { alpha0 = *pipe->alpha0Ptr++; - alpha2 = aResult + alpha0 - div255(aResult * alpha0); + alphaI = aResult + alpha0 - div255(aResult * alpha0); + alphaIm1 = alpha0 + aDest - div255(alpha0 * aDest); } else { - alpha2 = aResult; + alphaI = aResult; + alphaIm1 = aDest; } } @@ -416,151 +546,132 @@ inline void Splash::pipeRun(SplashPipe *pipe) { switch (pipe->resultColorCtrl) { + case splashPipeResultColorNoAlphaBlendMono: + cResult0 = state->grayTransfer[div255((255 - aDest) * cSrc[0] + + aDest * cBlend[0])]; + break; + case splashPipeResultColorNoAlphaBlendRGB: + cResult0 = state->rgbTransferR[div255((255 - aDest) * cSrc[0] + + aDest * cBlend[0])]; + cResult1 = state->rgbTransferG[div255((255 - aDest) * cSrc[1] + + aDest * cBlend[1])]; + cResult2 = state->rgbTransferB[div255((255 - aDest) * cSrc[2] + + aDest * cBlend[2])]; + break; #if SPLASH_CMYK case splashPipeResultColorNoAlphaBlendCMYK: - 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]); + 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; +#endif case splashPipeResultColorAlphaNoBlendMono: - if (alpha2 == 0) { + if (alphaI == 0) { cResult0 = 0; } else { - cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] + - aSrc * pipe->cSrc[0]) / alpha2); + cResult0 = state->grayTransfer[((alphaI - aSrc) * cDest[0] + + aSrc * cSrc[0]) / alphaI]; } break; case splashPipeResultColorAlphaNoBlendRGB: - if (alpha2 == 0) { + if (alphaI == 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); + cResult0 = state->rgbTransferR[((alphaI - aSrc) * cDest[0] + + aSrc * cSrc[0]) / alphaI]; + cResult1 = state->rgbTransferG[((alphaI - aSrc) * cDest[1] + + aSrc * cSrc[1]) / alphaI]; + cResult2 = state->rgbTransferB[((alphaI - aSrc) * cDest[2] + + aSrc * cSrc[2]) / alphaI]; } break; #if SPLASH_CMYK case splashPipeResultColorAlphaNoBlendCMYK: - if (alpha2 == 0) { + if (alphaI == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; cResult3 = 0; } else { - if (pipe->overprintPattern != NULL && - ((pipe->stroke && state->strokeOverprint) || - (!pipe->stroke && state->fillOverprint))) { - SplashColor cResult; - pipe->overprintPattern->overprint(state->overprintMode == 1, aSrc, pipe->cSrc, alpha2, cDest, cResult); - cResult0 = cResult[0]; - cResult1 = cResult[1]; - cResult2 = cResult[2]; - cResult3 = cResult[3]; - } 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); - } + 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; #endif case splashPipeResultColorAlphaBlendMono: - if (alpha2 == 0) { + if (alphaI == 0) { cResult0 = 0; } else { - cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] + - aSrc * ((255 - aDest) * pipe->cSrc[0] + - aDest * cBlend[0]) / 255) / - alpha2); + cResult0 = state->grayTransfer[((alphaI - aSrc) * cDest[0] + + aSrc * ((255 - alphaIm1) * cSrc[0] + + alphaIm1 * cBlend[0]) / 255) / + alphaI]; } break; case splashPipeResultColorAlphaBlendRGB: - if (alpha2 == 0) { + if (alphaI == 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); + cResult0 = state->rgbTransferR[((alphaI - aSrc) * cDest[0] + + aSrc * ((255 - alphaIm1) * cSrc[0] + + alphaIm1 * cBlend[0]) / 255) / + alphaI]; + cResult1 = state->rgbTransferG[((alphaI - aSrc) * cDest[1] + + aSrc * ((255 - alphaIm1) * cSrc[1] + + alphaIm1 * cBlend[1]) / 255) / + alphaI]; + cResult2 = state->rgbTransferB[((alphaI - aSrc) * cDest[2] + + aSrc * ((255 - alphaIm1) * cSrc[2] + + alphaIm1 * cBlend[2]) / 255) / + alphaI]; } break; #if SPLASH_CMYK case splashPipeResultColorAlphaBlendCMYK: - if (alpha2 == 0) { + if (alphaI == 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); + 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; #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) { @@ -596,10 +707,19 @@ inline void Splash::pipeRun(SplashPipe *pipe) { break; #if SPLASH_CMYK case splashModeCMYK8: - *pipe->destColorPtr++ = cResult0; - *pipe->destColorPtr++ = cResult1; - *pipe->destColorPtr++ = cResult2; - *pipe->destColorPtr++ = cResult3; + if (state->overprintMask & 1) { + pipe->destColorPtr[0] = cResult0; + } + if (state->overprintMask & 2) { + pipe->destColorPtr[1] = cResult1; + } + if (state->overprintMask & 4) { + pipe->destColorPtr[2] = cResult2; + } + if (state->overprintMask & 8) { + pipe->destColorPtr[3] = cResult3; + } + pipe->destColorPtr += 4; break; #endif } @@ -612,6 +732,376 @@ inline void Splash::pipeRun(SplashPipe *pipe) { ++pipe->x; } +// special case: +// !pipe->pattern && pipe->noTransparency && !state->blendFunc && +// bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr) { +void Splash::pipeRunSimpleMono1(SplashPipe *pipe) { + Guchar cResult0; + + //----- write destination pixel + cResult0 = state->grayTransfer[pipe->cSrc[0]]; + if (state->screen->test(pipe->x, pipe->y, cResult0)) { + *pipe->destColorPtr |= pipe->destColorMask; + } else { + *pipe->destColorPtr &= ~pipe->destColorMask; + } + if (!(pipe->destColorMask >>= 1)) { + pipe->destColorMask = 0x80; + ++pipe->destColorPtr; + } + + ++pipe->x; +} + +// special case: +// !pipe->pattern && pipe->noTransparency && !state->blendFunc && +// bitmap->mode == splashModeMono8 && pipe->destAlphaPtr) { +void Splash::pipeRunSimpleMono8(SplashPipe *pipe) { + //----- write destination pixel + *pipe->destColorPtr++ = state->grayTransfer[pipe->cSrc[0]]; + *pipe->destAlphaPtr++ = 255; + + ++pipe->x; +} + +// special case: +// !pipe->pattern && pipe->noTransparency && !state->blendFunc && +// bitmap->mode == splashModeRGB8 && pipe->destAlphaPtr) { +void Splash::pipeRunSimpleRGB8(SplashPipe *pipe) { + //----- write destination pixel + *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]]; + *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]]; + *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]]; + *pipe->destAlphaPtr++ = 255; + + ++pipe->x; +} + +// special case: +// !pipe->pattern && pipe->noTransparency && !state->blendFunc && +// bitmap->mode == splashModeXBGR8 && pipe->destAlphaPtr) { +void Splash::pipeRunSimpleXBGR8(SplashPipe *pipe) { + //----- write destination pixel + *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]]; + *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]]; + *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]]; + *pipe->destColorPtr++ = 255; + *pipe->destAlphaPtr++ = 255; + + ++pipe->x; +} + +// special case: +// !pipe->pattern && pipe->noTransparency && !state->blendFunc && +// bitmap->mode == splashModeBGR8 && pipe->destAlphaPtr) { +void Splash::pipeRunSimpleBGR8(SplashPipe *pipe) { + //----- write destination pixel + *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]]; + *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]]; + *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]]; + *pipe->destAlphaPtr++ = 255; + + ++pipe->x; +} + +#if SPLASH_CMYK +// special case: +// !pipe->pattern && pipe->noTransparency && !state->blendFunc && +// bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr) { +void Splash::pipeRunSimpleCMYK8(SplashPipe *pipe) { + //----- write destination pixel + if (state->overprintMask & 1) { + pipe->destColorPtr[0] = state->cmykTransferC[pipe->cSrc[0]]; + } + if (state->overprintMask & 2) { + pipe->destColorPtr[1] = state->cmykTransferM[pipe->cSrc[1]]; + } + if (state->overprintMask & 4) { + pipe->destColorPtr[2] = state->cmykTransferY[pipe->cSrc[2]]; + } + if (state->overprintMask & 8) { + pipe->destColorPtr[3] = state->cmykTransferK[pipe->cSrc[3]]; + } + pipe->destColorPtr += 4; + *pipe->destAlphaPtr++ = 255; + + ++pipe->x; +} +#endif + + +// special case: +// !pipe->pattern && !pipe->noTransparency && !state->softMask && +// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && +// !pipe->nonIsolatedGroup && +// bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr +void Splash::pipeRunAAMono1(SplashPipe *pipe) { + Guchar aSrc; + SplashColor cDest; + Guchar cResult0; + + //----- read destination pixel + cDest[0] = (*pipe->destColorPtr & pipe->destColorMask) ? 0xff : 0x00; + + //----- source alpha + aSrc = div255(pipe->aInput * pipe->shape); + + //----- result color + // note: aDest = alpha2 = aResult = 0xff + cResult0 = state->grayTransfer[(Guchar)div255((0xff - aSrc) * cDest[0] + + aSrc * pipe->cSrc[0])]; + + //----- write destination pixel + if (state->screen->test(pipe->x, pipe->y, cResult0)) { + *pipe->destColorPtr |= pipe->destColorMask; + } else { + *pipe->destColorPtr &= ~pipe->destColorMask; + } + if (!(pipe->destColorMask >>= 1)) { + pipe->destColorMask = 0x80; + ++pipe->destColorPtr; + } + + ++pipe->x; +} + +// special case: +// !pipe->pattern && !pipe->noTransparency && !state->softMask && +// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && +// !pipe->nonIsolatedGroup && +// bitmap->mode == splashModeMono8 && pipe->destAlphaPtr +void Splash::pipeRunAAMono8(SplashPipe *pipe) { + Guchar aSrc, aDest, alpha2, aResult; + SplashColor cDest; + Guchar cResult0; + + //----- read destination pixel + cDest[0] = *pipe->destColorPtr; + aDest = *pipe->destAlphaPtr; + + //----- source alpha + aSrc = div255(pipe->aInput * pipe->shape); + + //----- result alpha and non-isolated group element correction + aResult = aSrc + aDest - div255(aSrc * aDest); + alpha2 = aResult; + + //----- result color + if (alpha2 == 0) { + cResult0 = 0; + } else { + cResult0 = state->grayTransfer[(Guchar)(((alpha2 - aSrc) * cDest[0] + + aSrc * pipe->cSrc[0]) / alpha2)]; + } + + //----- write destination pixel + *pipe->destColorPtr++ = cResult0; + *pipe->destAlphaPtr++ = aResult; + + ++pipe->x; +} + +// special case: +// !pipe->pattern && !pipe->noTransparency && !state->softMask && +// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && +// !pipe->nonIsolatedGroup && +// bitmap->mode == splashModeRGB8 && pipe->destAlphaPtr +void Splash::pipeRunAARGB8(SplashPipe *pipe) { + Guchar aSrc, aDest, alpha2, aResult; + SplashColor cDest; + Guchar cResult0, cResult1, cResult2; + + //----- read destination pixel + cDest[0] = pipe->destColorPtr[0]; + cDest[1] = pipe->destColorPtr[1]; + cDest[2] = pipe->destColorPtr[2]; + aDest = *pipe->destAlphaPtr; + + //----- source alpha + aSrc = div255(pipe->aInput * pipe->shape); + + //----- result alpha and non-isolated group element correction + aResult = aSrc + aDest - div255(aSrc * aDest); + alpha2 = aResult; + + //----- result color + if (alpha2 == 0) { + cResult0 = 0; + cResult1 = 0; + cResult2 = 0; + } else { + cResult0 = state->rgbTransferR[(Guchar)(((alpha2 - aSrc) * cDest[0] + + aSrc * pipe->cSrc[0]) / alpha2)]; + cResult1 = state->rgbTransferG[(Guchar)(((alpha2 - aSrc) * cDest[1] + + aSrc * pipe->cSrc[1]) / alpha2)]; + cResult2 = state->rgbTransferB[(Guchar)(((alpha2 - aSrc) * cDest[2] + + aSrc * pipe->cSrc[2]) / alpha2)]; + } + + //----- write destination pixel + *pipe->destColorPtr++ = cResult0; + *pipe->destColorPtr++ = cResult1; + *pipe->destColorPtr++ = cResult2; + *pipe->destAlphaPtr++ = aResult; + + ++pipe->x; +} + +// special case: +// !pipe->pattern && !pipe->noTransparency && !state->softMask && +// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && +// !pipe->nonIsolatedGroup && +// bitmap->mode == splashModeXBGR8 && pipe->destAlphaPtr +void Splash::pipeRunAAXBGR8(SplashPipe *pipe) { + Guchar aSrc, aDest, alpha2, aResult; + SplashColor cDest; + Guchar cResult0, cResult1, cResult2; + + //----- read destination pixel + cDest[0] = pipe->destColorPtr[2]; + cDest[1] = pipe->destColorPtr[1]; + cDest[2] = pipe->destColorPtr[0]; + aDest = *pipe->destAlphaPtr; + + //----- source alpha + aSrc = div255(pipe->aInput * pipe->shape); + + //----- result alpha and non-isolated group element correction + aResult = aSrc + aDest - div255(aSrc * aDest); + alpha2 = aResult; + + //----- result color + if (alpha2 == 0) { + cResult0 = 0; + cResult1 = 0; + cResult2 = 0; + } else { + cResult0 = state->rgbTransferR[(Guchar)(((alpha2 - aSrc) * cDest[0] + + aSrc * pipe->cSrc[0]) / alpha2)]; + cResult1 = state->rgbTransferG[(Guchar)(((alpha2 - aSrc) * cDest[1] + + aSrc * pipe->cSrc[1]) / alpha2)]; + cResult2 = state->rgbTransferB[(Guchar)(((alpha2 - aSrc) * cDest[2] + + aSrc * pipe->cSrc[2]) / alpha2)]; + } + + //----- write destination pixel + *pipe->destColorPtr++ = cResult2; + *pipe->destColorPtr++ = cResult1; + *pipe->destColorPtr++ = cResult0; + *pipe->destColorPtr++ = 255; + *pipe->destAlphaPtr++ = aResult; + + ++pipe->x; +} + +// special case: +// !pipe->pattern && !pipe->noTransparency && !state->softMask && +// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && +// !pipe->nonIsolatedGroup && +// bitmap->mode == splashModeBGR8 && pipe->destAlphaPtr +void Splash::pipeRunAABGR8(SplashPipe *pipe) { + Guchar aSrc, aDest, alpha2, aResult; + SplashColor cDest; + Guchar cResult0, cResult1, cResult2; + + //----- read destination pixel + cDest[0] = pipe->destColorPtr[2]; + cDest[1] = pipe->destColorPtr[1]; + cDest[2] = pipe->destColorPtr[0]; + aDest = *pipe->destAlphaPtr; + + //----- source alpha + aSrc = div255(pipe->aInput * pipe->shape); + + //----- result alpha and non-isolated group element correction + aResult = aSrc + aDest - div255(aSrc * aDest); + alpha2 = aResult; + + //----- result color + if (alpha2 == 0) { + cResult0 = 0; + cResult1 = 0; + cResult2 = 0; + } else { + cResult0 = state->rgbTransferR[(Guchar)(((alpha2 - aSrc) * cDest[0] + + aSrc * pipe->cSrc[0]) / alpha2)]; + cResult1 = state->rgbTransferG[(Guchar)(((alpha2 - aSrc) * cDest[1] + + aSrc * pipe->cSrc[1]) / alpha2)]; + cResult2 = state->rgbTransferB[(Guchar)(((alpha2 - aSrc) * cDest[2] + + aSrc * pipe->cSrc[2]) / alpha2)]; + } + + //----- write destination pixel + *pipe->destColorPtr++ = cResult2; + *pipe->destColorPtr++ = cResult1; + *pipe->destColorPtr++ = cResult0; + *pipe->destAlphaPtr++ = aResult; + + ++pipe->x; +} + +#if SPLASH_CMYK +// special case: +// !pipe->pattern && !pipe->noTransparency && !state->softMask && +// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && +// !pipe->nonIsolatedGroup && +// bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr +void Splash::pipeRunAACMYK8(SplashPipe *pipe) { + Guchar aSrc, aDest, alpha2, aResult; + SplashColor cDest; + Guchar cResult0, cResult1, cResult2, cResult3; + + //----- read destination pixel + cDest[0] = pipe->destColorPtr[0]; + cDest[1] = pipe->destColorPtr[1]; + cDest[2] = pipe->destColorPtr[2]; + cDest[3] = pipe->destColorPtr[3]; + aDest = *pipe->destAlphaPtr; + + //----- source alpha + aSrc = div255(pipe->aInput * pipe->shape); + + //----- result alpha and non-isolated group element correction + aResult = aSrc + aDest - div255(aSrc * aDest); + alpha2 = aResult; + + //----- result color + if (alpha2 == 0) { + cResult0 = 0; + cResult1 = 0; + cResult2 = 0; + cResult3 = 0; + } else { + cResult0 = state->cmykTransferC[(Guchar)(((alpha2 - aSrc) * cDest[0] + + aSrc * pipe->cSrc[0]) / alpha2)]; + cResult1 = state->cmykTransferM[(Guchar)(((alpha2 - aSrc) * cDest[1] + + aSrc * pipe->cSrc[1]) / alpha2)]; + cResult2 = state->cmykTransferY[(Guchar)(((alpha2 - aSrc) * cDest[2] + + aSrc * pipe->cSrc[2]) / alpha2)]; + cResult3 = state->cmykTransferK[(Guchar)(((alpha2 - aSrc) * cDest[3] + + aSrc * pipe->cSrc[3]) / alpha2)]; + } + + //----- write destination pixel + if (state->overprintMask & 1) { + pipe->destColorPtr[0] = cResult0; + } + if (state->overprintMask & 2) { + pipe->destColorPtr[1] = cResult1; + } + if (state->overprintMask & 4) { + pipe->destColorPtr[2] = cResult2; + } + if (state->overprintMask & 8) { + pipe->destColorPtr[3] = cResult3; + } + pipe->destColorPtr += 4; + *pipe->destAlphaPtr++ = aResult; + + ++pipe->x; +} +#endif + inline void Splash::pipeSetXY(SplashPipe *pipe, int x, int y) { pipe->x = x; pipe->y = y; @@ -696,7 +1186,7 @@ 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); + (this->*pipe->run)(pipe); updateModX(x); updateModY(y); } @@ -757,8 +1247,8 @@ inline void Splash::drawAAPixel(SplashPipe *pipe, int x, int y) { // draw the pixel if (t != 0) { pipeSetXY(pipe, x, y); - pipe->shape *= aaGamma[t]; - pipeRun(pipe); + pipe->shape = div255(aaGamma[t] * pipe->shape); + (this->*pipe->run)(pipe); updateModX(x); updateModY(y); } @@ -768,18 +1258,25 @@ inline void Splash::drawSpan(SplashPipe *pipe, int x0, int x1, int y, GBool noClip) { int x; - pipeSetXY(pipe, x0, y); if (noClip) { + pipeSetXY(pipe, x0, y); for (x = x0; x <= x1; ++x) { - pipeRun(pipe); + (this->*pipe->run)(pipe); } updateModX(x0); updateModX(x1); updateModY(y); } else { + if (x0 < state->clip->getXMinI()) { + x0 = state->clip->getXMinI(); + } + if (x1 > state->clip->getXMaxI()) { + x1 = state->clip->getXMaxI(); + } + pipeSetXY(pipe, x0, y); for (x = x0; x <= x1; ++x) { if (state->clip->test(x, y)) { - pipeRun(pipe); + (this->*pipe->run)(pipe); updateModX(x); updateModY(y); } else { @@ -833,7 +1330,7 @@ inline void Splash::drawAALine(SplashPipe *pipe, int x0, int x1, int y) { if (t != 0) { pipe->shape = aaGamma[t]; - pipeRun(pipe); + (this->*pipe->run)(pipe); updateModX(x); updateModY(y); } else { @@ -865,19 +1362,22 @@ Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, bitmap = bitmapA; vectorAntialias = vectorAntialiasA; + inShading = gFalse; state = new SplashState(bitmap->width, bitmap->height, vectorAntialias, screenParams); if (vectorAntialias) { aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize, 1, splashModeMono1, gFalse); for (i = 0; i <= splashAASize * splashAASize; ++i) { - aaGamma[i] = splashPow((SplashCoord)i / - (SplashCoord)(splashAASize * splashAASize), - 1.5); + aaGamma[i] = (Guchar)splashRound( + splashPow((SplashCoord)i / + (SplashCoord)(splashAASize * splashAASize), + splashAAGamma) * 255); } } else { aaBuf = NULL; } + minLineWidth = 0; clearModRegion(); debugMode = gFalse; } @@ -887,6 +1387,7 @@ Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, int i; bitmap = bitmapA; + inShading = gFalse; vectorAntialias = vectorAntialiasA; state = new SplashState(bitmap->width, bitmap->height, vectorAntialias, screenA); @@ -894,13 +1395,15 @@ Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, 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); + aaGamma[i] = (Guchar)splashRound( + splashPow((SplashCoord)i / + (SplashCoord)(splashAASize * splashAASize), + splashAAGamma) * 255); } } else { aaBuf = NULL; } + minLineWidth = 0; clearModRegion(); debugMode = gFalse; } @@ -979,6 +1482,10 @@ SplashCoord Splash::getLineDashPhase() { return state->lineDashPhase; } +GBool Splash::getStrokeAdjust() { + return state->strokeAdjust; +} + SplashClip *Splash::getClip() { return state->clip; } @@ -1094,6 +1601,15 @@ void Splash::setInNonIsolatedGroup(SplashBitmap *alpha0BitmapA, state->inNonIsolatedGroup = gTrue; } +void Splash::setTransfer(Guchar *red, Guchar *green, Guchar *blue, + Guchar *gray) { + state->setTransfer(red, green, blue, gray); +} + +void Splash::setOverprintMask(Guint overprintMask) { + state->overprintMask = overprintMask; +} + //------------------------------------------------------------------------ // state save/restore //------------------------------------------------------------------------ @@ -1247,6 +1763,7 @@ void Splash::clear(SplashColorPtr color, Guchar alpha) { SplashError Splash::stroke(SplashPath *path) { SplashPath *path2, *dPath; + SplashCoord d1, d2, t1, t2, w; if (debugMode) { printf("stroke [dash:%d] [width:%.2f]:\n", @@ -1262,12 +1779,44 @@ SplashError Splash::stroke(SplashPath *path) { 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; } - if (state->lineWidth == 0) { - strokeNarrow(path2); + 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 <= 2) { + strokeNarrow(path2); + } else { + strokeWide(path2, state->lineWidth); + } } else { - strokeWide(path2); + if (state->lineWidth == 0) { + strokeNarrow(path2); + } else { + strokeWide(path2, state->lineWidth); + } } + delete path2; return splashOk; } @@ -1276,8 +1825,8 @@ 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; + int x0, x1, y0, y1, xa, xb, y; + SplashCoord dxdy; SplashClipResult clipRes; int nClipRes[3]; int i; @@ -1286,86 +1835,75 @@ void Splash::strokeNarrow(SplashPath *path) { xPath = new SplashXPath(path, state->matrix, state->flatness, gFalse); - pipeInit(&pipe, 0, 0, state->strokePattern, NULL, state->strokeAlpha, - gFalse, gFalse, state->strokePattern, gTrue); + pipeInit(&pipe, 0, 0, state->strokePattern, NULL, + (Guchar)splashRound(state->strokeAlpha * 255), + gFalse, gFalse); for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) { - - 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; + if (seg->y0 <= seg->y1) { + y0 = splashFloor(seg->y0); + y1 = splashFloor(seg->y1); + x0 = splashFloor(seg->x0); + x1 = splashFloor(seg->x1); + } else { + y0 = splashFloor(seg->y1); + y1 = splashFloor(seg->y0); + x0 = splashFloor(seg->x1); + x1 = splashFloor(seg->x0); + } + if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0, + x0 <= x1 ? x1 : x0, y1)) + != splashClipAllOutside) { + if (y0 == y1) { + if (x0 <= x1) { + drawSpan(&pipe, x0, x1, y0, clipRes == splashClipAllInside); + } else { + drawSpan(&pipe, x1, x0, y0, clipRes == splashClipAllInside); + } + } else { + dxdy = seg->dxdy; + if (y0 < state->clip->getYMinI()) { + y0 = state->clip->getYMinI(); + x0 = splashFloor(seg->x0 + ((SplashCoord)y0 - seg->y0) * dxdy); + } + if (y1 > state->clip->getYMaxI()) { + y1 = state->clip->getYMaxI(); + x1 = splashFloor(seg->x0 + ((SplashCoord)y1 - seg->y0) * dxdy); + } + if (x0 <= x1) { + xa = x0; + for (y = y0; y <= y1; ++y) { + if (y < y1) { + xb = splashFloor(seg->x0 + + ((SplashCoord)y + 1 - seg->y0) * dxdy); + } else { + xb = x1 + 1; + } + if (xa == xb) { + drawPixel(&pipe, xa, y, clipRes == splashClipAllInside); + } else { + drawSpan(&pipe, xa, xb - 1, y, clipRes == splashClipAllInside); + } + xa = xb; } - 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; + 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; } - 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]; } @@ -1381,10 +1919,10 @@ void Splash::strokeNarrow(SplashPath *path) { delete xPath; } -void Splash::strokeWide(SplashPath *path) { +void Splash::strokeWide(SplashPath *path, SplashCoord w) { SplashPath *path2; - path2 = makeStrokePath(path, gFalse); + path2 = makeStrokePath(path, w, gFalse); fillWithPattern(path2, gFalse, state->strokePattern, state->strokeAlpha); delete path2; } @@ -1397,7 +1935,11 @@ SplashPath *Splash::flattenPath(SplashPath *path, SplashCoord *matrix, int i; fPath = new SplashPath(); +#if USE_FIXEDPOINT + flatness2 = flatness; +#else flatness2 = flatness * flatness; +#endif i = 0; while (i < path->length) { flag = path->flags[i]; @@ -1462,13 +2004,21 @@ void Splash::flattenCurve(SplashCoord x0, SplashCoord y0, // line) transform(matrix, (xl0 + xr3) * 0.5, (yl0 + yr3) * 0.5, &mx, &my); transform(matrix, xx1, yy1, &tx, &ty); +#if USE_FIXEDPOINT + d1 = splashDist(tx, ty, mx, my); +#else dx = tx - mx; dy = ty - my; d1 = dx*dx + dy*dy; +#endif transform(matrix, xx2, yy2, &tx, &ty); +#if USE_FIXEDPOINT + d2 = splashDist(tx, ty, mx, my); +#else dx = tx - mx; dy = ty - my; d2 = dx*dx + dy*dy; +#endif // if the curve is flat enough, or no more subdivisions are // allowed, add the straight line segment @@ -1478,18 +2028,18 @@ void Splash::flattenCurve(SplashCoord x0, SplashCoord y0, // 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; + 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; @@ -1516,15 +2066,21 @@ SplashPath *Splash::makeDashedPath(SplashPath *path) { for (i = 0; i < state->lineDashLength; ++i) { lineDashTotal += state->lineDash[i]; } + // Acrobat simply draws nothing if the dash array is [0] + if (lineDashTotal == 0) { + return new SplashPath(); + } lineDashStartPhase = state->lineDashPhase; i = splashFloor(lineDashStartPhase / lineDashTotal); lineDashStartPhase -= (SplashCoord)i * lineDashTotal; lineDashStartOn = gTrue; lineDashStartIdx = 0; - while (lineDashStartPhase >= state->lineDash[lineDashStartIdx]) { - lineDashStartOn = !lineDashStartOn; - lineDashStartPhase -= state->lineDash[lineDashStartIdx]; - ++lineDashStartIdx; + if (lineDashStartPhase > 0) { + while (lineDashStartPhase >= state->lineDash[lineDashStartIdx]) { + lineDashStartOn = !lineDashStartOn; + lineDashStartPhase -= state->lineDash[lineDashStartIdx]; + ++lineDashStartIdx; + } } dPath = new SplashPath(); @@ -1634,15 +2190,49 @@ SplashError Splash::fillWithPattern(SplashPath *path, GBool eo, if (path->length == 0) { return splashErrEmptyPath; } + if (pathAllOutside(path)) { + opClipRes = splashClipAllOutside; + return splashOk; + } + + // add stroke adjustment hints for filled rectangles -- this only + // applies to paths that consist of a single subpath + // (this appears to match Acrobat's behavior) + if (state->strokeAdjust && !path->hints) { + int n; + n = path->getLength(); + if (n == 4 && + !(path->flags[0] & splashPathClosed) && + !(path->flags[1] & splashPathLast) && + !(path->flags[2] & splashPathLast)) { + path->close(gTrue); + path->addStrokeAdjustHint(0, 2, 0, 4); + path->addStrokeAdjustHint(1, 3, 0, 4); + } else if (n == 5 && + (path->flags[0] & splashPathClosed) && + !(path->flags[1] & splashPathLast) && + !(path->flags[2] & splashPathLast) && + !(path->flags[3] & splashPathLast)) { + path->addStrokeAdjustHint(0, 2, 0, 4); + path->addStrokeAdjustHint(1, 3, 0, 4); + } + } + xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue); - if (vectorAntialias) { + if (vectorAntialias && !inShading) { xPath->aaScale(); } xPath->sort(); - scanner = new SplashXPathScanner(xPath, eo); + yMinI = state->clip->getYMinI(); + yMaxI = state->clip->getYMaxI(); + if (vectorAntialias && !inShading) { + yMinI = yMinI * splashAASize; + yMaxI = (yMaxI + 1) * splashAASize - 1; + } + scanner = new SplashXPathScanner(xPath, eo, yMinI, yMaxI); // get the min and max x and y values - if (vectorAntialias) { + if (vectorAntialias && !inShading) { scanner->getBBoxAA(&xMinI, &yMinI, &xMaxI, &yMaxI); } else { scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI); @@ -1651,19 +2241,15 @@ SplashError Splash::fillWithPattern(SplashPath *path, GBool eo, // 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(); + if (scanner->hasPartialClip()) { + clipRes = splashClipPartial; } - pipeInit(&pipe, 0, yMinI, pattern, NULL, alpha, vectorAntialias, gFalse, pattern); + pipeInit(&pipe, 0, yMinI, pattern, NULL, (Guchar)splashRound(alpha * 255), + vectorAntialias && !inShading, gFalse); // draw the spans - if (vectorAntialias) { + if (vectorAntialias && !inShading) { for (y = yMinI; y <= yMaxI; ++y) { scanner->renderAALine(aaBuf, &x0, &x1, y); if (clipRes != splashClipAllInside) { @@ -1698,6 +2284,73 @@ SplashError Splash::fillWithPattern(SplashPath *path, GBool eo, return splashOk; } +GBool Splash::pathAllOutside(SplashPath *path) { + SplashCoord xMin1, yMin1, xMax1, yMax1; + SplashCoord xMin2, yMin2, xMax2, yMax2; + SplashCoord x, y; + int xMinI, yMinI, xMaxI, yMaxI; + int i; + + xMin1 = xMax1 = path->pts[0].x; + yMin1 = yMax1 = path->pts[0].y; + for (i = 1; i < path->length; ++i) { + if (path->pts[i].x < xMin1) { + xMin1 = path->pts[i].x; + } else if (path->pts[i].x > xMax1) { + xMax1 = path->pts[i].x; + } + if (path->pts[i].y < yMin1) { + yMin1 = path->pts[i].y; + } else if (path->pts[i].y > yMax1) { + yMax1 = path->pts[i].y; + } + } + + transform(state->matrix, xMin1, yMin1, &x, &y); + xMin2 = xMax2 = x; + yMin2 = yMax2 = y; + transform(state->matrix, xMin1, yMax1, &x, &y); + if (x < xMin2) { + xMin2 = x; + } else if (x > xMax2) { + xMax2 = x; + } + if (y < yMin2) { + yMin2 = y; + } else if (y > yMax2) { + yMax2 = y; + } + transform(state->matrix, xMax1, yMin1, &x, &y); + if (x < xMin2) { + xMin2 = x; + } else if (x > xMax2) { + xMax2 = x; + } + if (y < yMin2) { + yMin2 = y; + } else if (y > yMax2) { + yMax2 = y; + } + transform(state->matrix, xMax1, yMax1, &x, &y); + if (x < xMin2) { + xMin2 = x; + } else if (x > xMax2) { + xMax2 = x; + } + if (y < yMin2) { + yMin2 = y; + } else if (y > yMax2) { + yMax2 = y; + } + xMinI = splashFloor(xMin2); + yMinI = splashFloor(yMin2); + xMaxI = splashFloor(xMax2); + yMaxI = splashFloor(yMax2); + + return state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI) == + splashClipAllOutside; +} + SplashError Splash::xorFill(SplashPath *path, GBool eo) { SplashPipe pipe; SplashXPath *xPath; @@ -1711,7 +2364,8 @@ SplashError Splash::xorFill(SplashPath *path, GBool eo) { } xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue); xPath->sort(); - scanner = new SplashXPathScanner(xPath, eo); + scanner = new SplashXPathScanner(xPath, eo, state->clip->getYMinI(), + state->clip->getYMaxI()); // get the min and max x and y values scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI); @@ -1719,18 +2373,13 @@ SplashError Splash::xorFill(SplashPath *path, GBool eo) { // 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(); + if (scanner->hasPartialClip()) { + clipRes = splashClipPartial; } origBlendFunc = state->blendFunc; state->blendFunc = &blendXor; - pipeInit(&pipe, 0, yMinI, state->fillPattern, NULL, 1, gFalse, gFalse, state->fillPattern); + pipeInit(&pipe, 0, yMinI, state->fillPattern, NULL, 255, gFalse, gFalse); // draw the spans for (y = yMinI; y <= yMaxI; ++y) { @@ -1808,7 +2457,8 @@ void Splash::fillGlyph(SplashCoord x, SplashCoord y, void Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph, GBool noClip) { SplashPipe pipe; - int alpha0, alpha; + int alpha0; + Guchar alpha; Guchar *p; int x1, y1, xx, xx1, yy; @@ -1844,14 +2494,14 @@ void Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph, GBool noClip) if (noClip) { if (glyph->aa) { pipeInit(&pipe, xStart, yStart, - state->fillPattern, NULL, state->fillAlpha, gTrue, gFalse, state->fillPattern); + state->fillPattern, NULL, (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) { pipeSetXY(&pipe, xStart, y1); for (xx = 0, x1 = xStart; xx < xxLimit; ++xx, ++x1) { alpha = p[xx]; if (alpha != 0) { - pipe.shape = (SplashCoord)(alpha / 255.0); - pipeRun(&pipe); + pipe.shape = alpha; + (this->*pipe.run)(&pipe); updateModX(x1); updateModY(y1); } else { @@ -1864,14 +2514,14 @@ void Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph, GBool noClip) const int widthEight = splashCeil(glyph->w / 8.0); pipeInit(&pipe, xStart, yStart, - state->fillPattern, NULL, state->fillAlpha, gFalse, gFalse, state->fillPattern); + state->fillPattern, NULL, (Guchar)splashRound(state->fillAlpha * 255), gFalse, gFalse); for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) { pipeSetXY(&pipe, xStart, y1); for (xx = 0, x1 = xStart; xx < xxLimit; xx += 8) { alpha0 = (xShift > 0 ? (p[xx / 8] << xShift) | (p[xx / 8 + 1] >> (8 - xShift)) : p[xx / 8]); for (xx1 = 0; xx1 < 8 && xx + xx1 < xxLimit; ++xx1, ++x1) { if (alpha0 & 0x80) { - pipeRun(&pipe); + (this->*pipe.run)(&pipe); updateModX(x1); updateModY(y1); } else { @@ -1886,15 +2536,15 @@ void Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph, GBool noClip) } else { if (glyph->aa) { pipeInit(&pipe, xStart, yStart, - state->fillPattern, NULL, state->fillAlpha, gTrue, gFalse, state->fillPattern); + state->fillPattern, NULL, (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) { pipeSetXY(&pipe, xStart, y1); for (xx = 0, x1 = xStart; xx < xxLimit; ++xx, ++x1) { if (state->clip->test(x1, y1)) { alpha = p[xx]; if (alpha != 0) { - pipe.shape = (SplashCoord)(alpha / 255.0); - pipeRun(&pipe); + pipe.shape = alpha; + (this->*pipe.run)(&pipe); updateModX(x1); updateModY(y1); } else { @@ -1910,7 +2560,7 @@ void Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph, GBool noClip) const int widthEight = splashCeil(glyph->w / 8.0); pipeInit(&pipe, xStart, yStart, - state->fillPattern, NULL, state->fillAlpha, gFalse, gFalse, state->fillPattern); + state->fillPattern, NULL, (Guchar)splashRound(state->fillAlpha * 255), gFalse, gFalse); for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) { pipeSetXY(&pipe, xStart, y1); for (xx = 0, x1 = xStart; xx < xxLimit; xx += 8) { @@ -1918,7 +2568,7 @@ void Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph, GBool noClip) for (xx1 = 0; xx1 < 8 && xx + xx1 < xxLimit; ++xx1, ++x1) { if (state->clip->test(x1, y1)) { if (alpha0 & 0x80) { - pipeRun(&pipe); + (this->*pipe.run)(&pipe); updateModX(x1); updateModY(y1); } else { @@ -1939,22 +2589,11 @@ void Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph, GBool noClip) 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; + SplashBitmap *scaledMask; + SplashClipResult clipRes; + GBool minorAxisZero; + int x0, y0, x1, y1, scaledWidth, scaledHeight; + int yp; if (debugMode) { printf("fillImageMask: w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n", @@ -1965,289 +2604,738 @@ SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData, if (w == 0 && h == 0) return splashErrZeroImage; // check for singular matrix - if (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.000001) { + if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 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); + minorAxisZero = mat[1] == 0 && mat[2] == 0; + + // scaling only + if (mat[0] > 0 && minorAxisZero && mat[3] > 0) { + x0 = imgCoordMungeLowerC(mat[4], glyphMode); + y0 = imgCoordMungeLowerC(mat[5], glyphMode); + x1 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode); + y1 = imgCoordMungeUpperC(mat[3] + mat[5], glyphMode); + // make sure narrow images cover at least one pixel + if (x0 == x1) { + ++x1; } - } 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); + if (y0 == y1) { + ++y1; } - } - 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); + clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1); + opClipRes = clipRes; + if (clipRes != splashClipAllOutside) { + scaledWidth = x1 - x0; + scaledHeight = y1 - y0; + yp = h / scaledHeight; + if (yp < 0 || yp > INT_MAX - 1) { + return splashErrBadArg; + } + scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight); + blitMask(scaledMask, x0, y0, clipRes); + delete scaledMask; } - } 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); + + // scaling plus vertical flip + } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) { + x0 = imgCoordMungeLowerC(mat[4], glyphMode); + y0 = imgCoordMungeLowerC(mat[3] + mat[5], glyphMode); + x1 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode); + y1 = imgCoordMungeUpperC(mat[5], glyphMode); + // make sure narrow images cover at least one pixel + if (x0 == x1) { + ++x1; + } + if (y0 == y1) { + ++y1; + } + clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1); + opClipRes = clipRes; + if (clipRes != splashClipAllOutside) { + scaledWidth = x1 - x0; + scaledHeight = y1 - y0; + yp = h / scaledHeight; + if (yp < 0 || yp > INT_MAX - 1) { + return splashErrBadArg; + } + scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight); + vertFlipImage(scaledMask, scaledWidth, scaledHeight, 1); + blitMask(scaledMask, x0, y0, clipRes); + delete scaledMask; } + + // all other cases + } else { + arbitraryTransformMask(src, srcData, w, h, mat, glyphMode); } - scaledHeight = abs(ty2 - ty) + 1; - xSign = (xScale < 0) ? -1 : 1; - ySign = (yScale < 0) ? -1 : 1; - yShear1 = (SplashCoord)xSign * yShear; + + return splashOk; +} + +void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData, + int srcWidth, int srcHeight, + SplashCoord *mat, GBool glyphMode) { + SplashBitmap *scaledMask; + SplashClipResult clipRes, clipRes2; + SplashPipe pipe; + int scaledWidth, scaledHeight, t0, t1; + SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11; + SplashCoord vx[4], vy[4]; + int xMin, yMin, xMax, yMax; + ImageSection section[3]; + int nSections; + int y, xa, xb, x, i, xx, yy; + + // compute the four vertices of the target quadrilateral + vx[0] = mat[4]; vy[0] = mat[5]; + vx[1] = mat[2] + mat[4]; vy[1] = mat[3] + mat[5]; + vx[2] = mat[0] + mat[2] + mat[4]; vy[2] = mat[1] + mat[3] + mat[5]; + vx[3] = mat[0] + mat[4]; vy[3] = mat[1] + mat[5]; // clipping - 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); + xMin = imgCoordMungeLowerC(vx[0], glyphMode); + xMax = imgCoordMungeUpperC(vx[0], glyphMode); + yMin = imgCoordMungeLowerC(vy[0], glyphMode); + yMax = imgCoordMungeUpperC(vy[0], glyphMode); + for (i = 1; i < 4; ++i) { + t0 = imgCoordMungeLowerC(vx[i], glyphMode); + if (t0 < xMin) { + xMin = t0; + } + t0 = imgCoordMungeUpperC(vx[i], glyphMode); + if (t0 > xMax) { + xMax = t0; + } + t1 = imgCoordMungeLowerC(vy[i], glyphMode); + if (t1 < yMin) { + yMin = t1; + } + t1 = imgCoordMungeUpperC(vy[i], glyphMode); + if (t1 > yMax) { + yMax = t1; + } + } + clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1); opClipRes = clipRes; + if (clipRes == splashClipAllOutside) { + return; + } - // compute Bresenham parameters for x and y scaling - yp = h / scaledHeight; - yq = h % scaledHeight; - xp = w / scaledWidth; - xq = w % scaledWidth; + // compute the scale factors + if (mat[0] >= 0) { + t0 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode) - + imgCoordMungeLowerC(mat[4], glyphMode); + } else { + t0 = imgCoordMungeUpperC(mat[4], glyphMode) - + imgCoordMungeLowerC(mat[0] + mat[4], glyphMode); + } + if (mat[1] >= 0) { + t1 = imgCoordMungeUpperC(mat[1] + mat[5], glyphMode) - + imgCoordMungeLowerC(mat[5], glyphMode); + } else { + t1 = imgCoordMungeUpperC(mat[5], glyphMode) - + imgCoordMungeLowerC(mat[1] + mat[5], glyphMode); + } + scaledWidth = t0 > t1 ? t0 : t1; + if (mat[2] >= 0) { + t0 = imgCoordMungeUpperC(mat[2] + mat[4], glyphMode) - + imgCoordMungeLowerC(mat[4], glyphMode); + } else { + t0 = imgCoordMungeUpperC(mat[4], glyphMode) - + imgCoordMungeLowerC(mat[2] + mat[4], glyphMode); + } + if (mat[3] >= 0) { + t1 = imgCoordMungeUpperC(mat[3] + mat[5], glyphMode) - + imgCoordMungeLowerC(mat[5], glyphMode); + } else { + t1 = imgCoordMungeUpperC(mat[5], glyphMode) - + imgCoordMungeLowerC(mat[3] + mat[5], glyphMode); + } + scaledHeight = t0 > t1 ? t0 : t1; + if (scaledWidth == 0) { + scaledWidth = 1; + } + if (scaledHeight == 0) { + scaledHeight = 1; + } - // allocate pixel buffer - if (yp < 0 || yp > INT_MAX - 1) { - return splashErrBadArg; + // compute the inverse transform (after scaling) matrix + r00 = mat[0] / scaledWidth; + r01 = mat[1] / scaledWidth; + r10 = mat[2] / scaledHeight; + r11 = mat[3] / scaledHeight; + det = r00 * r11 - r01 * r10; + if (splashAbs(det) < 1e-6) { + // this should be caught by the singular matrix check in fillImageMask + return; + } + ir00 = r11 / det; + ir01 = -r01 / det; + ir10 = -r10 / det; + ir11 = r00 / det; + + // scale the input image + scaledMask = scaleMask(src, srcData, srcWidth, srcHeight, + scaledWidth, scaledHeight); + + // construct the three sections + i = (vy[2] <= vy[3]) ? 2 : 3; + if (vy[1] <= vy[i]) { + i = 1; + } + if (vy[0] < vy[i] || (i != 3 && vy[0] == vy[i])) { + i = 0; + } + if (vy[i] == vy[(i+1) & 3]) { + section[0].y0 = imgCoordMungeLowerC(vy[i], glyphMode); + section[0].y1 = imgCoordMungeUpperC(vy[(i+2) & 3], glyphMode) - 1; + if (vx[i] < vx[(i+1) & 3]) { + section[0].ia0 = i; + section[0].ia1 = (i+3) & 3; + section[0].ib0 = (i+1) & 3; + section[0].ib1 = (i+2) & 3; + } else { + section[0].ia0 = (i+1) & 3; + section[0].ia1 = (i+2) & 3; + section[0].ib0 = i; + section[0].ib1 = (i+3) & 3; + } + nSections = 1; + } else { + section[0].y0 = imgCoordMungeLowerC(vy[i], glyphMode); + section[2].y1 = imgCoordMungeUpperC(vy[(i+2) & 3], glyphMode) - 1; + section[0].ia0 = section[0].ib0 = i; + section[2].ia1 = section[2].ib1 = (i+2) & 3; + if (vx[(i+1) & 3] < vx[(i+3) & 3]) { + section[0].ia1 = section[2].ia0 = (i+1) & 3; + section[0].ib1 = section[2].ib0 = (i+3) & 3; + } else { + section[0].ia1 = section[2].ia0 = (i+3) & 3; + section[0].ib1 = section[2].ib0 = (i+1) & 3; + } + if (vy[(i+1) & 3] < vy[(i+3) & 3]) { + section[1].y0 = imgCoordMungeLowerC(vy[(i+1) & 3], glyphMode); + section[2].y0 = imgCoordMungeUpperC(vy[(i+3) & 3], glyphMode); + if (vx[(i+1) & 3] < vx[(i+3) & 3]) { + section[1].ia0 = (i+1) & 3; + section[1].ia1 = (i+2) & 3; + section[1].ib0 = i; + section[1].ib1 = (i+3) & 3; + } else { + section[1].ia0 = i; + section[1].ia1 = (i+3) & 3; + section[1].ib0 = (i+1) & 3; + section[1].ib1 = (i+2) & 3; + } + } else { + section[1].y0 = imgCoordMungeLowerC(vy[(i+3) & 3], glyphMode); + section[2].y0 = imgCoordMungeUpperC(vy[(i+1) & 3], glyphMode); + if (vx[(i+1) & 3] < vx[(i+3) & 3]) { + section[1].ia0 = i; + section[1].ia1 = (i+1) & 3; + section[1].ib0 = (i+3) & 3; + section[1].ib1 = (i+2) & 3; + } else { + section[1].ia0 = (i+3) & 3; + section[1].ia1 = (i+2) & 3; + section[1].ib0 = i; + section[1].ib1 = (i+1) & 3; + } + } + section[0].y1 = section[1].y0 - 1; + section[1].y1 = section[2].y0 - 1; + nSections = 3; + } + for (i = 0; i < nSections; ++i) { + section[i].xa0 = vx[section[i].ia0]; + section[i].ya0 = vy[section[i].ia0]; + section[i].xa1 = vx[section[i].ia1]; + section[i].ya1 = vy[section[i].ia1]; + section[i].xb0 = vx[section[i].ib0]; + section[i].yb0 = vy[section[i].ib0]; + section[i].xb1 = vx[section[i].ib1]; + section[i].yb1 = vy[section[i].ib1]; + section[i].dxdya = (section[i].xa1 - section[i].xa0) / + (section[i].ya1 - section[i].ya0); + section[i].dxdyb = (section[i].xb1 - section[i].xb0) / + (section[i].yb1 - section[i].yb0); } - pixBuf = (SplashColorPtr)gmallocn((yp + 1), w); // initialize the pixel pipe - pipeInit(&pipe, 0, 0, state->fillPattern, NULL, state->fillAlpha, - gTrue, gFalse, state->fillPattern); + pipeInit(&pipe, 0, 0, state->fillPattern, NULL, + (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); if (vectorAntialias) { drawAAPixelInit(); } + // make sure narrow images cover at least one pixel + if (nSections == 1) { + if (section[0].y0 == section[0].y1) { + ++section[0].y1; + clipRes = opClipRes = splashClipPartial; + } + } else { + if (section[0].y0 == section[2].y1) { + ++section[1].y1; + clipRes = opClipRes = splashClipPartial; + } + } + + // scan all pixels inside the target region + for (i = 0; i < nSections; ++i) { + for (y = section[i].y0; y <= section[i].y1; ++y) { + xa = imgCoordMungeLowerC(section[i].xa0 + + ((SplashCoord)y + 0.5 - section[i].ya0) * + section[i].dxdya, + glyphMode); + xb = imgCoordMungeUpperC(section[i].xb0 + + ((SplashCoord)y + 0.5 - section[i].yb0) * + section[i].dxdyb, + glyphMode); + // make sure narrow images cover at least one pixel + if (xa == xb) { + ++xb; + } + if (clipRes != splashClipAllInside) { + clipRes2 = state->clip->testSpan(xa, xb - 1, y); + } else { + clipRes2 = clipRes; + } + for (x = xa; x < xb; ++x) { + // map (x+0.5, y+0.5) back to the scaled image + xx = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir00 + + ((SplashCoord)y + 0.5 - mat[5]) * ir10); + yy = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir01 + + ((SplashCoord)y + 0.5 - mat[5]) * ir11); + // xx should always be within bounds, but floating point + // inaccuracy can cause problems + if (xx < 0) { + xx = 0; + } else if (xx >= scaledWidth) { + xx = scaledWidth - 1; + } + if (yy < 0) { + yy = 0; + } else if (yy >= scaledHeight) { + yy = scaledHeight - 1; + } + pipe.shape = scaledMask->data[yy * scaledWidth + xx]; + if (vectorAntialias && clipRes2 != splashClipAllInside) { + drawAAPixel(&pipe, x, y); + } else { + drawPixel(&pipe, x, y, clipRes2 == splashClipAllInside); + } + } + } + } + + delete scaledMask; +} + +// Scale an image mask into a SplashBitmap. +SplashBitmap *Splash::scaleMask(SplashImageMaskSource src, void *srcData, + int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight) { + SplashBitmap *dest; + + dest = new SplashBitmap(scaledWidth, scaledHeight, 1, splashModeMono8, + gFalse); + if (scaledHeight < srcHeight) { + if (scaledWidth < srcWidth) { + scaleMaskYdXd(src, srcData, srcWidth, srcHeight, + scaledWidth, scaledHeight, dest); + } else { + scaleMaskYdXu(src, srcData, srcWidth, srcHeight, + scaledWidth, scaledHeight, dest); + } + } else { + if (scaledWidth < srcWidth) { + scaleMaskYuXd(src, srcData, srcWidth, srcHeight, + scaledWidth, scaledHeight, dest); + } else { + scaleMaskYuXu(src, srcData, srcWidth, srcHeight, + scaledWidth, scaledHeight, dest); + } + } + return dest; +} + +void Splash::scaleMaskYdXd(SplashImageMaskSource src, void *srcData, + int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight, + SplashBitmap *dest) { + Guchar *lineBuf; + Guint *pixBuf; + Guint pix; + Guchar *destPtr; + int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, d, d0, d1; + int i, j; + + // Bresenham parameters for y scale + yp = srcHeight / scaledHeight; + yq = srcHeight % scaledHeight; + + // Bresenham parameters for x scale + xp = srcWidth / scaledWidth; + xq = srcWidth % scaledWidth; + + // allocate buffers + lineBuf = (Guchar *)gmalloc(srcWidth); + pixBuf = (Guint *)gmallocn(srcWidth, sizeof(int)); + // init y scale Bresenham yt = 0; - lastYStep = 1; + destPtr = dest->data; for (y = 0; y < scaledHeight; ++y) { // y scale Bresenham - yStep = yp; - yt += yq; - if (yt >= scaledHeight) { + if ((yt += yq) >= scaledHeight) { yt -= scaledHeight; - ++yStep; + yStep = yp + 1; + } else { + yStep = yp; } - // 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; + // read rows from image + memset(pixBuf, 0, srcWidth * sizeof(int)); + for (i = 0; i < yStep; ++i) { + (*src)(srcData, lineBuf); + for (j = 0; j < srcWidth; ++j) { + pixBuf[j] += lineBuf[j]; } } - lastYStep = yStep; - // loop-invariant constants - k1 = splashRound(xShear * ySign * y); + // init x scale Bresenham + xt = 0; + d0 = (255 << 23) / (yStep * xp); + d1 = (255 << 23) / (yStep * (xp + 1)); - // 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); + xx = 0; + for (x = 0; x < scaledWidth; ++x) { + + // x scale Bresenham + if ((xt += xq) >= scaledWidth) { + xt -= scaledWidth; + xStep = xp + 1; + d = d1; } else { - spanXMax = tx + k1; - spanXMin = spanXMax - (scaledWidth - 1); + xStep = xp; + d = d0; } - spanY = ty + ySign * y + (int)(yShear * k1); - clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY); - if (clipRes2 == splashClipAllOutside) { - continue; + + // compute the final pixel + pix = 0; + for (i = 0; i < xStep; ++i) { + pix += pixBuf[xx++]; } + // (255 * pix) / xStep * yStep + pix = (pix * d) >> 23; + + // store the pixel + *destPtr++ = (Guchar)pix; + } + } + + gfree(pixBuf); + gfree(lineBuf); +} + +void Splash::scaleMaskYdXu(SplashImageMaskSource src, void *srcData, + int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight, + SplashBitmap *dest) { + Guchar *lineBuf; + Guint *pixBuf; + Guint pix; + Guchar *destPtr; + int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, d; + int i, j; + + // Bresenham parameters for y scale + yp = srcHeight / scaledHeight; + yq = srcHeight % scaledHeight; + + // Bresenham parameters for x scale + xp = scaledWidth / srcWidth; + xq = scaledWidth % srcWidth; + + // allocate buffers + lineBuf = (Guchar *)gmalloc(srcWidth); + pixBuf = (Guint *)gmallocn(srcWidth, sizeof(int)); + + // init y scale Bresenham + yt = 0; + + destPtr = dest->data; + for (y = 0; y < scaledHeight; ++y) { + + // y scale Bresenham + if ((yt += yq) >= scaledHeight) { + yt -= scaledHeight; + yStep = yp + 1; } else { - clipRes2 = clipRes; + yStep = yp; + } + + // read rows from image + memset(pixBuf, 0, srcWidth * sizeof(int)); + for (i = 0; i < yStep; ++i) { + (*src)(srcData, lineBuf); + for (j = 0; j < srcWidth; ++j) { + pixBuf[j] += lineBuf[j]; + } } // init x scale Bresenham xt = 0; - xSrc = 0; + d = (255 << 23) / yStep; + + for (x = 0; x < srcWidth; ++x) { + + // x scale Bresenham + if ((xt += xq) >= srcWidth) { + xt -= srcWidth; + xStep = xp + 1; + } else { + xStep = xp; + } + + // compute the final pixel + pix = pixBuf[x]; + // (255 * pix) / yStep + pix = (pix * d) >> 23; + + // store the pixel + for (i = 0; i < xStep; ++i) { + *destPtr++ = (Guchar)pix; + } + } + } - // x shear - x1 = k1; + gfree(pixBuf); + gfree(lineBuf); +} + +void Splash::scaleMaskYuXd(SplashImageMaskSource src, void *srcData, + int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight, + SplashBitmap *dest) { + Guchar *lineBuf; + Guint pix; + Guchar *destPtr0, *destPtr; + int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, d, d0, d1; + int i; - // 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; + // Bresenham parameters for y scale + yp = scaledHeight / srcHeight; + yq = scaledHeight % srcHeight; + + // Bresenham parameters for x scale + xp = srcWidth / scaledWidth; + xq = srcWidth % scaledWidth; + + // allocate buffers + lineBuf = (Guchar *)gmalloc(srcWidth); + + // init y scale Bresenham + yt = 0; + + destPtr0 = dest->data; + for (y = 0; y < srcHeight; ++y) { + + // y scale Bresenham + if ((yt += yq) >= srcHeight) { + yt -= srcHeight; + yStep = yp + 1; + } else { + yStep = yp; } - // loop-invariant constants - n = yStep > 0 ? yStep : 1; + // read row from image + (*src)(srcData, lineBuf); + + // init x scale Bresenham + xt = 0; + d0 = (255 << 23) / xp; + d1 = (255 << 23) / (xp + 1); + xx = 0; for (x = 0; x < scaledWidth; ++x) { // x scale Bresenham - xStep = xp; - xt += xq; - if (xt >= scaledWidth) { + if ((xt += xq) >= scaledWidth) { xt -= scaledWidth; - ++xStep; + xStep = xp + 1; + d = d1; + } else { + xStep = xp; + d = d0; } - // 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; + // compute the final pixel + pix = 0; + for (i = 0; i < xStep; ++i) { + pix += lineBuf[xx++]; } + // (255 * pix) / xStep + pix = (pix * d) >> 23; - // 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); - } + // store the pixel + for (i = 0; i < yStep; ++i) { + destPtr = destPtr0 + i * scaledWidth + x; + *destPtr = (Guchar)pix; } + } + + destPtr0 += yStep * scaledWidth; + } + + gfree(lineBuf); +} + +void Splash::scaleMaskYuXu(SplashImageMaskSource src, void *srcData, + int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight, + SplashBitmap *dest) { + Guchar *lineBuf; + Guint pix; + Guchar *destPtr0, *destPtr; + int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx; + int i, j; + + // Bresenham parameters for y scale + yp = scaledHeight / srcHeight; + yq = scaledHeight % srcHeight; + + // Bresenham parameters for x scale + xp = scaledWidth / srcWidth; + xq = scaledWidth % srcWidth; + + // allocate buffers + lineBuf = (Guchar *)gmalloc(srcWidth); + + // init y scale Bresenham + yt = 0; + + destPtr0 = dest->data; + for (y = 0; y < srcHeight; ++y) { + + // y scale Bresenham + if ((yt += yq) >= srcHeight) { + yt -= srcHeight; + yStep = yp + 1; + } else { + yStep = yp; + } + + // read row from image + (*src)(srcData, lineBuf); + + // init x scale Bresenham + xt = 0; + + xx = 0; + for (x = 0; x < srcWidth; ++x) { // x scale Bresenham - xSrc += xStep; + if ((xt += xq) >= srcWidth) { + xt -= srcWidth; + xStep = xp + 1; + } else { + xStep = xp; + } + + // compute the final pixel + pix = lineBuf[x] ? 255 : 0; - // x shear - x1 += xSign; + // store the pixel + for (i = 0; i < yStep; ++i) { + for (j = 0; j < xStep; ++j) { + destPtr = destPtr0 + i * scaledWidth + xx + j; + *destPtr++ = (Guchar)pix; + } + } - // y shear - y1 += yShear1; + xx += xStep; } + + destPtr0 += yStep * scaledWidth; } - // free memory - gfree(pixBuf); + gfree(lineBuf); +} - return splashOk; +void Splash::blitMask(SplashBitmap *src, int xDest, int yDest, + SplashClipResult clipRes) { + SplashPipe pipe; + Guchar *p; + int w, h, x, y; + + w = src->getWidth(); + h = src->getHeight(); + if (vectorAntialias && clipRes != splashClipAllInside) { + pipeInit(&pipe, xDest, yDest, state->fillPattern, NULL, + (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); + drawAAPixelInit(); + p = src->getDataPtr(); + for (y = 0; y < h; ++y) { + for (x = 0; x < w; ++x) { + pipe.shape = *p++; + drawAAPixel(&pipe, xDest + x, yDest + y); + } + } + } else { + pipeInit(&pipe, xDest, yDest, state->fillPattern, NULL, + (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); + p = src->getDataPtr(); + if (clipRes == splashClipAllInside) { + for (y = 0; y < h; ++y) { + pipeSetXY(&pipe, xDest, yDest + y); + for (x = 0; x < w; ++x) { + if (*p) { + pipe.shape = *p; + (this->*pipe.run)(&pipe); + } else { + pipeIncX(&pipe); + } + ++p; + } + } + updateModX(xDest); + updateModX(xDest + w - 1); + updateModY(yDest); + updateModY(yDest + h - 1); + } else { + for (y = 0; y < h; ++y) { + pipeSetXY(&pipe, xDest, yDest + y); + for (x = 0; x < w; ++x) { + if (*p && state->clip->test(xDest + x, yDest + y)) { + pipe.shape = *p; + (this->*pipe.run)(&pipe); + updateModX(xDest + x); + updateModY(yDest + y); + } else { + pipeIncX(&pipe); + } + ++p; + } + } + } + } } SplashError Splash::drawImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, GBool srcAlpha, - int w, int h, SplashCoord *mat, SplashPattern *opImagePattern) { - 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; + int w, int h, SplashCoord *mat) { + GBool ok; + SplashBitmap *scaledImg; + SplashClipResult clipRes; + GBool minorAxisZero; + int x0, y0, x1, y1, scaledWidth, scaledHeight; + int nComps; + int yp; if (debugMode) { printf("drawImage: srcMode=%d srcAlpha=%d w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n", @@ -2282,799 +3370,1249 @@ SplashError Splash::drawImage(SplashImageSource src, void *srcData, nComps = 4; break; #endif + default: + ok = gFalse; + break; } if (!ok) { return splashErrModeMismatch; } // check for singular matrix - if (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.000001) { + if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 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); + minorAxisZero = mat[1] == 0 && mat[2] == 0; + + // scaling only + if (mat[0] > 0 && minorAxisZero && mat[3] > 0) { + x0 = imgCoordMungeLower(mat[4]); + y0 = imgCoordMungeLower(mat[5]); + x1 = imgCoordMungeUpper(mat[0] + mat[4]); + y1 = imgCoordMungeUpper(mat[3] + mat[5]); + // make sure narrow images cover at least one pixel + if (x0 == x1) { + ++x1; + } + if (y0 == y1) { + ++y1; + } + clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1); + opClipRes = clipRes; + if (clipRes != splashClipAllOutside) { + scaledWidth = x1 - x0; + scaledHeight = y1 - y0; + yp = h / scaledHeight; + if (yp < 0 || yp > INT_MAX - 1) { + return splashErrBadArg; + } + scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, + scaledWidth, scaledHeight); + blitImage(scaledImg, srcAlpha, x0, y0, clipRes); + delete scaledImg; + } + + // scaling plus vertical flip + } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) { + x0 = imgCoordMungeLower(mat[4]); + y0 = imgCoordMungeLower(mat[3] + mat[5]); + x1 = imgCoordMungeUpper(mat[0] + mat[4]); + y1 = imgCoordMungeUpper(mat[5]); + if (x0 == x1) { + if (mat[4] + mat[0] * 0.5 < x0) { + --x0; + } else { + ++x1; + } + } + if (y0 == y1) { + if (mat[5] + mat[1] * 0.5 < y0) { + --y0; + } else { + ++y1; + } + } + clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1); + opClipRes = clipRes; + if (clipRes != splashClipAllOutside) { + scaledWidth = x1 - x0; + scaledHeight = y1 - y0; + yp = h / scaledHeight; + if (yp < 0 || yp > INT_MAX - 1) { + return splashErrBadArg; + } + scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, + scaledWidth, scaledHeight); + vertFlipImage(scaledImg, scaledWidth, scaledHeight, nComps); + blitImage(scaledImg, srcAlpha, x0, y0, clipRes); + delete scaledImg; + } + + // all other cases } else { - ty = splashFloor(mat[5] + 0.01); - ty2 = splashFloor(mat[5] + yScale - 0.01); + return arbitraryTransformImage(src, srcData, srcMode, nComps, srcAlpha, + w, h, mat); } - scaledHeight = abs(ty2 - ty) + 1; - xSign = (xScale < 0) ? -1 : 1; - ySign = (yScale < 0) ? -1 : 1; - yShear1 = (SplashCoord)xSign * yShear; + + return splashOk; +} + +SplashError Splash::arbitraryTransformImage(SplashImageSource src, void *srcData, + SplashColorMode srcMode, int nComps, + GBool srcAlpha, + int srcWidth, int srcHeight, + SplashCoord *mat) { + SplashBitmap *scaledImg; + SplashClipResult clipRes, clipRes2; + SplashPipe pipe; + SplashColor pixel; + int scaledWidth, scaledHeight, t0, t1, th; + SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11; + SplashCoord vx[4], vy[4]; + int xMin, yMin, xMax, yMax; + ImageSection section[3]; + int nSections; + int y, xa, xb, x, i, xx, yy, yp; + + // compute the four vertices of the target quadrilateral + vx[0] = mat[4]; vy[0] = mat[5]; + vx[1] = mat[2] + mat[4]; vy[1] = mat[3] + mat[5]; + vx[2] = mat[0] + mat[2] + mat[4]; vy[2] = mat[1] + mat[3] + mat[5]; + vx[3] = mat[0] + mat[4]; vy[3] = mat[1] + mat[5]; // clipping - 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); + xMin = imgCoordMungeLower(vx[0]); + xMax = imgCoordMungeUpper(vx[0]); + yMin = imgCoordMungeLower(vy[0]); + yMax = imgCoordMungeUpper(vy[0]); + for (i = 1; i < 4; ++i) { + t0 = imgCoordMungeLower(vx[i]); + if (t0 < xMin) { + xMin = t0; + } + t0 = imgCoordMungeUpper(vx[i]); + if (t0 > xMax) { + xMax = t0; + } + t1 = imgCoordMungeLower(vy[i]); + if (t1 < yMin) { + yMin = t1; + } + t1 = imgCoordMungeUpper(vy[i]); + if (t1 > yMax) { + yMax = t1; + } + } + clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1); 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; + // compute the scale factors + if (mat[0] >= 0) { + t0 = imgCoordMungeUpper(mat[0] + mat[4]) - imgCoordMungeLower(mat[4]); + } else { + t0 = imgCoordMungeUpper(mat[4]) - imgCoordMungeLower(mat[0] + mat[4]); + } + if (mat[1] >= 0) { + t1 = imgCoordMungeUpper(mat[1] + mat[5]) - imgCoordMungeLower(mat[5]); + } else { + t1 = imgCoordMungeUpper(mat[5]) - imgCoordMungeLower(mat[1] + mat[5]); + } + scaledWidth = t0 > t1 ? t0 : t1; + if (mat[2] >= 0) { + t0 = imgCoordMungeUpper(mat[2] + mat[4]) - imgCoordMungeLower(mat[4]); + if (splashAbs(mat[1]) >= 1) { + th = imgCoordMungeUpper(mat[2]) - imgCoordMungeLower(mat[0] * mat[3] / mat[1]); + if (th > t0) t0 = th; + } + } else { + t0 = imgCoordMungeUpper(mat[4]) - imgCoordMungeLower(mat[2] + mat[4]); + if (splashAbs(mat[1]) >= 1) { + th = imgCoordMungeUpper(mat[0] * mat[3] / mat[1]) - imgCoordMungeLower(mat[2]); + if (th > t0) t0 = th; + } + } + if (mat[3] >= 0) { + t1 = imgCoordMungeUpper(mat[3] + mat[5]) - imgCoordMungeLower(mat[5]); + if (splashAbs(mat[0]) >= 1) { + th = imgCoordMungeUpper(mat[3]) - imgCoordMungeLower(mat[1] * mat[2] / mat[0]); + if (th > t1) t1 = th; + } + } else { + t1 = imgCoordMungeUpper(mat[5]) - imgCoordMungeLower(mat[3] + mat[5]); + if (splashAbs(mat[0]) >= 1) { + th = imgCoordMungeUpper(mat[1] * mat[2] / mat[0]) - imgCoordMungeLower(mat[3]); + if (th > t1) t1 = th; + } + } + scaledHeight = t0 > t1 ? t0 : t1; + if (scaledWidth == 0) { + scaledWidth = 1; + } + if (scaledHeight == 0) { + scaledHeight = 1; + } + + // compute the inverse transform (after scaling) matrix + r00 = mat[0] / scaledWidth; + r01 = mat[1] / scaledWidth; + r10 = mat[2] / scaledHeight; + r11 = mat[3] / scaledHeight; + det = r00 * r11 - r01 * r10; + if (splashAbs(det) < 1e-6) { + // this should be caught by the singular matrix check in drawImage + return splashErrBadArg; + } + ir00 = r11 / det; + ir01 = -r01 / det; + ir10 = -r10 / det; + ir11 = r00 / det; - // allocate pixel buffers + // scale the input image + yp = srcHeight / scaledHeight; if (yp < 0 || yp > INT_MAX - 1) { return splashErrBadArg; } - colorBuf = (SplashColorPtr)gmallocn3((yp + 1), w, nComps); - if (srcAlpha) { - alphaBuf = (Guchar *)gmallocn((yp + 1), w); + scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, + srcWidth, srcHeight, scaledWidth, scaledHeight); + + // construct the three sections + i = 0; + if (vy[1] < vy[i]) { + i = 1; + } + if (vy[2] < vy[i]) { + i = 2; + } + if (vy[3] < vy[i]) { + i = 3; + } + // NB: if using fixed point, 0.000001 will be truncated to zero, + // so these two comparisons must be <=, not < + if (splashAbs(vy[i] - vy[(i-1) & 3]) <= 0.000001 && + vy[(i-1) & 3] < vy[(i+1) & 3]) { + i = (i-1) & 3; + } + if (splashAbs(vy[i] - vy[(i+1) & 3]) <= 0.000001) { + section[0].y0 = imgCoordMungeLower(vy[i]); + section[0].y1 = imgCoordMungeUpper(vy[(i+2) & 3]) - 1; + if (vx[i] < vx[(i+1) & 3]) { + section[0].ia0 = i; + section[0].ia1 = (i+3) & 3; + section[0].ib0 = (i+1) & 3; + section[0].ib1 = (i+2) & 3; + } else { + section[0].ia0 = (i+1) & 3; + section[0].ia1 = (i+2) & 3; + section[0].ib0 = i; + section[0].ib1 = (i+3) & 3; + } + nSections = 1; } else { - alphaBuf = NULL; + section[0].y0 = imgCoordMungeLower(vy[i]); + section[2].y1 = imgCoordMungeUpper(vy[(i+2) & 3]) - 1; + section[0].ia0 = section[0].ib0 = i; + section[2].ia1 = section[2].ib1 = (i+2) & 3; + if (vx[(i+1) & 3] < vx[(i+3) & 3]) { + section[0].ia1 = section[2].ia0 = (i+1) & 3; + section[0].ib1 = section[2].ib0 = (i+3) & 3; + } else { + section[0].ia1 = section[2].ia0 = (i+3) & 3; + section[0].ib1 = section[2].ib0 = (i+1) & 3; + } + if (vy[(i+1) & 3] < vy[(i+3) & 3]) { + section[1].y0 = imgCoordMungeLower(vy[(i+1) & 3]); + section[2].y0 = imgCoordMungeUpper(vy[(i+3) & 3]); + if (vx[(i+1) & 3] < vx[(i+3) & 3]) { + section[1].ia0 = (i+1) & 3; + section[1].ia1 = (i+2) & 3; + section[1].ib0 = i; + section[1].ib1 = (i+3) & 3; + } else { + section[1].ia0 = i; + section[1].ia1 = (i+3) & 3; + section[1].ib0 = (i+1) & 3; + section[1].ib1 = (i+2) & 3; + } + } else { + section[1].y0 = imgCoordMungeLower(vy[(i+3) & 3]); + section[2].y0 = imgCoordMungeUpper(vy[(i+1) & 3]); + if (vx[(i+1) & 3] < vx[(i+3) & 3]) { + section[1].ia0 = i; + section[1].ia1 = (i+1) & 3; + section[1].ib0 = (i+3) & 3; + section[1].ib1 = (i+2) & 3; + } else { + section[1].ia0 = (i+3) & 3; + section[1].ia1 = (i+2) & 3; + section[1].ib0 = i; + section[1].ib1 = (i+1) & 3; + } + } + section[0].y1 = section[1].y0 - 1; + section[1].y1 = section[2].y0 - 1; + nSections = 3; + } + for (i = 0; i < nSections; ++i) { + section[i].xa0 = vx[section[i].ia0]; + section[i].ya0 = vy[section[i].ia0]; + section[i].xa1 = vx[section[i].ia1]; + section[i].ya1 = vy[section[i].ia1]; + section[i].xb0 = vx[section[i].ib0]; + section[i].yb0 = vy[section[i].ib0]; + section[i].xb1 = vx[section[i].ib1]; + section[i].yb1 = vy[section[i].ib1]; + section[i].dxdya = (section[i].xa1 - section[i].xa0) / + (section[i].ya1 - section[i].ya0); + section[i].dxdyb = (section[i].xb1 - section[i].xb0) / + (section[i].yb1 - section[i].yb0); } - - 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, + pipeInit(&pipe, 0, 0, NULL, pixel, + (Guchar)splashRound(state->fillAlpha * 255), srcAlpha || (vectorAntialias && clipRes != splashClipAllInside), - gFalse, opImagePattern); + gFalse); if (vectorAntialias) { drawAAPixelInit(); } - if (srcAlpha) { - - // init y scale Bresenham - yt = 0; - lastYStep = 1; - - for (y = 0; y < scaledHeight; ++y) { + // make sure narrow images cover at least one pixel + if (nSections == 1) { + if (section[0].y0 == section[0].y1) { + ++section[0].y1; + clipRes = opClipRes = splashClipPartial; + } + } else { + if (section[0].y0 == section[2].y1) { + ++section[1].y1; + clipRes = opClipRes = splashClipPartial; + } + } - // 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; - } + // scan all pixels inside the target region + for (i = 0; i < nSections; ++i) { + for (y = section[i].y0; y <= section[i].y1; ++y) { + xa = imgCoordMungeLower(section[i].xa0 + + ((SplashCoord)y + 0.5 - section[i].ya0) * + section[i].dxdya); + xb = imgCoordMungeUpper(section[i].xb0 + + ((SplashCoord)y + 0.5 - section[i].yb0) * + section[i].dxdyb); + // make sure narrow images cover at least one pixel + if (xa == xb) { + ++xb; } - 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); + if (clipRes != splashClipAllInside) { + clipRes2 = state->clip->testSpan(xa, xb - 1, y); + } else { + clipRes2 = clipRes; + } + for (x = xa; x < xb; ++x) { + // map (x+0.5, y+0.5) back to the scaled image + xx = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir00 + + ((SplashCoord)y + 0.5 - mat[5]) * ir10); + yy = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir01 + + ((SplashCoord)y + 0.5 - mat[5]) * ir11); + // xx should always be within bounds, but floating point + // inaccuracy can cause problems + if (xx < 0) { + xx = 0; + } else if (xx >= scaledWidth) { + xx = scaledWidth - 1; + } + if (yy < 0) { + yy = 0; + } else if (yy >= scaledHeight) { + yy = scaledHeight - 1; + } + scaledImg->getPixel(xx, yy, pixel); + if (srcAlpha) { + pipe.shape = scaledImg->alpha[yy * scaledWidth + xx]; } else { - spanXMax = tx + k1; - spanXMin = spanXMax - (scaledWidth - 1); + pipe.shape = 255; } - spanY = ty + ySign * y + (int)(yShear * k1); - clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY); - if (clipRes2 == splashClipAllOutside) { - continue; + if (vectorAntialias && clipRes2 != splashClipAllInside) { + drawAAPixel(&pipe, x, y); + } else { + drawPixel(&pipe, x, y, clipRes2 == splashClipAllInside); } - } else { - clipRes2 = clipRes; } + } + } - // init x scale Bresenham - xt = 0; - xSrc = 0; + delete scaledImg; + return splashOk; +} - // x shear - x1 = k1; +// Scale an image into a SplashBitmap. +SplashBitmap *Splash::scaleImage(SplashImageSource src, void *srcData, + SplashColorMode srcMode, int nComps, + GBool srcAlpha, int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight) { + SplashBitmap *dest; + + dest = new SplashBitmap(scaledWidth, scaledHeight, 1, srcMode, srcAlpha); + if (scaledHeight < srcHeight) { + if (scaledWidth < srcWidth) { + scaleImageYdXd(src, srcData, srcMode, nComps, srcAlpha, + srcWidth, srcHeight, scaledWidth, scaledHeight, dest); + } else { + scaleImageYdXu(src, srcData, srcMode, nComps, srcAlpha, + srcWidth, srcHeight, scaledWidth, scaledHeight, dest); + } + } else { + if (scaledWidth < srcWidth) { + scaleImageYuXd(src, srcData, srcMode, nComps, srcAlpha, + srcWidth, srcHeight, scaledWidth, scaledHeight, dest); + } else { + scaleImageYuXu(src, srcData, srcMode, nComps, srcAlpha, + srcWidth, srcHeight, scaledWidth, scaledHeight, dest); + } + } + return dest; +} - // 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; - } +void Splash::scaleImageYdXd(SplashImageSource src, void *srcData, + SplashColorMode srcMode, int nComps, + GBool srcAlpha, int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight, + SplashBitmap *dest) { + Guchar *lineBuf, *alphaLineBuf; + Guint *pixBuf, *alphaPixBuf; + Guint pix0, pix1, pix2; +#if SPLASH_CMYK + Guint pix3; +#endif + Guint alpha; + Guchar *destPtr, *destAlphaPtr; + int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, xxa, d, d0, d1; + int i, j; + + // Bresenham parameters for y scale + yp = srcHeight / scaledHeight; + yq = srcHeight % scaledHeight; + + // Bresenham parameters for x scale + xp = srcWidth / scaledWidth; + xq = srcWidth % scaledWidth; + + // allocate buffers + lineBuf = (Guchar *)gmallocn(srcWidth, nComps); + pixBuf = (Guint *)gmallocn(srcWidth, nComps * sizeof(int)); + if (srcAlpha) { + alphaLineBuf = (Guchar *)gmalloc(srcWidth); + alphaPixBuf = (Guint *)gmallocn(srcWidth, sizeof(int)); + } else { + alphaLineBuf = NULL; + alphaPixBuf = NULL; + } - // loop-invariant constants - n = yStep > 0 ? yStep : 1; + // init y scale Bresenham + yt = 0; - switch (srcMode) { + destPtr = dest->data; + destAlphaPtr = dest->alpha; + for (y = 0; y < scaledHeight; ++y) { - case splashModeMono1: - case splashModeMono8: - for (x = 0; x < scaledWidth; ++x) { - - // x scale Bresenham - xStep = xp; - xt += xq; - if (xt >= scaledWidth) { - xt -= scaledWidth; - ++xStep; - } + // y scale Bresenham + if ((yt += yq) >= scaledHeight) { + yt -= scaledHeight; + yStep = yp + 1; + } else { + yStep = yp; + } - // rotation - if (rot) { - x2 = (int)y1; - y2 = -x1; - } else { - x2 = x1; - y2 = (int)y1; - } + // read rows from image + memset(pixBuf, 0, srcWidth * nComps * sizeof(int)); + if (srcAlpha) { + memset(alphaPixBuf, 0, srcWidth * sizeof(int)); + } + for (i = 0; i < yStep; ++i) { + (*src)(srcData, lineBuf, alphaLineBuf); + for (j = 0; j < srcWidth * nComps; ++j) { + pixBuf[j] += lineBuf[j]; + } + if (srcAlpha) { + for (j = 0; j < srcWidth; ++j) { + alphaPixBuf[j] += alphaLineBuf[j]; + } + } + } - // 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; + // init x scale Bresenham + xt = 0; + d0 = (1 << 23) / (yStep * xp); + d1 = (1 << 23) / (yStep * (xp + 1)); - if (alpha > 0) { - pix[0] = (int)((SplashCoord)pixAcc0 * pixMul); + xx = xxa = 0; + for (x = 0; x < scaledWidth; ++x) { - // 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 + if ((xt += xq) >= scaledWidth) { + xt -= scaledWidth; + xStep = xp + 1; + d = d1; + } else { + xStep = xp; + d = d0; + } - // x scale Bresenham - xSrc += xStep; + switch (srcMode) { - // x shear - x1 += xSign; + case splashModeMono8: - // y shear - y1 += yShear1; + // compute the final pixel + pix0 = 0; + for (i = 0; i < xStep; ++i) { + pix0 += pixBuf[xx++]; } + // pix / xStep * yStep + pix0 = (pix0 * d) >> 23; + + // store the pixel + *destPtr++ = (Guchar)pix0; break; case splashModeRGB8: - 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; + // compute the final pixel + pix0 = pix1 = pix2 = 0; + for (i = 0; i < xStep; ++i) { + pix0 += pixBuf[xx]; + pix1 += pixBuf[xx+1]; + pix2 += pixBuf[xx+2]; + xx += 3; } + // pix / xStep * yStep + pix0 = (pix0 * d) >> 23; + pix1 = (pix1 * d) >> 23; + pix2 = (pix2 * d) >> 23; + + // store the pixel + *destPtr++ = (Guchar)pix0; + *destPtr++ = (Guchar)pix1; + *destPtr++ = (Guchar)pix2; break; case splashModeXBGR8: - 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 = 0; - for (i = 0; i < n; ++i) { - for (j = 0; j < m; ++j) { - pixAcc0 += *p++; - pixAcc1 += *p++; - pixAcc2 += *p++; - 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] = 255; - - // 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; + // compute the final pixel + pix0 = pix1 = pix2 = 0; + for (i = 0; i < xStep; ++i) { + pix0 += pixBuf[xx]; + pix1 += pixBuf[xx+1]; + pix2 += pixBuf[xx+2]; + xx += 4; + } + // pix / xStep * yStep + pix0 = (pix0 * d) >> 23; + pix1 = (pix1 * d) >> 23; + pix2 = (pix2 * d) >> 23; + + // store the pixel + *destPtr++ = (Guchar)pix2; + *destPtr++ = (Guchar)pix1; + *destPtr++ = (Guchar)pix0; + *destPtr++ = (Guchar)255; + break; - // x shear - x1 += xSign; + case splashModeBGR8: - // y shear - y1 += yShear1; + // compute the final pixel + pix0 = pix1 = pix2 = 0; + for (i = 0; i < xStep; ++i) { + pix0 += pixBuf[xx]; + pix1 += pixBuf[xx+1]; + pix2 += pixBuf[xx+2]; + xx += 3; } + // pix / xStep * yStep + pix0 = (pix0 * d) >> 23; + pix1 = (pix1 * d) >> 23; + pix2 = (pix2 * d) >> 23; + + // store the pixel + *destPtr++ = (Guchar)pix2; + *destPtr++ = (Guchar)pix1; + *destPtr++ = (Guchar)pix0; break; - #if SPLASH_CMYK case splashModeCMYK8: - 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); - } - } + // compute the final pixel + pix0 = pix1 = pix2 = pix3 = 0; + for (i = 0; i < xStep; ++i) { + pix0 += pixBuf[xx]; + pix1 += pixBuf[xx+1]; + pix2 += pixBuf[xx+2]; + pix3 += pixBuf[xx+3]; + xx += 4; + } + // pix / xStep * yStep + pix0 = (pix0 * d) >> 23; + pix1 = (pix1 * d) >> 23; + pix2 = (pix2 * d) >> 23; + pix3 = (pix3 * d) >> 23; + + // store the pixel + *destPtr++ = (Guchar)pix0; + *destPtr++ = (Guchar)pix1; + *destPtr++ = (Guchar)pix2; + *destPtr++ = (Guchar)pix3; + break; +#endif - // x scale Bresenham - xSrc += xStep; - // x shear - x1 += xSign; + case splashModeMono1: // mono1 is not allowed + default: + break; + } - // y shear - y1 += yShear1; + // process alpha + if (srcAlpha) { + alpha = 0; + for (i = 0; i < xStep; ++i, ++xxa) { + alpha += alphaPixBuf[xxa]; } - break; -#endif // SPLASH_CMYK + // alpha / xStep * yStep + alpha = (alpha * d) >> 23; + *destAlphaPtr++ = (Guchar)alpha; } } + } + gfree(alphaPixBuf); + gfree(alphaLineBuf); + gfree(pixBuf); + gfree(lineBuf); +} + +void Splash::scaleImageYdXu(SplashImageSource src, void *srcData, + SplashColorMode srcMode, int nComps, + GBool srcAlpha, int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight, + SplashBitmap *dest) { + Guchar *lineBuf, *alphaLineBuf; + Guint *pixBuf, *alphaPixBuf; + Guint pix[splashMaxColorComps]; + Guint alpha; + Guchar *destPtr, *destAlphaPtr; + int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, d; + int i, j; + + // Bresenham parameters for y scale + yp = srcHeight / scaledHeight; + yq = srcHeight % scaledHeight; + + // Bresenham parameters for x scale + xp = scaledWidth / srcWidth; + xq = scaledWidth % srcWidth; + + // allocate buffers + lineBuf = (Guchar *)gmallocn(srcWidth, nComps); + pixBuf = (Guint *)gmallocn(srcWidth, nComps * sizeof(int)); + if (srcAlpha) { + alphaLineBuf = (Guchar *)gmalloc(srcWidth); + alphaPixBuf = (Guint *)gmallocn(srcWidth, sizeof(int)); } else { + alphaLineBuf = NULL; + alphaPixBuf = NULL; + } - // init y scale Bresenham - yt = 0; - lastYStep = 1; + // init y scale Bresenham + yt = 0; - for (y = 0; y < scaledHeight; ++y) { + destPtr = dest->data; + destAlphaPtr = dest->alpha; + for (y = 0; y < scaledHeight; ++y) { - // y scale Bresenham + // y scale Bresenham + if ((yt += yq) >= scaledHeight) { + yt -= scaledHeight; + yStep = yp + 1; + } else { 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; + } + + // read rows from image + memset(pixBuf, 0, srcWidth * nComps * sizeof(int)); + if (srcAlpha) { + memset(alphaPixBuf, 0, srcWidth * sizeof(int)); + } + for (i = 0; i < yStep; ++i) { + (*src)(srcData, lineBuf, alphaLineBuf); + for (j = 0; j < srcWidth * nComps; ++j) { + pixBuf[j] += lineBuf[j]; + } + if (srcAlpha) { + for (j = 0; j < srcWidth; ++j) { + alphaPixBuf[j] += alphaLineBuf[j]; } } - lastYStep = yStep; + } - // loop-invariant constants - k1 = splashRound(xShear * ySign * y); + // init x scale Bresenham + xt = 0; + d = (1 << 23) / yStep; - // 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; - } + for (x = 0; x < srcWidth; ++x) { + + // x scale Bresenham + if ((xt += xq) >= srcWidth) { + xt -= srcWidth; + xStep = xp + 1; } else { - clipRes2 = clipRes; + xStep = xp; } - // init x scale Bresenham - xt = 0; - xSrc = 0; + // compute the final pixel + for (i = 0; i < nComps; ++i) { + // pixBuf[] / yStep + pix[i] = (pixBuf[x * nComps + i] * d) >> 23; + } - // x shear - x1 = k1; + // store the pixel + switch (srcMode) { + case splashModeMono1: // mono1 is not allowed + break; + case splashModeMono8: + for (i = 0; i < xStep; ++i) { + *destPtr++ = (Guchar)pix[0]; + } + break; + case splashModeRGB8: + for (i = 0; i < xStep; ++i) { + *destPtr++ = (Guchar)pix[0]; + *destPtr++ = (Guchar)pix[1]; + *destPtr++ = (Guchar)pix[2]; + } + break; + case splashModeXBGR8: + for (i = 0; i < xStep; ++i) { + *destPtr++ = (Guchar)pix[2]; + *destPtr++ = (Guchar)pix[1]; + *destPtr++ = (Guchar)pix[0]; + *destPtr++ = (Guchar)255; + } + break; + case splashModeBGR8: + for (i = 0; i < xStep; ++i) { + *destPtr++ = (Guchar)pix[2]; + *destPtr++ = (Guchar)pix[1]; + *destPtr++ = (Guchar)pix[0]; + } + break; +#if SPLASH_CMYK + case splashModeCMYK8: + for (i = 0; i < xStep; ++i) { + *destPtr++ = (Guchar)pix[0]; + *destPtr++ = (Guchar)pix[1]; + *destPtr++ = (Guchar)pix[2]; + *destPtr++ = (Guchar)pix[3]; + } + break; +#endif + } - // 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; + // process alpha + if (srcAlpha) { + // alphaPixBuf[] / yStep + alpha = (alphaPixBuf[x] * d) >> 23; + for (i = 0; i < xStep; ++i) { + *destAlphaPtr++ = (Guchar)alpha; + } } + } + } - // loop-invariant constants - n = yStep > 0 ? yStep : 1; + gfree(alphaPixBuf); + gfree(alphaLineBuf); + gfree(pixBuf); + gfree(lineBuf); +} - switch (srcMode) { +void Splash::scaleImageYuXd(SplashImageSource src, void *srcData, + SplashColorMode srcMode, int nComps, + GBool srcAlpha, int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight, + SplashBitmap *dest) { + Guchar *lineBuf, *alphaLineBuf; + Guint pix[splashMaxColorComps]; + Guint alpha; + Guchar *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr; + int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, xxa, d, d0, d1; + int i, j; + + // Bresenham parameters for y scale + yp = scaledHeight / srcHeight; + yq = scaledHeight % srcHeight; + + // Bresenham parameters for x scale + xp = srcWidth / scaledWidth; + xq = srcWidth % scaledWidth; + + // allocate buffers + lineBuf = (Guchar *)gmallocn(srcWidth, nComps); + if (srcAlpha) { + alphaLineBuf = (Guchar *)gmalloc(srcWidth); + } else { + alphaLineBuf = NULL; + } - case splashModeMono1: - case splashModeMono8: - for (x = 0; x < scaledWidth; ++x) { - - // x scale Bresenham - xStep = xp; - xt += xq; - if (xt >= scaledWidth) { - xt -= scaledWidth; - ++xStep; - } + // init y scale Bresenham + yt = 0; - // rotation - if (rot) { - x2 = (int)y1; - y2 = -x1; - } else { - x2 = x1; - y2 = (int)y1; - } + destPtr0 = dest->data; + destAlphaPtr0 = dest->alpha; + for (y = 0; y < srcHeight; ++y) { - // 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); + // y scale Bresenham + if ((yt += yq) >= srcHeight) { + yt -= srcHeight; + yStep = yp + 1; + } else { + yStep = yp; + } - pix[0] = (int)((SplashCoord)pixAcc0 * pixMul); + // read row from image + (*src)(srcData, lineBuf, alphaLineBuf); - // 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); - } + // init x scale Bresenham + xt = 0; + d0 = (1 << 23) / xp; + d1 = (1 << 23) / (xp + 1); - // x scale Bresenham - xSrc += xStep; + xx = xxa = 0; + for (x = 0; x < scaledWidth; ++x) { - // x shear - x1 += xSign; + // x scale Bresenham + if ((xt += xq) >= scaledWidth) { + xt -= scaledWidth; + xStep = xp + 1; + d = d1; + } else { + xStep = xp; + d = d0; + } - // y shear - y1 += yShear1; + // compute the final pixel + for (i = 0; i < nComps; ++i) { + pix[i] = 0; + } + for (i = 0; i < xStep; ++i) { + for (j = 0; j < nComps; ++j, ++xx) { + pix[j] += lineBuf[xx]; } - break; + } + for (i = 0; i < nComps; ++i) { + // pix[] / xStep + pix[i] = (pix[i] * d) >> 23; + } + // store the pixel + switch (srcMode) { + case splashModeMono1: // mono1 is not allowed + break; + case splashModeMono8: + for (i = 0; i < yStep; ++i) { + destPtr = destPtr0 + (i * scaledWidth + x) * nComps; + *destPtr++ = (Guchar)pix[0]; + } + break; case splashModeRGB8: + for (i = 0; i < yStep; ++i) { + destPtr = destPtr0 + (i * scaledWidth + x) * nComps; + *destPtr++ = (Guchar)pix[0]; + *destPtr++ = (Guchar)pix[1]; + *destPtr++ = (Guchar)pix[2]; + } + break; + case splashModeXBGR8: + for (i = 0; i < yStep; ++i) { + destPtr = destPtr0 + (i * scaledWidth + x) * nComps; + *destPtr++ = (Guchar)pix[2]; + *destPtr++ = (Guchar)pix[1]; + *destPtr++ = (Guchar)pix[0]; + *destPtr++ = (Guchar)255; + } + break; case splashModeBGR8: - for (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); + for (i = 0; i < yStep; ++i) { + destPtr = destPtr0 + (i * scaledWidth + x) * nComps; + *destPtr++ = (Guchar)pix[2]; + *destPtr++ = (Guchar)pix[1]; + *destPtr++ = (Guchar)pix[0]; + } + break; +#if SPLASH_CMYK + case splashModeCMYK8: + for (i = 0; i < yStep; ++i) { + destPtr = destPtr0 + (i * scaledWidth + x) * nComps; + *destPtr++ = (Guchar)pix[0]; + *destPtr++ = (Guchar)pix[1]; + *destPtr++ = (Guchar)pix[2]; + *destPtr++ = (Guchar)pix[3]; + } + break; +#endif + } - pix[0] = (int)((SplashCoord)pixAcc0 * pixMul); - pix[1] = (int)((SplashCoord)pixAcc1 * pixMul); - pix[2] = (int)((SplashCoord)pixAcc2 * pixMul); + // process alpha + if (srcAlpha) { + alpha = 0; + for (i = 0; i < xStep; ++i, ++xxa) { + alpha += alphaLineBuf[xxa]; + } + // alpha / xStep + alpha = (alpha * d) >> 23; + for (i = 0; i < yStep; ++i) { + destAlphaPtr = destAlphaPtr0 + i * scaledWidth + x; + *destAlphaPtr = (Guchar)alpha; + } + } + } - // 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); - } + destPtr0 += yStep * scaledWidth * nComps; + if (srcAlpha) { + destAlphaPtr0 += yStep * scaledWidth; + } + } - // x scale Bresenham - xSrc += xStep; + gfree(alphaLineBuf); + gfree(lineBuf); +} - // x shear - x1 += xSign; +void Splash::scaleImageYuXu(SplashImageSource src, void *srcData, + SplashColorMode srcMode, int nComps, + GBool srcAlpha, int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight, + SplashBitmap *dest) { + Guchar *lineBuf, *alphaLineBuf; + Guint pix[splashMaxColorComps]; + Guint alpha; + Guchar *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr; + int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx; + int i, j; + + // Bresenham parameters for y scale + yp = scaledHeight / srcHeight; + yq = scaledHeight % srcHeight; + + // Bresenham parameters for x scale + xp = scaledWidth / srcWidth; + xq = scaledWidth % srcWidth; + + // allocate buffers + lineBuf = (Guchar *)gmallocn(srcWidth, nComps); + if (srcAlpha) { + alphaLineBuf = (Guchar *)gmalloc(srcWidth); + } else { + alphaLineBuf = NULL; + } - // y shear - y1 += yShear1; - } - break; + // init y scale Bresenham + yt = 0; - case splashModeXBGR8: - for (x = 0; x < scaledWidth; ++x) { - - // x scale Bresenham - xStep = xp; - xt += xq; - if (xt >= scaledWidth) { - xt -= scaledWidth; - ++xStep; - } + destPtr0 = dest->data; + destAlphaPtr0 = dest->alpha; + for (y = 0; y < srcHeight; ++y) { - // rotation - if (rot) { - x2 = (int)y1; - y2 = -x1; - } else { - x2 = x1; - y2 = (int)y1; - } + // y scale Bresenham + if ((yt += yq) >= srcHeight) { + yt -= srcHeight; + yStep = yp + 1; + } else { + yStep = yp; + } - // 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 = 0; - for (i = 0; i < n; ++i) { - for (j = 0; j < m; ++j) { - pixAcc0 += *p++; - pixAcc1 += *p++; - pixAcc2 += *p++; - p++; - } - p += 4 * (w - m); - } - pixMul = (SplashCoord)1 / (SplashCoord)(n * m); + // read row from image + (*src)(srcData, lineBuf, alphaLineBuf); - pix[0] = (int)((SplashCoord)pixAcc0 * pixMul); - pix[1] = (int)((SplashCoord)pixAcc1 * pixMul); - pix[2] = (int)((SplashCoord)pixAcc2 * pixMul); - pix[3] = 255; + // init x scale Bresenham + xt = 0; - // 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); - } + xx = 0; + for (x = 0; x < srcWidth; ++x) { - // x scale Bresenham - xSrc += xStep; + // x scale Bresenham + if ((xt += xq) >= srcWidth) { + xt -= srcWidth; + xStep = xp + 1; + } else { + xStep = xp; + } - // x shear - x1 += xSign; + // compute the final pixel + for (i = 0; i < nComps; ++i) { + pix[i] = lineBuf[x * nComps + i]; + } - // y shear - y1 += yShear1; + // store the pixel + switch (srcMode) { + case splashModeMono1: // mono1 is not allowed + break; + case splashModeMono8: + for (i = 0; i < yStep; ++i) { + for (j = 0; j < xStep; ++j) { + destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; + *destPtr++ = (Guchar)pix[0]; + } + } + break; + case splashModeRGB8: + for (i = 0; i < yStep; ++i) { + for (j = 0; j < xStep; ++j) { + destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; + *destPtr++ = (Guchar)pix[0]; + *destPtr++ = (Guchar)pix[1]; + *destPtr++ = (Guchar)pix[2]; + } + } + break; + case splashModeXBGR8: + for (i = 0; i < yStep; ++i) { + for (j = 0; j < xStep; ++j) { + destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; + *destPtr++ = (Guchar)pix[2]; + *destPtr++ = (Guchar)pix[1]; + *destPtr++ = (Guchar)pix[0]; + *destPtr++ = (Guchar)255; + } + } + break; + case splashModeBGR8: + for (i = 0; i < yStep; ++i) { + for (j = 0; j < xStep; ++j) { + destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; + *destPtr++ = (Guchar)pix[2]; + *destPtr++ = (Guchar)pix[1]; + *destPtr++ = (Guchar)pix[0]; + } } break; - #if SPLASH_CMYK case splashModeCMYK8: - for (x = 0; x < scaledWidth; ++x) { - - // x scale Bresenham - xStep = xp; - xt += xq; - if (xt >= scaledWidth) { - xt -= scaledWidth; - ++xStep; + for (i = 0; i < yStep; ++i) { + for (j = 0; j < xStep; ++j) { + destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; + *destPtr++ = (Guchar)pix[0]; + *destPtr++ = (Guchar)pix[1]; + *destPtr++ = (Guchar)pix[2]; + *destPtr++ = (Guchar)pix[3]; } + } + break; +#endif + } - // rotation - if (rot) { - x2 = (int)y1; - y2 = -x1; - } else { - x2 = x1; - y2 = (int)y1; + // process alpha + if (srcAlpha) { + alpha = alphaLineBuf[x]; + for (i = 0; i < yStep; ++i) { + for (j = 0; j < xStep; ++j) { + destAlphaPtr = destAlphaPtr0 + i * scaledWidth + xx + j; + *destAlphaPtr = (Guchar)alpha; } + } + } - // 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); + xx += xStep; + } - pix[0] = (int)((SplashCoord)pixAcc0 * pixMul); - pix[1] = (int)((SplashCoord)pixAcc1 * pixMul); - pix[2] = (int)((SplashCoord)pixAcc2 * pixMul); - pix[3] = (int)((SplashCoord)pixAcc3 * pixMul); + destPtr0 += yStep * scaledWidth * nComps; + if (srcAlpha) { + destAlphaPtr0 += yStep * scaledWidth; + } + } - // 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); - } + gfree(alphaLineBuf); + gfree(lineBuf); +} + +void Splash::vertFlipImage(SplashBitmap *img, int width, int height, + int nComps) { + Guchar *lineBuf; + Guchar *p0, *p1; + int w; + + w = width * nComps; + lineBuf = (Guchar *)gmalloc(w); + for (p0 = img->data, p1 = img->data + (height - 1) * w; + p0 < p1; + p0 += w, p1 -= w) { + memcpy(lineBuf, p0, w); + memcpy(p0, p1, w); + memcpy(p1, lineBuf, w); + } + if (img->alpha) { + for (p0 = img->alpha, p1 = img->alpha + (height - 1) * width; + p0 < p1; + p0 += width, p1 -= width) { + memcpy(lineBuf, p0, width); + memcpy(p0, p1, width); + memcpy(p1, lineBuf, width); + } + } + gfree(lineBuf); +} - // x scale Bresenham - xSrc += xStep; +void Splash::blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest, + SplashClipResult clipRes) { + SplashPipe pipe; + SplashColor pixel; + Guchar *ap; + int w, h, x0, y0, x1, y1, x, y; - // x shear - x1 += xSign; + // split the image into clipped and unclipped regions + w = src->getWidth(); + h = src->getHeight(); + if (clipRes == splashClipAllInside) { + x0 = 0; + y0 = 0; + x1 = w; + y1 = h; + } else { + if (state->clip->getNumPaths()) { + x0 = x1 = w; + y0 = y1 = h; + } else { + if ((x0 = splashCeil(state->clip->getXMin()) - xDest) < 0) { + x0 = 0; + } + if ((y0 = splashCeil(state->clip->getYMin()) - yDest) < 0) { + y0 = 0; + } + if ((x1 = splashFloor(state->clip->getXMax()) - xDest) > w) { + x1 = w; + } + if (x1 < x0) { + x1 = x0; + } + if ((y1 = splashFloor(state->clip->getYMax()) - yDest) > h) { + y1 = h; + } + if (y1 < y0) { + y1 = y0; + } + } + } - // y shear - y1 += yShear1; + // draw the unclipped region + if (x0 < w && y0 < h && x0 < x1 && y0 < y1) { + pipeInit(&pipe, xDest + x0, yDest + y0, NULL, pixel, + (Guchar)splashRound(state->fillAlpha * 255), srcAlpha, gFalse); + if (srcAlpha) { + for (y = y0; y < y1; ++y) { + pipeSetXY(&pipe, xDest + x0, yDest + y); + ap = src->getAlphaPtr() + y * w + x0; + for (x = x0; x < x1; ++x) { + src->getPixel(x, y, pixel); + pipe.shape = *ap++; + (this->*pipe.run)(&pipe); + } + } + } else { + for (y = y0; y < y1; ++y) { + pipeSetXY(&pipe, xDest + x0, yDest + y); + for (x = x0; x < x1; ++x) { + src->getPixel(x, y, pixel); + (this->*pipe.run)(&pipe); } - break; -#endif // SPLASH_CMYK } } + updateModX(xDest + x0); + updateModX(xDest + x1 - 1); + updateModY(yDest + y0); + updateModY(yDest + y1 - 1); + } + // draw the clipped regions + if (y0 > 0) { + blitImageClipped(src, srcAlpha, 0, 0, xDest, yDest, w, y0); } + if (y1 < h) { + blitImageClipped(src, srcAlpha, 0, y1, xDest, yDest + y1, w, h - y1); + } + if (x0 > 0 && y0 < y1) { + blitImageClipped(src, srcAlpha, 0, y0, xDest, yDest + y0, x0, y1 - y0); + } + if (x1 < w && y0 < y1) { + blitImageClipped(src, srcAlpha, x1, y0, xDest + x1, yDest + y0, + w - x1, y1 - y0); + } +} - gfree(colorBuf); - gfree(alphaBuf); +void Splash::blitImageClipped(SplashBitmap *src, GBool srcAlpha, + int xSrc, int ySrc, int xDest, int yDest, + int w, int h) { + SplashPipe pipe; + SplashColor pixel; + Guchar *ap; + int x, y; - return splashOk; + if (vectorAntialias) { + pipeInit(&pipe, xDest, yDest, NULL, pixel, + (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); + drawAAPixelInit(); + if (srcAlpha) { + for (y = 0; y < h; ++y) { + ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc; + for (x = 0; x < w; ++x) { + src->getPixel(xSrc + x, ySrc + y, pixel); + pipe.shape = *ap++; + drawAAPixel(&pipe, xDest + x, yDest + y); + } + } + } else { + for (y = 0; y < h; ++y) { + for (x = 0; x < w; ++x) { + src->getPixel(xSrc + x, ySrc + y, pixel); + pipe.shape = 255; + drawAAPixel(&pipe, xDest + x, yDest + y); + } + } + } + } else { + pipeInit(&pipe, xDest, yDest, NULL, pixel, + (Guchar)splashRound(state->fillAlpha * 255), srcAlpha, gFalse); + if (srcAlpha) { + for (y = 0; y < h; ++y) { + ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc; + pipeSetXY(&pipe, xDest, yDest + y); + for (x = 0; x < w; ++x) { + if (state->clip->test(xDest + x, yDest + y)) { + src->getPixel(xSrc + x, ySrc + y, pixel); + pipe.shape = *ap++; + (this->*pipe.run)(&pipe); + updateModX(xDest + x); + updateModY(yDest + y); + } else { + pipeIncX(&pipe); + ++ap; + } + } + } + } else { + for (y = 0; y < h; ++y) { + pipeSetXY(&pipe, xDest, yDest + y); + for (x = 0; x < w; ++x) { + if (state->clip->test(xDest + x, yDest + y)) { + src->getPixel(xSrc + x, ySrc + y, pixel); + (this->*pipe.run)(&pipe); + updateModX(xDest + x); + updateModY(yDest + y); + } else { + pipeIncX(&pipe); + } + } + } + } + } } SplashError Splash::composite(SplashBitmap *src, int xSrc, int ySrc, @@ -3091,39 +4629,72 @@ SplashError Splash::composite(SplashBitmap *src, int xSrc, int ySrc, } 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) { - alpha = *ap++; - if (noClip || state->clip->test(xDest + x, yDest + y)) { + pipeInit(&pipe, xDest, yDest, NULL, pixel, + (Guchar)splashRound(state->fillAlpha * 255), gTrue, nonIsolated); + if (noClip) { + for (y = 0; y < h; ++y) { + pipeSetXY(&pipe, xDest, yDest + y); + ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc; + for (x = 0; x < w; ++x) { + src->getPixel(xSrc + x, ySrc + y, pixel); + alpha = *ap++; // this uses shape instead of alpha, which isn't technically // correct, but works out the same + pipe.shape = alpha; + (this->*pipe.run)(&pipe); + } + } + updateModX(xDest); + updateModX(xDest + w - 1); + updateModY(yDest); + updateModY(yDest + h - 1); + } else { + for (y = 0; y < h; ++y) { + pipeSetXY(&pipe, xDest, yDest + y); + ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc; + for (x = 0; x < w; ++x) { src->getPixel(xSrc + x, ySrc + y, pixel); - pipe.shape = (SplashCoord)(alpha / 255.0); - pipeRun(&pipe); - updateModX(xDest + x); - updateModY(yDest + y); - } else { - pipeIncX(&pipe); + alpha = *ap++; + if (state->clip->test(xDest + x, yDest + y)) { + // this uses shape instead of alpha, which isn't technically + // correct, but works out the same + pipe.shape = alpha; + (this->*pipe.run)(&pipe); + updateModX(xDest + x); + updateModY(yDest + y); + } else { + pipeIncX(&pipe); + } } } } } else { - pipeInit(&pipe, xDest, yDest, NULL, pixel, state->fillAlpha, - gFalse, nonIsolated); - for (y = 0; y < h; ++y) { - pipeSetXY(&pipe, xDest, yDest + y); - for (x = 0; x < w; ++x) { - if (noClip || state->clip->test(xDest + x, yDest + y)) { + pipeInit(&pipe, xDest, yDest, NULL, pixel, + (Guchar)splashRound(state->fillAlpha * 255), gFalse, nonIsolated); + if (noClip) { + for (y = 0; y < h; ++y) { + pipeSetXY(&pipe, xDest, yDest + y); + for (x = 0; x < w; ++x) { src->getPixel(xSrc + x, ySrc + y, pixel); - pipeRun(&pipe); - updateModX(xDest + x); - updateModY(yDest + y); - } else { - pipeIncX(&pipe); + (this->*pipe.run)(&pipe); + } + } + updateModX(xDest); + updateModX(xDest + w - 1); + updateModY(yDest); + updateModY(yDest + h - 1); + } else { + for (y = 0; y < h; ++y) { + pipeSetXY(&pipe, xDest, yDest + y); + for (x = 0; x < w; ++x) { + src->getPixel(xSrc + x, ySrc + y, pixel); + if (state->clip->test(xDest + x, yDest + y)) { + (this->*pipe.run)(&pipe); + updateModX(xDest + x); + updateModY(yDest + y); + } else { + pipeIncX(&pipe); + } } } } @@ -3142,7 +4713,7 @@ void Splash::compositeBackground(SplashColorPtr color) { int x, y, mask; if (unlikely(bitmap->alpha == NULL)) { - error(-1, "bitmap->alpha is NULL in Splash::compositeBackground"); + error(errInternal, -1, "bitmap->alpha is NULL in Splash::compositeBackground"); return; } @@ -3248,11 +4819,21 @@ void Splash::compositeBackground(SplashColorPtr color) { 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]); + if (alpha == 0) + { + p[0] = color0; + p[1] = color1; + p[2] = color2; + p[3] = color3; + } + else if (alpha != 255) + { + alpha1 = 255 - alpha; + p[0] = div255(alpha1 * color0 + alpha * p[0]); + p[1] = div255(alpha1 * color1 + alpha * p[1]); + p[2] = div255(alpha1 * color2 + alpha * p[2]); + p[3] = div255(alpha1 * color3 + alpha * p[3]); + } p += 4; } } @@ -3322,7 +4903,7 @@ GBool Splash::gouraudTriangleShadedFill(SplashGouraudColor *shading) SplashPipe pipe; SplashColor cSrcVal; - pipeInit(&pipe, 0, 0, NULL, cSrcVal, state->strokeAlpha, gFalse, gFalse, NULL, gTrue); + pipeInit(&pipe, 0, 0, NULL, cSrcVal, (Guchar)splashRound(state->strokeAlpha * 255), gFalse, gFalse); if (vectorAntialias) { if (aaBuf == NULL) @@ -3577,7 +5158,7 @@ SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc, int xDest, int yDest, int w, int h) { SplashColorPtr p, sp; Guchar *q; - int x, y, mask; + int x, y, mask, srcMask; if (src->mode != bitmap->mode) { return splashErrModeMismatch; @@ -3587,10 +5168,11 @@ SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc, case splashModeMono1: for (y = 0; y < h; ++y) { p = &bitmap->data[(yDest + y) * bitmap->rowSize + (xDest >> 3)]; - sp = &src->data[(ySrc + y) * bitmap->rowSize + (xSrc >> 3)]; mask = 0x80 >> (xDest & 7); + sp = &src->data[(ySrc + y) * src->rowSize + (xSrc >> 3)]; + srcMask = 0x80 >> (xSrc & 7); for (x = 0; x < w; ++x) { - if (sp[0] & (0x80 >> (x & 7))) { + if (*sp & srcMask) { *p |= mask; } else { *p &= ~mask; @@ -3598,6 +5180,9 @@ SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc, if (!(mask >>= 1)) { mask = 0x80; ++p; + } + if (!(srcMask >>= 1)) { + srcMask = 0x80; ++sp; } } @@ -3663,166 +5248,246 @@ SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc, return splashOk; } -SplashPath *Splash::makeStrokePath(SplashPath *path, GBool flatten) { - SplashPath *pathIn, *pathOut; - SplashCoord w, d, dx, dy, wdx, wdy, dxNext, dyNext, wdxNext, wdyNext; +SplashPath *Splash::makeStrokePath(SplashPath *path, SplashCoord w, + GBool flatten) { +SplashPath *pathIn, *dashPath, *pathOut; + SplashCoord d, dx, dy, wdx, wdy, dxNext, dyNext, wdxNext, wdyNext; SplashCoord crossprod, dotprod, miter, m; GBool first, last, closed; - int subpathStart, next, i; + int subpathStart0, subpathStart1, seg, i0, i1, j0, j1, k0, k1; int left0, left1, left2, right0, right1, right2, join0, join1, join2; int leftFirst, rightFirst, firstPt; + pathOut = new SplashPath(); + + if (path->length == 0) { + return pathOut; + } + if (flatten) { pathIn = flattenPath(path, state->matrix, state->flatness); if (state->lineDashLength > 0) { - pathOut = makeDashedPath(pathIn); + dashPath = makeDashedPath(pathIn); delete pathIn; - pathIn = pathOut; + pathIn = dashPath; + if (pathIn->length == 0) { + delete pathIn; + return pathOut; + } } } else { pathIn = path; } - subpathStart = 0; // make gcc happy + subpathStart0 = subpathStart1 = 0; // make gcc happy + seg = 0; // make gcc happy closed = gFalse; // make gcc happy left0 = left1 = right0 = right1 = join0 = join1 = 0; // make gcc happy leftFirst = rightFirst = firstPt = 0; // make gcc happy - pathOut = new SplashPath(); - w = state->lineWidth; - - for (i = 0; i < pathIn->length - 1; ++i) { - if (pathIn->flags[i] & splashPathLast) { + i0 = 0; + for (i1 = i0; + !(pathIn->flags[i1] & splashPathLast) && + i1 + 1 < pathIn->length && + pathIn->pts[i1+1].x == pathIn->pts[i1].x && + pathIn->pts[i1+1].y == pathIn->pts[i1].y; + ++i1) ; + + while (i1 < pathIn->length) { + if ((first = pathIn->flags[i0] & splashPathFirst)) { + subpathStart0 = i0; + subpathStart1 = i1; + seg = 0; + closed = pathIn->flags[i0] & splashPathClosed; + } + j0 = i1 + 1; + if (j0 < pathIn->length) { + for (j1 = j0; + !(pathIn->flags[j1] & splashPathLast) && + j1 + 1 < pathIn->length && + pathIn->pts[j1+1].x == pathIn->pts[j1].x && + pathIn->pts[j1+1].y == pathIn->pts[j1].y; + ++j1) ; + } else { + j1 = j0; + } + if (pathIn->flags[i1] & splashPathLast) { + if (first && state->lineCap == splashLineCapRound) { + // special case: zero-length subpath with round line caps --> + // draw a circle + pathOut->moveTo(pathIn->pts[i0].x + (SplashCoord)0.5 * w, + pathIn->pts[i0].y); + pathOut->curveTo(pathIn->pts[i0].x + (SplashCoord)0.5 * w, + pathIn->pts[i0].y + bezierCircle2 * w, + pathIn->pts[i0].x + bezierCircle2 * w, + pathIn->pts[i0].y + (SplashCoord)0.5 * w, + pathIn->pts[i0].x, + pathIn->pts[i0].y + (SplashCoord)0.5 * w); + pathOut->curveTo(pathIn->pts[i0].x - bezierCircle2 * w, + pathIn->pts[i0].y + (SplashCoord)0.5 * w, + pathIn->pts[i0].x - (SplashCoord)0.5 * w, + pathIn->pts[i0].y + bezierCircle2 * w, + pathIn->pts[i0].x - (SplashCoord)0.5 * w, + pathIn->pts[i0].y); + pathOut->curveTo(pathIn->pts[i0].x - (SplashCoord)0.5 * w, + pathIn->pts[i0].y - bezierCircle2 * w, + pathIn->pts[i0].x - bezierCircle2 * w, + pathIn->pts[i0].y - (SplashCoord)0.5 * w, + pathIn->pts[i0].x, + pathIn->pts[i0].y - (SplashCoord)0.5 * w); + pathOut->curveTo(pathIn->pts[i0].x + bezierCircle2 * w, + pathIn->pts[i0].y - (SplashCoord)0.5 * w, + pathIn->pts[i0].x + (SplashCoord)0.5 * w, + pathIn->pts[i0].y - bezierCircle2 * w, + pathIn->pts[i0].x + (SplashCoord)0.5 * w, + pathIn->pts[i0].y); + pathOut->close(); + } + i0 = j0; + i1 = j1; continue; } - 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; + last = pathIn->flags[j1] & splashPathLast; + if (last) { + k0 = subpathStart1 + 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); + k0 = j1 + 1; } + for (k1 = k0; + !(pathIn->flags[k1] & splashPathLast) && + k1 + 1 < pathIn->length && + pathIn->pts[k1+1].x == pathIn->pts[k1].x && + pathIn->pts[k1+1].y == pathIn->pts[k1].y; + ++k1) ; + + // compute the deltas for segment (i1, j0) +#if USE_FIXEDPOINT + // the 1/d value can be small, which introduces significant + // inaccuracies in fixed point mode + d = splashDist(pathIn->pts[i1].x, pathIn->pts[i1].y, + pathIn->pts[j0].x, pathIn->pts[j0].y); + dx = (pathIn->pts[j0].x - pathIn->pts[i1].x) / d; + dy = (pathIn->pts[j0].y - pathIn->pts[i1].y) / d; +#else + d = (SplashCoord)1 / splashDist(pathIn->pts[i1].x, pathIn->pts[i1].y, + pathIn->pts[j0].x, pathIn->pts[j0].y); + dx = d * (pathIn->pts[j0].x - pathIn->pts[i1].x); + dy = d * (pathIn->pts[j0].y - pathIn->pts[i1].y); +#endif wdx = (SplashCoord)0.5 * w * dx; wdy = (SplashCoord)0.5 * w * dy; - // 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) { + pathOut->moveTo(pathIn->pts[i0].x - wdy, pathIn->pts[i0].y + wdx); + if (i0 == subpathStart0) { firstPt = pathOut->length - 1; } if (first && !closed) { switch (state->lineCap) { case splashLineCapButt: - pathOut->lineTo(pathIn->pts[i].x + wdy, pathIn->pts[i].y - wdx); + pathOut->lineTo(pathIn->pts[i0].x + wdy, pathIn->pts[i0].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); + pathOut->curveTo(pathIn->pts[i0].x - wdy - bezierCircle * wdx, + pathIn->pts[i0].y + wdx - bezierCircle * wdy, + pathIn->pts[i0].x - wdx - bezierCircle * wdy, + pathIn->pts[i0].y - wdy + bezierCircle * wdx, + pathIn->pts[i0].x - wdx, + pathIn->pts[i0].y - wdy); + pathOut->curveTo(pathIn->pts[i0].x - wdx + bezierCircle * wdy, + pathIn->pts[i0].y - wdy - bezierCircle * wdx, + pathIn->pts[i0].x + wdy - bezierCircle * wdx, + pathIn->pts[i0].y - wdx - bezierCircle * wdy, + pathIn->pts[i0].x + wdy, + pathIn->pts[i0].y - wdx); break; case splashLineCapProjecting: - pathOut->lineTo(pathIn->pts[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); + pathOut->lineTo(pathIn->pts[i0].x - wdx - wdy, + pathIn->pts[i0].y + wdx - wdy); + pathOut->lineTo(pathIn->pts[i0].x - wdx + wdy, + pathIn->pts[i0].y - wdx - wdy); + pathOut->lineTo(pathIn->pts[i0].x + wdy, + pathIn->pts[i0].y - wdx); break; } } else { - pathOut->lineTo(pathIn->pts[i].x + wdy, pathIn->pts[i].y - wdx); + pathOut->lineTo(pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx); } // draw the left side of the segment rectangle left2 = pathOut->length - 1; - pathOut->lineTo(pathIn->pts[i+1].x + wdy, pathIn->pts[i+1].y - wdx); + pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx); // draw the end cap if (last && !closed) { switch (state->lineCap) { case splashLineCapButt: - pathOut->lineTo(pathIn->pts[i+1].x - wdy, pathIn->pts[i+1].y + wdx); + pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].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); + pathOut->curveTo(pathIn->pts[j0].x + wdy + bezierCircle * wdx, + pathIn->pts[j0].y - wdx + bezierCircle * wdy, + pathIn->pts[j0].x + wdx + bezierCircle * wdy, + pathIn->pts[j0].y + wdy - bezierCircle * wdx, + pathIn->pts[j0].x + wdx, + pathIn->pts[j0].y + wdy); + pathOut->curveTo(pathIn->pts[j0].x + wdx - bezierCircle * wdy, + pathIn->pts[j0].y + wdy + bezierCircle * wdx, + pathIn->pts[j0].x - wdy + bezierCircle * wdx, + pathIn->pts[j0].y + wdx + bezierCircle * wdy, + pathIn->pts[j0].x - wdy, + pathIn->pts[j0].y + wdx); break; case splashLineCapProjecting: - pathOut->lineTo(pathIn->pts[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); + pathOut->lineTo(pathIn->pts[j0].x + wdy + wdx, + pathIn->pts[j0].y - wdx + wdy); + pathOut->lineTo(pathIn->pts[j0].x - wdy + wdx, + pathIn->pts[j0].y + wdx + wdy); + pathOut->lineTo(pathIn->pts[j0].x - wdy, + pathIn->pts[j0].y + wdx); break; } } else { - pathOut->lineTo(pathIn->pts[i+1].x - wdy, pathIn->pts[i+1].y + wdx); + pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); } // draw the right side of the segment rectangle + // (NB: if stroke adjustment is enabled, the closepath operation MUST + // add a segment because this segment is used for a hint) right2 = pathOut->length - 1; - pathOut->close(); + pathOut->close(state->strokeAdjust); // draw the join join2 = pathOut->length; if (!last || closed) { + + // compute the deltas for segment (j1, k0) +#if USE_FIXEDPOINT + // the 1/d value can be small, which introduces significant + // inaccuracies in fixed point mode + d = splashDist(pathIn->pts[j1].x, pathIn->pts[j1].y, + pathIn->pts[k0].x, pathIn->pts[k0].y); + dxNext = (pathIn->pts[k0].x - pathIn->pts[j1].x) / d; + dyNext = (pathIn->pts[k0].y - pathIn->pts[j1].y) / d; +#else + d = (SplashCoord)1 / splashDist(pathIn->pts[j1].x, pathIn->pts[j1].y, + pathIn->pts[k0].x, pathIn->pts[k0].y); + dxNext = d * (pathIn->pts[k0].x - pathIn->pts[j1].x); + dyNext = d * (pathIn->pts[k0].y - pathIn->pts[j1].y); +#endif + wdxNext = (SplashCoord)0.5 * w * dxNext; + wdyNext = (SplashCoord)0.5 * w * dyNext; + + // compute the join parameters crossprod = dx * dyNext - dy * dxNext; dotprod = -(dx * dxNext + dy * dyNext); - if (dotprod > 0.99999) { + if (dotprod > 0.9999) { // avoid a divide-by-zero -- set miter to something arbitrary // such that sqrt(miter) will exceed miterLimit (and m is never // used in that situation) + // (note: the comparison value (0.9999) has to be less than + // 1-epsilon, where epsilon is the smallest value + // representable in the fixed point format) miter = (state->miterLimit + 1) * (state->miterLimit + 1); m = 0; } else { @@ -3836,67 +5501,68 @@ SplashPath *Splash::makeStrokePath(SplashPath *path, GBool flatten) { // 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); + pathOut->moveTo(pathIn->pts[j0].x + (SplashCoord)0.5 * w, + pathIn->pts[j0].y); + pathOut->curveTo(pathIn->pts[j0].x + (SplashCoord)0.5 * w, + pathIn->pts[j0].y + bezierCircle2 * w, + pathIn->pts[j0].x + bezierCircle2 * w, + pathIn->pts[j0].y + (SplashCoord)0.5 * w, + pathIn->pts[j0].x, + pathIn->pts[j0].y + (SplashCoord)0.5 * w); + pathOut->curveTo(pathIn->pts[j0].x - bezierCircle2 * w, + pathIn->pts[j0].y + (SplashCoord)0.5 * w, + pathIn->pts[j0].x - (SplashCoord)0.5 * w, + pathIn->pts[j0].y + bezierCircle2 * w, + pathIn->pts[j0].x - (SplashCoord)0.5 * w, + pathIn->pts[j0].y); + pathOut->curveTo(pathIn->pts[j0].x - (SplashCoord)0.5 * w, + pathIn->pts[j0].y - bezierCircle2 * w, + pathIn->pts[j0].x - bezierCircle2 * w, + pathIn->pts[j0].y - (SplashCoord)0.5 * w, + pathIn->pts[j0].x, + pathIn->pts[j0].y - (SplashCoord)0.5 * w); + pathOut->curveTo(pathIn->pts[j0].x + bezierCircle2 * w, + pathIn->pts[j0].y - (SplashCoord)0.5 * w, + pathIn->pts[j0].x + (SplashCoord)0.5 * w, + pathIn->pts[j0].y - bezierCircle2 * w, + pathIn->pts[j0].x + (SplashCoord)0.5 * w, + pathIn->pts[j0].y); } else { - pathOut->moveTo(pathIn->pts[i+1].x, pathIn->pts[i+1].y); + pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y); // angle < 180 if (crossprod < 0) { - pathOut->lineTo(pathIn->pts[i+1].x - wdyNext, - pathIn->pts[i+1].y + wdxNext); + pathOut->lineTo(pathIn->pts[j0].x - wdyNext, + pathIn->pts[j0].y + wdxNext); // miter join inside limit if (state->lineJoin == splashLineJoinMiter && splashSqrt(miter) <= state->miterLimit) { - pathOut->lineTo(pathIn->pts[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); + pathOut->lineTo(pathIn->pts[j0].x - wdy + wdx * m, + pathIn->pts[j0].y + wdx + wdy * m); + pathOut->lineTo(pathIn->pts[j0].x - wdy, + pathIn->pts[j0].y + wdx); // bevel join or miter join outside limit } else { - pathOut->lineTo(pathIn->pts[i+1].x - wdy, pathIn->pts[i+1].y + wdx); + pathOut->lineTo(pathIn->pts[j0].x - wdy, + pathIn->pts[j0].y + wdx); } // angle >= 180 } else { - pathOut->lineTo(pathIn->pts[i+1].x + wdy, - pathIn->pts[i+1].y - wdx); + pathOut->lineTo(pathIn->pts[j0].x + wdy, + pathIn->pts[j0].y - wdx); // miter join inside limit if (state->lineJoin == splashLineJoinMiter && splashSqrt(miter) <= state->miterLimit) { - pathOut->lineTo(pathIn->pts[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); + pathOut->lineTo(pathIn->pts[j0].x + wdy + wdx * m, + pathIn->pts[j0].y - wdx + wdy * m); + pathOut->lineTo(pathIn->pts[j0].x + wdyNext, + pathIn->pts[j0].y - wdxNext); // bevel join or miter join outside limit } else { - pathOut->lineTo(pathIn->pts[i+1].x + wdyNext, - pathIn->pts[i+1].y - wdxNext); + pathOut->lineTo(pathIn->pts[j0].x + wdyNext, + pathIn->pts[j0].y - wdxNext); } } } @@ -3906,8 +5572,28 @@ SplashPath *Splash::makeStrokePath(SplashPath *path, GBool flatten) { // add stroke adjustment hints if (state->strokeAdjust) { - if (i >= subpathStart + 1) { - if (i >= subpathStart + 2) { + if (seg == 0 && !closed) { + if (state->lineCap == splashLineCapButt) { + pathOut->addStrokeAdjustHint(firstPt, left2 + 1, + firstPt, firstPt + 1); + if (last) { + pathOut->addStrokeAdjustHint(firstPt, left2 + 1, + left2 + 1, left2 + 2); + } + } else if (state->lineCap == splashLineCapProjecting) { + if (last) { + pathOut->addStrokeAdjustHint(firstPt + 1, left2 + 2, + firstPt + 1, firstPt + 2); + pathOut->addStrokeAdjustHint(firstPt + 1, left2 + 2, + left2 + 2, left2 + 3); + } else { + pathOut->addStrokeAdjustHint(firstPt + 1, left2 + 1, + firstPt + 1, firstPt + 2); + } + } + } + if (seg >= 1) { + if (seg >= 2) { pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0); pathOut->addStrokeAdjustHint(left1, right1, join0, left2); } else { @@ -3921,12 +5607,12 @@ SplashPath *Splash::makeStrokePath(SplashPath *path, GBool flatten) { right1 = right2; join0 = join1; join1 = join2; - if (i == subpathStart) { + if (seg == 0) { leftFirst = left2; rightFirst = right2; } if (last) { - if (i >= subpathStart + 2) { + if (seg >= 2) { pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0); pathOut->addStrokeAdjustHint(left1, right1, join0, pathOut->length - 1); @@ -3943,8 +5629,21 @@ SplashPath *Splash::makeStrokePath(SplashPath *path, GBool flatten) { pathOut->addStrokeAdjustHint(leftFirst, rightFirst, join1, pathOut->length - 1); } + if (!closed && seg > 0) { + if (state->lineCap == splashLineCapButt) { + pathOut->addStrokeAdjustHint(left1 - 1, left1 + 1, + left1 + 1, left1 + 2); + } else if (state->lineCap == splashLineCapProjecting) { + pathOut->addStrokeAdjustHint(left1 - 1, left1 + 2, + left1 + 2, left1 + 3); + } + } } } + + i0 = j0; + i1 = j1; + ++seg; } if (pathIn != path) { @@ -3971,13 +5670,9 @@ 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", + printf(" %4d: x0=%8.2f y0=%8.2f x1=%8.2f y1=%8.2f %s%s%s\n", i, (double)path->segs[i].x0, (double)path->segs[i].y0, (double)path->segs[i].x1, (double)path->segs[i].y1, - (path->segs[i].flags & 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" : " "); @@ -4003,7 +5698,13 @@ SplashError Splash::shadedFill(SplashPath *path, GBool hasBBox, xPath->aaScale(); } xPath->sort(); - scanner = new SplashXPathScanner(xPath, gFalse); + yMinI = state->clip->getYMinI(); + yMaxI = state->clip->getYMaxI(); + if (vectorAntialias && !inShading) { + yMinI = yMinI * splashAASize; + yMaxI = (yMaxI + 1) * splashAASize - 1; + } + scanner = new SplashXPathScanner(xPath, gFalse, yMinI, yMaxI); // get the min and max x and y values if (vectorAntialias) { @@ -4022,7 +5723,7 @@ SplashError Splash::shadedFill(SplashPath *path, GBool hasBBox, yMaxI = state->clip->getYMaxI(); } - pipeInit(&pipe, 0, yMinI, pattern, NULL, state->fillAlpha, vectorAntialias && !hasBBox, gFalse, pattern); + pipeInit(&pipe, 0, yMinI, pattern, NULL, (Guchar)splashRound(state->fillAlpha * 255), vectorAntialias && !hasBBox, gFalse); // draw the spans if (vectorAntialias) { diff --git a/splash/Splash.h b/splash/Splash.h index 85f92ed9..3c2bf9f1 100644 --- a/splash/Splash.h +++ b/splash/Splash.h @@ -108,6 +108,7 @@ public: SplashCoord *getLineDash(); int getLineDashLength(); SplashCoord getLineDashPhase(); + GBool getStrokeAdjust(); SplashClip *getClip(); SplashBitmap *getSoftMask(); GBool getInNonIsolatedGroup(); @@ -144,6 +145,8 @@ public: void setSoftMask(SplashBitmap *softMask); void setInNonIsolatedGroup(SplashBitmap *alpha0BitmapA, int alpha0XA, int alpha0YA); + void setTransfer(Guchar *red, Guchar *green, Guchar *blue, Guchar *gray); + void setOverprintMask(Guint overprintMask); //----- state save/restore @@ -206,7 +209,7 @@ public: // The matrix behaves as for fillImageMask. SplashError drawImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, GBool srcAlpha, - int w, int h, SplashCoord *mat, SplashPattern *overprintPattern = NULL); + int w, int h, SplashCoord *mat); // Composite a rectangular region from <src> onto this Splash // object. @@ -226,14 +229,19 @@ public: //----- misc - // Construct a path for a stroke, given the path to be stroked, and - // using the current line parameters. If <flatten> is true, this - // function will first flatten the path and handle the linedash. - SplashPath *makeStrokePath(SplashPath *path, GBool flatten = gTrue); + // Construct a path for a stroke, given the path to be stroked and + // the line width <w>. All other stroke parameters are taken from + // the current state. If <flatten> is true, this function will + // first flatten the path and handle the linedash. + SplashPath *makeStrokePath(SplashPath *path, SplashCoord w, + GBool flatten = gTrue); // Return the associated bitmap. SplashBitmap *getBitmap() { return bitmap; } + // Set the minimum line width. + void setMinLineWidth(SplashCoord w) { minLineWidth = w; } + // Get a bounding box which includes all modifications since the // last call to clearModRegion. void getModRegion(int *xMin, int *yMin, int *xMax, int *yMax) @@ -250,6 +258,7 @@ public: void setDebugMode(GBool debugModeA) { debugMode = debugModeA; } #if 1 //~tmp: turn off anti-aliasing temporarily + void setInShading(GBool sh) { inShading = sh; } GBool getVectorAntialias() { return vectorAntialias; } void setVectorAntialias(GBool vaa) { vectorAntialias = vaa; } #endif @@ -264,9 +273,25 @@ private: void pipeInit(SplashPipe *pipe, int x, int y, SplashPattern *pattern, SplashColorPtr cSrc, - SplashCoord aInput, GBool usesShape, - GBool nonIsolatedGroup, SplashPattern *overprintPattern = NULL, GBool stroke = gFalse); + Guchar aInput, GBool usesShape, + GBool nonIsolatedGroup); void pipeRun(SplashPipe *pipe); + void pipeRunSimpleMono1(SplashPipe *pipe); + void pipeRunSimpleMono8(SplashPipe *pipe); + void pipeRunSimpleRGB8(SplashPipe *pipe); + void pipeRunSimpleXBGR8(SplashPipe *pipe); + void pipeRunSimpleBGR8(SplashPipe *pipe); +#if SPLASH_CMYK + void pipeRunSimpleCMYK8(SplashPipe *pipe); +#endif + void pipeRunAAMono1(SplashPipe *pipe); + void pipeRunAAMono8(SplashPipe *pipe); + void pipeRunAARGB8(SplashPipe *pipe); + void pipeRunAAXBGR8(SplashPipe *pipe); + void pipeRunAABGR8(SplashPipe *pipe); +#if SPLASH_CMYK + void pipeRunAACMYK8(SplashPipe *pipe); +#endif void pipeSetXY(SplashPipe *pipe, int x, int y); void pipeIncX(SplashPipe *pipe); void drawPixel(SplashPipe *pipe, int x, int y, GBool noClip); @@ -279,7 +304,7 @@ private: void updateModX(int x); void updateModY(int y); void strokeNarrow(SplashPath *path); - void strokeWide(SplashPath *path); + void strokeWide(SplashPath *path, SplashCoord w); SplashPath *flattenPath(SplashPath *path, SplashCoord *matrix, SplashCoord flatness); void flattenCurve(SplashCoord x0, SplashCoord y0, @@ -291,7 +316,68 @@ private: SplashPath *makeDashedPath(SplashPath *xPath); SplashError fillWithPattern(SplashPath *path, GBool eo, SplashPattern *pattern, SplashCoord alpha); + GBool pathAllOutside(SplashPath *path); void fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph, GBool noclip); + void arbitraryTransformMask(SplashImageMaskSource src, void *srcData, + int srcWidth, int srcHeight, + SplashCoord *mat, GBool glyphMode); + SplashBitmap *scaleMask(SplashImageMaskSource src, void *srcData, + int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight); + void scaleMaskYdXd(SplashImageMaskSource src, void *srcData, + int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight, + SplashBitmap *dest); + void scaleMaskYdXu(SplashImageMaskSource src, void *srcData, + int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight, + SplashBitmap *dest); + void scaleMaskYuXd(SplashImageMaskSource src, void *srcData, + int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight, + SplashBitmap *dest); + void scaleMaskYuXu(SplashImageMaskSource src, void *srcData, + int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight, + SplashBitmap *dest); + void blitMask(SplashBitmap *src, int xDest, int yDest, + SplashClipResult clipRes); + SplashError arbitraryTransformImage(SplashImageSource src, void *srcData, + SplashColorMode srcMode, int nComps, + GBool srcAlpha, + int srcWidth, int srcHeight, + SplashCoord *mat); + SplashBitmap *scaleImage(SplashImageSource src, void *srcData, + SplashColorMode srcMode, int nComps, + GBool srcAlpha, int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight); + void scaleImageYdXd(SplashImageSource src, void *srcData, + SplashColorMode srcMode, int nComps, + GBool srcAlpha, int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight, + SplashBitmap *dest); + void scaleImageYdXu(SplashImageSource src, void *srcData, + SplashColorMode srcMode, int nComps, + GBool srcAlpha, int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight, + SplashBitmap *dest); + void scaleImageYuXd(SplashImageSource src, void *srcData, + SplashColorMode srcMode, int nComps, + GBool srcAlpha, int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight, + SplashBitmap *dest); + void scaleImageYuXu(SplashImageSource src, void *srcData, + SplashColorMode srcMode, int nComps, + GBool srcAlpha, int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight, + SplashBitmap *dest); + void vertFlipImage(SplashBitmap *img, int width, int height, + int nComps); + void blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest, + SplashClipResult clipRes); + void blitImageClipped(SplashBitmap *src, GBool srcAlpha, + int xSrc, int ySrc, int xDest, int yDest, + int w, int h); void dumpPath(SplashPath *path); void dumpXPath(SplashXPath *path); @@ -308,9 +394,11 @@ private: // bitmap containing the alpha0 values int alpha0X, alpha0Y; // offset within alpha0Bitmap SplashCoord aaGamma[splashAASize * splashAASize + 1]; + SplashCoord minLineWidth; int modXMin, modYMin, modXMax, modYMax; SplashClipResult opClipRes; GBool vectorAntialias; + GBool inShading; GBool debugMode; }; diff --git a/splash/SplashBitmap.cc b/splash/SplashBitmap.cc index 562fbdad..ab5176e5 100644 --- a/splash/SplashBitmap.cc +++ b/splash/SplashBitmap.cc @@ -111,12 +111,13 @@ SplashBitmap::SplashBitmap(int widthA, int heightA, int rowPadA, } } - SplashBitmap::~SplashBitmap() { - if (rowSize < 0) { - gfree(data + (height - 1) * rowSize); - } else { - gfree(data); + if (data) { + if (rowSize < 0) { + gfree(data + (height - 1) * rowSize); + } else { + gfree(data); + } } gfree(alpha); } @@ -160,11 +161,7 @@ SplashError SplashBitmap::writePNMFile(FILE *f) { fprintf(f, "P5\n%d %d\n255\n", width, height); row = data; for (y = 0; y < height; ++y) { - p = row; - for (x = 0; x < width; ++x) { - fputc(*p, f); - ++p; - } + fwrite(row, 1, width, f); row += rowSize; } break; @@ -173,13 +170,7 @@ SplashError SplashBitmap::writePNMFile(FILE *f) { fprintf(f, "P6\n%d %d\n255\n", width, height); row = data; for (y = 0; y < height; ++y) { - p = row; - for (x = 0; x < width; ++x) { - fputc(splashRGB8R(p), f); - fputc(splashRGB8G(p), f); - fputc(splashRGB8B(p), f); - p += 3; - } + fwrite(row, 1, 3 * width, f); row += rowSize; } break; @@ -218,7 +209,7 @@ SplashError SplashBitmap::writePNMFile(FILE *f) { #if SPLASH_CMYK case splashModeCMYK8: // PNM doesn't support CMYK - error(-1, "unsupported SplashBitmap mode"); + error(errInternal, -1, "unsupported SplashBitmap mode"); return splashErrGeneric; break; #endif @@ -226,6 +217,20 @@ SplashError SplashBitmap::writePNMFile(FILE *f) { return splashOk; } +SplashError SplashBitmap::writeAlphaPGMFile(char *fileName) { + FILE *f; + + if (!alpha) { + return splashErrModeMismatch; + } + if (!(f = fopen(fileName, "wb"))) { + return splashErrOpenFile; + } + fprintf(f, "P5\n%d %d\n255\n", width, height); + fwrite(alpha, 1, width * height, f); + fclose(f); + return splashOk; +} void SplashBitmap::getPixel(int x, int y, SplashColorPtr pixel) { SplashColorPtr p; @@ -277,6 +282,14 @@ Guchar SplashBitmap::getAlpha(int x, int y) { return alpha[y * width + x]; } +SplashColorPtr SplashBitmap::takeData() { + SplashColorPtr data2; + + data2 = data; + data = NULL; + return data2; +} + SplashError SplashBitmap::writeImgFile(SplashImageFileFormat format, char *fileName, int hDPI, int vDPI, const char *compressionString) { FILE *f; SplashError e; @@ -326,7 +339,7 @@ SplashError SplashBitmap::writeImgFile(SplashImageFileFormat format, FILE *f, in default: // Not the greatest error message, but users of this function should // have already checked whether their desired format is compiled in. - error(-1, "Support for this image type not compiled in"); + error(errInternal, -1, "Support for this image type not compiled in"); return splashErrGeneric; } @@ -364,7 +377,7 @@ SplashError SplashBitmap::writeImgFile(ImgWriter *writer, FILE *f, int hDPI, int && mode != splashModeCMYK8 #endif ) { - error(-1, "unsupported SplashBitmap mode"); + error(errInternal, -1, "unsupported SplashBitmap mode"); return splashErrGeneric; } diff --git a/splash/SplashBitmap.h b/splash/SplashBitmap.h index 33365074..8bcc9418 100644 --- a/splash/SplashBitmap.h +++ b/splash/SplashBitmap.h @@ -65,6 +65,7 @@ public: SplashError writePNMFile(char *fileName); SplashError writePNMFile(FILE *f); + SplashError writeAlphaPGMFile(char *fileName); SplashError writeImgFile(SplashImageFileFormat format, char *fileName, int hDPI, int vDPI, const char *compressionString = ""); SplashError writeImgFile(SplashImageFileFormat format, FILE *f, int hDPI, int vDPI, const char *compressionString = ""); @@ -74,6 +75,11 @@ public: void getRGBLine(int y, SplashColorPtr line); Guchar getAlpha(int x, int y); + // Caller takes ownership of the bitmap data. The SplashBitmap + // object is no longer valid -- the next call should be to the + // destructor. + SplashColorPtr takeData(); + private: int width, height; // size of bitmap diff --git a/splash/SplashClip.cc b/splash/SplashClip.cc index 5add1523..41b73c84 100644 --- a/splash/SplashClip.cc +++ b/splash/SplashClip.cc @@ -64,8 +64,8 @@ SplashClip::SplashClip(SplashCoord x0, SplashCoord y0, } xMinI = splashFloor(xMin); yMinI = splashFloor(yMin); - xMaxI = splashFloor(xMax); - yMaxI = splashFloor(yMax); + xMaxI = splashCeil(xMax) - 1; + yMaxI = splashCeil(yMax) - 1; paths = NULL; flags = NULL; scanners = NULL; @@ -73,6 +73,7 @@ SplashClip::SplashClip(SplashCoord x0, SplashCoord y0, } SplashClip::SplashClip(SplashClip *clip) { + int yMinAA, yMaxAA; int i; antialias = clip->antialias; @@ -93,7 +94,15 @@ SplashClip::SplashClip(SplashClip *clip) { for (i = 0; i < length; ++i) { paths[i] = clip->paths[i]->copy(); flags[i] = clip->flags[i]; - scanners[i] = new SplashXPathScanner(paths[i], flags[i] & splashClipEO); + if (antialias) { + yMinAA = yMinI * splashAASize; + yMaxAA = (yMaxI + 1) * splashAASize - 1; + } else { + yMinAA = yMinI; + yMaxAA = yMaxI; + } + scanners[i] = new SplashXPathScanner(paths[i], flags[i] & splashClipEO, + yMinAA, yMaxAA); } } @@ -156,8 +165,8 @@ void SplashClip::resetToRect(SplashCoord x0, SplashCoord y0, } xMinI = splashFloor(xMin); yMinI = splashFloor(yMin); - xMaxI = splashFloor(xMax); - yMaxI = splashFloor(yMax); + xMaxI = splashCeil(xMax) - 1; + yMaxI = splashCeil(yMax) - 1; } SplashError SplashClip::clipToRect(SplashCoord x0, SplashCoord y0, @@ -169,7 +178,7 @@ SplashError SplashClip::clipToRect(SplashCoord x0, SplashCoord y0, } if (x1 < xMax) { xMax = x1; - xMaxI = splashFloor(xMax); + xMaxI = splashCeil(xMax) - 1; } } else { if (x1 > xMin) { @@ -178,7 +187,7 @@ SplashError SplashClip::clipToRect(SplashCoord x0, SplashCoord y0, } if (x0 < xMax) { xMax = x0; - xMaxI = splashFloor(xMax); + xMaxI = splashCeil(xMax) - 1; } } if (y0 < y1) { @@ -188,7 +197,7 @@ SplashError SplashClip::clipToRect(SplashCoord x0, SplashCoord y0, } if (y1 < yMax) { yMax = y1; - yMaxI = splashFloor(yMax); + yMaxI = splashCeil(yMax) - 1; } } else { if (y1 > yMin) { @@ -197,7 +206,7 @@ SplashError SplashClip::clipToRect(SplashCoord x0, SplashCoord y0, } if (y0 < yMax) { yMax = y0; - yMaxI = splashFloor(yMax); + yMaxI = splashCeil(yMax) - 1; } } return splashOk; @@ -206,6 +215,7 @@ SplashError SplashClip::clipToRect(SplashCoord x0, SplashCoord y0, SplashError SplashClip::clipToPath(SplashPath *path, SplashCoord *matrix, SplashCoord flatness, GBool eo) { SplashXPath *xPath; + int yMinAA, yMaxAA; xPath = new SplashXPath(path, matrix, flatness, gTrue); @@ -213,8 +223,8 @@ SplashError SplashClip::clipToPath(SplashPath *path, SplashCoord *matrix, if (xPath->length == 0) { xMax = xMin - 1; yMax = yMin - 1; - xMaxI = splashFloor(xMax); - yMaxI = splashFloor(yMax); + xMaxI = splashCeil(xMax) - 1; + yMaxI = splashCeil(yMax) - 1; delete xPath; // check for a rectangle @@ -255,7 +265,14 @@ SplashError SplashClip::clipToPath(SplashPath *path, SplashCoord *matrix, xPath->sort(); paths[length] = xPath; flags[length] = eo ? splashClipEO : 0; - scanners[length] = new SplashXPathScanner(xPath, eo); + if (antialias) { + yMinAA = yMinI * splashAASize; + yMaxAA = (yMaxI + 1) * splashAASize - 1; + } else { + yMinAA = yMinI; + yMaxAA = yMaxI; + } + scanners[length] = new SplashXPathScanner(xPath, eo, yMinAA, yMaxAA); ++length; } @@ -268,10 +285,10 @@ SplashClipResult SplashClip::testRect(int rectXMin, int rectYMin, // x = [rectXMin, rectXMax + 1) (note: rect coords are ints) // y = [rectYMin, rectYMax + 1) // against the clipping region: - // x = [xMin, xMax] (note: clipping coords are fp) - // y = [yMin, yMax] - if ((SplashCoord)(rectXMax + 1) <= xMin || (SplashCoord)rectXMin > xMax || - (SplashCoord)(rectYMax + 1) <= yMin || (SplashCoord)rectYMin > yMax) { + // x = [xMin, xMax) (note: clipping coords are fp) + // y = [yMin, yMax) + if ((SplashCoord)(rectXMax + 1) <= xMin || (SplashCoord)rectXMin >= xMax || + (SplashCoord)(rectYMax + 1) <= yMin || (SplashCoord)rectYMin >= yMax) { return splashClipAllOutside; } if ((SplashCoord)rectXMin >= xMin && (SplashCoord)(rectXMax + 1) <= xMax && @@ -289,10 +306,10 @@ SplashClipResult SplashClip::testSpan(int spanXMin, int spanXMax, int spanY) { // x = [spanXMin, spanXMax + 1) (note: span coords are ints) // y = [spanY, spanY + 1) // against the clipping region: - // x = [xMin, xMax] (note: clipping coords are fp) - // y = [yMin, yMax] - if ((SplashCoord)(spanXMax + 1) <= xMin || (SplashCoord)spanXMin > xMax || - (SplashCoord)(spanY + 1) <= yMin || (SplashCoord)spanY > yMax) { + // x = [xMin, xMax) (note: clipping coords are fp) + // y = [yMin, yMax) + if ((SplashCoord)(spanXMax + 1) <= xMin || (SplashCoord)spanXMin >= xMax || + (SplashCoord)(spanY + 1) <= yMin || (SplashCoord)spanY >= yMax) { return splashClipAllOutside; } if (!((SplashCoord)spanXMin >= xMin && (SplashCoord)(spanXMax + 1) <= xMax && diff --git a/splash/SplashClip.h b/splash/SplashClip.h index 7933017f..3eb2d780 100644 --- a/splash/SplashClip.h +++ b/splash/SplashClip.h @@ -118,6 +118,12 @@ public: // will update <x0> and <x1>. void clipAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y); + // Get the rectangle part of the clip region. + SplashCoord getXMin() { return xMin; } + SplashCoord getXMax() { return xMax; } + SplashCoord getYMin() { return yMin; } + SplashCoord getYMax() { return yMax; } + // Get the rectangle part of the clip region, in integer coordinates. int getXMinI() { return xMinI; } int getXMaxI() { return xMaxI; } diff --git a/splash/SplashFTFont.cc b/splash/SplashFTFont.cc index eea3d641..b511c96c 100644 --- a/splash/SplashFTFont.cc +++ b/splash/SplashFTFont.cc @@ -58,31 +58,89 @@ static int glyphPathCubicTo(const FT_Vector *ctrl1, const FT_Vector *ctrl2, SplashFTFont::SplashFTFont(SplashFTFontFile *fontFileA, SplashCoord *matA, SplashCoord *textMatA): SplashFont(fontFileA, matA, textMatA, fontFileA->engine->aa), + enableAutoHinting(fontFileA->engine->enableAutoHinting), enableFreeTypeHinting(fontFileA->engine->enableFreeTypeHinting), enableSlightHinting(fontFileA->engine->enableSlightHinting) { FT_Face face; - double div; + int div; int x, y; +#if USE_FIXEDPOINT + SplashCoord scale; +#endif face = fontFileA->face; if (FT_New_Size(face, &sizeObj)) { return; } face->size = sizeObj; - size = splashSqrt(mat[2]*mat[2] + mat[3]*mat[3]); - if ((int)size < 1) { + size = splashRound(splashDist(0, 0, mat[2], mat[3])); + if (size < 1) { size = 1; } - if (FT_Set_Pixel_Sizes(face, 0, (int)size)) { + if (FT_Set_Pixel_Sizes(face, 0, size)) { return; } // if the textMat values are too small, FreeType's fixed point // arithmetic doesn't work so well - textScale = splashSqrt(textMat[2]*textMat[2] + textMat[3]*textMat[3]) / size; + textScale = splashDist(0, 0, textMat[2], textMat[3]) / size; div = face->bbox.xMax > 20000 ? 65536 : 1; +#if USE_FIXEDPOINT + scale = (SplashCoord)1 / (SplashCoord)face->units_per_EM; + + // transform the four corners of the font bounding box -- the min + // and max values form the bounding box of the transformed font + x = (int)(mat[0] * (scale * (face->bbox.xMin / div)) + + mat[2] * (scale * (face->bbox.yMin / div))); + xMin = xMax = x; + y = (int)(mat[1] * (scale * (face->bbox.xMin / div)) + + mat[3] * (scale * (face->bbox.yMin / div))); + yMin = yMax = y; + x = (int)(mat[0] * (scale * (face->bbox.xMin / div)) + + mat[2] * (scale * (face->bbox.yMax / div))); + if (x < xMin) { + xMin = x; + } else if (x > xMax) { + xMax = x; + } + y = (int)(mat[1] * (scale * (face->bbox.xMin / div)) + + mat[3] * (scale * (face->bbox.yMax / div))); + if (y < yMin) { + yMin = y; + } else if (y > yMax) { + yMax = y; + } + x = (int)(mat[0] * (scale * (face->bbox.xMax / div)) + + mat[2] * (scale * (face->bbox.yMin / div))); + if (x < xMin) { + xMin = x; + } else if (x > xMax) { + xMax = x; + } + y = (int)(mat[1] * (scale * (face->bbox.xMax / div)) + + mat[3] * (scale * (face->bbox.yMin / div))); + if (y < yMin) { + yMin = y; + } else if (y > yMax) { + yMax = y; + } + x = (int)(mat[0] * (scale * (face->bbox.xMax / div)) + + mat[2] * (scale * (face->bbox.yMax / div))); + if (x < xMin) { + xMin = x; + } else if (x > xMax) { + xMax = x; + } + y = (int)(mat[1] * (scale * (face->bbox.xMax / div)) + + mat[3] * (scale * (face->bbox.yMax / div))); + if (y < yMin) { + yMin = y; + } else if (y > yMax) { + yMax = y; + } +#else // USE_FIXEDPOINT // transform the four corners of the font bounding box -- the min // and max values form the bounding box of the transformed font x = (int)((mat[0] * face->bbox.xMin + mat[2] * face->bbox.yMin) / @@ -133,11 +191,12 @@ SplashFTFont::SplashFTFont(SplashFTFontFile *fontFileA, SplashCoord *matA, } else if (y > yMax) { yMax = y; } +#endif // USE_FIXEDPOINT // This is a kludge: some buggy PDF generators embed fonts with // zero bounding boxes. if (xMax == xMin) { xMin = 0; - xMax = (int)size; + xMax = size; } if (yMax == yMin) { yMin = 0; @@ -146,23 +205,23 @@ SplashFTFont::SplashFTFont(SplashFTFontFile *fontFileA, SplashCoord *matA, // compute the transform matrix #if USE_FIXEDPOINT - matrix.xx = (FT_Fixed)((mat[0] / size).getRaw()); - matrix.yx = (FT_Fixed)((mat[1] / size).getRaw()); - matrix.xy = (FT_Fixed)((mat[2] / size).getRaw()); - matrix.yy = (FT_Fixed)((mat[3] / size).getRaw()); - textMatrix.xx = (FT_Fixed)((textMat[0] / (textScale * size)).getRaw()); - textMatrix.yx = (FT_Fixed)((textMat[1] / (textScale * size)).getRaw()); - textMatrix.xy = (FT_Fixed)((textMat[2] / (textScale * size)).getRaw()); - textMatrix.yy = (FT_Fixed)((textMat[3] / (textScale * size)).getRaw()); + matrix.xx = (FT_Fixed)((mat[0] / size).get16Dot16()); + matrix.yx = (FT_Fixed)((mat[1] / size).get16Dot16()); + matrix.xy = (FT_Fixed)((mat[2] / size).get16Dot16()); + matrix.yy = (FT_Fixed)((mat[3] / size).get16Dot16()); + textMatrix.xx = (FT_Fixed)((textMat[0] / (textScale * size)).get16Dot16()); + textMatrix.yx = (FT_Fixed)((textMat[1] / (textScale * size)).get16Dot16()); + textMatrix.xy = (FT_Fixed)((textMat[2] / (textScale * size)).get16Dot16()); + textMatrix.yy = (FT_Fixed)((textMat[3] / (textScale * size)).get16Dot16()); #else matrix.xx = (FT_Fixed)((mat[0] / size) * 65536); matrix.yx = (FT_Fixed)((mat[1] / size) * 65536); matrix.xy = (FT_Fixed)((mat[2] / size) * 65536); matrix.yy = (FT_Fixed)((mat[3] / size) * 65536); - textMatrix.xx = (FT_Fixed)((textMat[0] / (size * textScale)) * 65536); - textMatrix.yx = (FT_Fixed)((textMat[1] / (size * textScale)) * 65536); - textMatrix.xy = (FT_Fixed)((textMat[2] / (size * textScale)) * 65536); - textMatrix.yy = (FT_Fixed)((textMat[3] / (size * textScale)) * 65536); + textMatrix.xx = (FT_Fixed)((textMat[0] / (textScale * size)) * 65536); + textMatrix.yx = (FT_Fixed)((textMat[1] / (textScale * size)) * 65536); + textMatrix.xy = (FT_Fixed)((textMat[2] / (textScale * size)) * 65536); + textMatrix.yy = (FT_Fixed)((textMat[3] / (textScale * size)) * 65536); #endif } @@ -174,13 +233,28 @@ GBool SplashFTFont::getGlyph(int c, int xFrac, int yFrac, return SplashFont::getGlyph(c, xFrac, 0, bitmap, x0, y0, clip, clipRes); } -static FT_Int32 getFTLoadFlags(GBool aa, GBool enableFreeTypeHinting, GBool enableSlightHinting) +static FT_Int32 getFTLoadFlags(GBool type1, GBool trueType, GBool aa, + GBool enableAutoHinting, GBool enableFreeTypeHinting, GBool enableSlightHinting) { int ret = FT_LOAD_DEFAULT; if (aa) ret |= FT_LOAD_NO_BITMAP; - if (enableFreeTypeHinting) { + if (enableAutoHinting) { + if (trueType) { + // FT2's autohinting doesn't always work very well (especially with + // font subsets), so turn it off if anti-aliasing is enabled; if + // anti-aliasing is disabled, this seems to be a tossup - some fonts + // look better with hinting, some without, so leave hinting on + if (aa) { + ret |= FT_LOAD_NO_AUTOHINT; + } + } else if (type1) { + // Type 1 fonts seem to look better with 'light' hinting mode + ret |= FT_LOAD_TARGET_LIGHT; + } + + } else if (enableFreeTypeHinting) { if (enableSlightHinting) ret |= FT_LOAD_TARGET_LIGHT; } else { @@ -213,7 +287,7 @@ GBool SplashFTFont::makeGlyph(int c, int xFrac, int yFrac, gid = (FT_UInt)c; } - if (FT_Load_Glyph(ff->face, gid, getFTLoadFlags(aa, enableFreeTypeHinting, enableSlightHinting))) { + if (FT_Load_Glyph(ff->face, gid, getFTLoadFlags(ff->type1, ff->trueType, aa, enableAutoHinting, enableFreeTypeHinting, enableSlightHinting))) { return gFalse; } @@ -240,6 +314,12 @@ GBool SplashFTFont::makeGlyph(int c, int xFrac, int yFrac, return gFalse; } + if (slot->bitmap.width == 0 || slot->bitmap.rows == 0) { + // this can happen if (a) the glyph is really tiny or (b) the + // metrics in the TrueType file are broken + return gFalse; + } + bitmap->x = -slot->bitmap_left; bitmap->y = slot->bitmap_top; bitmap->w = slot->bitmap.width; @@ -291,12 +371,12 @@ double SplashFTFont::getGlyphAdvance(int c) } else { gid = (FT_UInt)c; } - if (ff->trueType && gid == 0) { + if (ff->trueType && gid < 0) { // skip the TrueType notdef glyph return -1; } - if (FT_Load_Glyph(ff->face, gid, getFTLoadFlags(aa, enableFreeTypeHinting, enableSlightHinting))) { + if (FT_Load_Glyph(ff->face, gid, getFTLoadFlags(ff->type1, ff->trueType, aa, enableAutoHinting, enableFreeTypeHinting, enableSlightHinting))) { return -1; } @@ -340,11 +420,11 @@ SplashPath *SplashFTFont::getGlyphPath(int c) { } else { gid = (FT_UInt)c; } - if (ff->trueType && gid == 0) { + if (ff->trueType && gid < 0) { // skip the TrueType notdef glyph return NULL; } - if (FT_Load_Glyph(ff->face, gid, getFTLoadFlags(aa, enableFreeTypeHinting, enableSlightHinting))) { + if (FT_Load_Glyph(ff->face, gid, getFTLoadFlags(ff->type1, ff->trueType, aa, enableAutoHinting, enableFreeTypeHinting, enableSlightHinting))) { return NULL; } if (FT_Get_Glyph(slot, &glyph)) { diff --git a/splash/SplashFTFont.h b/splash/SplashFTFont.h index 157eff33..17458079 100644 --- a/splash/SplashFTFont.h +++ b/splash/SplashFTFont.h @@ -70,7 +70,8 @@ private: FT_Matrix matrix; FT_Matrix textMatrix; SplashCoord textScale; - double size; + int size; + GBool enableAutoHinting; GBool enableFreeTypeHinting; GBool enableSlightHinting; }; diff --git a/splash/SplashFTFontEngine.cc b/splash/SplashFTFontEngine.cc index 144f8f4b..dee57286 100644 --- a/splash/SplashFTFontEngine.cc +++ b/splash/SplashFTFontEngine.cc @@ -50,7 +50,7 @@ extern "C" int unlink(char *filename); //------------------------------------------------------------------------ #if 0 -static void fileWrite(void *stream, char *data, int len) { +static void fileWrite(void *stream, const char *data, int len) { fwrite(data, 1, len, (FILE *)stream); } #endif @@ -59,11 +59,12 @@ static void fileWrite(void *stream, char *data, int len) { // SplashFTFontEngine //------------------------------------------------------------------------ -SplashFTFontEngine::SplashFTFontEngine(GBool aaA, GBool enableFreeTypeHintingA, +SplashFTFontEngine::SplashFTFontEngine(GBool aaA, GBool enableAutoHintingA, GBool enableFreeTypeHintingA, GBool enableSlightHintingA, FT_Library libA) { FT_Int major, minor, patch; aa = aaA; + enableAutoHinting = enableAutoHintingA; enableFreeTypeHinting = enableFreeTypeHintingA; enableSlightHinting = enableSlightHintingA; lib = libA; @@ -74,14 +75,14 @@ SplashFTFontEngine::SplashFTFontEngine(GBool aaA, GBool enableFreeTypeHintingA, (major == 2 && (minor > 1 || (minor == 1 && patch > 7))); } -SplashFTFontEngine *SplashFTFontEngine::init(GBool aaA, GBool enableFreeTypeHintingA, +SplashFTFontEngine *SplashFTFontEngine::init(GBool aaA, GBool enableAutoHintingA, GBool enableFreeTypeHintingA, GBool enableSlightHintingA) { FT_Library libA; if (FT_Init_FreeType(&libA)) { return NULL; } - return new SplashFTFontEngine(aaA, enableFreeTypeHintingA, enableSlightHintingA, libA); + return new SplashFTFontEngine(aaA, enableAutoHintingA, enableFreeTypeHintingA, enableSlightHintingA, libA); } SplashFTFontEngine::~SplashFTFontEngine() { @@ -90,26 +91,26 @@ SplashFTFontEngine::~SplashFTFontEngine() { SplashFontFile *SplashFTFontEngine::loadType1Font(SplashFontFileID *idA, SplashFontSrc *src, - char **enc) { + const char **enc) { return SplashFTFontFile::loadType1Font(this, idA, src, enc); } SplashFontFile *SplashFTFontEngine::loadType1CFont(SplashFontFileID *idA, SplashFontSrc *src, - char **enc) { + const char **enc) { return SplashFTFontFile::loadType1Font(this, idA, src, enc); } SplashFontFile *SplashFTFontEngine::loadOpenTypeT1CFont(SplashFontFileID *idA, SplashFontSrc *src, - char **enc) { + const char **enc) { return SplashFTFontFile::loadType1Font(this, idA, src, enc); } SplashFontFile *SplashFTFontEngine::loadCIDFont(SplashFontFileID *idA, SplashFontSrc *src) { FoFiType1C *ff; - Gushort *cidToGIDMap; + int *cidToGIDMap; int nCIDs; SplashFontFile *ret; @@ -139,29 +140,34 @@ SplashFontFile *SplashFTFontEngine::loadCIDFont(SplashFontFileID *idA, } SplashFontFile *SplashFTFontEngine::loadOpenTypeCFFFont(SplashFontFileID *idA, - SplashFontSrc *src) { + SplashFontSrc *src, + int *codeToGID, + int codeToGIDLen) { FoFiTrueType *ff; - Gushort *cidToGIDMap; + int *cidToGIDMap; int nCIDs; SplashFontFile *ret; cidToGIDMap = NULL; nCIDs = 0; - if (!useCIDs) { - if (src->isFile) { - ff = FoFiTrueType::load(src->fileName->getCString()); - } else { - ff = FoFiTrueType::make(src->buf, src->bufLen); - } - if (ff) { - if (ff->isOpenTypeCFF()) { - cidToGIDMap = ff->getCIDToGIDMap(&nCIDs); + if (!codeToGID) { + if (!useCIDs) { + if (src->isFile) { + ff = FoFiTrueType::load(src->fileName->getCString()); + } else { + ff = FoFiTrueType::make(src->buf, src->bufLen); + } + if (ff) { + if (ff->isOpenTypeCFF()) { + cidToGIDMap = ff->getCIDToGIDMap(&nCIDs); + } + delete ff; } - delete ff; } } ret = SplashFTFontFile::loadCIDFont(this, idA, src, - cidToGIDMap, nCIDs); + codeToGID ? codeToGID : cidToGIDMap, + codeToGID ? codeToGIDLen : nCIDs); if (!ret) { gfree(cidToGIDMap); } @@ -170,7 +176,7 @@ SplashFontFile *SplashFTFontEngine::loadOpenTypeCFFFont(SplashFontFileID *idA, SplashFontFile *SplashFTFontEngine::loadTrueTypeFont(SplashFontFileID *idA, SplashFontSrc *src, - Gushort *codeToGID, + int *codeToGID, int codeToGIDLen, int faceIndex) { #if 0 diff --git a/splash/SplashFTFontEngine.h b/splash/SplashFTFontEngine.h index 42207fb0..d236e4fc 100644 --- a/splash/SplashFTFontEngine.h +++ b/splash/SplashFTFontEngine.h @@ -45,24 +45,26 @@ class SplashFontSrc; class SplashFTFontEngine { public: - static SplashFTFontEngine *init(GBool aaA, GBool enableFreeTypeHintingA, GBool enableSlightHinting); + static SplashFTFontEngine *init(GBool aaA, GBool enableAutoHintingA, GBool enableFreeTypeHintingA, GBool enableSlightHinting); ~SplashFTFontEngine(); // Load fonts. - SplashFontFile *loadType1Font(SplashFontFileID *idA, SplashFontSrc *src, char **enc); - SplashFontFile *loadType1CFont(SplashFontFileID *idA, SplashFontSrc *src, char **enc); - SplashFontFile *loadOpenTypeT1CFont(SplashFontFileID *idA, SplashFontSrc *src, char **enc); + SplashFontFile *loadType1Font(SplashFontFileID *idA, SplashFontSrc *src, const char **enc); + SplashFontFile *loadType1CFont(SplashFontFileID *idA, SplashFontSrc *src, const char **enc); + SplashFontFile *loadOpenTypeT1CFont(SplashFontFileID *idA, SplashFontSrc *src, const char **enc); SplashFontFile *loadCIDFont(SplashFontFileID *idA, SplashFontSrc *src); - SplashFontFile *loadOpenTypeCFFFont(SplashFontFileID *idA, SplashFontSrc *src); + SplashFontFile *loadOpenTypeCFFFont(SplashFontFileID *idA, SplashFontSrc *src, + int *codeToGID, int codeToGIDLen); SplashFontFile *loadTrueTypeFont(SplashFontFileID *idA, SplashFontSrc *src, - Gushort *codeToGID, int codeToGIDLen, int faceIndex = 0); + int *codeToGID, int codeToGIDLen, int faceIndex = 0); private: - SplashFTFontEngine(GBool aaA, GBool enableFreeTypeHintingA, GBool enableSlightHintingA, FT_Library libA); + SplashFTFontEngine(GBool aaA, GBool enableAutoHintingA, GBool enableFreeTypeHintingA, GBool enableSlightHintingA, FT_Library libA); GBool aa; + GBool enableAutoHinting; GBool enableFreeTypeHinting; GBool enableSlightHinting; FT_Library lib; diff --git a/splash/SplashFTFontFile.cc b/splash/SplashFTFontFile.cc index 160481e7..34f6ce5c 100644 --- a/splash/SplashFTFontFile.cc +++ b/splash/SplashFTFontFile.cc @@ -39,10 +39,10 @@ SplashFontFile *SplashFTFontFile::loadType1Font(SplashFTFontEngine *engineA, SplashFontFileID *idA, SplashFontSrc *src, - char **encA) { + const char **encA) { FT_Face faceA; - Gushort *codeToGIDA; - char *name; + int *codeToGIDA; + const char *name; int i; if (src->isFile) { @@ -52,22 +52,22 @@ SplashFontFile *SplashFTFontFile::loadType1Font(SplashFTFontEngine *engineA, if (FT_New_Memory_Face(engineA->lib, (const FT_Byte *)src->buf, src->bufLen, 0, &faceA)) return NULL; } - codeToGIDA = (Gushort *)gmallocn(256, sizeof(int)); + codeToGIDA = (int *)gmallocn(256, sizeof(int)); for (i = 0; i < 256; ++i) { codeToGIDA[i] = 0; if ((name = encA[i])) { - codeToGIDA[i] = (Gushort)FT_Get_Name_Index(faceA, name); + codeToGIDA[i] = (int)FT_Get_Name_Index(faceA, (char *)name); } } return new SplashFTFontFile(engineA, idA, src, - faceA, codeToGIDA, 256, gFalse); + faceA, codeToGIDA, 256, gFalse, gTrue); } SplashFontFile *SplashFTFontFile::loadCIDFont(SplashFTFontEngine *engineA, SplashFontFileID *idA, SplashFontSrc *src, - Gushort *codeToGIDA, + int *codeToGIDA, int codeToGIDLenA) { FT_Face faceA; @@ -80,13 +80,13 @@ SplashFontFile *SplashFTFontFile::loadCIDFont(SplashFTFontEngine *engineA, } return new SplashFTFontFile(engineA, idA, src, - faceA, codeToGIDA, codeToGIDLenA, gFalse); + faceA, codeToGIDA, codeToGIDLenA, gFalse, gFalse); } SplashFontFile *SplashFTFontFile::loadTrueTypeFont(SplashFTFontEngine *engineA, SplashFontFileID *idA, SplashFontSrc *src, - Gushort *codeToGIDA, + int *codeToGIDA, int codeToGIDLenA, int faceIndexA) { FT_Face faceA; @@ -100,15 +100,15 @@ SplashFontFile *SplashFTFontFile::loadTrueTypeFont(SplashFTFontEngine *engineA, } return new SplashFTFontFile(engineA, idA, src, - faceA, codeToGIDA, codeToGIDLenA, gTrue); + faceA, codeToGIDA, codeToGIDLenA, gTrue, gFalse); } SplashFTFontFile::SplashFTFontFile(SplashFTFontEngine *engineA, SplashFontFileID *idA, SplashFontSrc *src, FT_Face faceA, - Gushort *codeToGIDA, int codeToGIDLenA, - GBool trueTypeA): + int *codeToGIDA, int codeToGIDLenA, + GBool trueTypeA, GBool type1A): SplashFontFile(idA, src) { engine = engineA; @@ -116,6 +116,7 @@ SplashFTFontFile::SplashFTFontFile(SplashFTFontEngine *engineA, codeToGID = codeToGIDA; codeToGIDLen = codeToGIDLenA; trueType = trueTypeA; + type1 = type1A; } SplashFTFontFile::~SplashFTFontFile() { diff --git a/splash/SplashFTFontFile.h b/splash/SplashFTFontFile.h index 079fb1cb..d642af0a 100644 --- a/splash/SplashFTFontFile.h +++ b/splash/SplashFTFontFile.h @@ -43,15 +43,15 @@ public: static SplashFontFile *loadType1Font(SplashFTFontEngine *engineA, SplashFontFileID *idA, - SplashFontSrc *src, char **encA); + SplashFontSrc *src, const char **encA); static SplashFontFile *loadCIDFont(SplashFTFontEngine *engineA, SplashFontFileID *idA, SplashFontSrc *src, - Gushort *codeToCIDA, int codeToGIDLenA); + int *codeToCIDA, int codeToGIDLenA); static SplashFontFile *loadTrueTypeFont(SplashFTFontEngine *engineA, SplashFontFileID *idA, SplashFontSrc *src, - Gushort *codeToGIDA, + int *codeToGIDA, int codeToGIDLenA, int faceIndexA=0); @@ -68,14 +68,15 @@ private: SplashFontFileID *idA, SplashFontSrc *src, FT_Face faceA, - Gushort *codeToGIDA, int codeToGIDLenA, - GBool trueTypeA); + int *codeToGIDA, int codeToGIDLenA, + GBool trueTypeA, GBool type1A); SplashFTFontEngine *engine; FT_Face face; - Gushort *codeToGID; + int *codeToGID; int codeToGIDLen; GBool trueType; + GBool type1; friend class SplashFTFont; }; diff --git a/splash/SplashFontEngine.cc b/splash/SplashFontEngine.cc index 5fe8aeb5..ab9beb91 100644 --- a/splash/SplashFontEngine.cc +++ b/splash/SplashFontEngine.cc @@ -69,6 +69,7 @@ SplashFontEngine::SplashFontEngine( #endif #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H GBool enableFreeType, + GBool enableAutoHinting, GBool enableFreeTypeHinting, GBool enableSlightHinting, #endif @@ -88,7 +89,7 @@ SplashFontEngine::SplashFontEngine( #endif #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H if (enableFreeType) { - ftEngine = SplashFTFontEngine::init(aa, enableFreeTypeHinting, enableSlightHinting); + ftEngine = SplashFTFontEngine::init(aa, enableAutoHinting, enableFreeTypeHinting, enableSlightHinting); } else { ftEngine = NULL; } @@ -133,7 +134,7 @@ SplashFontFile *SplashFontEngine::getFontFile(SplashFontFileID *id) { SplashFontFile *SplashFontEngine::loadType1Font(SplashFontFileID *idA, SplashFontSrc *src, - char **enc) { + const char **enc) { SplashFontFile *fontFile; fontFile = NULL; @@ -162,7 +163,7 @@ SplashFontFile *SplashFontEngine::loadType1Font(SplashFontFileID *idA, SplashFontFile *SplashFontEngine::loadType1CFont(SplashFontFileID *idA, SplashFontSrc *src, - char **enc) { + const char **enc) { SplashFontFile *fontFile; fontFile = NULL; @@ -191,7 +192,7 @@ SplashFontFile *SplashFontEngine::loadType1CFont(SplashFontFileID *idA, SplashFontFile *SplashFontEngine::loadOpenTypeT1CFont(SplashFontFileID *idA, SplashFontSrc *src, - char **enc) { + const char **enc) { SplashFontFile *fontFile; fontFile = NULL; @@ -235,13 +236,15 @@ SplashFontFile *SplashFontEngine::loadCIDFont(SplashFontFileID *idA, } SplashFontFile *SplashFontEngine::loadOpenTypeCFFFont(SplashFontFileID *idA, - SplashFontSrc *src) { + SplashFontSrc *src, + int *codeToGID, + int codeToGIDLen) { SplashFontFile *fontFile; fontFile = NULL; #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H if (!fontFile && ftEngine) { - fontFile = ftEngine->loadOpenTypeCFFFont(idA, src); + fontFile = ftEngine->loadOpenTypeCFFFont(idA, src, codeToGID, codeToGIDLen); } #endif @@ -257,7 +260,7 @@ SplashFontFile *SplashFontEngine::loadOpenTypeCFFFont(SplashFontFileID *idA, SplashFontFile *SplashFontEngine::loadTrueTypeFont(SplashFontFileID *idA, SplashFontSrc *src, - Gushort *codeToGID, + int *codeToGID, int codeToGIDLen, int faceIndex) { SplashFontFile *fontFile; @@ -297,7 +300,7 @@ SplashFont *SplashFontEngine::getFont(SplashFontFile *fontFile, mat[1] = -(textMat[0] * ctm[1] + textMat[1] * ctm[3]); mat[2] = textMat[2] * ctm[0] + textMat[3] * ctm[2]; mat[3] = -(textMat[2] * ctm[1] + textMat[3] * ctm[3]); - if (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.01) { + if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.01)) { // avoid a singular (or close-to-singular) matrix mat[0] = 0.01; mat[1] = 0; mat[2] = 0; mat[3] = 0.01; diff --git a/splash/SplashFontEngine.h b/splash/SplashFontEngine.h index 38efa48a..f0340a5c 100644 --- a/splash/SplashFontEngine.h +++ b/splash/SplashFontEngine.h @@ -58,6 +58,7 @@ public: #endif #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H GBool enableFreeType, + GBool enabbleAutoHinting, GBool enableFreeTypeHinting, GBool enableSlightHinting, #endif @@ -70,13 +71,14 @@ public: SplashFontFile *getFontFile(SplashFontFileID *id); // Load fonts - these create new SplashFontFile objects. - SplashFontFile *loadType1Font(SplashFontFileID *idA, SplashFontSrc *src, char **enc); - SplashFontFile *loadType1CFont(SplashFontFileID *idA, SplashFontSrc *src, char **enc); - SplashFontFile *loadOpenTypeT1CFont(SplashFontFileID *idA, SplashFontSrc *src, char **enc); + SplashFontFile *loadType1Font(SplashFontFileID *idA, SplashFontSrc *src, const char **enc); + SplashFontFile *loadType1CFont(SplashFontFileID *idA, SplashFontSrc *src, const char **enc); + SplashFontFile *loadOpenTypeT1CFont(SplashFontFileID *idA, SplashFontSrc *src, const char **enc); SplashFontFile *loadCIDFont(SplashFontFileID *idA, SplashFontSrc *src); - SplashFontFile *loadOpenTypeCFFFont(SplashFontFileID *idA, SplashFontSrc *src); + SplashFontFile *loadOpenTypeCFFFont(SplashFontFileID *idA, SplashFontSrc *src, + int *codeToGID, int codeToGIDLen); SplashFontFile *loadTrueTypeFont(SplashFontFileID *idA, SplashFontSrc *src, - Gushort *codeToGID, int codeToGIDLen, int faceIndex = 0); + int *codeToGID, int codeToGIDLen, int faceIndex = 0); // Get a font - this does a cache lookup first, and if not found, // creates a new SplashFont object and adds it to the cache. The diff --git a/splash/SplashMath.h b/splash/SplashMath.h index 924af6aa..1886dec6 100644 --- a/splash/SplashMath.h +++ b/splash/SplashMath.h @@ -45,6 +45,42 @@ static inline int splashFloor(SplashCoord x) { return FixedPoint::floor(x); #elif USE_FLOAT return (int)floorf(x); + #elif __GNUC__ && __i386__ + // floor() and (int)() are implemented separately, which results + // in changing the FPCW multiple times - so we optimize it with + // some inline assembly + Gushort oldCW, newCW, t; + int result; + + __asm__ volatile("fldl %4\n" + "fnstcw %0\n" + "movw %0, %3\n" + "andw $0xf3ff, %3\n" + "orw $0x0400, %3\n" + "movw %3, %1\n" // round down + "fldcw %1\n" + "fistpl %2\n" + "fldcw %0\n" + : "=m" (oldCW), "=m" (newCW), "=m" (result), "=r" (t) + : "m" (x)); + return result; + #elif defined(WIN32) && defined(_M_IX86) + // floor() and (int)() are implemented separately, which results + // in changing the FPCW multiple times - so we optimize it with + // some inline assembly + Gushort oldCW, newCW; + int result; + + __asm fld QWORD PTR x + __asm fnstcw WORD PTR oldCW + __asm mov ax, WORD PTR oldCW + __asm and ax, 0xf3ff + __asm or ax, 0x0400 + __asm mov WORD PTR newCW, ax // round down + __asm fldcw WORD PTR newCW + __asm fistp DWORD PTR result + __asm fldcw WORD PTR oldCW + return result; #else if (x > 0) return (int)x; else return (int)floor(x); @@ -56,6 +92,42 @@ static inline int splashCeil(SplashCoord x) { return FixedPoint::ceil(x); #elif USE_FLOAT return (int)ceilf(x); +#elif __GNUC__ && __i386__ + // ceil() and (int)() are implemented separately, which results + // in changing the FPCW multiple times - so we optimize it with + // some inline assembly + Gushort oldCW, newCW, t; + int result; + + __asm__ volatile("fldl %4\n" + "fnstcw %0\n" + "movw %0, %3\n" + "andw $0xf3ff, %3\n" + "orw $0x0800, %3\n" + "movw %3, %1\n" // round up + "fldcw %1\n" + "fistpl %2\n" + "fldcw %0\n" + : "=m" (oldCW), "=m" (newCW), "=m" (result), "=r" (t) + : "m" (x)); + return result; +#elif defined(WIN32) && defined(_M_IX86) + // ceil() and (int)() are implemented separately, which results + // in changing the FPCW multiple times - so we optimize it with + // some inline assembly + Gushort oldCW, newCW; + int result; + + __asm fld QWORD PTR x + __asm fnstcw WORD PTR oldCW + __asm mov ax, WORD PTR oldCW + __asm and ax, 0xf3ff + __asm or ax, 0x0800 + __asm mov WORD PTR newCW, ax // round up + __asm fldcw WORD PTR newCW + __asm fistp DWORD PTR result + __asm fldcw WORD PTR oldCW + return result; #else return (int)ceil(x); #endif @@ -64,11 +136,57 @@ static inline int splashCeil(SplashCoord x) { static inline int splashRound(SplashCoord x) { #if USE_FIXEDPOINT return FixedPoint::round(x); +#elif __GNUC__ && __i386__ + // this could use round-to-nearest mode and avoid the "+0.5", + // but that produces slightly different results (because i+0.5 + // sometimes rounds up and sometimes down using the even rule) + Gushort oldCW, newCW, t; + int result; + + x += 0.5; + __asm__ volatile("fldl %4\n" + "fnstcw %0\n" + "movw %0, %3\n" + "andw $0xf3ff, %3\n" + "orw $0x0400, %3\n" + "movw %3, %1\n" // round down + "fldcw %1\n" + "fistpl %2\n" + "fldcw %0\n" + : "=m" (oldCW), "=m" (newCW), "=m" (result), "=r" (t) + : "m" (x)); + return result; +#elif defined(WIN32) && defined(_M_IX86) + // this could use round-to-nearest mode and avoid the "+0.5", + // but that produces slightly different results (because i+0.5 + // sometimes rounds up and sometimes down using the even rule) + Gushort oldCW, newCW; + int result; + + x += 0.5; + __asm fld QWORD PTR x + __asm fnstcw WORD PTR oldCW + __asm mov ax, WORD PTR oldCW + __asm and ax, 0xf3ff + __asm or ax, 0x0400 + __asm mov WORD PTR newCW, ax // round down + __asm fldcw WORD PTR newCW + __asm fistp DWORD PTR result + __asm fldcw WORD PTR oldCW + return result; #else return (int)splashFloor(x + 0.5); #endif } +static inline SplashCoord splashAvg(SplashCoord x, SplashCoord y) { +#if USE_FIXEDPOINT + return FixedPoint::avg(x, y); +#else + return 0.5 * (x + y); +#endif +} + static inline SplashCoord splashSqrt(SplashCoord x) { #if USE_FIXEDPOINT return FixedPoint::sqrt(x); @@ -97,19 +215,31 @@ static inline SplashCoord splashDist(SplashCoord x0, SplashCoord y0, #if USE_FIXEDPOINT // this handles the situation where dx*dx or dy*dy is too large to // fit in the 16.16 fixed point format - SplashCoord dxa, dya; + SplashCoord dxa, dya, d; dxa = splashAbs(dx); dya = splashAbs(dy); if (dxa == 0 && dya == 0) { return 0; } else if (dxa > dya) { - return dxa * FixedPoint::sqrt(dya / dxa + 1); + d = dya / dxa; + return dxa * FixedPoint::sqrt(d*d + 1); } else { - return dya * FixedPoint::sqrt(dxa / dya + 1); + d = dxa / dya; + return dya * FixedPoint::sqrt(d*d + 1); } #else return splashSqrt(dx * dx + dy * dy); #endif } +static inline GBool splashCheckDet(SplashCoord m11, SplashCoord m12, + SplashCoord m21, SplashCoord m22, + SplashCoord epsilon) { +#if USE_FIXEDPOINT + return FixedPoint::checkDet(m11, m12, m21, m22, epsilon); +#else + return fabs(m11 * m22 - m12 * m21) >= epsilon; +#endif +} + #endif diff --git a/splash/SplashPath.cc b/splash/SplashPath.cc index 261f7788..bc4ccd52 100644 --- a/splash/SplashPath.cc +++ b/splash/SplashPath.cc @@ -136,11 +136,12 @@ SplashError SplashPath::curveTo(SplashCoord x1, SplashCoord y1, return splashOk; } -SplashError SplashPath::close() { +SplashError SplashPath::close(GBool force) { if (noCurrentPoint()) { return splashErrNoCurPt; } - if (curSubpath == length - 1 || + if (force || + curSubpath == length - 1 || pts[length - 1].x != pts[curSubpath].x || pts[length - 1].y != pts[curSubpath].y) { lineTo(pts[curSubpath].x, pts[curSubpath].y); diff --git a/splash/SplashPath.h b/splash/SplashPath.h index 991e1143..81273c61 100644 --- a/splash/SplashPath.h +++ b/splash/SplashPath.h @@ -77,8 +77,10 @@ public: SplashCoord x2, SplashCoord y2, SplashCoord x3, SplashCoord y3); - // Close the last subpath, adding a line segment if necessary. - SplashError close(); + // Close the last subpath, adding a line segment if necessary. If + // <force> is true, this adds a line segment even if the current + // point is equal to the first point in the subpath. + SplashError close(GBool force = gFalse); // Add a stroke adjustment hint. The controlling segments are // <ctrl0> and <ctrl1> (where segments are identified by their first diff --git a/splash/SplashPattern.cc b/splash/SplashPattern.cc index 67479734..28ca4995 100644 --- a/splash/SplashPattern.cc +++ b/splash/SplashPattern.cc @@ -53,17 +53,3 @@ GBool SplashSolidColor::getColor(int x, int y, SplashColorPtr c) { splashColorCopy(c, color); return gTrue; } - -void SplashSolidColor::overprint(GBool op, Guchar aSrc, SplashColorPtr cSrc, - Guchar aDest, SplashColorPtr cDest, - SplashColorPtr colorResult) { - // default for overprint is knockout: - colorResult[0] = (Guchar)(((aDest - aSrc) * cDest[0] + - aSrc * cSrc[0]) / aDest); - colorResult[1] = (Guchar)(((aDest - aSrc) * cDest[1] + - aSrc * cSrc[1]) / aDest); - colorResult[2] = (Guchar)(((aDest - aSrc) * cDest[2] + - aSrc * cSrc[2]) / aDest); - colorResult[3] = (Guchar)(((aDest - aSrc) * cDest[3] + - aSrc * cSrc[3]) / aDest); -} diff --git a/splash/SplashPattern.h b/splash/SplashPattern.h index de1f8877..42c1660f 100644 --- a/splash/SplashPattern.h +++ b/splash/SplashPattern.h @@ -52,10 +52,6 @@ public: // value for all pixels. virtual GBool isStatic() = 0; - // calculate destination color if overprint is enables - virtual void overprint(GBool op, Guchar alphaSrc, SplashColorPtr colorSrc, - Guchar alphaDest, SplashColorPtr colorDest, SplashColorPtr colorResult) = 0; - private: }; @@ -78,9 +74,6 @@ public: virtual GBool isStatic() { return gTrue; } - virtual void overprint(GBool op, Guchar alphaSrc, SplashColorPtr colorSrc, - Guchar alphaDest, SplashColorPtr colorDest, SplashColorPtr colorResult); - private: SplashColor color; diff --git a/splash/SplashScreen.cc b/splash/SplashScreen.cc index 6b75c0ca..d7412469 100644 --- a/splash/SplashScreen.cc +++ b/splash/SplashScreen.cc @@ -26,6 +26,7 @@ #include <stdlib.h> #include <string.h> +#include <algorithm> #include "goo/gmem.h" #include "SplashMath.h" #include "SplashScreen.h" @@ -46,9 +47,12 @@ struct SplashScreenPoint { int dist; }; -static int cmpDistances(const void *p0, const void *p1) { - return ((SplashScreenPoint *)p0)->dist - ((SplashScreenPoint *)p1)->dist; -} + +struct cmpDistancesFunctor { + bool operator()(const SplashScreenPoint &p0, const SplashScreenPoint &p1) { + return p0.dist < p1.dist; + } +}; //------------------------------------------------------------------------ // SplashScreen @@ -74,41 +78,38 @@ SplashScreen::SplashScreen(SplashScreenParams *params) { void SplashScreen::createMatrix() { - Guchar u, black, white; - int i; + Guchar u; + int black, white, i; SplashScreenParams *params = screenParams; + // size must be a power of 2, and at least 2 + for (size = 2, log2Size = 1; size < params->size; size <<= 1, ++log2Size) ; + switch (params->type) { case splashScreenDispersed: - // size must be a power of 2 - for (size = 1; size < params->size; size <<= 1) ; mat = (Guchar *)gmallocn(size * size, sizeof(Guchar)); buildDispersedMatrix(size/2, size/2, 1, size/2, 1); break; case splashScreenClustered: - // size must be even - size = (params->size >> 1) << 1; - if (size < 2) { - size = 2; - } mat = (Guchar *)gmallocn(size * size, sizeof(Guchar)); buildClusteredMatrix(); break; case splashScreenStochasticClustered: // size must be at least 2*r - if (params->size < 2 * params->dotRadius) { - size = 2 * params->dotRadius; - } else { - size = params->size; + while (size < (params->dotRadius << 1)) { + size <<= 1; + ++log2Size; } mat = (Guchar *)gmallocn(size * size, sizeof(Guchar)); buildSCDMatrix(params->dotRadius); break; } + + sizeM1 = size - 1; // do gamma correction and compute minVal/maxVal minVal = 255; @@ -127,9 +128,9 @@ void SplashScreen::createMatrix() u = splashRound((SplashCoord)255.0 * splashPow((SplashCoord)mat[i] / 255.0, params->gamma)); if (u < black) { - u = black; + u = (Guchar)black; } else if (u >= white) { - u = white; + u = (Guchar)white; } mat[i] = u; if (u < minVal) { @@ -144,7 +145,7 @@ void SplashScreen::buildDispersedMatrix(int i, int j, int val, int delta, int offset) { if (delta == 0) { // map values in [1, size^2] --> [1, 255] - mat[i * size + j] = 1 + (254 * (val - 1)) / (size * size - 1); + mat[(i << log2Size) + j] = 1 + (254 * (val - 1)) / (size * size - 1); } else { buildDispersedMatrix(i, j, val, delta / 2, 4*offset); @@ -168,7 +169,7 @@ void SplashScreen::buildClusteredMatrix() { // initialize the threshold matrix for (y = 0; y < size; ++y) { for (x = 0; x < size; ++x) { - mat[y * size + x] = 0; + mat[(y << log2Size) + x] = 0; } } @@ -200,14 +201,12 @@ void SplashScreen::buildClusteredMatrix() { } // build the threshold matrix - minVal = 1; - maxVal = 0; x1 = y1 = 0; // make gcc happy for (i = 0; i < size * size2; ++i) { d = -1; for (y = 0; y < size; ++y) { for (x = 0; x < size2; ++x) { - if (mat[y * size + x] == 0 && + if (mat[(y << log2Size) + x] == 0 && dist[y * size2 + x] > d) { x1 = x; y1 = y; @@ -217,12 +216,12 @@ void SplashScreen::buildClusteredMatrix() { } // map values in [0, 2*size*size2-1] --> [1, 255] val = 1 + (254 * (2*i)) / (2*size*size2 - 1); - mat[y1 * size + x1] = val; + mat[(y1 << log2Size) + x1] = val; val = 1 + (254 * (2*i+1)) / (2*size*size2 - 1); if (y1 < size2) { - mat[(y1 + size2) * size + x1 + size2] = val; + mat[((y1 + size2) << log2Size) + x1 + size2] = val; } else { - mat[(y1 - size2) * size + x1 + size2] = val; + mat[((y1 - size2) << log2Size) + x1 + size2] = val; } } @@ -290,7 +289,7 @@ void SplashScreen::buildSCDMatrix(int r) { grid = (char *)gmallocn(size * size, sizeof(char)); for (y = 0; y < size; ++y) { for (x = 0; x < size; ++x) { - grid[y*size + x] = 0; + grid[(y << log2Size) + x] = 0; } } @@ -301,7 +300,7 @@ void SplashScreen::buildSCDMatrix(int r) { for (i = 0; i < size * size; ++i) { x = pts[i].x; y = pts[i].y; - if (!grid[y*size + x]) { + if (!grid[(y << log2Size) + x]) { if (dotsLen == dotsSize) { dotsSize *= 2; dots = (SplashScreenPoint *)greallocn(dots, dotsSize, @@ -315,10 +314,10 @@ void SplashScreen::buildSCDMatrix(int r) { if (tmpl[yy*(r+1) + xx]) { x0 = (x + xx) % size; x1 = (x - xx + size) % size; - grid[y0*size + x0] = 1; - grid[y0*size + x1] = 1; - grid[y1*size + x0] = 1; - grid[y1*size + x1] = 1; + grid[(y0 << log2Size) + x0] = 1; + grid[(y0 << log2Size) + x1] = 1; + grid[(y1 << log2Size) + x0] = 1; + grid[(y1 << log2Size) + x1] = 1; } } } @@ -342,8 +341,8 @@ void SplashScreen::buildSCDMatrix(int r) { dMin = d; } } - region[y*size + x] = iMin; - dist[y*size + x] = dMin; + region[(y << log2Size) + x] = iMin; + dist[(y << log2Size) + x] = dMin; } } @@ -352,7 +351,7 @@ void SplashScreen::buildSCDMatrix(int r) { n = 0; for (y = 0; y < size; ++y) { for (x = 0; x < size; ++x) { - if (region[y*size + x] == i) { + if (region[(y << log2Size) + x] == i) { pts[n].x = x; pts[n].y = y; pts[n].dist = distance(dots[i].x, dots[i].y, x, y); @@ -360,10 +359,10 @@ void SplashScreen::buildSCDMatrix(int r) { } } } - qsort(pts, n, sizeof(SplashScreenPoint), &cmpDistances); + std::sort(pts, pts + n, cmpDistancesFunctor()); for (j = 0; j < n; ++j) { // map values in [0 .. n-1] --> [255 .. 1] - mat[pts[j].y * size + pts[j].x] = 255 - (254 * j) / (n - 1); + mat[(pts[j].y << log2Size) + pts[j].x] = 255 - (254 * j) / (n - 1); } } @@ -377,6 +376,8 @@ void SplashScreen::buildSCDMatrix(int r) { SplashScreen::SplashScreen(SplashScreen *screen) { screenParams = screen->screenParams; size = screen->size; + sizeM1 = screen->sizeM1; + log2Size = screen->log2Size; mat = (Guchar *)gmallocn(size * size, sizeof(Guchar)); memcpy(mat, screen->mat, size * size * sizeof(Guchar)); minVal = screen->minVal; @@ -386,29 +387,3 @@ SplashScreen::SplashScreen(SplashScreen *screen) { SplashScreen::~SplashScreen() { gfree(mat); } - -int SplashScreen::test(int x, int y, Guchar value) { - int xx, yy; - - if (mat == NULL) createMatrix(); - - if (value < minVal) { - return 0; - } - if (value >= maxVal) { - return 1; - } - if ((xx = x % size) < 0) { - xx = -xx; - } - if ((yy = y % size) < 0) { - yy = -yy; - } - return value < mat[yy * size + xx] ? 0 : 1; -} - -GBool SplashScreen::isStatic(Guchar value) { - if (mat == NULL) createMatrix(); - - return value < minVal || value >= maxVal; -} diff --git a/splash/SplashScreen.h b/splash/SplashScreen.h index fe3b5c81..a7fc4559 100644 --- a/splash/SplashScreen.h +++ b/splash/SplashScreen.h @@ -27,6 +27,8 @@ #include "SplashTypes.h" +#include <stdlib.h> + //------------------------------------------------------------------------ // SplashScreen //------------------------------------------------------------------------ @@ -42,12 +44,18 @@ public: // Return the computed pixel value (0=black, 1=white) for the gray // level <value> at (<x>, <y>). - int test(int x, int y, Guchar value); + int test(int x, int y, Guchar value) { + int xx, yy; + if (mat == NULL) createMatrix(); + xx = x & sizeM1; + yy = y & sizeM1; + return value < mat[(yy << log2Size) + xx] ? 0 : 1; + } // Returns true if value is above the white threshold or below the // black threshold, i.e., if the corresponding halftone will be // solid white or black. - GBool isStatic(Guchar value); + GBool isStatic(Guchar value) { if (mat == NULL) createMatrix(); return value < minVal || value >= maxVal; } private: void createMatrix(); @@ -61,6 +69,8 @@ private: SplashScreenParams *screenParams; // params to create the other members Guchar *mat; // threshold matrix int size; // size of the threshold matrix + int sizeM1; // size - 1 + int log2Size; // log2(size) Guchar minVal; // any pixel value below minVal generates // solid black Guchar maxVal; // any pixel value above maxVal generates diff --git a/splash/SplashState.cc b/splash/SplashState.cc index 15b6a72a..7b632b84 100644 --- a/splash/SplashState.cc +++ b/splash/SplashState.cc @@ -47,6 +47,7 @@ int splashColorModeNComps[] = { SplashState::SplashState(int width, int height, GBool vectorAntialias, SplashScreenParams *screenParams) { SplashColor color; + int i; matrix[0] = 1; matrix[1] = 0; matrix[2] = 0; matrix[3] = 1; @@ -74,12 +75,24 @@ SplashState::SplashState(int width, int height, GBool vectorAntialias, fillOverprint = gFalse; strokeOverprint = gFalse; overprintMode = 0; + for (i = 0; i < 256; ++i) { + rgbTransferR[i] = (Guchar)i; + rgbTransferG[i] = (Guchar)i; + rgbTransferB[i] = (Guchar)i; + grayTransfer[i] = (Guchar)i; + cmykTransferC[i] = (Guchar)i; + cmykTransferM[i] = (Guchar)i; + cmykTransferY[i] = (Guchar)i; + cmykTransferK[i] = (Guchar)i; + } + overprintMask = 0xffffffff; next = NULL; } SplashState::SplashState(int width, int height, GBool vectorAntialias, SplashScreen *screenA) { SplashColor color; + int i; matrix[0] = 1; matrix[1] = 0; matrix[2] = 0; matrix[3] = 1; @@ -107,6 +120,17 @@ SplashState::SplashState(int width, int height, GBool vectorAntialias, fillOverprint = gFalse; strokeOverprint = gFalse; overprintMode = 0; + for (i = 0; i < 256; ++i) { + rgbTransferR[i] = (Guchar)i; + rgbTransferG[i] = (Guchar)i; + rgbTransferB[i] = (Guchar)i; + grayTransfer[i] = (Guchar)i; + cmykTransferC[i] = (Guchar)i; + cmykTransferM[i] = (Guchar)i; + cmykTransferY[i] = (Guchar)i; + cmykTransferK[i] = (Guchar)i; + } + overprintMask = 0xffffffff; next = NULL; } @@ -140,6 +164,15 @@ SplashState::SplashState(SplashState *state) { fillOverprint = state->fillOverprint; strokeOverprint = state->strokeOverprint; overprintMode = state->overprintMode; + memcpy(rgbTransferR, state->rgbTransferR, 256); + memcpy(rgbTransferG, state->rgbTransferG, 256); + memcpy(rgbTransferB, state->rgbTransferB, 256); + memcpy(grayTransfer, state->grayTransfer, 256); + memcpy(cmykTransferC, state->cmykTransferC, 256); + memcpy(cmykTransferM, state->cmykTransferM, 256); + memcpy(cmykTransferY, state->cmykTransferY, 256); + memcpy(cmykTransferK, state->cmykTransferK, 256); + overprintMask = state->overprintMask; next = NULL; } @@ -189,3 +222,19 @@ void SplashState::setSoftMask(SplashBitmap *softMaskA) { softMask = softMaskA; deleteSoftMask = gTrue; } + +void SplashState::setTransfer(Guchar *red, Guchar *green, Guchar *blue, + Guchar *gray) { + int i; + + memcpy(rgbTransferR, red, 256); + memcpy(rgbTransferG, green, 256); + memcpy(rgbTransferB, blue, 256); + memcpy(grayTransfer, gray, 256); + for (i = 0; i < 256; ++i) { + cmykTransferC[i] = 255 - rgbTransferR[255 - i]; + cmykTransferM[i] = 255 - rgbTransferG[255 - i]; + cmykTransferY[i] = 255 - rgbTransferB[255 - i]; + cmykTransferK[i] = 255 - grayTransfer[255 - i]; + } +} diff --git a/splash/SplashState.h b/splash/SplashState.h index 63dadc62..214bffa1 100644 --- a/splash/SplashState.h +++ b/splash/SplashState.h @@ -87,6 +87,9 @@ public: void setStrokeOverprint(GBool strokeOverprintA) { strokeOverprint = strokeOverprintA; } void setOverprintMode(int overprintModeA) { overprintMode = overprintModeA; } + // Set the transfer function.
+ void setTransfer(Guchar *red, Guchar *green, Guchar *blue, Guchar *gray);
+
private: SplashState(SplashState *state); @@ -114,6 +117,15 @@ private: GBool fillOverprint; GBool strokeOverprint; int overprintMode; + Guchar rgbTransferR[256],
+ rgbTransferG[256],
+ rgbTransferB[256];
+ Guchar grayTransfer[256];
+ Guchar cmykTransferC[256],
+ cmykTransferM[256],
+ cmykTransferY[256],
+ cmykTransferK[256];
+ Guint overprintMask;
SplashState *next; // used by Splash class diff --git a/splash/SplashT1Font.cc b/splash/SplashT1Font.cc index 536c5605..0fdfaaf5 100644 --- a/splash/SplashT1Font.cc +++ b/splash/SplashT1Font.cc @@ -90,7 +90,7 @@ SplashT1Font::SplashT1Font(SplashT1FontFile *fontFileA, SplashCoord *matA, outlineID = -1; // compute font size - size = (float)splashSqrt(mat[2]*mat[2] + mat[3]*mat[3]); + size = (float)splashDist(0, 0, mat[2], mat[3]); // transform the four corners of the font bounding box -- the min // and max values form the bounding box of the transformed font @@ -239,13 +239,12 @@ SplashPath *SplashT1Font::getGlyphPath(int c) { T1_OUTLINE *outline; T1_PATHSEGMENT *seg; T1_BEZIERSEGMENT *bez; - SplashCoord x, y, x1, y1; + int x, y, x1, y1; GBool needClose; if (outlineID < 0) { outlineID = T1_CopyFont(((SplashT1FontFile *)fontFile)->t1libID); - outlineSize = (float)splashSqrt(textMat[2]*textMat[2] + - textMat[3]*textMat[3]); + outlineSize = (float)splashDist(0, 0, textMat[2], textMat[3]); matrix.cxx = (double)textMat[0] / outlineSize; matrix.cxy = (double)textMat[1] / outlineSize; matrix.cyx = (double)textMat[2] / outlineSize; @@ -259,8 +258,11 @@ SplashPath *SplashT1Font::getGlyphPath(int c) { path = new SplashPath(); if ((outline = T1_GetCharOutline(outlineID, c, outlineSize, NULL))) { - x = 0; - y = 0; + // NB: t1lib uses integer coordinates here; we keep a running + // (x,y) total as integers, so that the final point in the path is + // exactly the same as the first point, thus avoiding weird + // mitered join glitches + x = y = 0; needClose = gFalse; for (seg = outline; seg; seg = seg->link) { switch (seg->type) { @@ -269,25 +271,26 @@ SplashPath *SplashT1Font::getGlyphPath(int c) { path->close(); needClose = gFalse; } - x += seg->dest.x * outlineMul; - y += seg->dest.y * outlineMul; - path->moveTo(x, -y); + x += seg->dest.x; + y += seg->dest.y; + path->moveTo(outlineMul * x, -outlineMul * y); break; case T1_PATHTYPE_LINE: - x += seg->dest.x * outlineMul; - y += seg->dest.y * outlineMul; - path->lineTo(x, -y); + x += seg->dest.x; + y += seg->dest.y; + path->lineTo(outlineMul * x, -outlineMul * y); needClose = gTrue; break; case T1_PATHTYPE_BEZIER: bez = (T1_BEZIERSEGMENT *)seg; - x1 = x + (SplashCoord)(bez->dest.x * outlineMul); - y1 = y + (SplashCoord)(bez->dest.y * outlineMul); - path->curveTo(x + (SplashCoord)(bez->B.x * outlineMul), - -(y + (SplashCoord)(bez->B.y * outlineMul)), - x + (SplashCoord)(bez->C.x * outlineMul), - -(y + (SplashCoord)(bez->C.y * outlineMul)), - x1, -y1); + x1 = x + bez->dest.x; + y1 = y + bez->dest.y; + path->curveTo(outlineMul * (x + bez->B.x), + -outlineMul * (y + bez->B.y), + outlineMul * (x + bez->C.x), + -outlineMul * (y + bez->C.y), + outlineMul * x1, + -outlineMul * y1); x = x1; y = y1; needClose = gTrue; diff --git a/splash/SplashT1FontEngine.cc b/splash/SplashT1FontEngine.cc index a09a8839..fb4b38da 100644 --- a/splash/SplashT1FontEngine.cc +++ b/splash/SplashT1FontEngine.cc @@ -51,7 +51,7 @@ int SplashT1FontEngine::t1libInitCount = 0; //------------------------------------------------------------------------ -static void fileWrite(void *stream, char *data, int len) { +static void fileWrite(void *stream, const char *data, int len) { fwrite(data, 1, len, (FILE *)stream); } @@ -98,13 +98,13 @@ SplashT1FontEngine::~SplashT1FontEngine() { SplashFontFile *SplashT1FontEngine::loadType1Font(SplashFontFileID *idA, SplashFontSrc *src, - char **enc) { + const char **enc) { return SplashT1FontFile::loadType1Font(this, idA, src, enc); } SplashFontFile *SplashT1FontEngine::loadType1CFont(SplashFontFileID *idA, SplashFontSrc *src, - char **enc) { + const char **enc) { FoFiType1C *ff; GooString *tmpFileName; FILE *tmpFile; diff --git a/splash/SplashT1FontEngine.h b/splash/SplashT1FontEngine.h index ffc285bd..268a0147 100644 --- a/splash/SplashT1FontEngine.h +++ b/splash/SplashT1FontEngine.h @@ -31,8 +31,8 @@ public: ~SplashT1FontEngine(); // Load fonts. - SplashFontFile *loadType1Font(SplashFontFileID *idA, SplashFontSrc *src, char **enc); - SplashFontFile *loadType1CFont(SplashFontFileID *idA, SplashFontSrc *src, char **enc); + SplashFontFile *loadType1Font(SplashFontFileID *idA, SplashFontSrc *src, const char **enc); + SplashFontFile *loadType1CFont(SplashFontFileID *idA, SplashFontSrc *src, const char **enc); private: diff --git a/splash/SplashT1FontFile.cc b/splash/SplashT1FontFile.cc index 3f46ba63..1832a916 100644 --- a/splash/SplashT1FontFile.cc +++ b/splash/SplashT1FontFile.cc @@ -43,9 +43,9 @@ SplashFontFile *SplashT1FontFile::loadType1Font(SplashT1FontEngine *engineA, SplashFontFileID *idA, SplashFontSrc *src, - char **encA) { + const char **encA) { int t1libIDA; - char **encTmp; + const char **encTmp; char *encStrTmp; int encStrSize; char *encPtr; @@ -82,7 +82,7 @@ SplashFontFile *SplashT1FontFile::loadType1Font(SplashT1FontEngine *engineA, encStrSize += strlen(encA[i]) + 1; } } - encTmp = (char **)gmallocn(257, sizeof(char *)); + encTmp = (const char **)gmallocn(257, sizeof(char *)); encStrTmp = (char *)gmallocn(encStrSize, sizeof(char)); encPtr = encStrTmp; for (i = 0; i < 256; ++i) { @@ -95,7 +95,7 @@ SplashFontFile *SplashT1FontFile::loadType1Font(SplashT1FontEngine *engineA, } } encTmp[256] = "custom"; - T1_ReencodeFont(t1libIDA, encTmp); + T1_ReencodeFont(t1libIDA, (char **)encTmp); ff = new SplashT1FontFile(engineA, idA, src, t1libIDA, encTmp, encStrTmp); @@ -107,7 +107,7 @@ SplashFontFile *SplashT1FontFile::loadType1Font(SplashT1FontEngine *engineA, SplashT1FontFile::SplashT1FontFile(SplashT1FontEngine *engineA, SplashFontFileID *idA, SplashFontSrc *srcA, - int t1libIDA, char **encA, char *encStrA): + int t1libIDA, const char **encA, char *encStrA): SplashFontFile(idA, srcA) { engine = engineA; diff --git a/splash/SplashT1FontFile.h b/splash/SplashT1FontFile.h index 24c958bc..c094b6d8 100644 --- a/splash/SplashT1FontFile.h +++ b/splash/SplashT1FontFile.h @@ -41,7 +41,7 @@ public: static SplashFontFile *loadType1Font(SplashT1FontEngine *engineA, SplashFontFileID *idA, SplashFontSrc *src, - char **encA); + const char **encA); virtual ~SplashT1FontFile(); @@ -55,11 +55,11 @@ private: SplashT1FontFile(SplashT1FontEngine *engineA, SplashFontFileID *idA, SplashFontSrc *src, - int t1libIDA, char **encA, char *encStrA); + int t1libIDA, const char **encA, char *encStrA); SplashT1FontEngine *engine; int t1libID; // t1lib font ID - char **enc; + const char **enc; char *encStr; friend class SplashT1Font; diff --git a/splash/SplashXPath.cc b/splash/SplashXPath.cc index 4950de4e..b82d3559 100644 --- a/splash/SplashXPath.cc +++ b/splash/SplashXPath.cc @@ -65,19 +65,13 @@ inline void SplashXPath::transform(SplashCoord *matrix, // SplashXPath //------------------------------------------------------------------------ -SplashXPath::SplashXPath() { - segs = NULL; - length = size = 0; -} - SplashXPath::SplashXPath(SplashPath *path, SplashCoord *matrix, SplashCoord flatness, GBool closeSubpaths) { SplashPathHint *hint; SplashXPathPoint *pts; SplashXPathAdjust *adjusts, *adjust; SplashCoord x0, y0, x1, y1, x2, y2, x3, y3, xsp, ysp; - SplashCoord adj0, adj1, w; - int ww; + SplashCoord adj0, adj1; int curSubpath, i, j; // transform the points @@ -119,19 +113,24 @@ SplashXPath::SplashXPath(SplashPath *path, SplashCoord *matrix, adj0 = adj1; adj1 = x0; } - w = adj1 - adj0; - ww = splashRound(w); - if (ww == 0) { - ww = 1; - } adjusts[i].x0a = adj0 - 0.01; adjusts[i].x0b = adj0 + 0.01; adjusts[i].xma = (SplashCoord)0.5 * (adj0 + adj1) - 0.01; adjusts[i].xmb = (SplashCoord)0.5 * (adj0 + adj1) + 0.01; adjusts[i].x1a = adj1 - 0.01; adjusts[i].x1b = adj1 + 0.01; - adjusts[i].x0 = (SplashCoord)splashRound(adj0); - adjusts[i].x1 = adjusts[i].x0 + ww - 0.01; + // rounding both edge coordinates can result in lines of + // different widths (e.g., adj=10.1, adj1=11.3 --> x0=10, x1=11; + // adj0=10.4, adj1=11.6 --> x0=10, x1=12), but it has the + // benefit of making adjacent strokes/fills line up without any + // gaps between them + x0 = splashRound(adj0); + x1 = splashRound(adj1); + if (x1 == x0) { + x1 = x1 + 1; + } + adjusts[i].x0 = (SplashCoord)x0; + adjusts[i].x1 = (SplashCoord)x1 - 0.01; adjusts[i].xm = (SplashCoord)0.5 * (adjusts[i].x0 + adjusts[i].x1); adjusts[i].firstPt = hint->firstPt; adjusts[i].lastPt = hint->lastPt; @@ -197,15 +196,7 @@ SplashXPath::SplashXPath(SplashPath *path, SplashCoord *matrix, } else { x1 = pts[i].x; y1 = pts[i].y; - addSegment(x0, y0, x1, y1, - path->flags[i-1] & splashPathFirst, - path->flags[i] & splashPathLast, - !closeSubpaths && - (path->flags[i-1] & splashPathFirst) && - !(path->flags[i-1] & splashPathClosed), - !closeSubpaths && - (path->flags[i] & splashPathLast) && - !(path->flags[i] & splashPathClosed)); + addSegment(x0, y0, x1, y1); x0 = x1; y0 = y1; ++i; @@ -216,8 +207,7 @@ SplashXPath::SplashXPath(SplashPath *path, SplashCoord *matrix, (path->flags[i-1] & splashPathLast) && (pts[i-1].x != pts[curSubpath].x || pts[i-1].y != pts[curSubpath].y)) { - addSegment(x0, y0, xsp, ysp, - gFalse, gTrue, gFalse, gFalse); + addSegment(x0, y0, xsp, ysp); } } } @@ -289,7 +279,11 @@ void SplashXPath::addCurve(SplashCoord x0, SplashCoord y0, SplashCoord dx, dy, mx, my, d1, d2, flatness2; int p1, p2, p3; +#if USE_FIXEDPOINT + flatness2 = flatness; +#else flatness2 = flatness * flatness; +#endif // initial segment p1 = 0; @@ -329,21 +323,22 @@ void SplashXPath::addCurve(SplashCoord x0, SplashCoord y0, // line) mx = (xl0 + xr3) * 0.5; my = (yl0 + yr3) * 0.5; +#if USE_FIXEDPOINT + d1 = splashDist(xx1, yy1, mx, my); + d2 = splashDist(xx2, yy2, mx, my); +#else dx = xx1 - mx; dy = yy1 - my; d1 = dx*dx + dy*dy; dx = xx2 - mx; dy = yy2 - my; d2 = dx*dx + dy*dy; +#endif // if the curve is flat enough, or no more subdivisions are // allowed, add the straight line segment if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) { - addSegment(xl0, yl0, xr3, yr3, - p1 == 0 && first, - p2 == splashMaxCurveSplits && last, - p1 == 0 && end0, - p2 == splashMaxCurveSplits && end1); + addSegment(xl0, yl0, xr3, yr3); p1 = p2; // otherwise, subdivide the curve @@ -389,26 +384,13 @@ void SplashXPath::addCurve(SplashCoord x0, SplashCoord y0, } void SplashXPath::addSegment(SplashCoord x0, SplashCoord y0, - SplashCoord x1, SplashCoord y1, - GBool first, GBool last, GBool end0, GBool end1) { + SplashCoord x1, SplashCoord y1) { grow(1); segs[length].x0 = x0; segs[length].y0 = y0; segs[length].x1 = x1; segs[length].y1 = y1; segs[length].flags = 0; - if (first) { - segs[length].flags |= splashXPathFirst; - } - if (last) { - segs[length].flags |= splashXPathLast; - } - if (end0) { - segs[length].flags |= splashXPathEnd0; - } - if (end1) { - segs[length].flags |= splashXPathEnd1; - } if (y1 == y0) { segs[length].dxdy = segs[length].dydx = 0; segs[length].flags |= splashXPathHoriz; @@ -441,31 +423,27 @@ void SplashXPath::addSegment(SplashCoord x0, SplashCoord y0, ++length; } -static bool cmpXPathSegs(const SplashXPathSeg &seg0, const SplashXPathSeg &seg1) { - SplashCoord x0, y0, x1, y1; +struct cmpXPathSegsFunctor { + bool operator()(const SplashXPathSeg &seg0, const SplashXPathSeg &seg1) { + SplashCoord x0, y0, x1, y1; - if (seg0.flags & splashXPathFlip) { - x0 = seg0.x1; - y0 = seg0.y1; - } else { - x0 = seg0.x0; - y0 = seg0.y0; - } - if (seg1.flags & splashXPathFlip) { - x1 = seg1.x1; - y1 = seg1.y1; - } else { - x1 = seg1.x0; - y1 = seg1.y0; - } - if (y0 != y1) { - return (y0 < y1) ? true : false; - } - if (x0 != x1) { - return (x0 < x1) ? true : false; + if (seg0.flags & splashXPathFlip) { + x0 = seg0.x1; + y0 = seg0.y1; + } else { + x0 = seg0.x0; + y0 = seg0.y0; + } + if (seg1.flags & splashXPathFlip) { + x1 = seg1.x1; + y1 = seg1.y1; + } else { + x1 = seg1.x0; + y1 = seg1.y0; + } + return (y0 != y1) ? (y0 < y1) : (x0 < x1); } - return false; -} +}; void SplashXPath::aaScale() { SplashXPathSeg *seg; @@ -480,5 +458,5 @@ void SplashXPath::aaScale() { } void SplashXPath::sort() { - std::sort(segs, segs + length, &cmpXPathSegs); + std::sort(segs, segs + length, cmpXPathSegsFunctor()); } diff --git a/splash/SplashXPath.h b/splash/SplashXPath.h index 64c47969..db06978a 100644 --- a/splash/SplashXPath.h +++ b/splash/SplashXPath.h @@ -32,15 +32,11 @@ struct SplashXPathSeg { Guint flags; }; -#define splashXPathFirst 0x01 // first segment of a subpath -#define splashXPathLast 0x02 // last segment of a subpath -#define splashXPathEnd0 0x04 // first endpoint is end of an open subpath -#define splashXPathEnd1 0x08 // second endpoint is end of an open subpath -#define splashXPathHoriz 0x10 // segment is vertical (y0 == y1) +#define splashXPathHoriz 0x01 // segment is vertical (y0 == y1) // (dxdy is undef) -#define splashXPathVert 0x20 // segment is horizontal (x0 == x1) +#define splashXPathVert 0x02 // segment is horizontal (x0 == x1) // (dydx is undef) -#define splashXPathFlip 0x40 // y0 > y1 +#define splashXPathFlip 0x04 // y0 > y1 //------------------------------------------------------------------------ // SplashXPath @@ -70,7 +66,6 @@ public: protected: - SplashXPath(); SplashXPath(SplashXPath *xPath); void transform(SplashCoord *matrix, SplashCoord xi, SplashCoord yi, SplashCoord *xo, SplashCoord *yo); @@ -84,8 +79,7 @@ protected: SplashCoord flatness, GBool first, GBool last, GBool end0, GBool end1); void addSegment(SplashCoord x0, SplashCoord y0, - SplashCoord x1, SplashCoord y1, - GBool first, GBool last, GBool end0, GBool end1); + SplashCoord x1, SplashCoord y1); SplashXPathSeg *segs; int length, size; // length and size of segs array diff --git a/splash/SplashXPathScanner.cc b/splash/SplashXPathScanner.cc index 33aa3923..c9fe5e5d 100644 --- a/splash/SplashXPathScanner.cc +++ b/splash/SplashXPathScanner.cc @@ -37,25 +37,30 @@ //------------------------------------------------------------------------ struct SplashIntersect { + int y; int x0, x1; // intersection of segment with [y, y+1) int count; // EO/NZWN counter increment }; -static bool cmpIntersect(const SplashIntersect &p0, const SplashIntersect &p1) { - return p0.x0 < p1.x0; -} +struct cmpIntersectFunctor { + bool operator()(const SplashIntersect &i0, const SplashIntersect &i1) { + return (i0.y != i1.y) ? (i0.y < i1.y) : (i0.x0 < i1.x0); + } +}; //------------------------------------------------------------------------ // SplashXPathScanner //------------------------------------------------------------------------ -SplashXPathScanner::SplashXPathScanner(SplashXPath *xPathA, GBool eoA) { +SplashXPathScanner::SplashXPathScanner(SplashXPath *xPathA, GBool eoA, + int clipYMin, int clipYMax) { SplashXPathSeg *seg; SplashCoord xMinFP, yMinFP, xMaxFP, yMaxFP; int i; xPath = xPathA; eo = eoA; + partialClip = gFalse; // compute the bbox if (xPath->length == 0) { @@ -103,16 +108,25 @@ SplashXPathScanner::SplashXPathScanner(SplashXPath *xPathA, GBool eoA) { xMax = splashFloor(xMaxFP); yMin = splashFloor(yMinFP); yMax = splashFloor(yMaxFP); + if (clipYMin > yMin) { + yMin = clipYMin; + partialClip = gTrue; + } + if (clipYMax < yMax) { + yMax = clipYMax; + partialClip = gTrue; + } } - interY = yMin - 1; - xPathIdx = 0; + allInter = NULL; inter = NULL; - interLen = interSize = 0; + computeIntersections(); + interY = yMin - 1; } SplashXPathScanner::~SplashXPathScanner() { gfree(inter); + gfree(allInter); } void SplashXPathScanner::getBBoxAA(int *xMinA, int *yMinA, @@ -124,12 +138,23 @@ void SplashXPathScanner::getBBoxAA(int *xMinA, int *yMinA, } void SplashXPathScanner::getSpanBounds(int y, int *spanXMin, int *spanXMax) { - if (interY != y) { - computeIntersections(y); + int interBegin, interEnd, xx, i; + + if (y < yMin || y > yMax) { + interBegin = interEnd = 0; + } else { + interBegin = inter[y - yMin]; + interEnd = inter[y - yMin + 1]; } - if (interLen > 0) { - *spanXMin = inter[0].x0; - *spanXMax = inter[interLen - 1].x1; + if (interBegin < interEnd) { + *spanXMin = allInter[interBegin].x0; + xx = allInter[interBegin].x1; + for (i = interBegin + 1; i < interEnd; ++i) { + if (allInter[i].x1 > xx) { + xx = allInter[i].x1; + } + } + *spanXMax = xx; } else { *spanXMin = xMax + 1; *spanXMax = xMax; @@ -137,47 +162,50 @@ void SplashXPathScanner::getSpanBounds(int y, int *spanXMin, int *spanXMax) { } GBool SplashXPathScanner::test(int x, int y) { - int count, i; + int interBegin, interEnd, count, i; - if (interY != y) { - computeIntersections(y); + if (y < yMin || y > yMax) { + return gFalse; } + interBegin = inter[y - yMin]; + interEnd = inter[y - yMin + 1]; count = 0; - for (i = 0; i < interLen && inter[i].x0 <= x; ++i) { - if (x <= inter[i].x1) { + for (i = interBegin; i < interEnd && allInter[i].x0 <= x; ++i) { + if (x <= allInter[i].x1) { return gTrue; } - count += inter[i].count; + count += allInter[i].count; } return eo ? (count & 1) : (count != 0); } GBool SplashXPathScanner::testSpan(int x0, int x1, int y) { - int count, xx1, i; + int interBegin, interEnd, count, xx1, i; - if (interY != y) { - computeIntersections(y); + if (y < yMin || y > yMax) { + return gFalse; } - + interBegin = inter[y - yMin]; + interEnd = inter[y - yMin + 1]; count = 0; - for (i = 0; i < interLen && inter[i].x1 < x0; ++i) { - count += inter[i].count; + for (i = interBegin; i < interEnd && allInter[i].x1 < x0; ++i) { + count += allInter[i].count; } // invariant: the subspan [x0,xx1] is inside the path xx1 = x0 - 1; while (xx1 < x1) { - if (i >= interLen) { + if (i >= interEnd) { return gFalse; } - if (inter[i].x0 > xx1 + 1 && + if (allInter[i].x0 > xx1 + 1 && !(eo ? (count & 1) : (count != 0))) { return gFalse; } - if (inter[i].x1 > xx1) { - xx1 = inter[i].x1; + if (allInter[i].x1 > xx1) { + xx1 = allInter[i].x1; } - count += inter[i].count; + count += allInter[i].count; ++i; } @@ -185,25 +213,31 @@ GBool SplashXPathScanner::testSpan(int x0, int x1, int y) { } GBool SplashXPathScanner::getNextSpan(int y, int *x0, int *x1) { - int xx0, xx1; + int interEnd, xx0, xx1; + if (y < yMin || y > yMax) { + return gFalse; + } if (interY != y) { - computeIntersections(y); + interY = y; + interIdx = inter[y - yMin]; + interCount = 0; } - if (interIdx >= interLen) { + interEnd = inter[y - yMin + 1]; + if (interIdx >= interEnd) { return gFalse; } - xx0 = inter[interIdx].x0; - xx1 = inter[interIdx].x1; - interCount += inter[interIdx].count; + xx0 = allInter[interIdx].x0; + xx1 = allInter[interIdx].x1; + interCount += allInter[interIdx].count; ++interIdx; - while (interIdx < interLen && - (inter[interIdx].x0 <= xx1 || + while (interIdx < interEnd && + (allInter[interIdx].x0 <= xx1 || (eo ? (interCount & 1) : (interCount != 0)))) { - if (inter[interIdx].x1 > xx1) { - xx1 = inter[interIdx].x1; + if (allInter[interIdx].x1 > xx1) { + xx1 = allInter[interIdx].x1; } - interCount += inter[interIdx].count; + interCount += allInter[interIdx].count; ++interIdx; } *x0 = xx0; @@ -211,161 +245,199 @@ GBool SplashXPathScanner::getNextSpan(int y, int *x0, int *x1) { return gTrue; } -void SplashXPathScanner::computeIntersections(int y) { - SplashCoord xSegMin, xSegMax, ySegMin, ySegMax, xx0, xx1; +void SplashXPathScanner::computeIntersections() { SplashXPathSeg *seg; - int i, j; + SplashCoord segXMin, segXMax, segYMin, segYMax, xx0, xx1; + int x, y, y0, y1, i; - // find the first segment that intersects [y, y+1) - i = (y >= interY) ? xPathIdx : 0; - while (i < xPath->length && - xPath->segs[i].y0 < y && xPath->segs[i].y1 < y) { - ++i; + if (yMin > yMax) { + return; } - xPathIdx = i; - // find all of the segments that intersect [y, y+1) and create an - // Intersect element for each one - interLen = 0; - for (j = i; j < xPath->length; ++j) { - seg = &xPath->segs[j]; + // build the list of all intersections + allInterLen = 0; + allInterSize = 16; + allInter = (SplashIntersect *)gmallocn(allInterSize, + sizeof(SplashIntersect)); + for (i = 0; i < xPath->length; ++i) { + seg = &xPath->segs[i]; if (seg->flags & splashXPathFlip) { - ySegMin = seg->y1; - ySegMax = seg->y0; + segYMin = seg->y1; + segYMax = seg->y0; } else { - ySegMin = seg->y0; - ySegMax = seg->y1; - } - - // ensure that: ySegMin < y+1 - // y <= ySegMax - if (ySegMin >= y + 1) { - break; + segYMin = seg->y0; + segYMax = seg->y1; } - if (ySegMax < y) { - continue; - } - - if (interLen == interSize) { - if (interSize == 0) { - interSize = 16; - } else { - interSize *= 2; - } - inter = (SplashIntersect *)greallocn(inter, interSize, - sizeof(SplashIntersect)); - } - if (seg->flags & splashXPathHoriz) { - xx0 = seg->x0; - xx1 = seg->x1; + y = splashFloor(seg->y0); + if (y >= yMin && y <= yMax) { + addIntersection(segYMin, segYMax, seg->flags, + y, splashFloor(seg->x0), splashFloor(seg->x1)); + } } else if (seg->flags & splashXPathVert) { - xx0 = xx1 = seg->x0; + y0 = splashFloor(segYMin); + if (y0 < yMin) { + y0 = yMin; + } + y1 = splashFloor(segYMax); + if (y1 > yMax) { + y1 = yMax; + } + x = splashFloor(seg->x0); + for (y = y0; y <= y1; ++y) { + addIntersection(segYMin, segYMax, seg->flags, y, x, x); + } } else { if (seg->x0 < seg->x1) { - xSegMin = seg->x0; - xSegMax = seg->x1; + segXMin = seg->x0; + segXMax = seg->x1; } else { - xSegMin = seg->x1; - xSegMax = seg->x0; + segXMin = seg->x1; + segXMax = seg->x0; } - // intersection with top edge - xx0 = seg->x0 + ((SplashCoord)y - seg->y0) * seg->dxdy; - // intersection with bottom edge - xx1 = seg->x0 + ((SplashCoord)y + 1 - seg->y0) * seg->dxdy; - // the segment may not actually extend to the top and/or bottom edges - if (xx0 < xSegMin) { - xx0 = xSegMin; - } else if (xx0 > xSegMax) { - xx0 = xSegMax; + y0 = splashFloor(segYMin); + if (y0 < yMin) { + y0 = yMin; } - if (xx1 < xSegMin) { - xx1 = xSegMin; - } else if (xx1 > xSegMax) { - xx1 = xSegMax; + y1 = splashFloor(segYMax); + if (y1 > yMax) { + y1 = yMax; + } + // this loop could just add seg->dxdy to xx1 on each iteration, + // but that introduces numerical accuracy problems + xx1 = seg->x0 + ((SplashCoord)y0 - seg->y0) * seg->dxdy; + for (y = y0; y <= y1; ++y) { + xx0 = xx1; + xx1 = seg->x0 + ((SplashCoord)(y + 1) - seg->y0) * seg->dxdy; + // the segment may not actually extend to the top and/or bottom edges + if (xx0 < segXMin) { + xx0 = segXMin; + } else if (xx0 > segXMax) { + xx0 = segXMax; + } + if (xx1 < segXMin) { + xx1 = segXMin; + } else if (xx1 > segXMax) { + xx1 = segXMax; + } + addIntersection(segYMin, segYMax, seg->flags, y, + splashFloor(xx0), splashFloor(xx1)); } } - if (xx0 < xx1) { - inter[interLen].x0 = splashFloor(xx0); - inter[interLen].x1 = splashFloor(xx1); - } else { - inter[interLen].x0 = splashFloor(xx1); - inter[interLen].x1 = splashFloor(xx0); - } - if (ySegMin <= y && - (SplashCoord)y < ySegMax && - !(seg->flags & splashXPathHoriz)) { - inter[interLen].count = eo ? 1 - : (seg->flags & splashXPathFlip) ? 1 : -1; - } else { - inter[interLen].count = 0; + } + std::sort(allInter, allInter + allInterLen, cmpIntersectFunctor()); + + // build the list of y pointers + inter = (int *)gmallocn(yMax - yMin + 2, sizeof(int)); + i = 0; + for (y = yMin; y <= yMax; ++y) { + inter[y - yMin] = i; + while (i < allInterLen && allInter[i].y <= y) { + ++i; } - ++interLen; } + inter[yMax - yMin + 1] = i; +} - std::sort(inter, inter + interLen, cmpIntersect); - - interY = y; - interIdx = 0; - interCount = 0; +void SplashXPathScanner::addIntersection(double segYMin, double segYMax, + Guint segFlags, + int y, int x0, int x1) { + if (allInterLen == allInterSize) { + allInterSize *= 2; + allInter = (SplashIntersect *)greallocn(allInter, allInterSize, + sizeof(SplashIntersect)); + } + allInter[allInterLen].y = y; + if (x0 < x1) { + allInter[allInterLen].x0 = x0; + allInter[allInterLen].x1 = x1; + } else { + allInter[allInterLen].x0 = x1; + allInter[allInterLen].x1 = x0; + } + if (segYMin <= y && + (SplashCoord)y < segYMax && + !(segFlags & splashXPathHoriz)) { + allInter[allInterLen].count = eo ? 1 + : (segFlags & splashXPathFlip) ? 1 : -1; + } else { + allInter[allInterLen].count = 0; + } + ++allInterLen; } void SplashXPathScanner::renderAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y) { - int xx0, xx1, xx, xxMin, xxMax, yy; + int xx0, xx1, xx, xxMin, xxMax, yy, interEnd; Guchar mask; SplashColorPtr p; memset(aaBuf->getDataPtr(), 0, aaBuf->getRowSize() * aaBuf->getHeight()); xxMin = aaBuf->getWidth(); xxMax = -1; - for (yy = 0; yy < splashAASize; ++yy) { - computeIntersections(splashAASize * y + yy); - while (interIdx < interLen) { - xx0 = inter[interIdx].x0; - xx1 = inter[interIdx].x1; - interCount += inter[interIdx].count; - ++interIdx; - while (interIdx < interLen && - (inter[interIdx].x0 <= xx1 || - (eo ? (interCount & 1) : (interCount != 0)))) { - if (inter[interIdx].x1 > xx1) { - xx1 = inter[interIdx].x1; - } - interCount += inter[interIdx].count; - ++interIdx; - } - if (xx0 < 0) { - xx0 = 0; - } - ++xx1; - if (xx1 > aaBuf->getWidth()) { - xx1 = aaBuf->getWidth(); + if (yMin <= yMax) { + if (splashAASize * y < yMin) { + interIdx = inter[0]; + } else if (splashAASize * y > yMax) { + interIdx = inter[yMax - yMin + 1]; + } else { + interIdx = inter[splashAASize * y - yMin]; + } + for (yy = 0; yy < splashAASize; ++yy) { + if (splashAASize * y + yy < yMin) { + interEnd = inter[0]; + } else if (splashAASize * y + yy > yMax) { + interEnd = inter[yMax - yMin + 1]; + } else { + interEnd = inter[splashAASize * y + yy - yMin + 1]; } - // set [xx0, xx1) to 1 - if (xx0 < xx1) { - xx = xx0; - p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + (xx >> 3); - if (xx & 7) { - mask = 0xff >> (xx & 7); - if ((xx & ~7) == (xx1 & ~7)) { - mask &= (Guchar)(0xff00 >> (xx1 & 7)); + interCount = 0; + while (interIdx < interEnd) { + xx0 = allInter[interIdx].x0; + xx1 = allInter[interIdx].x1; + interCount += allInter[interIdx].count; + ++interIdx; + while (interIdx < interEnd && + (allInter[interIdx].x0 <= xx1 || + (eo ? (interCount & 1) : (interCount != 0)))) { + if (allInter[interIdx].x1 > xx1) { + xx1 = allInter[interIdx].x1; } - *p++ |= mask; - xx = (xx & ~7) + 8; + interCount += allInter[interIdx].count; + ++interIdx; } - for (; xx + 7 < xx1; xx += 8) { - *p++ |= 0xff; + if (xx0 < 0) { + xx0 = 0; } - if (xx < xx1) { - *p |= (Guchar)(0xff00 >> (xx1 & 7)); + ++xx1; + if (xx1 > aaBuf->getWidth()) { + xx1 = aaBuf->getWidth(); + } + // set [xx0, xx1) to 1 + if (xx0 < xx1) { + xx = xx0; + p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + (xx >> 3); + if (xx & 7) { + mask = 0xff >> (xx & 7); + if ((xx & ~7) == (xx1 & ~7)) { + mask &= (Guchar)(0xff00 >> (xx1 & 7)); + } + *p++ |= mask; + xx = (xx & ~7) + 8; + } + for (; xx + 7 < xx1; xx += 8) { + *p++ |= 0xff; + } + if (xx < xx1) { + *p |= (Guchar)(0xff00 >> (xx1 & 7)); + } + } + if (xx0 < xxMin) { + xxMin = xx0; + } + if (xx1 > xxMax) { + xxMax = xx1; } - } - if (xx0 < xxMin) { - xxMin = xx0; - } - if (xx1 > xxMax) { - xxMax = xx1; } } } @@ -375,50 +447,64 @@ void SplashXPathScanner::renderAALine(SplashBitmap *aaBuf, void SplashXPathScanner::clipAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y) { - int xx0, xx1, xx, yy; + int xx0, xx1, xx, yy, interEnd; Guchar mask; SplashColorPtr p; for (yy = 0; yy < splashAASize; ++yy) { xx = *x0 * splashAASize; - computeIntersections(splashAASize * y + yy); - while (interIdx < interLen && xx < (*x1 + 1) * splashAASize) { - xx0 = inter[interIdx].x0; - xx1 = inter[interIdx].x1; - interCount += inter[interIdx].count; - ++interIdx; - while (interIdx < interLen && - (inter[interIdx].x0 <= xx1 || - (eo ? (interCount & 1) : (interCount != 0)))) { - if (inter[interIdx].x1 > xx1) { - xx1 = inter[interIdx].x1; + if (yMin <= yMax) { + if (splashAASize * y + yy < yMin) { + interIdx = interEnd = inter[0]; + } else if (splashAASize * y + yy > yMax) { + interIdx = interEnd = inter[yMax - yMin + 1]; + } else { + interIdx = inter[splashAASize * y + yy - yMin]; + if (splashAASize * y + yy > yMax) { + interEnd = inter[yMax - yMin + 1]; + } else { + interEnd = inter[splashAASize * y + yy - yMin + 1]; } - interCount += inter[interIdx].count; - ++interIdx; - } - if (xx0 > aaBuf->getWidth()) { - xx0 = aaBuf->getWidth(); } - // set [xx, xx0) to 0 - if (xx < xx0) { - p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + (xx >> 3); - if (xx & 7) { - mask = (Guchar)(0xff00 >> (xx & 7)); - if ((xx & ~7) == (xx0 & ~7)) { - mask |= 0xff >> (xx0 & 7); + interCount = 0; + while (interIdx < interEnd && xx < (*x1 + 1) * splashAASize) { + xx0 = allInter[interIdx].x0; + xx1 = allInter[interIdx].x1; + interCount += allInter[interIdx].count; + ++interIdx; + while (interIdx < interEnd && + (allInter[interIdx].x0 <= xx1 || + (eo ? (interCount & 1) : (interCount != 0)))) { + if (allInter[interIdx].x1 > xx1) { + xx1 = allInter[interIdx].x1; } - *p++ &= mask; - xx = (xx & ~7) + 8; + interCount += allInter[interIdx].count; + ++interIdx; } - for (; xx + 7 <= xx0; xx += 8) { - *p++ = 0x00; + if (xx0 > aaBuf->getWidth()) { + xx0 = aaBuf->getWidth(); } + // set [xx, xx0) to 0 if (xx < xx0) { - *p &= 0xff >> (xx0 & 7); + p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + (xx >> 3); + if (xx & 7) { + mask = (Guchar)(0xff00 >> (xx & 7)); + if ((xx & ~7) == (xx0 & ~7)) { + mask |= 0xff >> (xx0 & 7); + } + *p++ &= mask; + xx = (xx & ~7) + 8; + } + for (; xx + 7 < xx0; xx += 8) { + *p++ = 0x00; + } + if (xx < xx0) { + *p &= 0xff >> (xx0 & 7); + } + } + if (xx1 >= xx) { + xx = xx1 + 1; } - } - if (xx1 >= xx) { - xx = xx1 + 1; } } xx0 = (*x1 + 1) * splashAASize; @@ -434,7 +520,7 @@ void SplashXPathScanner::clipAALine(SplashBitmap *aaBuf, *p++ &= mask; xx = (xx & ~7) + 8; } - for (; xx + 7 <= xx0; xx += 8) { + for (; xx + 7 < xx0; xx += 8) { *p++ = 0x00; } if (xx < xx0) { diff --git a/splash/SplashXPathScanner.h b/splash/SplashXPathScanner.h index 1cc4f3cd..719fae48 100644 --- a/splash/SplashXPathScanner.h +++ b/splash/SplashXPathScanner.h @@ -25,7 +25,8 @@ class SplashXPathScanner { public: // Create a new SplashXPathScanner object. <xPathA> must be sorted. - SplashXPathScanner(SplashXPath *xPathA, GBool eoA); + SplashXPathScanner(SplashXPath *xPathA, GBool eoA, + int clipYMin, int clipYMax); ~SplashXPathScanner(); @@ -36,6 +37,10 @@ public: // Return the path's bounding box. void getBBoxAA(int *xMinA, int *yMinA, int *xMaxA, int *yMaxA); + // Returns true if at least part of the path was outside the + // clipYMin/clipYMax bounds passed to the constructor. + GBool hasPartialClip() { return partialClip; } + // Return the min/max x values for the span at <y>. void getSpanBounds(int y, int *spanXMin, int *spanXMax); @@ -64,22 +69,25 @@ public: private: - void computeIntersections(int y); + void computeIntersections(); + void addIntersection(double segYMin, double segYMax, + Guint segFlags, + int y, int x0, int x1); SplashXPath *xPath; GBool eo; int xMin, yMin, xMax, yMax; + GBool partialClip; - int interY; // current y value + SplashIntersect *allInter; // array of intersections + int allInterLen; // number of intersections in <allInter> + int allInterSize; // size of the <allInter> array + int *inter; // indexes into <allInter> for each y value + int interY; // current y value - used by getNextSpan int interIdx; // current index into <inter> - used by // getNextSpan int interCount; // current EO/NZWN counter - used by // getNextSpan - int xPathIdx; // current index into <xPath> - used by - // computeIntersections - SplashIntersect *inter; // intersections array for <interY> - int interLen; // number of intersections in <inter> - int interSize; // size of the <inter> array }; #endif |