diff options
Diffstat (limited to 'splash')
44 files changed, 4880 insertions, 3317 deletions
diff --git a/splash/Makefile.in b/splash/Makefile.in index 66c449b..6479b15 100644 --- a/splash/Makefile.in +++ b/splash/Makefile.in @@ -16,7 +16,7 @@ GOOLIBDIR = ../goo FOFISRCDIR = $(srcdir)/../fofi FOFILIBDIR = ../fofi -CXXFLAGS = @CXXFLAGS@ @DEFS@ -I.. -I$(GOOSRCDIR) -I$(FOFISRCDIR) -I$(srcdir) @t1_CFLAGS@ @freetype2_CFLAGS@ +CXXFLAGS = @CXXFLAGS@ @DEFS@ -I.. -I$(srcdir)/.. -I$(GOOSRCDIR) -I$(FOFISRCDIR) -I$(srcdir) @freetype2_CFLAGS@ CXX = @CXX@ AR = @AR@ @@ -48,9 +48,6 @@ CXX_SRC = \ $(srcdir)/SplashPattern.cc \ $(srcdir)/SplashScreen.cc \ $(srcdir)/SplashState.cc \ - $(srcdir)/SplashT1Font.cc \ - $(srcdir)/SplashT1FontEngine.cc \ - $(srcdir)/SplashT1FontFile.cc \ $(srcdir)/SplashXPath.cc \ $(srcdir)/SplashXPathScanner.cc @@ -75,9 +72,6 @@ SPLASH_OBJS = \ SplashPattern.o \ SplashScreen.o \ SplashState.o \ - SplashT1Font.o \ - SplashT1FontEngine.o \ - SplashT1FontFile.o \ SplashXPath.o \ SplashXPathScanner.o @@ -96,4 +90,4 @@ clean: depend: $(CXX) $(CXXFLAGS) -MM $(CXX_SRC) >Makefile.dep -include Makefile.dep +-include Makefile.dep diff --git a/splash/Splash.cc b/splash/Splash.cc index 879a4f2..d9035b3 100644 --- a/splash/Splash.cc +++ b/splash/Splash.cc @@ -2,6 +2,8 @@ // // Splash.cc // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #include <aconf.h> @@ -29,7 +31,7 @@ //------------------------------------------------------------------------ -#define splashAAGamma 1.5 +#define splashAAGamma 0.67 // distance of Bezier control point from center for circle approximation // = (4 * (sqrt(2) - 1) / 3) * r @@ -46,41 +48,12 @@ static inline Guchar clip255(int x) { return x < 0 ? 0 : x > 255 ? 255 : x; } -// 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 + int ib0, ib1; // vertex indices for edge B SplashCoord xa0, ya0, xa1, ya1; // edge A SplashCoord dxdya; // slope of edge A SplashCoord xb0, yb0, xb1, yb1; // edge B @@ -94,41 +67,26 @@ struct ImageSection { #define splashPipeMaxStages 9 struct SplashPipe { - // pixel coordinates - int x, y; - // source pattern SplashPattern *pattern; // source alpha and color Guchar aInput; - GBool usesShape; - SplashColorPtr cSrc; SplashColor cSrcVal; - // non-isolated group alpha0 - Guchar *alpha0Ptr; - - // soft mask - SplashColorPtr softMaskPtr; - - // destination alpha and color - SplashColorPtr destColorPtr; - int destColorMask; - Guchar *destAlphaPtr; - - // shape - Guchar shape; - - // result alpha and color + // special cases and result color GBool noTransparency; + GBool shapeOnly; SplashPipeResultColorCtrl resultColorCtrl; // non-isolated group correction + // (this is only used when Splash::composite() is called to composite + // a non-isolated group onto the backdrop) GBool nonIsolatedGroup; // the "run" function - void (Splash::*run)(SplashPipe *pipe); + void (Splash::*run)(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr); }; SplashPipeResultColorCtrl Splash::pipeResultColorNoAlphaBlend[] = { @@ -208,36 +166,37 @@ inline void Splash::updateModY(int y) { // pipeline //------------------------------------------------------------------------ -inline void Splash::pipeInit(SplashPipe *pipe, int x, int y, - SplashPattern *pattern, SplashColorPtr cSrc, +inline void Splash::pipeInit(SplashPipe *pipe, SplashPattern *pattern, Guchar aInput, GBool usesShape, GBool nonIsolatedGroup) { - pipeSetXY(pipe, x, y); pipe->pattern = NULL; // source color - if (pattern) { - if (pattern->isStatic()) { - pattern->getColor(x, y, pipe->cSrcVal); - } else { - pipe->pattern = pattern; - } - pipe->cSrc = pipe->cSrcVal; + if (pattern && pattern->isStatic()) { + pattern->getColor(0, 0, pipe->cSrcVal); + pipe->pattern = NULL; } else { - pipe->cSrc = cSrc; + pipe->pattern = pattern; } // source alpha pipe->aInput = aInput; - pipe->usesShape = usesShape; - // result alpha - if (aInput == 255 && !state->softMask && !usesShape && - !state->inNonIsolatedGroup && !nonIsolatedGroup) { - pipe->noTransparency = gTrue; - } else { - pipe->noTransparency = gFalse; - } + // special cases + pipe->noTransparency = aInput == 255 && + !state->softMask && + !usesShape && + !state->inNonIsolatedGroup && + !state->inKnockoutGroup && + !nonIsolatedGroup && + state->overprintMask == 0xffffffff; + pipe->shapeOnly = aInput == 255 && + !state->softMask && + usesShape && + !state->inNonIsolatedGroup && + !state->inKnockoutGroup && + !nonIsolatedGroup && + state->overprintMask == 0xffffffff; // result color if (pipe->noTransparency) { @@ -255,33 +214,48 @@ inline void Splash::pipeInit(SplashPipe *pipe, int x, int y, // select the 'run' function pipe->run = &Splash::pipeRun; if (!pipe->pattern && pipe->noTransparency && !state->blendFunc) { - if (bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr) { + if (bitmap->mode == splashModeMono1 && !bitmap->alpha) { pipe->run = &Splash::pipeRunSimpleMono1; - } else if (bitmap->mode == splashModeMono8 && pipe->destAlphaPtr) { + } else if (bitmap->mode == splashModeMono8 && bitmap->alpha) { pipe->run = &Splash::pipeRunSimpleMono8; - } else if (bitmap->mode == splashModeRGB8 && pipe->destAlphaPtr) { + } else if (bitmap->mode == splashModeRGB8 && bitmap->alpha) { pipe->run = &Splash::pipeRunSimpleRGB8; - } else if (bitmap->mode == splashModeBGR8 && pipe->destAlphaPtr) { + } else if (bitmap->mode == splashModeBGR8 && bitmap->alpha) { pipe->run = &Splash::pipeRunSimpleBGR8; #if SPLASH_CMYK - } else if (bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr) { + } else if (bitmap->mode == splashModeCMYK8 && bitmap->alpha) { pipe->run = &Splash::pipeRunSimpleCMYK8; #endif } + } else if (!pipe->pattern && pipe->shapeOnly && !state->blendFunc) { + if (bitmap->mode == splashModeMono1 && !bitmap->alpha) { + pipe->run = &Splash::pipeRunShapeMono1; + } else if (bitmap->mode == splashModeMono8 && bitmap->alpha) { + pipe->run = &Splash::pipeRunShapeMono8; + } else if (bitmap->mode == splashModeRGB8 && bitmap->alpha) { + pipe->run = &Splash::pipeRunShapeRGB8; + } else if (bitmap->mode == splashModeBGR8 && bitmap->alpha) { + pipe->run = &Splash::pipeRunShapeBGR8; +#if SPLASH_CMYK + } else if (bitmap->mode == splashModeCMYK8 && bitmap->alpha) { + pipe->run = &Splash::pipeRunShapeCMYK8; +#endif + } } else if (!pipe->pattern && !pipe->noTransparency && !state->softMask && - pipe->usesShape && - !(state->inNonIsolatedGroup && alpha0Bitmap->alpha) && + usesShape && + !(state->inNonIsolatedGroup && groupBackBitmap->alpha) && + !state->inKnockoutGroup && !state->blendFunc && !pipe->nonIsolatedGroup) { - if (bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr) { + if (bitmap->mode == splashModeMono1 && !bitmap->alpha) { pipe->run = &Splash::pipeRunAAMono1; - } else if (bitmap->mode == splashModeMono8 && pipe->destAlphaPtr) { + } else if (bitmap->mode == splashModeMono8 && bitmap->alpha) { pipe->run = &Splash::pipeRunAAMono8; - } else if (bitmap->mode == splashModeRGB8 && pipe->destAlphaPtr) { + } else if (bitmap->mode == splashModeRGB8 && bitmap->alpha) { pipe->run = &Splash::pipeRunAARGB8; - } else if (bitmap->mode == splashModeBGR8 && pipe->destAlphaPtr) { + } else if (bitmap->mode == splashModeBGR8 && bitmap->alpha) { pipe->run = &Splash::pipeRunAABGR8; #if SPLASH_CMYK - } else if (bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr) { + } else if (bitmap->mode == splashModeCMYK8 && bitmap->alpha) { pipe->run = &Splash::pipeRunAACMYK8; #endif } @@ -289,932 +263,1654 @@ inline void Splash::pipeInit(SplashPipe *pipe, int x, int y, } // 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; +void Splash::pipeRun(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr) { + Guchar *shapePtr2; + Guchar shape, aSrc, aDest, alphaI, alphaIm1, alpha0, aResult; + SplashColor cSrc, cDest, cBlend; + Guchar shapeVal, cResult0, cResult1, cResult2, cResult3; + int cSrcStride, shapeStride, x, lastX, t, i; + SplashColorPtr destColorPtr; + Guchar destColorMask; + Guchar *destAlphaPtr; + SplashColorPtr color0Ptr; + Guchar color0Mask; + Guchar *alpha0Ptr; + SplashColorPtr softMaskPtr; #if SPLASH_CMYK SplashColor cSrc2, cDest2; #endif - //----- source color + if (cSrcPtr && !pipe->pattern) { + cSrcStride = bitmapComps; + } else { + cSrcPtr = pipe->cSrcVal; + cSrcStride = 0; + } - // static pattern: handled in pipeInit - // fixed color: handled in pipeInit + if (shapePtr) { + shapePtr2 = shapePtr; + shapeStride = 1; + for (; x0 <= x1; ++x0) { + if (*shapePtr2) { + break; + } + cSrcPtr += cSrcStride; + ++shapePtr2; + } + } else { + shapeVal = 0xff; + shapePtr2 = &shapeVal; + shapeStride = 0; + } + if (x0 > x1) { + return; + } + updateModX(x0); + updateModY(y); + lastX = x0; - // dynamic pattern - if (pipe->pattern) { - pipe->pattern->getColor(pipe->x, pipe->y, pipe->cSrcVal); + if (bitmap->mode == splashModeMono1) { + destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)]; + destColorMask = 0x80 >> (x0 & 7); + } else { + destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * bitmapComps]; + destColorMask = 0; // make gcc happy + } + if (bitmap->alpha) { + destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0]; + } else { + destAlphaPtr = NULL; + } + if (state->softMask) { + softMaskPtr = &state->softMask->data[y * state->softMask->rowSize + x0]; + } else { + softMaskPtr = NULL; + } + if (state->inKnockoutGroup) { + if (bitmap->mode == splashModeMono1) { + color0Ptr = + &groupBackBitmap->data[(groupBackY + y) * groupBackBitmap->rowSize + + ((groupBackX + x0) >> 3)]; + color0Mask = 0x80 >> ((groupBackX + x0) & 7); + } else { + color0Ptr = + &groupBackBitmap->data[(groupBackY + y) * groupBackBitmap->rowSize + + (groupBackX + x0) * bitmapComps]; + color0Mask = 0; // make gcc happy + } + } else { + color0Ptr = NULL; + color0Mask = 0; // make gcc happy + } + if (state->inNonIsolatedGroup && groupBackBitmap->alpha) { + alpha0Ptr = + &groupBackBitmap->alpha[(groupBackY + y) * groupBackBitmap->width + + (groupBackX + x0)]; + } else { + alpha0Ptr = NULL; } - if (pipe->noTransparency && !state->blendFunc) { + for (x = x0; x <= x1; ++x) { - //----- write destination pixel + //----- shape - switch (bitmap->mode) { - case splashModeMono1: - cResult0 = state->grayTransfer[pipe->cSrc[0]]; - if (state->screen->test(pipe->x, pipe->y, cResult0)) { - *pipe->destColorPtr |= pipe->destColorMask; + shape = *shapePtr2; + if (!shape) { + if (bitmap->mode == splashModeMono1) { + destColorPtr += destColorMask & 1; + destColorMask = (destColorMask << 7) | (destColorMask >> 1); } else { - *pipe->destColorPtr &= ~pipe->destColorMask; + destColorPtr += bitmapComps; } - if (!(pipe->destColorMask >>= 1)) { - pipe->destColorMask = 0x80; - ++pipe->destColorPtr; - } - break; - case splashModeMono8: - *pipe->destColorPtr++ = state->grayTransfer[pipe->cSrc[0]]; - break; - case splashModeRGB8: - *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]]; - *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]]; - *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]]; - break; - case splashModeBGR8: - *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]]; - *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]]; - *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]]; - break; -#if SPLASH_CMYK - case splashModeCMYK8: - if (state->overprintMask & 1) { - pipe->destColorPtr[0] = state->cmykTransferC[pipe->cSrc[0]]; + if (destAlphaPtr) { + ++destAlphaPtr; } - if (state->overprintMask & 2) { - pipe->destColorPtr[1] = state->cmykTransferM[pipe->cSrc[1]]; + if (softMaskPtr) { + ++softMaskPtr; } - if (state->overprintMask & 4) { - pipe->destColorPtr[2] = state->cmykTransferY[pipe->cSrc[2]]; + if (color0Ptr) { + if (bitmap->mode == splashModeMono1) { + color0Ptr += color0Mask & 1; + color0Mask = (color0Mask << 7) | (color0Mask >> 1); + } else { + color0Ptr += bitmapComps; + } } - if (state->overprintMask & 8) { - pipe->destColorPtr[3] = state->cmykTransferK[pipe->cSrc[3]]; + if (alpha0Ptr) { + ++alpha0Ptr; } - pipe->destColorPtr += 4; - break; -#endif + cSrcPtr += cSrcStride; + shapePtr2 += shapeStride; + continue; } - if (pipe->destAlphaPtr) { - *pipe->destAlphaPtr++ = 255; + lastX = x; + + //----- source color + + // static pattern: handled in pipeInit + // fixed color: handled in pipeInit + + // dynamic pattern + if (pipe->pattern) { + pipe->pattern->getColor(x, y, pipe->cSrcVal); } - } else { + if (pipe->noTransparency && !state->blendFunc) { - //----- read destination pixel + //----- write destination pixel - switch (bitmap->mode) { - case splashModeMono1: - cDest[0] = (*pipe->destColorPtr & pipe->destColorMask) ? 0xff : 0x00; - break; - case splashModeMono8: - cDest[0] = *pipe->destColorPtr; - break; - case splashModeRGB8: - cDest[0] = pipe->destColorPtr[0]; - cDest[1] = pipe->destColorPtr[1]; - cDest[2] = pipe->destColorPtr[2]; - break; - case splashModeBGR8: - cDest[0] = pipe->destColorPtr[2]; - cDest[1] = pipe->destColorPtr[1]; - cDest[2] = pipe->destColorPtr[0]; - break; + switch (bitmap->mode) { + case splashModeMono1: + cResult0 = state->grayTransfer[cSrcPtr[0]]; + if (state->screen->test(x, y, cResult0)) { + *destColorPtr |= destColorMask; + } else { + *destColorPtr &= ~destColorMask; + } + destColorPtr += destColorMask & 1; + destColorMask = (destColorMask << 7) | (destColorMask >> 1); + break; + case splashModeMono8: + *destColorPtr++ = state->grayTransfer[cSrcPtr[0]]; + break; + case splashModeRGB8: + destColorPtr[0] = state->rgbTransferR[cSrcPtr[0]]; + destColorPtr[1] = state->rgbTransferG[cSrcPtr[1]]; + destColorPtr[2] = state->rgbTransferB[cSrcPtr[2]]; + destColorPtr += 3; + break; + case splashModeBGR8: + destColorPtr[0] = state->rgbTransferB[cSrcPtr[2]]; + destColorPtr[1] = state->rgbTransferG[cSrcPtr[1]]; + destColorPtr[2] = state->rgbTransferR[cSrcPtr[0]]; + destColorPtr += 3; + break; #if SPLASH_CMYK - case splashModeCMYK8: - cDest[0] = pipe->destColorPtr[0]; - cDest[1] = pipe->destColorPtr[1]; - cDest[2] = pipe->destColorPtr[2]; - cDest[3] = pipe->destColorPtr[3]; - break; + case splashModeCMYK8: + destColorPtr[0] = state->cmykTransferC[cSrcPtr[0]]; + destColorPtr[1] = state->cmykTransferM[cSrcPtr[1]]; + destColorPtr[2] = state->cmykTransferY[cSrcPtr[2]]; + destColorPtr[3] = state->cmykTransferK[cSrcPtr[3]]; + destColorPtr += 4; + break; #endif - } - if (pipe->destAlphaPtr) { - aDest = *pipe->destAlphaPtr; - } else { - aDest = 0xff; - } + } + if (destAlphaPtr) { + *destAlphaPtr++ = 255; + } - //----- source alpha + } else { // if (noTransparency && !blendFunc) + + //----- read destination pixel + // (or backdrop color, for knockout groups) + + if (color0Ptr) { + + switch (bitmap->mode) { + case splashModeMono1: + cDest[0] = (*color0Ptr & color0Mask) ? 0xff : 0x00; + color0Ptr += color0Mask & 1; + color0Mask = (color0Mask << 7) | (color0Mask >> 1); + break; + case splashModeMono8: + cDest[0] = *color0Ptr++; + break; + case splashModeRGB8: + cDest[0] = color0Ptr[0]; + cDest[1] = color0Ptr[1]; + cDest[2] = color0Ptr[2]; + color0Ptr += 3; + break; + case splashModeBGR8: + cDest[2] = color0Ptr[0]; + cDest[1] = color0Ptr[1]; + cDest[0] = color0Ptr[2]; + color0Ptr += 3; + break; +#if SPLASH_CMYK + case splashModeCMYK8: + cDest[0] = color0Ptr[0]; + cDest[1] = color0Ptr[1]; + cDest[2] = color0Ptr[2]; + cDest[3] = color0Ptr[3]; + color0Ptr += 4; + break; +#endif + } - if (state->softMask) { - if (pipe->usesShape) { - aSrc = div255(div255(pipe->aInput * *pipe->softMaskPtr++) * - pipe->shape); } else { - aSrc = div255(pipe->aInput * *pipe->softMaskPtr++); + + switch (bitmap->mode) { + case splashModeMono1: + cDest[0] = (*destColorPtr & destColorMask) ? 0xff : 0x00; + break; + case splashModeMono8: + cDest[0] = *destColorPtr; + break; + case splashModeRGB8: + cDest[0] = destColorPtr[0]; + cDest[1] = destColorPtr[1]; + cDest[2] = destColorPtr[2]; + break; + case splashModeBGR8: + cDest[0] = destColorPtr[2]; + cDest[1] = destColorPtr[1]; + cDest[2] = destColorPtr[0]; + break; +#if SPLASH_CMYK + case splashModeCMYK8: + cDest[0] = destColorPtr[0]; + cDest[1] = destColorPtr[1]; + cDest[2] = destColorPtr[2]; + cDest[3] = destColorPtr[3]; + break; +#endif + } + } - } else if (pipe->usesShape) { - aSrc = div255(pipe->aInput * pipe->shape); - } else { - aSrc = pipe->aInput; - } - //----- non-isolated group correction + if (destAlphaPtr) { + aDest = *destAlphaPtr; + } else { + aDest = 0xff; + } + + //----- overprint - 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; + for (i = 0; i < bitmapComps; ++i) { +#if SPLASH_CMYK + if (state->overprintMask & (1 << i)) { + cSrc[i] = cSrcPtr[i]; + } else { + cSrc[i] = div255(aDest * cDest[i]); + } +#else + cSrc[i] = cSrcPtr[i]; +#endif + } + + //----- source alpha + + if (softMaskPtr) { + if (shapePtr) { + aSrc = div255(div255(pipe->aInput * *softMaskPtr++) * shape); + } else { + aSrc = div255(pipe->aInput * *softMaskPtr++); + } + } else if (shapePtr) { + aSrc = div255(pipe->aInput * shape); } else { - t = (aDest * 255) / pipe->shape - aDest; + 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, shape is the source (group) alpha. + t = (aDest * 255) / shape - aDest; switch (bitmap->mode) { #if SPLASH_CMYK case splashModeCMYK8: - cSrcNonIso[3] = clip255(pipe->cSrc[3] + - ((pipe->cSrc[3] - cDest[3]) * t) / 255); + cSrc[3] = clip255(cSrc[3] + ((cSrc[3] - cDest[3]) * t) / 255); #endif case splashModeRGB8: case splashModeBGR8: - cSrcNonIso[2] = clip255(pipe->cSrc[2] + - ((pipe->cSrc[2] - cDest[2]) * t) / 255); - cSrcNonIso[1] = clip255(pipe->cSrc[1] + - ((pipe->cSrc[1] - cDest[1]) * t) / 255); + cSrc[2] = clip255(cSrc[2] + ((cSrc[2] - cDest[2]) * t) / 255); + cSrc[1] = clip255(cSrc[1] + ((cSrc[1] - cDest[1]) * t) / 255); case splashModeMono1: case splashModeMono8: - cSrcNonIso[0] = clip255(pipe->cSrc[0] + - ((pipe->cSrc[0] - cDest[0]) * t) / 255); + cSrc[0] = clip255(cSrc[0] + ((cSrc[0] - cDest[0]) * t) / 255); break; } - cSrc = cSrcNonIso; } - } else { - cSrc = pipe->cSrc; - } - //----- blend function + //----- blend function - if (state->blendFunc) { + 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 + 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 + (*state->blendFunc)(cSrc, cDest, cBlend, bitmap->mode); + } - if (pipe->noTransparency) { - alphaI = alphaIm1 = aResult = 255; - } else { - aResult = aSrc + aDest - div255(aSrc * aDest); + //----- result alpha and non-isolated group element correction // alphaI = alpha_i // alphaIm1 = alpha_(i-1) - if (pipe->alpha0Ptr) { - alpha0 = *pipe->alpha0Ptr++; - alphaI = aResult + alpha0 - div255(aResult * alpha0); - alphaIm1 = alpha0 + aDest - div255(alpha0 * aDest); + + if (pipe->noTransparency) { + alphaI = alphaIm1 = aResult = 255; + } else if (alpha0Ptr) { + if (color0Ptr) { + // non-isolated, knockout + aResult = aSrc; + alpha0 = *alpha0Ptr++; + alphaI = aSrc + alpha0 - div255(aSrc * alpha0); + alphaIm1 = alpha0; + } else { + // non-isolated, non-knockout + aResult = aSrc + aDest - div255(aSrc * aDest); + alpha0 = *alpha0Ptr++; + alphaI = aResult + alpha0 - div255(aResult * alpha0); + alphaIm1 = alpha0 + aDest - div255(alpha0 * aDest); + } } else { - alphaI = aResult; - alphaIm1 = aDest; + if (color0Ptr) { + // isolated, knockout + aResult = aSrc; + alphaI = aSrc; + alphaIm1 = 0; + } else { + // isolated, non-knockout + aResult = aSrc + aDest - div255(aSrc * aDest); + alphaI = aResult; + alphaIm1 = aDest; + } } - } - //----- result color + //----- result color - cResult0 = cResult1 = cResult2 = cResult3 = 0; // make gcc happy + cResult0 = cResult1 = cResult2 = cResult3 = 0; // make gcc happy - switch (pipe->resultColorCtrl) { + switch (pipe->resultColorCtrl) { - case splashPipeResultColorNoAlphaBlendMono: - cResult0 = state->grayTransfer[div255((255 - aDest) * cSrc[0] + - aDest * cBlend[0])]; - break; - case splashPipeResultColorNoAlphaBlendRGB: - cResult0 = state->rgbTransferR[div255((255 - aDest) * cSrc[0] + - aDest * cBlend[0])]; - cResult1 = state->rgbTransferG[div255((255 - aDest) * cSrc[1] + - aDest * cBlend[1])]; - cResult2 = state->rgbTransferB[div255((255 - aDest) * cSrc[2] + - aDest * cBlend[2])]; - break; + case splashPipeResultColorNoAlphaBlendMono: + cResult0 = state->grayTransfer[div255((255 - aDest) * cSrc[0] + + aDest * cBlend[0])]; + break; + case splashPipeResultColorNoAlphaBlendRGB: + cResult0 = state->rgbTransferR[div255((255 - aDest) * cSrc[0] + + aDest * cBlend[0])]; + cResult1 = state->rgbTransferG[div255((255 - aDest) * cSrc[1] + + aDest * cBlend[1])]; + cResult2 = state->rgbTransferB[div255((255 - aDest) * cSrc[2] + + aDest * cBlend[2])]; + break; #if SPLASH_CMYK - case splashPipeResultColorNoAlphaBlendCMYK: - cResult0 = state->cmykTransferC[div255((255 - aDest) * cSrc[0] + - aDest * cBlend[0])]; - cResult1 = state->cmykTransferM[div255((255 - aDest) * cSrc[1] + - aDest * cBlend[1])]; - cResult2 = state->cmykTransferY[div255((255 - aDest) * cSrc[2] + - aDest * cBlend[2])]; - cResult3 = state->cmykTransferK[div255((255 - aDest) * cSrc[3] + - aDest * cBlend[3])]; - break; + case splashPipeResultColorNoAlphaBlendCMYK: + cResult0 = state->cmykTransferC[div255((255 - aDest) * cSrc[0] + + aDest * cBlend[0])]; + cResult1 = state->cmykTransferM[div255((255 - aDest) * cSrc[1] + + aDest * cBlend[1])]; + cResult2 = state->cmykTransferY[div255((255 - aDest) * cSrc[2] + + aDest * cBlend[2])]; + cResult3 = state->cmykTransferK[div255((255 - aDest) * cSrc[3] + + aDest * cBlend[3])]; + break; #endif - case splashPipeResultColorAlphaNoBlendMono: - if (alphaI == 0) { - cResult0 = 0; - } else { - cResult0 = state->grayTransfer[((alphaI - aSrc) * cDest[0] + - aSrc * cSrc[0]) / alphaI]; - } - break; - case splashPipeResultColorAlphaNoBlendRGB: - if (alphaI == 0) { - cResult0 = 0; - cResult1 = 0; - cResult2 = 0; - } else { - cResult0 = state->rgbTransferR[((alphaI - aSrc) * cDest[0] + - aSrc * cSrc[0]) / alphaI]; - cResult1 = state->rgbTransferG[((alphaI - aSrc) * cDest[1] + - aSrc * cSrc[1]) / alphaI]; - cResult2 = state->rgbTransferB[((alphaI - aSrc) * cDest[2] + - aSrc * cSrc[2]) / alphaI]; - } - break; + case splashPipeResultColorAlphaNoBlendMono: + if (alphaI == 0) { + cResult0 = 0; + } else { + cResult0 = state->grayTransfer[((alphaI - aSrc) * cDest[0] + + aSrc * cSrc[0]) / alphaI]; + } + break; + case splashPipeResultColorAlphaNoBlendRGB: + if (alphaI == 0) { + cResult0 = 0; + cResult1 = 0; + cResult2 = 0; + } else { + cResult0 = state->rgbTransferR[((alphaI - aSrc) * cDest[0] + + aSrc * cSrc[0]) / alphaI]; + cResult1 = state->rgbTransferG[((alphaI - aSrc) * cDest[1] + + aSrc * cSrc[1]) / alphaI]; + cResult2 = state->rgbTransferB[((alphaI - aSrc) * cDest[2] + + aSrc * cSrc[2]) / alphaI]; + } + break; #if SPLASH_CMYK - case splashPipeResultColorAlphaNoBlendCMYK: - if (alphaI == 0) { - cResult0 = 0; - cResult1 = 0; - cResult2 = 0; - cResult3 = 0; - } else { - cResult0 = state->cmykTransferC[((alphaI - aSrc) * cDest[0] + - aSrc * cSrc[0]) / alphaI]; - cResult1 = state->cmykTransferM[((alphaI - aSrc) * cDest[1] + - aSrc * cSrc[1]) / alphaI]; - cResult2 = state->cmykTransferY[((alphaI - aSrc) * cDest[2] + - aSrc * cSrc[2]) / alphaI]; - cResult3 = state->cmykTransferK[((alphaI - aSrc) * cDest[3] + - aSrc * cSrc[3]) / alphaI]; - } - break; + case splashPipeResultColorAlphaNoBlendCMYK: + if (alphaI == 0) { + cResult0 = 0; + cResult1 = 0; + cResult2 = 0; + cResult3 = 0; + } else { + cResult0 = state->cmykTransferC[((alphaI - aSrc) * cDest[0] + + aSrc * cSrc[0]) / alphaI]; + cResult1 = state->cmykTransferM[((alphaI - aSrc) * cDest[1] + + aSrc * cSrc[1]) / alphaI]; + cResult2 = state->cmykTransferY[((alphaI - aSrc) * cDest[2] + + aSrc * cSrc[2]) / alphaI]; + cResult3 = state->cmykTransferK[((alphaI - aSrc) * cDest[3] + + aSrc * cSrc[3]) / alphaI]; + } + break; #endif - case splashPipeResultColorAlphaBlendMono: - if (alphaI == 0) { - cResult0 = 0; - } else { - cResult0 = state->grayTransfer[((alphaI - aSrc) * cDest[0] + - aSrc * ((255 - alphaIm1) * cSrc[0] + - alphaIm1 * cBlend[0]) / 255) / - alphaI]; - } - break; - case splashPipeResultColorAlphaBlendRGB: - if (alphaI == 0) { - cResult0 = 0; - cResult1 = 0; - cResult2 = 0; - } else { - cResult0 = state->rgbTransferR[((alphaI - aSrc) * cDest[0] + - aSrc * ((255 - alphaIm1) * cSrc[0] + - alphaIm1 * cBlend[0]) / 255) / - alphaI]; - cResult1 = state->rgbTransferG[((alphaI - aSrc) * cDest[1] + - aSrc * ((255 - alphaIm1) * cSrc[1] + - alphaIm1 * cBlend[1]) / 255) / - alphaI]; - cResult2 = state->rgbTransferB[((alphaI - aSrc) * cDest[2] + - aSrc * ((255 - alphaIm1) * cSrc[2] + - alphaIm1 * cBlend[2]) / 255) / - alphaI]; - } - break; + case splashPipeResultColorAlphaBlendMono: + if (alphaI == 0) { + cResult0 = 0; + } else { + cResult0 = state->grayTransfer[((alphaI - aSrc) * cDest[0] + + aSrc * ((255 - alphaIm1) * cSrc[0] + + alphaIm1 * cBlend[0]) / 255) / + alphaI]; + } + break; + case splashPipeResultColorAlphaBlendRGB: + if (alphaI == 0) { + cResult0 = 0; + cResult1 = 0; + cResult2 = 0; + } else { + cResult0 = state->rgbTransferR[((alphaI - aSrc) * cDest[0] + + aSrc * ((255 - alphaIm1) * cSrc[0] + + alphaIm1 * cBlend[0]) / 255) / + alphaI]; + cResult1 = state->rgbTransferG[((alphaI - aSrc) * cDest[1] + + aSrc * ((255 - alphaIm1) * cSrc[1] + + alphaIm1 * cBlend[1]) / 255) / + alphaI]; + cResult2 = state->rgbTransferB[((alphaI - aSrc) * cDest[2] + + aSrc * ((255 - alphaIm1) * cSrc[2] + + alphaIm1 * cBlend[2]) / 255) / + alphaI]; + } + break; #if SPLASH_CMYK - case splashPipeResultColorAlphaBlendCMYK: - if (alphaI == 0) { - cResult0 = 0; - cResult1 = 0; - cResult2 = 0; - cResult3 = 0; - } else { - cResult0 = state->cmykTransferC[((alphaI - aSrc) * cDest[0] + - aSrc * ((255 - alphaIm1) * cSrc[0] + - alphaIm1 * cBlend[0]) / 255) / - alphaI]; - cResult1 = state->cmykTransferM[((alphaI - aSrc) * cDest[1] + - aSrc * ((255 - alphaIm1) * cSrc[1] + - alphaIm1 * cBlend[1]) / 255) / - alphaI]; - cResult2 = state->cmykTransferY[((alphaI - aSrc) * cDest[2] + - aSrc * ((255 - alphaIm1) * cSrc[2] + - alphaIm1 * cBlend[2]) / 255) / - alphaI]; - cResult3 = state->cmykTransferK[((alphaI - aSrc) * cDest[3] + - aSrc * ((255 - alphaIm1) * cSrc[3] + - alphaIm1 * cBlend[3]) / 255) / - alphaI]; - } - break; + case splashPipeResultColorAlphaBlendCMYK: + if (alphaI == 0) { + cResult0 = 0; + cResult1 = 0; + cResult2 = 0; + cResult3 = 0; + } else { + cResult0 = + state->cmykTransferC[((alphaI - aSrc) * cDest[0] + + aSrc * ((255 - alphaIm1) * cSrc[0] + + alphaIm1 * cBlend[0]) / 255) / + alphaI]; + cResult1 = + state->cmykTransferM[((alphaI - aSrc) * cDest[1] + + aSrc * ((255 - alphaIm1) * cSrc[1] + + alphaIm1 * cBlend[1]) / 255) / + alphaI]; + cResult2 = + state->cmykTransferY[((alphaI - aSrc) * cDest[2] + + aSrc * ((255 - alphaIm1) * cSrc[2] + + alphaIm1 * cBlend[2]) / 255) / + alphaI]; + cResult3 = + state->cmykTransferK[((alphaI - aSrc) * cDest[3] + + aSrc * ((255 - alphaIm1) * cSrc[3] + + alphaIm1 * cBlend[3]) / 255) / + alphaI]; + } + break; #endif - } + } - //----- write destination pixel + //----- write destination pixel - switch (bitmap->mode) { - case splashModeMono1: - if (state->screen->test(pipe->x, pipe->y, cResult0)) { - *pipe->destColorPtr |= pipe->destColorMask; - } else { - *pipe->destColorPtr &= ~pipe->destColorMask; - } - if (!(pipe->destColorMask >>= 1)) { - pipe->destColorMask = 0x80; - ++pipe->destColorPtr; - } - break; - case splashModeMono8: - *pipe->destColorPtr++ = cResult0; - break; - case splashModeRGB8: - *pipe->destColorPtr++ = cResult0; - *pipe->destColorPtr++ = cResult1; - *pipe->destColorPtr++ = cResult2; - break; - case splashModeBGR8: - *pipe->destColorPtr++ = cResult2; - *pipe->destColorPtr++ = cResult1; - *pipe->destColorPtr++ = cResult0; - break; + switch (bitmap->mode) { + case splashModeMono1: + if (state->screen->test(x, y, cResult0)) { + *destColorPtr |= destColorMask; + } else { + *destColorPtr &= ~destColorMask; + } + destColorPtr += destColorMask & 1; + destColorMask = (destColorMask << 7) | (destColorMask >> 1); + break; + case splashModeMono8: + *destColorPtr++ = cResult0; + break; + case splashModeRGB8: + destColorPtr[0] = cResult0; + destColorPtr[1] = cResult1; + destColorPtr[2] = cResult2; + destColorPtr += 3; + break; + case splashModeBGR8: + destColorPtr[0] = cResult2; + destColorPtr[1] = cResult1; + destColorPtr[2] = cResult0; + destColorPtr += 3; + break; #if SPLASH_CMYK - case splashModeCMYK8: - if (state->overprintMask & 1) { - pipe->destColorPtr[0] = cResult0; - } - if (state->overprintMask & 2) { - pipe->destColorPtr[1] = cResult1; - } - if (state->overprintMask & 4) { - pipe->destColorPtr[2] = cResult2; + case splashModeCMYK8: + destColorPtr[0] = cResult0; + destColorPtr[1] = cResult1; + destColorPtr[2] = cResult2; + destColorPtr[3] = cResult3; + destColorPtr += 4; + break; +#endif } - if (state->overprintMask & 8) { - pipe->destColorPtr[3] = cResult3; + if (destAlphaPtr) { + *destAlphaPtr++ = aResult; } - pipe->destColorPtr += 4; - break; -#endif - } - if (pipe->destAlphaPtr) { - *pipe->destAlphaPtr++ = aResult; - } - } + } // if (noTransparency && !blendFunc) + + cSrcPtr += cSrcStride; + shapePtr2 += shapeStride; + } // for (x ...) - ++pipe->x; + updateModX(lastX); } // special case: // !pipe->pattern && pipe->noTransparency && !state->blendFunc && -// bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr) { -void Splash::pipeRunSimpleMono1(SplashPipe *pipe) { +// bitmap->mode == splashModeMono1 && !bitmap->alpha) { +void Splash::pipeRunSimpleMono1(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar cResult0; + SplashColorPtr destColorPtr; + Guchar destColorMask; + int cSrcStride, x; - //----- write destination pixel - cResult0 = state->grayTransfer[pipe->cSrc[0]]; - if (state->screen->test(pipe->x, pipe->y, cResult0)) { - *pipe->destColorPtr |= pipe->destColorMask; + if (cSrcPtr) { + cSrcStride = 1; } else { - *pipe->destColorPtr &= ~pipe->destColorMask; + cSrcPtr = pipe->cSrcVal; + cSrcStride = 0; } - if (!(pipe->destColorMask >>= 1)) { - pipe->destColorMask = 0x80; - ++pipe->destColorPtr; + if (x0 > x1) { + return; } + updateModX(x0); + updateModX(x1); + updateModY(y); - ++pipe->x; + destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)]; + destColorMask = 0x80 >> (x0 & 7); + + for (x = x0; x <= x1; ++x) { + + //----- write destination pixel + cResult0 = state->grayTransfer[cSrcPtr[0]]; + if (state->screen->test(x, y, cResult0)) { + *destColorPtr |= destColorMask; + } else { + *destColorPtr &= ~destColorMask; + } + destColorPtr += destColorMask & 1; + destColorMask = (destColorMask << 7) | (destColorMask >> 1); + + cSrcPtr += cSrcStride; + } } // 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; +// bitmap->mode == splashModeMono8 && bitmap->alpha) { +void Splash::pipeRunSimpleMono8(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr) { + SplashColorPtr destColorPtr; + Guchar *destAlphaPtr; + int cSrcStride, x; + + if (cSrcPtr) { + cSrcStride = 1; + } else { + cSrcPtr = pipe->cSrcVal; + cSrcStride = 0; + } + if (x0 > x1) { + return; + } + updateModX(x0); + updateModX(x1); + updateModY(y); + + destColorPtr = &bitmap->data[y * bitmap->rowSize + x0]; + destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0]; + + for (x = x0; x <= x1; ++x) { + + //----- write destination pixel + *destColorPtr++ = state->grayTransfer[cSrcPtr[0]]; + *destAlphaPtr++ = 255; - ++pipe->x; + cSrcPtr += cSrcStride; + } } // 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; +// bitmap->mode == splashModeRGB8 && bitmap->alpha) { +void Splash::pipeRunSimpleRGB8(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr) { + SplashColorPtr destColorPtr; + Guchar *destAlphaPtr; + int cSrcStride, x; + + if (cSrcPtr) { + cSrcStride = 3; + } else { + cSrcPtr = pipe->cSrcVal; + cSrcStride = 0; + } + if (x0 > x1) { + return; + } + updateModX(x0); + updateModX(x1); + updateModY(y); + + destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; + destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0]; + + for (x = x0; x <= x1; ++x) { + + //----- write destination pixel + destColorPtr[0] = state->rgbTransferR[cSrcPtr[0]]; + destColorPtr[1] = state->rgbTransferG[cSrcPtr[1]]; + destColorPtr[2] = state->rgbTransferB[cSrcPtr[2]]; + destColorPtr += 3; + *destAlphaPtr++ = 255; + + cSrcPtr += cSrcStride; + } } // 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; +// bitmap->mode == splashModeBGR8 && bitmap->alpha) { +void Splash::pipeRunSimpleBGR8(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr) { + SplashColorPtr destColorPtr; + Guchar *destAlphaPtr; + int cSrcStride, x; + + if (cSrcPtr) { + cSrcStride = 3; + } else { + cSrcPtr = pipe->cSrcVal; + cSrcStride = 0; + } + if (x0 > x1) { + return; + } + updateModX(x0); + updateModX(x1); + updateModY(y); + + destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; + destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0]; + + for (x = x0; x <= x1; ++x) { + + //----- write destination pixel + destColorPtr[0] = state->rgbTransferB[cSrcPtr[2]]; + destColorPtr[1] = state->rgbTransferG[cSrcPtr[1]]; + destColorPtr[2] = state->rgbTransferR[cSrcPtr[0]]; + destColorPtr += 3; + *destAlphaPtr++ = 255; + + cSrcPtr += cSrcStride; + } } #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]]; +// bitmap->mode == splashModeCMYK8 && bitmap->alpha) { +void Splash::pipeRunSimpleCMYK8(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr) { + SplashColorPtr destColorPtr; + Guchar *destAlphaPtr; + int cSrcStride, x; + + if (cSrcPtr) { + cSrcStride = 4; + } else { + cSrcPtr = pipe->cSrcVal; + cSrcStride = 0; } - if (state->overprintMask & 8) { - pipe->destColorPtr[3] = state->cmykTransferK[pipe->cSrc[3]]; + if (x0 > x1) { + return; } - pipe->destColorPtr += 4; - *pipe->destAlphaPtr++ = 255; + updateModX(x0); + updateModX(x1); + updateModY(y); + + destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0]; + destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0]; + + for (x = x0; x <= x1; ++x) { + + //----- write destination pixel + destColorPtr[0] = state->cmykTransferC[cSrcPtr[0]]; + destColorPtr[1] = state->cmykTransferM[cSrcPtr[1]]; + destColorPtr[2] = state->cmykTransferY[cSrcPtr[2]]; + destColorPtr[3] = state->cmykTransferK[cSrcPtr[3]]; + destColorPtr += 4; + *destAlphaPtr++ = 255; - ++pipe->x; + cSrcPtr += cSrcStride; + } } #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; +// !pipe->pattern && pipe->shapeOnly && !state->blendFunc && +// bitmap->mode == splashModeMono1 && !bitmap->alpha +void Splash::pipeRunShapeMono1(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr) { + Guchar shape, aSrc, cDest0, cResult0; + SplashColorPtr destColorPtr; + Guchar destColorMask; + int cSrcStride, x, lastX; + + if (cSrcPtr) { + cSrcStride = 1; + } else { + cSrcPtr = pipe->cSrcVal; + cSrcStride = 0; + } + for (; x0 <= x1; ++x0) { + if (*shapePtr) { + break; + } + cSrcPtr += cSrcStride; + ++shapePtr; + } + if (x0 > x1) { + return; + } + updateModX(x0); + updateModY(y); + lastX = x0; - //----- read destination pixel - cDest[0] = (*pipe->destColorPtr & pipe->destColorMask) ? 0xff : 0x00; + destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)]; + destColorMask = 0x80 >> (x0 & 7); - //----- source alpha - aSrc = div255(pipe->aInput * pipe->shape); + for (x = x0; x <= x1; ++x) { - //----- result color - // note: aDest = alpha2 = aResult = 0xff - cResult0 = state->grayTransfer[(Guchar)div255((0xff - aSrc) * cDest[0] + - aSrc * pipe->cSrc[0])]; + //----- shape + shape = *shapePtr; + if (!shape) { + destColorPtr += destColorMask & 1; + destColorMask = (destColorMask << 7) | (destColorMask >> 1); + cSrcPtr += cSrcStride; + ++shapePtr; + continue; + } + lastX = x; - //----- 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; + //----- read destination pixel + cDest0 = (*destColorPtr & destColorMask) ? 0xff : 0x00; + + //----- source alpha + aSrc = shape; + + //----- result color + // note: aDest = alphaI = aResult = 0xff + cResult0 = state->grayTransfer[(Guchar)div255((0xff - aSrc) * cDest0 + + aSrc * cSrcPtr[0])]; + + //----- write destination pixel + if (state->screen->test(x, y, cResult0)) { + *destColorPtr |= destColorMask; + } else { + *destColorPtr &= ~destColorMask; + } + destColorPtr += destColorMask & 1; + destColorMask = (destColorMask << 7) | (destColorMask >> 1); + + cSrcPtr += cSrcStride; + ++shapePtr; } - ++pipe->x; + updateModX(lastX); } // 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; +// !pipe->pattern && pipe->shapeOnly && !state->blendFunc && +// bitmap->mode == splashModeMono8 && bitmap->alpha +void Splash::pipeRunShapeMono8(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr) { + Guchar shape, aSrc, aDest, alphaI, aResult, cDest0, cResult0; + SplashColorPtr destColorPtr; + Guchar *destAlphaPtr; + int cSrcStride, x, lastX; - //----- read destination pixel - cDest[0] = *pipe->destColorPtr; - aDest = *pipe->destAlphaPtr; + if (cSrcPtr) { + cSrcStride = 1; + } else { + cSrcPtr = pipe->cSrcVal; + cSrcStride = 0; + } + for (; x0 <= x1; ++x0) { + if (*shapePtr) { + break; + } + cSrcPtr += cSrcStride; + ++shapePtr; + } + if (x0 > x1) { + return; + } + updateModX(x0); + updateModY(y); + lastX = x0; - //----- source alpha - aSrc = div255(pipe->aInput * pipe->shape); + destColorPtr = &bitmap->data[y * bitmap->rowSize + x0]; + destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0]; - //----- result alpha and non-isolated group element correction - aResult = aSrc + aDest - div255(aSrc * aDest); - alpha2 = aResult; + for (x = x0; x <= x1; ++x) { - //----- result color - if (alpha2 == 0) { - cResult0 = 0; - } else { - cResult0 = state->grayTransfer[(Guchar)(((alpha2 - aSrc) * cDest[0] + - aSrc * pipe->cSrc[0]) / alpha2)]; - } + //----- shape + shape = *shapePtr; + if (!shape) { + ++destColorPtr; + ++destAlphaPtr; + cSrcPtr += cSrcStride; + ++shapePtr; + continue; + } + lastX = x; - //----- write destination pixel - *pipe->destColorPtr++ = cResult0; - *pipe->destAlphaPtr++ = aResult; + //----- read destination pixel + cDest0 = *destColorPtr; + aDest = *destAlphaPtr; + + //----- source alpha + aSrc = shape; + + //----- result alpha and non-isolated group element correction + aResult = aSrc + aDest - div255(aSrc * aDest); + alphaI = aResult; - ++pipe->x; + //----- result color + if (alphaI == 0) { + cResult0 = 0; + } else { + cResult0 = state->grayTransfer[(Guchar)(((alphaI - aSrc) * cDest0 + + aSrc * cSrcPtr[0]) / alphaI)]; + } + + //----- write destination pixel + *destColorPtr++ = cResult0; + *destAlphaPtr++ = aResult; + + cSrcPtr += cSrcStride; + ++shapePtr; + } + + updateModX(lastX); } // 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; +// !pipe->pattern && pipe->shapeOnly && !state->blendFunc && +// bitmap->mode == splashModeRGB8 && bitmap->alpha +void Splash::pipeRunShapeRGB8(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr) { + Guchar shape, aSrc, aDest, alphaI, aResult; + Guchar cDest0, cDest1, cDest2; Guchar cResult0, cResult1, cResult2; + SplashColorPtr destColorPtr; + Guchar *destAlphaPtr; + int cSrcStride, x, lastX; + + if (cSrcPtr) { + cSrcStride = 3; + } else { + cSrcPtr = pipe->cSrcVal; + cSrcStride = 0; + } + for (; x0 <= x1; ++x0) { + if (*shapePtr) { + break; + } + cSrcPtr += cSrcStride; + ++shapePtr; + } + if (x0 > x1) { + return; + } + updateModX(x0); + updateModY(y); + lastX = x0; - //----- read destination pixel - cDest[0] = pipe->destColorPtr[0]; - cDest[1] = pipe->destColorPtr[1]; - cDest[2] = pipe->destColorPtr[2]; - aDest = *pipe->destAlphaPtr; + destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; + destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0]; - //----- source alpha - aSrc = div255(pipe->aInput * pipe->shape); + for (x = x0; x <= x1; ++x) { - //----- result alpha and non-isolated group element correction - aResult = aSrc + aDest - div255(aSrc * aDest); - alpha2 = aResult; + //----- shape + shape = *shapePtr; + if (!shape) { + destColorPtr += 3; + ++destAlphaPtr; + cSrcPtr += cSrcStride; + ++shapePtr; + continue; + } + lastX = x; - //----- 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)]; - } + //----- read destination pixel + cDest0 = destColorPtr[0]; + cDest1 = destColorPtr[1]; + cDest2 = destColorPtr[2]; + aDest = *destAlphaPtr; - //----- write destination pixel - *pipe->destColorPtr++ = cResult0; - *pipe->destColorPtr++ = cResult1; - *pipe->destColorPtr++ = cResult2; - *pipe->destAlphaPtr++ = aResult; + //----- source alpha + aSrc = shape; - ++pipe->x; + //----- result alpha and non-isolated group element correction + aResult = aSrc + aDest - div255(aSrc * aDest); + alphaI = aResult; + + //----- result color + if (alphaI == 0) { + cResult0 = 0; + cResult1 = 0; + cResult2 = 0; + } else { + cResult0 = state->rgbTransferR[(Guchar)(((alphaI - aSrc) * cDest0 + + aSrc * cSrcPtr[0]) / alphaI)]; + cResult1 = state->rgbTransferG[(Guchar)(((alphaI - aSrc) * cDest1 + + aSrc * cSrcPtr[1]) / alphaI)]; + cResult2 = state->rgbTransferB[(Guchar)(((alphaI - aSrc) * cDest2 + + aSrc * cSrcPtr[2]) / alphaI)]; + } + + //----- write destination pixel + destColorPtr[0] = cResult0; + destColorPtr[1] = cResult1; + destColorPtr[2] = cResult2; + destColorPtr += 3; + *destAlphaPtr++ = aResult; + + cSrcPtr += cSrcStride; + ++shapePtr; + } + + updateModX(lastX); } // 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; +// !pipe->pattern && pipe->shapeOnly && !state->blendFunc && +// bitmap->mode == splashModeBGR8 && bitmap->alpha +void Splash::pipeRunShapeBGR8(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr) { + Guchar shape, aSrc, aDest, alphaI, aResult; + Guchar cDest0, cDest1, cDest2; Guchar cResult0, cResult1, cResult2; + SplashColorPtr destColorPtr; + Guchar *destAlphaPtr; + int cSrcStride, x, lastX; - //----- read destination pixel - cDest[0] = pipe->destColorPtr[2]; - cDest[1] = pipe->destColorPtr[1]; - cDest[2] = pipe->destColorPtr[0]; - aDest = *pipe->destAlphaPtr; + if (cSrcPtr) { + cSrcStride = 3; + } else { + cSrcPtr = pipe->cSrcVal; + cSrcStride = 0; + } + for (; x0 <= x1; ++x0) { + if (*shapePtr) { + break; + } + cSrcPtr += cSrcStride; + ++shapePtr; + } + if (x0 > x1) { + return; + } + updateModX(x0); + updateModY(y); + lastX = x0; - //----- source alpha - aSrc = div255(pipe->aInput * pipe->shape); + destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; + destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0]; - //----- result alpha and non-isolated group element correction - aResult = aSrc + aDest - div255(aSrc * aDest); - alpha2 = aResult; + for (x = x0; x <= x1; ++x) { - //----- 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)]; - } + //----- shape + shape = *shapePtr; + if (!shape) { + destColorPtr += 3; + ++destAlphaPtr; + cSrcPtr += cSrcStride; + ++shapePtr; + continue; + } + lastX = x; + + //----- read destination pixel + cDest0 = destColorPtr[2]; + cDest1 = destColorPtr[1]; + cDest2 = destColorPtr[0]; + aDest = *destAlphaPtr; - //----- write destination pixel - *pipe->destColorPtr++ = cResult2; - *pipe->destColorPtr++ = cResult1; - *pipe->destColorPtr++ = cResult0; - *pipe->destAlphaPtr++ = aResult; + //----- source alpha + aSrc = shape; + + //----- result alpha and non-isolated group element correction + aResult = aSrc + aDest - div255(aSrc * aDest); + alphaI = aResult; - ++pipe->x; + //----- result color + if (alphaI == 0) { + cResult0 = 0; + cResult1 = 0; + cResult2 = 0; + } else { + cResult0 = state->rgbTransferR[(Guchar)(((alphaI - aSrc) * cDest0 + + aSrc * cSrcPtr[0]) / alphaI)]; + cResult1 = state->rgbTransferG[(Guchar)(((alphaI - aSrc) * cDest1 + + aSrc * cSrcPtr[1]) / alphaI)]; + cResult2 = state->rgbTransferB[(Guchar)(((alphaI - aSrc) * cDest2 + + aSrc * cSrcPtr[2]) / alphaI)]; + } + + //----- write destination pixel + destColorPtr[0] = cResult2; + destColorPtr[1] = cResult1; + destColorPtr[2] = cResult0; + destColorPtr += 3; + *destAlphaPtr++ = aResult; + + cSrcPtr += cSrcStride; + ++shapePtr; + } + + updateModX(lastX); } #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; +// !pipe->pattern && pipe->shapeOnly && !state->blendFunc && +// bitmap->mode == splashModeCMYK8 && bitmap->alpha +void Splash::pipeRunShapeCMYK8(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr) { + Guchar shape, aSrc, aDest, alphaI, aResult; + Guchar cSrc0, cSrc1, cSrc2, cSrc3; + Guchar cDest0, cDest1, cDest2, cDest3; Guchar cResult0, cResult1, cResult2, cResult3; + SplashColorPtr destColorPtr; + Guchar *destAlphaPtr; + int cSrcStride, x, lastX; - //----- 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; + if (cSrcPtr) { + cSrcStride = 4; } 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)]; + cSrcPtr = pipe->cSrcVal; + cSrcStride = 0; } - - //----- write destination pixel - if (state->overprintMask & 1) { - pipe->destColorPtr[0] = cResult0; - } - if (state->overprintMask & 2) { - pipe->destColorPtr[1] = cResult1; + for (; x0 <= x1; ++x0) { + if (*shapePtr) { + break; + } + cSrcPtr += cSrcStride; + ++shapePtr; } - if (state->overprintMask & 4) { - pipe->destColorPtr[2] = cResult2; + if (x0 > x1) { + return; } - if (state->overprintMask & 8) { - pipe->destColorPtr[3] = cResult3; + updateModX(x0); + updateModY(y); + lastX = x0; + + destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0]; + destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0]; + + for (x = x0; x <= x1; ++x) { + + //----- shape + shape = *shapePtr; + if (!shape) { + destColorPtr += 4; + ++destAlphaPtr; + cSrcPtr += cSrcStride; + ++shapePtr; + continue; + } + lastX = x; + + //----- read destination pixel + cDest0 = destColorPtr[0]; + cDest1 = destColorPtr[1]; + cDest2 = destColorPtr[2]; + cDest3 = destColorPtr[3]; + aDest = *destAlphaPtr; + + //----- overprint + if (state->overprintMask & 1) { + cSrc0 = cSrcPtr[0]; + } else { + cSrc0 = div255(aDest * cDest0); + } + if (state->overprintMask & 2) { + cSrc1 = cSrcPtr[1]; + } else { + cSrc1 = div255(aDest * cDest1); + } + if (state->overprintMask & 4) { + cSrc2 = cSrcPtr[2]; + } else { + cSrc2 = div255(aDest * cDest2); + } + if (state->overprintMask & 8) { + cSrc3 = cSrcPtr[3]; + } else { + cSrc3 = div255(aDest * cDest3); + } + + //----- source alpha + aSrc = shape; + + //----- result alpha and non-isolated group element correction + aResult = aSrc + aDest - div255(aSrc * aDest); + alphaI = aResult; + + //----- result color + if (alphaI == 0) { + cResult0 = 0; + cResult1 = 0; + cResult2 = 0; + cResult3 = 0; + } else { + cResult0 = state->cmykTransferC[(Guchar)(((alphaI - aSrc) * cDest0 + + aSrc * cSrc0) / alphaI)]; + cResult1 = state->cmykTransferM[(Guchar)(((alphaI - aSrc) * cDest1 + + aSrc * cSrc1) / alphaI)]; + cResult2 = state->cmykTransferY[(Guchar)(((alphaI - aSrc) * cDest2 + + aSrc * cSrc2) / alphaI)]; + cResult3 = state->cmykTransferK[(Guchar)(((alphaI - aSrc) * cDest3 + + aSrc * cSrc3) / alphaI)]; + } + + //----- write destination pixel + destColorPtr[0] = cResult0; + destColorPtr[1] = cResult1; + destColorPtr[2] = cResult2; + destColorPtr[3] = cResult3; + destColorPtr += 4; + *destAlphaPtr++ = aResult; + + cSrcPtr += cSrcStride; + ++shapePtr; } - pipe->destColorPtr += 4; - *pipe->destAlphaPtr++ = aResult; - ++pipe->x; + updateModX(lastX); } #endif -inline void Splash::pipeSetXY(SplashPipe *pipe, int x, int y) { - pipe->x = x; - pipe->y = y; - if (state->softMask) { - pipe->softMaskPtr = - &state->softMask->data[y * state->softMask->rowSize + x]; +// special case: +// !pipe->pattern && !pipe->noTransparency && !state->softMask && +// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && +// !pipe->nonIsolatedGroup && +// bitmap->mode == splashModeMono1 && !bitmap->alpha +void Splash::pipeRunAAMono1(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr) { + Guchar shape, aSrc, cDest0, cResult0; + SplashColorPtr destColorPtr; + Guchar destColorMask; + int cSrcStride, x, lastX; + + if (cSrcPtr) { + cSrcStride = 1; + } else { + cSrcPtr = pipe->cSrcVal; + cSrcStride = 0; } - switch (bitmap->mode) { - case splashModeMono1: - pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + (x >> 3)]; - pipe->destColorMask = 0x80 >> (x & 7); - break; - case splashModeMono8: - pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + x]; - break; - case splashModeRGB8: - case splashModeBGR8: - pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x]; - break; -#if SPLASH_CMYK - case splashModeCMYK8: - pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x]; - break; -#endif + for (; x0 <= x1; ++x0) { + if (*shapePtr) { + break; + } + cSrcPtr += cSrcStride; + ++shapePtr; } - if (bitmap->alpha) { - pipe->destAlphaPtr = &bitmap->alpha[y * bitmap->width + x]; - } else { - pipe->destAlphaPtr = NULL; + if (x0 > x1) { + return; } - if (state->inNonIsolatedGroup && alpha0Bitmap->alpha) { - pipe->alpha0Ptr = - &alpha0Bitmap->alpha[(alpha0Y + y) * alpha0Bitmap->width + - (alpha0X + x)]; - } else { - pipe->alpha0Ptr = NULL; + updateModX(x0); + updateModY(y); + lastX = x0; + + destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)]; + destColorMask = 0x80 >> (x0 & 7); + + for (x = x0; x <= x1; ++x) { + + //----- shape + shape = *shapePtr; + if (!shape) { + destColorPtr += destColorMask & 1; + destColorMask = (destColorMask << 7) | (destColorMask >> 1); + cSrcPtr += cSrcStride; + ++shapePtr; + continue; + } + lastX = x; + + //----- read destination pixel + cDest0 = (*destColorPtr & destColorMask) ? 0xff : 0x00; + + //----- source alpha + aSrc = div255(pipe->aInput * shape); + + //----- result color + // note: aDest = alphaI = aResult = 0xff + cResult0 = state->grayTransfer[(Guchar)div255((0xff - aSrc) * cDest0 + + aSrc * cSrcPtr[0])]; + + //----- write destination pixel + if (state->screen->test(x, y, cResult0)) { + *destColorPtr |= destColorMask; + } else { + *destColorPtr &= ~destColorMask; + } + destColorPtr += destColorMask & 1; + destColorMask = (destColorMask << 7) | (destColorMask >> 1); + + cSrcPtr += cSrcStride; + ++shapePtr; } + + updateModX(lastX); } -inline void Splash::pipeIncX(SplashPipe *pipe) { - ++pipe->x; - if (state->softMask) { - ++pipe->softMaskPtr; +// special case: +// !pipe->pattern && !pipe->noTransparency && !state->softMask && +// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && +// !pipe->nonIsolatedGroup && +// bitmap->mode == splashModeMono8 && bitmap->alpha +void Splash::pipeRunAAMono8(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr) { + Guchar shape, aSrc, aDest, alphaI, aResult, cDest0, cResult0; + SplashColorPtr destColorPtr; + Guchar *destAlphaPtr; + int cSrcStride, x, lastX; + + if (cSrcPtr) { + cSrcStride = 1; + } else { + cSrcPtr = pipe->cSrcVal; + cSrcStride = 0; } - switch (bitmap->mode) { - case splashModeMono1: - if (!(pipe->destColorMask >>= 1)) { - pipe->destColorMask = 0x80; - ++pipe->destColorPtr; + for (; x0 <= x1; ++x0) { + if (*shapePtr) { + break; } - break; - case splashModeMono8: - ++pipe->destColorPtr; - break; - case splashModeRGB8: - case splashModeBGR8: - pipe->destColorPtr += 3; - break; -#if SPLASH_CMYK - case splashModeCMYK8: - pipe->destColorPtr += 4; - break; -#endif - } - if (pipe->destAlphaPtr) { - ++pipe->destAlphaPtr; + cSrcPtr += cSrcStride; + ++shapePtr; } - if (pipe->alpha0Ptr) { - ++pipe->alpha0Ptr; + if (x0 > x1) { + return; } -} + updateModX(x0); + updateModY(y); + lastX = x0; + + destColorPtr = &bitmap->data[y * bitmap->rowSize + x0]; + destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0]; + + for (x = x0; x <= x1; ++x) { + + //----- shape + shape = *shapePtr; + if (!shape) { + ++destColorPtr; + ++destAlphaPtr; + cSrcPtr += cSrcStride; + ++shapePtr; + continue; + } + lastX = x; + + //----- read destination pixel + cDest0 = *destColorPtr; + aDest = *destAlphaPtr; + + //----- source alpha + aSrc = div255(pipe->aInput * shape); -inline void Splash::drawPixel(SplashPipe *pipe, int x, int y, GBool noClip) { - if (noClip || state->clip->test(x, y)) { - pipeSetXY(pipe, x, y); - (this->*pipe->run)(pipe); - updateModX(x); - updateModY(y); + //----- result alpha and non-isolated group element correction + aResult = aSrc + aDest - div255(aSrc * aDest); + alphaI = aResult; + + //----- result color + if (alphaI == 0) { + cResult0 = 0; + } else { + cResult0 = state->grayTransfer[(Guchar)(((alphaI - aSrc) * cDest0 + + aSrc * cSrcPtr[0]) / alphaI)]; + } + + //----- write destination pixel + *destColorPtr++ = cResult0; + *destAlphaPtr++ = aResult; + + cSrcPtr += cSrcStride; + ++shapePtr; } -} -inline void Splash::drawAAPixelInit() { - aaBufY = -1; + updateModX(lastX); } -inline void Splash::drawAAPixel(SplashPipe *pipe, int x, int y) { -#if splashAASize == 4 - static int bitCount4[16] = { 0, 1, 1, 2, 1, 2, 2, 3, - 1, 2, 2, 3, 2, 3, 3, 4 }; - int w; -#else - int xx, yy; -#endif - SplashColorPtr p; - int x0, x1, t; +// special case: +// !pipe->pattern && !pipe->noTransparency && !state->softMask && +// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && +// !pipe->nonIsolatedGroup && +// bitmap->mode == splashModeRGB8 && bitmap->alpha +void Splash::pipeRunAARGB8(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr) { + Guchar shape, aSrc, aDest, alphaI, aResult; + Guchar cDest0, cDest1, cDest2; + Guchar cResult0, cResult1, cResult2; + SplashColorPtr destColorPtr; + Guchar *destAlphaPtr; + int cSrcStride, x, lastX; - if (x < 0 || x >= bitmap->width || - y < state->clip->getYMinI() || y > state->clip->getYMaxI()) { + if (cSrcPtr) { + cSrcStride = 3; + } else { + cSrcPtr = pipe->cSrcVal; + cSrcStride = 0; + } + for (; x0 <= x1; ++x0) { + if (*shapePtr) { + break; + } + cSrcPtr += cSrcStride; + ++shapePtr; + } + if (x0 > x1) { return; } + updateModX(x0); + updateModY(y); + lastX = x0; - // update aaBuf - if (y != aaBufY) { - memset(aaBuf->getDataPtr(), 0xff, - aaBuf->getRowSize() * aaBuf->getHeight()); - x0 = 0; - x1 = bitmap->width - 1; - state->clip->clipAALine(aaBuf, &x0, &x1, y); - aaBufY = y; - } - - // compute the shape value -#if splashAASize == 4 - p = aaBuf->getDataPtr() + (x >> 1); - w = aaBuf->getRowSize(); - if (x & 1) { - t = bitCount4[*p & 0x0f] + bitCount4[p[w] & 0x0f] + - bitCount4[p[2*w] & 0x0f] + bitCount4[p[3*w] & 0x0f]; + destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; + destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0]; + + for (x = x0; x <= x1; ++x) { + + //----- shape + shape = *shapePtr; + if (!shape) { + destColorPtr += 3; + ++destAlphaPtr; + cSrcPtr += cSrcStride; + ++shapePtr; + continue; + } + lastX = x; + + //----- read destination pixel + cDest0 = destColorPtr[0]; + cDest1 = destColorPtr[1]; + cDest2 = destColorPtr[2]; + aDest = *destAlphaPtr; + + //----- source alpha + aSrc = div255(pipe->aInput * shape); + + //----- result alpha and non-isolated group element correction + aResult = aSrc + aDest - div255(aSrc * aDest); + alphaI = aResult; + + //----- result color + if (alphaI == 0) { + cResult0 = 0; + cResult1 = 0; + cResult2 = 0; + } else { + cResult0 = state->rgbTransferR[(Guchar)(((alphaI - aSrc) * cDest0 + + aSrc * cSrcPtr[0]) / alphaI)]; + cResult1 = state->rgbTransferG[(Guchar)(((alphaI - aSrc) * cDest1 + + aSrc * cSrcPtr[1]) / alphaI)]; + cResult2 = state->rgbTransferB[(Guchar)(((alphaI - aSrc) * cDest2 + + aSrc * cSrcPtr[2]) / alphaI)]; + } + + //----- write destination pixel + destColorPtr[0] = cResult0; + destColorPtr[1] = cResult1; + destColorPtr[2] = cResult2; + destColorPtr += 3; + *destAlphaPtr++ = aResult; + + cSrcPtr += cSrcStride; + ++shapePtr; + } + + updateModX(lastX); +} + +// special case: +// !pipe->pattern && !pipe->noTransparency && !state->softMask && +// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && +// !pipe->nonIsolatedGroup && +// bitmap->mode == splashModeBGR8 && bitmap->alpha +void Splash::pipeRunAABGR8(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr) { + Guchar shape, aSrc, aDest, alphaI, aResult; + Guchar cDest0, cDest1, cDest2; + Guchar cResult0, cResult1, cResult2; + SplashColorPtr destColorPtr; + Guchar *destAlphaPtr; + int cSrcStride, x, lastX; + + if (cSrcPtr) { + cSrcStride = 3; } else { - t = bitCount4[*p >> 4] + bitCount4[p[w] >> 4] + - bitCount4[p[2*w] >> 4] + bitCount4[p[3*w] >> 4]; + cSrcPtr = pipe->cSrcVal; + cSrcStride = 0; } -#else - t = 0; - for (yy = 0; yy < splashAASize; ++yy) { - for (xx = 0; xx < splashAASize; ++xx) { - p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + - ((x * splashAASize + xx) >> 3); - t += (*p >> (7 - ((x * splashAASize + xx) & 7))) & 1; + for (; x0 <= x1; ++x0) { + if (*shapePtr) { + break; } + cSrcPtr += cSrcStride; + ++shapePtr; } -#endif + if (x0 > x1) { + return; + } + updateModX(x0); + updateModY(y); + lastX = x0; + + destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; + destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0]; + + for (x = x0; x <= x1; ++x) { + + //----- shape + shape = *shapePtr; + if (!shape) { + destColorPtr += 3; + ++destAlphaPtr; + cSrcPtr += cSrcStride; + ++shapePtr; + continue; + } + lastX = x; + + //----- read destination pixel + cDest0 = destColorPtr[2]; + cDest1 = destColorPtr[1]; + cDest2 = destColorPtr[0]; + aDest = *destAlphaPtr; + + //----- source alpha + aSrc = div255(pipe->aInput * shape); + + //----- result alpha and non-isolated group element correction + aResult = aSrc + aDest - div255(aSrc * aDest); + alphaI = aResult; + + //----- result color + if (alphaI == 0) { + cResult0 = 0; + cResult1 = 0; + cResult2 = 0; + } else { + cResult0 = state->rgbTransferR[(Guchar)(((alphaI - aSrc) * cDest0 + + aSrc * cSrcPtr[0]) / alphaI)]; + cResult1 = state->rgbTransferG[(Guchar)(((alphaI - aSrc) * cDest1 + + aSrc * cSrcPtr[1]) / alphaI)]; + cResult2 = state->rgbTransferB[(Guchar)(((alphaI - aSrc) * cDest2 + + aSrc * cSrcPtr[2]) / alphaI)]; + } - // draw the pixel - if (t != 0) { - pipeSetXY(pipe, x, y); - pipe->shape = div255(aaGamma[t] * pipe->shape); - (this->*pipe->run)(pipe); - updateModX(x); - updateModY(y); + //----- write destination pixel + destColorPtr[0] = cResult2; + destColorPtr[1] = cResult1; + destColorPtr[2] = cResult0; + destColorPtr += 3; + *destAlphaPtr++ = aResult; + + cSrcPtr += cSrcStride; + ++shapePtr; } + + updateModX(lastX); } -inline void Splash::drawSpan(SplashPipe *pipe, int x0, int x1, int y, - GBool noClip) { - int x; +#if SPLASH_CMYK +// special case: +// !pipe->pattern && !pipe->noTransparency && !state->softMask && +// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && +// !pipe->nonIsolatedGroup && +// bitmap->mode == splashModeCMYK8 && bitmap->alpha +void Splash::pipeRunAACMYK8(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr) { + Guchar shape, aSrc, aDest, alphaI, aResult; + Guchar cSrc0, cSrc1, cSrc2, cSrc3; + Guchar cDest0, cDest1, cDest2, cDest3; + Guchar cResult0, cResult1, cResult2, cResult3; + SplashColorPtr destColorPtr; + Guchar *destAlphaPtr; + int cSrcStride, x, lastX; - if (noClip) { - pipeSetXY(pipe, x0, y); - for (x = x0; x <= x1; ++x) { - (this->*pipe->run)(pipe); - } - updateModX(x0); - updateModX(x1); - updateModY(y); + if (cSrcPtr) { + cSrcStride = 4; } else { - if (x0 < state->clip->getXMinI()) { - x0 = state->clip->getXMinI(); - } - if (x1 > state->clip->getXMaxI()) { - x1 = state->clip->getXMaxI(); - } - pipeSetXY(pipe, x0, y); - for (x = x0; x <= x1; ++x) { - if (state->clip->test(x, y)) { - (this->*pipe->run)(pipe); - updateModX(x); - updateModY(y); - } else { - pipeIncX(pipe); - } + cSrcPtr = pipe->cSrcVal; + cSrcStride = 0; + } + for (; x0 <= x1; ++x0) { + if (*shapePtr) { + break; } + cSrcPtr += cSrcStride; + ++shapePtr; } -} + if (x0 > x1) { + return; + } + updateModX(x0); + updateModY(y); + lastX = x0; -inline void Splash::drawAALine(SplashPipe *pipe, int x0, int x1, int y) { -#if splashAASize == 4 - static int bitCount4[16] = { 0, 1, 1, 2, 1, 2, 2, 3, - 1, 2, 2, 3, 2, 3, 3, 4 }; - SplashColorPtr p0, p1, p2, p3; - int t; -#else - SplashColorPtr p; - int xx, yy, t; -#endif - int x; + destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0]; + destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0]; -#if splashAASize == 4 - p0 = aaBuf->getDataPtr() + (x0 >> 1); - p1 = p0 + aaBuf->getRowSize(); - p2 = p1 + aaBuf->getRowSize(); - p3 = p2 + aaBuf->getRowSize(); -#endif - pipeSetXY(pipe, x0, y); for (x = x0; x <= x1; ++x) { - // compute the shape value -#if splashAASize == 4 - if (x & 1) { - t = bitCount4[*p0 & 0x0f] + bitCount4[*p1 & 0x0f] + - bitCount4[*p2 & 0x0f] + bitCount4[*p3 & 0x0f]; - ++p0; ++p1; ++p2; ++p3; + //----- shape + shape = *shapePtr; + if (!shape) { + destColorPtr += 4; + ++destAlphaPtr; + cSrcPtr += cSrcStride; + ++shapePtr; + continue; + } + lastX = x; + + //----- read destination pixel + cDest0 = destColorPtr[0]; + cDest1 = destColorPtr[1]; + cDest2 = destColorPtr[2]; + cDest3 = destColorPtr[3]; + aDest = *destAlphaPtr; + + //----- overprint + if (state->overprintMask & 1) { + cSrc0 = cSrcPtr[0]; } else { - t = bitCount4[*p0 >> 4] + bitCount4[*p1 >> 4] + - bitCount4[*p2 >> 4] + bitCount4[*p3 >> 4]; + cSrc0 = div255(aDest * cDest0); } -#else - t = 0; - for (yy = 0; yy < splashAASize; ++yy) { - for (xx = 0; xx < splashAASize; ++xx) { - p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + - ((x * splashAASize + xx) >> 3); - t += (*p >> (7 - ((x * splashAASize + xx) & 7))) & 1; - } + if (state->overprintMask & 2) { + cSrc1 = cSrcPtr[1]; + } else { + cSrc1 = div255(aDest * cDest1); } -#endif + if (state->overprintMask & 4) { + cSrc2 = cSrcPtr[2]; + } else { + cSrc2 = div255(aDest * cDest2); + } + if (state->overprintMask & 8) { + cSrc3 = cSrcPtr[3]; + } else { + cSrc3 = div255(aDest * cDest3); + } + + //----- source alpha + aSrc = div255(pipe->aInput * shape); + + //----- result alpha and non-isolated group element correction + aResult = aSrc + aDest - div255(aSrc * aDest); + alphaI = aResult; - if (t != 0) { - pipe->shape = aaGamma[t]; - (this->*pipe->run)(pipe); - updateModX(x); - updateModY(y); + //----- result color + if (alphaI == 0) { + cResult0 = 0; + cResult1 = 0; + cResult2 = 0; + cResult3 = 0; } else { - pipeIncX(pipe); + cResult0 = state->cmykTransferC[(Guchar)(((alphaI - aSrc) * cDest0 + + aSrc * cSrc0) / alphaI)]; + cResult1 = state->cmykTransferM[(Guchar)(((alphaI - aSrc) * cDest1 + + aSrc * cSrc1) / alphaI)]; + cResult2 = state->cmykTransferY[(Guchar)(((alphaI - aSrc) * cDest2 + + aSrc * cSrc2) / alphaI)]; + cResult3 = state->cmykTransferK[(Guchar)(((alphaI - aSrc) * cDest3 + + aSrc * cSrc3) / alphaI)]; } + + //----- write destination pixel + destColorPtr[0] = cResult0; + destColorPtr[1] = cResult1; + destColorPtr[2] = cResult2; + destColorPtr[3] = cResult3; + destColorPtr += 4; + *destAlphaPtr++ = aResult; + + cSrcPtr += cSrcStride; + ++shapePtr; } + + updateModX(lastX); } +#endif + //------------------------------------------------------------------------ @@ -1238,21 +1934,18 @@ Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, int i; bitmap = bitmapA; + bitmapComps = splashColorModeNComps[bitmap->mode]; vectorAntialias = vectorAntialiasA; inShading = gFalse; state = new SplashState(bitmap->width, bitmap->height, vectorAntialias, screenParams); + scanBuf = (Guchar *)gmalloc(bitmap->width); if (vectorAntialias) { - aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize, - 1, splashModeMono1, gFalse); - for (i = 0; i <= splashAASize * splashAASize; ++i) { + for (i = 0; i <= 255; ++i) { aaGamma[i] = (Guchar)splashRound( - splashPow((SplashCoord)i / - (SplashCoord)(splashAASize * splashAASize), - splashAAGamma) * 255); + splashPow((SplashCoord)i / (SplashCoord)255, + splashAAGamma) * 255); } - } else { - aaBuf = NULL; } minLineWidth = 0; clearModRegion(); @@ -1264,21 +1957,18 @@ Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, int i; bitmap = bitmapA; + bitmapComps = splashColorModeNComps[bitmap->mode]; vectorAntialias = vectorAntialiasA; inShading = gFalse; state = new SplashState(bitmap->width, bitmap->height, vectorAntialias, screenA); + scanBuf = (Guchar *)gmalloc(bitmap->width); if (vectorAntialias) { - aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize, - 1, splashModeMono1, gFalse); - for (i = 0; i <= splashAASize * splashAASize; ++i) { + for (i = 0; i <= 255; ++i) { aaGamma[i] = (Guchar)splashRound( - splashPow((SplashCoord)i / - (SplashCoord)(splashAASize * splashAASize), - splashAAGamma) * 255); + splashPow((SplashCoord)i / (SplashCoord)255, + splashAAGamma) * 255); } - } else { - aaBuf = NULL; } minLineWidth = 0; clearModRegion(); @@ -1290,9 +1980,7 @@ Splash::~Splash() { restoreState(); } delete state; - if (vectorAntialias) { - delete aaBuf; - } + gfree(scanBuf); } //------------------------------------------------------------------------ @@ -1375,6 +2063,10 @@ GBool Splash::getInNonIsolatedGroup() { return state->inNonIsolatedGroup; } +GBool Splash::getInKnockoutGroup() { + return state->inKnockoutGroup; +} + //------------------------------------------------------------------------ // state write //------------------------------------------------------------------------ @@ -1442,28 +2134,30 @@ void Splash::setStrokeAdjust(GBool strokeAdjust) { void Splash::clipResetToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1) { - state->clip->resetToRect(x0, y0, x1, y1); + state->clipResetToRect(x0, y0, x1, y1); } SplashError Splash::clipToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1) { - return state->clip->clipToRect(x0, y0, x1, y1); + return state->clipToRect(x0, y0, x1, y1); } SplashError Splash::clipToPath(SplashPath *path, GBool eo) { - return state->clip->clipToPath(path, state->matrix, state->flatness, eo); + return state->clipToPath(path, eo); } void Splash::setSoftMask(SplashBitmap *softMask) { state->setSoftMask(softMask); } -void Splash::setInNonIsolatedGroup(SplashBitmap *alpha0BitmapA, - int alpha0XA, int alpha0YA) { - alpha0Bitmap = alpha0BitmapA; - alpha0X = alpha0XA; - alpha0Y = alpha0YA; - state->inNonIsolatedGroup = gTrue; +void Splash::setInTransparencyGroup(SplashBitmap *groupBackBitmapA, + int groupBackXA, int groupBackYA, + GBool nonIsolated, GBool knockout) { + groupBackBitmap = groupBackBitmapA; + groupBackX = groupBackXA; + groupBackY = groupBackYA; + state->inNonIsolatedGroup = nonIsolated; + state->inKnockoutGroup = knockout; } void Splash::setTransfer(Guchar *red, Guchar *green, Guchar *blue, @@ -1539,9 +2233,9 @@ void Splash::clear(SplashColorPtr color, Guchar alpha) { for (y = 0; y < bitmap->height; ++y) { p = row; for (x = 0; x < bitmap->width; ++x) { - *p++ = color[2]; - *p++ = color[1]; *p++ = color[0]; + *p++ = color[1]; + *p++ = color[2]; } row += bitmap->rowSize; } @@ -1560,9 +2254,9 @@ void Splash::clear(SplashColorPtr color, Guchar alpha) { for (y = 0; y < bitmap->height; ++y) { p = row; for (x = 0; x < bitmap->width; ++x) { - *p++ = color[0]; - *p++ = color[1]; *p++ = color[2]; + *p++ = color[1]; + *p++ = color[0]; } row += bitmap->rowSize; } @@ -1606,7 +2300,7 @@ void Splash::clear(SplashColorPtr color, Guchar alpha) { SplashError Splash::stroke(SplashPath *path) { SplashPath *path2, *dPath; - SplashCoord d1, d2, t1, t2, w; + SplashCoord t0, t1, t2, t3, w, w2; if (debugMode) { printf("stroke [dash:%d] [width:%.2f]:\n", @@ -1628,31 +2322,41 @@ SplashError Splash::stroke(SplashPath *path) { } } - // transform a unit square, and take the half the max of the two - // diagonals; the product of this number and the line width is the - // (approximate) transformed line width - t1 = state->matrix[0] + state->matrix[2]; - t2 = state->matrix[1] + state->matrix[3]; - d1 = t1 * t1 + t2 * t2; - t1 = state->matrix[0] - state->matrix[2]; - t2 = state->matrix[1] - state->matrix[3]; - d2 = t1 * t1 + t2 * t2; - if (d2 > d1) { - d1 = d2; - } - d1 *= 0.5; - if (d1 > 0 && - d1 * state->lineWidth * state->lineWidth < minLineWidth * minLineWidth) { - w = minLineWidth / splashSqrt(d1); - strokeWide(path2, w); + // Compute an approximation of the transformed line width. + // Given a CTM of [m0 m1], + // [m2 m3] + // if |m0|*|m3| >= |m1|*|m2| then use min{|m0|,|m3|}, else + // use min{|m1|,|m2|}. + // This handles the common cases -- [s 0 ] and [0 s] -- + // [0 +/-s] [+/-s 0] + // well, and still does something reasonable for the uncommon + // case transforms. + t0 = splashAbs(state->matrix[0]); + t1 = splashAbs(state->matrix[1]); + t2 = splashAbs(state->matrix[2]); + t3 = splashAbs(state->matrix[3]); + if (t0 * t3 >= t1 * t2) { + w = (t0 < t3) ? t0 : t3; + } else { + w = (t1 < t2) ? t1 : t2; + } + w2 = w * state->lineWidth; + // if there is a min line width set, and the transformed line width + // is smaller, use the min line width + if (w > 0 && w2 < minLineWidth) { + strokeWide(path2, minLineWidth / w); } else if (bitmap->mode == splashModeMono1) { - // this gets close to Adobe's behavior in mono mode - if (d1 <= 2) { + // in monochrome mode, use 0-width lines for any transformed line + // width <= 1 -- lines less than 1 pixel wide look too fat without + // antialiasing + if (w2 < 1.001) { strokeNarrow(path2); } else { strokeWide(path2, state->lineWidth); } } else { + // in gray and color modes, only use 0-width lines if the line + // width is explicitly set to 0 if (state->lineWidth == 0) { strokeNarrow(path2); } else { @@ -1678,9 +2382,9 @@ void Splash::strokeNarrow(SplashPath *path) { xPath = new SplashXPath(path, state->matrix, state->flatness, gFalse); - pipeInit(&pipe, 0, 0, state->strokePattern, NULL, + pipeInit(&pipe, state->strokePattern, (Guchar)splashRound(state->strokeAlpha * 255), - gFalse, gFalse); + gTrue, gFalse); for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) { if (seg->y0 <= seg->y1) { @@ -1695,22 +2399,25 @@ void Splash::strokeNarrow(SplashPath *path) { x1 = splashFloor(seg->x0); } if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0, - x0 <= x1 ? x1 : x0, y1)) + x0 <= x1 ? x1 : x0, y1, + state->strokeAdjust)) != splashClipAllOutside) { if (y0 == y1) { if (x0 <= x1) { - drawSpan(&pipe, x0, x1, y0, clipRes == splashClipAllInside); + drawStrokeSpan(&pipe, x0, x1, y0, clipRes == splashClipAllInside); } else { - drawSpan(&pipe, x1, x0, y0, clipRes == splashClipAllInside); + drawStrokeSpan(&pipe, x1, x0, y0, clipRes == splashClipAllInside); } } else { dxdy = seg->dxdy; - if (y0 < state->clip->getYMinI()) { - y0 = state->clip->getYMinI(); + y = state->clip->getYMinI(state->strokeAdjust); + if (y0 < y) { + y0 = y; x0 = splashFloor(seg->x0 + ((SplashCoord)y0 - seg->y0) * dxdy); } - if (y1 > state->clip->getYMaxI()) { - y1 = state->clip->getYMaxI(); + y = state->clip->getYMaxI(state->strokeAdjust); + if (y1 > y) { + y1 = y; x1 = splashFloor(seg->x0 + ((SplashCoord)y1 - seg->y0) * dxdy); } if (x0 <= x1) { @@ -1723,9 +2430,10 @@ void Splash::strokeNarrow(SplashPath *path) { xb = x1 + 1; } if (xa == xb) { - drawPixel(&pipe, xa, y, clipRes == splashClipAllInside); + drawStrokeSpan(&pipe, xa, xa, y, clipRes == splashClipAllInside); } else { - drawSpan(&pipe, xa, xb - 1, y, clipRes == splashClipAllInside); + drawStrokeSpan(&pipe, xa, xb - 1, y, + clipRes == splashClipAllInside); } xa = xb; } @@ -1739,9 +2447,10 @@ void Splash::strokeNarrow(SplashPath *path) { xb = x1 - 1; } if (xa == xb) { - drawPixel(&pipe, xa, y, clipRes == splashClipAllInside); + drawStrokeSpan(&pipe, xa, xa, y, clipRes == splashClipAllInside); } else { - drawSpan(&pipe, xb + 1, xa, y, clipRes == splashClipAllInside); + drawStrokeSpan(&pipe, xb + 1, xa, y, + clipRes == splashClipAllInside); } xa = xb; } @@ -1762,6 +2471,32 @@ void Splash::strokeNarrow(SplashPath *path) { delete xPath; } +void Splash::drawStrokeSpan(SplashPipe *pipe, int x0, int x1, int y, + GBool noClip) { + int x; + + x = state->clip->getXMinI(state->strokeAdjust); + if (x > x0) { + x0 = x; + } + x = state->clip->getXMaxI(state->strokeAdjust); + if (x < x1) { + x1 = x; + } + if (x0 > x1) { + return; + } + for (x = x0; x <= x1; ++x) { + scanBuf[x] = 0xff; + } + if (!noClip) { + if (!state->clip->clipSpanBinary(scanBuf, y, x0, x1, state->strokeAdjust)) { + return; + } + } + (this->*pipe->run)(pipe, x0, x1, y, scanBuf + x0, NULL); +} + void Splash::strokeWide(SplashPath *path, SplashCoord w) { SplashPath *path2; @@ -2012,10 +2747,11 @@ SplashError Splash::fillWithPattern(SplashPath *path, GBool eo, SplashPattern *pattern, SplashCoord alpha) { SplashPipe pipe; + SplashPath *path2; SplashXPath *xPath; SplashXPathScanner *scanner; - int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y; - SplashClipResult clipRes, clipRes2; + int xMin, yMin, xMax, yMax, x, y, t; + SplashClipResult clipRes; if (path->length == 0) { return splashErrEmptyPath; @@ -2025,85 +2761,68 @@ SplashError Splash::fillWithPattern(SplashPath *path, GBool eo, 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); - } - } + path2 = tweakFillPath(path); - xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue); - if (vectorAntialias && !inShading) { - xPath->aaScale(); - } - xPath->sort(); - yMinI = state->clip->getYMinI(); - yMaxI = state->clip->getYMaxI(); - if (vectorAntialias && !inShading) { - yMinI = yMinI * splashAASize; - yMaxI = (yMaxI + 1) * splashAASize - 1; + xPath = new SplashXPath(path2, state->matrix, state->flatness, gTrue); + if (path2 != path) { + delete path2; } - scanner = new SplashXPathScanner(xPath, eo, yMinI, yMaxI); - - // get the min and max x and y values - if (vectorAntialias && !inShading) { - scanner->getBBoxAA(&xMinI, &yMinI, &xMaxI, &yMaxI); - } else { - scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI); + xMin = xPath->getXMin(); + yMin = xPath->getYMin(); + xMax = xPath->getXMax(); + yMax = xPath->getYMax(); + if (xMin > xMax || yMin > yMax) { + delete xPath; + return splashOk; } + scanner = new SplashXPathScanner(xPath, eo, yMin, yMax); // check clipping - if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI)) + if ((clipRes = state->clip->testRect(xMin, yMin, xMax, yMax, + state->strokeAdjust)) != splashClipAllOutside) { - if (scanner->hasPartialClip()) { - clipRes = splashClipPartial; + + if ((t = state->clip->getXMinI(state->strokeAdjust)) > xMin) { + xMin = t; + } + if ((t = state->clip->getXMaxI(state->strokeAdjust)) < xMax) { + xMax = t; + } + if ((t = state->clip->getYMinI(state->strokeAdjust)) > yMin) { + yMin = t; + } + if ((t = state->clip->getYMaxI(state->strokeAdjust)) < yMax) { + yMax = t; + } + if (xMin > xMax || yMin > yMax) { + delete scanner; + delete xPath; + return splashOk; } - pipeInit(&pipe, 0, yMinI, pattern, NULL, (Guchar)splashRound(alpha * 255), - vectorAntialias && !inShading, gFalse); + pipeInit(&pipe, pattern, (Guchar)splashRound(alpha * 255), + gTrue, gFalse); // draw the spans if (vectorAntialias && !inShading) { - for (y = yMinI; y <= yMaxI; ++y) { - scanner->renderAALine(aaBuf, &x0, &x1, y); + for (y = yMin; y <= yMax; ++y) { + scanner->getSpan(scanBuf, y, xMin, xMax); if (clipRes != splashClipAllInside) { - state->clip->clipAALine(aaBuf, &x0, &x1, y); + state->clip->clipSpan(scanBuf, y, xMin, xMax, state->strokeAdjust); + } + for (x = xMin; x <= xMax; ++x) { + scanBuf[x] = aaGamma[scanBuf[x]]; } - drawAALine(&pipe, x0, x1, y); + (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL); } } else { - for (y = yMinI; y <= yMaxI; ++y) { - while (scanner->getNextSpan(y, &x0, &x1)) { - if (clipRes == splashClipAllInside) { - drawSpan(&pipe, x0, x1, y, gTrue); - } else { - // limit the x range - if (x0 < state->clip->getXMinI()) { - x0 = state->clip->getXMinI(); - } - if (x1 > state->clip->getXMaxI()) { - x1 = state->clip->getXMaxI(); - } - clipRes2 = state->clip->testSpan(x0, x1, y); - drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside); - } + for (y = yMin; y <= yMax; ++y) { + scanner->getSpanBinary(scanBuf, y, xMin, xMax); + if (clipRes != splashClipAllInside) { + state->clip->clipSpanBinary(scanBuf, y, xMin, xMax, + state->strokeAdjust); } + (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL); } } } @@ -2114,6 +2833,110 @@ SplashError Splash::fillWithPattern(SplashPath *path, GBool eo, return splashOk; } +// Applies various tweaks to a fill path: +// (1) add stroke adjust hints to a filled rectangle +// (2) applies a minimum width to a zero-width filled rectangle (so +// stroke adjustment works correctly +// (3) convert a degenerate fill ('moveto lineto fill' and 'moveto +// lineto closepath fill') to a minimum-width filled rectangle +// +// These tweaks only apply to paths with a single subpath. +// +// Returns either the unchanged input path or a new path (in which +// case the returned path must be deleted by the caller). +SplashPath *Splash::tweakFillPath(SplashPath *path) { + SplashPath *path2; + SplashCoord xx0, yy0, xx1, yy1, dx, dy, d, wx, wy, w; + int n; + + if (!state->strokeAdjust || path->hints) { + return path; + } + + n = path->getLength(); + if (!((n == 2) || + (n == 3 && + path->flags[1] == 0) || + (n == 4 && + path->flags[1] == 0 && + path->flags[2] == 0) || + (n == 5 && + path->flags[1] == 0 && + path->flags[2] == 0 && + path->flags[3] == 0))) { + return path; + } + + path2 = path; + + // degenerate fill (2 or 3 points) or rectangle of (nearly) zero + // width --> replace with a min-width rectangle and hint + if (n == 2 || + (n == 3 && (path->flags[0] & splashPathClosed)) || + (n == 3 && (splashAbs(path->pts[0].x - path->pts[2].x) < 0.001 && + splashAbs(path->pts[0].y - path->pts[2].y) < 0.001)) || + ((n == 4 || + (n == 5 && (path->flags[0] & splashPathClosed))) && + ((splashAbs(path->pts[0].x - path->pts[1].x) < 0.001 && + splashAbs(path->pts[0].y - path->pts[1].y) < 0.001 && + splashAbs(path->pts[2].x - path->pts[3].x) < 0.001 && + splashAbs(path->pts[2].y - path->pts[3].y) < 0.001) || + (splashAbs(path->pts[0].x - path->pts[3].x) < 0.001 && + splashAbs(path->pts[0].y - path->pts[3].y) < 0.001 && + splashAbs(path->pts[1].x - path->pts[2].x) < 0.001 && + splashAbs(path->pts[1].y - path->pts[2].y) < 0.001)))) { + wx = state->matrix[0] + state->matrix[2]; + wy = state->matrix[1] + state->matrix[3]; + w = sqrt(wx*wx + wy*wy); + if (w < 0.001) { + w = 0; + } else { + // min width is 0.1 -- this constant is minWidth * sqrt(2) + w = (SplashCoord)0.1414 / w; + } + xx0 = path->pts[0].x; + yy0 = path->pts[0].y; + if (n <= 3) { + xx1 = path->pts[1].x; + yy1 = path->pts[1].y; + } else { + xx1 = path->pts[2].x; + yy1 = path->pts[2].y; + } + dx = xx1 - xx0; + dy = yy1 - yy0; + d = sqrt(dx * dx + dy * dy); + if (d < 0.001) { + d = 0; + } else { + d = w / d; + } + dx *= d; + dy *= d; + path2 = new SplashPath(); + path2->moveTo(xx0 + dy, yy0 - dx); + path2->lineTo(xx1 + dy, yy1 - dx); + path2->lineTo(xx1 - dy, yy1 + dx); + path2->lineTo(xx0 - dy, yy0 + dx); + path2->close(gTrue); + path2->addStrokeAdjustHint(0, 2, 0, 4); + path2->addStrokeAdjustHint(1, 3, 0, 4); + + // unclosed rectangle --> close and hint + } else if (n == 4 && !(path->flags[0] & splashPathClosed)) { + path2->close(gTrue); + path2->addStrokeAdjustHint(0, 2, 0, 4); + path2->addStrokeAdjustHint(1, 3, 0, 4); + + // closed rectangle --> hint + } else if (n == 5 && (path->flags[0] & splashPathClosed)) { + path2->addStrokeAdjustHint(0, 2, 0, 4); + path2->addStrokeAdjustHint(1, 3, 0, 4); + } + + return path2; +} + GBool Splash::pathAllOutside(SplashPath *path) { SplashCoord xMin1, yMin1, xMax1, yMax1; SplashCoord xMin2, yMin2, xMax2, yMax2; @@ -2177,7 +3000,8 @@ GBool Splash::pathAllOutside(SplashPath *path) { xMaxI = splashFloor(xMax2); yMaxI = splashFloor(yMax2); - return state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI) == + return state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI, + state->strokeAdjust) == splashClipAllOutside; } @@ -2185,49 +3009,63 @@ SplashError Splash::xorFill(SplashPath *path, GBool eo) { SplashPipe pipe; SplashXPath *xPath; SplashXPathScanner *scanner; - int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y; - SplashClipResult clipRes, clipRes2; + int xMin, yMin, xMax, yMax, y, t; + SplashClipResult clipRes; SplashBlendFunc origBlendFunc; if (path->length == 0) { return splashErrEmptyPath; } + if (pathAllOutside(path)) { + opClipRes = splashClipAllOutside; + return splashOk; + } xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue); - xPath->sort(); - scanner = new SplashXPathScanner(xPath, eo, state->clip->getYMinI(), - state->clip->getYMaxI()); - - // get the min and max x and y values - scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI); + xMin = xPath->getXMin(); + yMin = xPath->getYMin(); + xMax = xPath->getXMax(); + yMax = xPath->getYMax(); + if (xMin > xMax || yMin > yMax) { + delete xPath; + return splashOk; + } + scanner = new SplashXPathScanner(xPath, eo, yMin, yMax); // check clipping - if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI)) + if ((clipRes = state->clip->testRect(xMin, yMin, xMax, yMax, + state->strokeAdjust)) != splashClipAllOutside) { - if (scanner->hasPartialClip()) { - clipRes = splashClipPartial; + + if ((t = state->clip->getXMinI(state->strokeAdjust)) > xMin) { + xMin = t; + } + if ((t = state->clip->getXMaxI(state->strokeAdjust)) < xMax) { + xMax = t; + } + if ((t = state->clip->getYMinI(state->strokeAdjust)) > yMin) { + yMin = t; + } + if ((t = state->clip->getYMaxI(state->strokeAdjust)) < yMax) { + yMax = t; + } + if (xMin > xMax || yMin > yMax) { + delete scanner; + delete xPath; + return splashOk; } origBlendFunc = state->blendFunc; state->blendFunc = &blendXor; - pipeInit(&pipe, 0, yMinI, state->fillPattern, NULL, 255, gFalse, gFalse); + pipeInit(&pipe, state->fillPattern, 255, gTrue, gFalse); // draw the spans - for (y = yMinI; y <= yMaxI; ++y) { - while (scanner->getNextSpan(y, &x0, &x1)) { - if (clipRes == splashClipAllInside) { - drawSpan(&pipe, x0, x1, y, gTrue); - } else { - // limit the x range - if (x0 < state->clip->getXMinI()) { - x0 = state->clip->getXMinI(); - } - if (x1 > state->clip->getXMaxI()) { - x1 = state->clip->getXMaxI(); - } - clipRes2 = state->clip->testSpan(x0, x1, y); - drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside); - } + for (y = yMin; y <= yMax; ++y) { + scanner->getSpanBinary(scanBuf, y, xMin, xMax); + if (clipRes != splashClipAllInside) { + state->clip->clipSpanBinary(scanBuf, y, xMin, xMax, + state->strokeAdjust); } + (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL); } state->blendFunc = origBlendFunc; } @@ -2278,105 +3116,86 @@ SplashError Splash::fillGlyph(SplashCoord x, SplashCoord y, SplashError Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph) { SplashPipe pipe; SplashClipResult clipRes; - GBool noClip; - int alpha0; Guchar alpha; Guchar *p; - int x1, y1, xx, xx1, yy; - - if ((clipRes = state->clip->testRect(x0 - glyph->x, - y0 - glyph->y, - x0 - glyph->x + glyph->w - 1, - y0 - glyph->y + glyph->h - 1)) + int xMin, yMin, xMax, yMax; + int x, y, xg, yg, xx, t; + + xg = x0 - glyph->x; + yg = y0 - glyph->y; + xMin = xg; + xMax = xg + glyph->w - 1; + yMin = yg; + yMax = yg + glyph->h - 1; + if ((clipRes = state->clip->testRect(xMin, yMin, xMax, yMax, + state->strokeAdjust)) != splashClipAllOutside) { - noClip = clipRes == splashClipAllInside; - - if (noClip) { + pipeInit(&pipe, state->fillPattern, + (Guchar)splashRound(state->fillAlpha * 255), + gTrue, gFalse); + if (clipRes == splashClipAllInside) { if (glyph->aa) { - pipeInit(&pipe, x0 - glyph->x, y0 - glyph->y, state->fillPattern, NULL, - (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); p = glyph->data; - for (yy = 0, y1 = y0 - glyph->y; yy < glyph->h; ++yy, ++y1) { - pipeSetXY(&pipe, x0 - glyph->x, y1); - for (xx = 0, x1 = x0 - glyph->x; xx < glyph->w; ++xx, ++x1) { - alpha = *p++; - if (alpha != 0) { - pipe.shape = alpha; - (this->*pipe.run)(&pipe); - updateModX(x1); - updateModY(y1); - } else { - pipeIncX(&pipe); - } - } + for (y = yMin; y <= yMax; ++y) { + (this->*pipe.run)(&pipe, xMin, xMax, y, + glyph->data + (y - yMin) * glyph->w, NULL); } } else { - pipeInit(&pipe, x0 - glyph->x, y0 - glyph->y, state->fillPattern, NULL, - (Guchar)splashRound(state->fillAlpha * 255), gFalse, gFalse); p = glyph->data; - for (yy = 0, y1 = y0 - glyph->y; yy < glyph->h; ++yy, ++y1) { - pipeSetXY(&pipe, x0 - glyph->x, y1); - for (xx = 0, x1 = x0 - glyph->x; xx < glyph->w; xx += 8) { - alpha0 = *p++; - for (xx1 = 0; xx1 < 8 && xx + xx1 < glyph->w; ++xx1, ++x1) { - if (alpha0 & 0x80) { - (this->*pipe.run)(&pipe); - updateModX(x1); - updateModY(y1); - } else { - pipeIncX(&pipe); - } - alpha0 <<= 1; + for (y = yMin; y <= yMax; ++y) { + for (x = xMin; x <= xMax; x += 8) { + alpha = *p++; + for (xx = 0; xx < 8 && x + xx <= xMax; ++xx) { + scanBuf[x + xx] = (alpha & 0x80) ? 0xff : 0x00; + alpha <<= 1; } } + (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL); } } } else { - if (glyph->aa) { - pipeInit(&pipe, x0 - glyph->x, y0 - glyph->y, state->fillPattern, NULL, - (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); - p = glyph->data; - for (yy = 0, y1 = y0 - glyph->y; yy < glyph->h; ++yy, ++y1) { - pipeSetXY(&pipe, x0 - glyph->x, y1); - for (xx = 0, x1 = x0 - glyph->x; xx < glyph->w; ++xx, ++x1) { - if (state->clip->test(x1, y1)) { - alpha = *p++; - if (alpha != 0) { - pipe.shape = alpha; - (this->*pipe.run)(&pipe); - updateModX(x1); - updateModY(y1); - } else { - pipeIncX(&pipe); - } - } else { - pipeIncX(&pipe); - ++p; - } + if ((t = state->clip->getXMinI(state->strokeAdjust)) > xMin) { + xMin = t; + } + if ((t = state->clip->getXMaxI(state->strokeAdjust)) < xMax) { + xMax = t; + } + if ((t = state->clip->getYMinI(state->strokeAdjust)) > yMin) { + yMin = t; + } + if ((t = state->clip->getYMaxI(state->strokeAdjust)) < yMax) { + yMax = t; + } + if (xMin <= xMax && yMin <= yMax) { + if (glyph->aa) { + for (y = yMin; y <= yMax; ++y) { + p = glyph->data + (y - yg) * glyph->w + (xMin - xg); + memcpy(scanBuf + xMin, p, xMax - xMin + 1); + state->clip->clipSpan(scanBuf, y, xMin, xMax, + state->strokeAdjust); + (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL); } - } - } else { - pipeInit(&pipe, x0 - glyph->x, y0 - glyph->y, state->fillPattern, NULL, - (Guchar)splashRound(state->fillAlpha * 255), gFalse, gFalse); - p = glyph->data; - for (yy = 0, y1 = y0 - glyph->y; yy < glyph->h; ++yy, ++y1) { - pipeSetXY(&pipe, x0 - glyph->x, y1); - for (xx = 0, x1 = x0 - glyph->x; xx < glyph->w; xx += 8) { - alpha0 = *p++; - for (xx1 = 0; xx1 < 8 && xx + xx1 < glyph->w; ++xx1, ++x1) { - if (state->clip->test(x1, y1)) { - if (alpha0 & 0x80) { - (this->*pipe.run)(&pipe); - updateModX(x1); - updateModY(y1); - } else { - pipeIncX(&pipe); - } - } else { - pipeIncX(&pipe); + } else { + for (y = yMin; y <= yMax; ++y) { + p = glyph->data + (y - yg) * ((glyph->w + 7) >> 3) + + ((xMin - xg) >> 3); + alpha = *p++; + xx = (xMin - xg) & 7; + alpha <<= xx; + for (x = xMin; xx < 8 && x <= xMax; ++x, ++xx) { + scanBuf[x] = (alpha & 0x80) ? 255 : 0; + alpha <<= 1; + } + for (; x <= xMax; x += 8) { + alpha = *p++; + for (xx = 0; xx < 8 && x + xx <= xMax; ++xx) { + scanBuf[x + xx] = (alpha & 0x80) ? 255 : 0; + alpha <<= 1; } - alpha0 <<= 1; } + state->clip->clipSpanBinary(scanBuf, y, xMin, xMax, + state->strokeAdjust); + (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL); } } } @@ -2387,12 +3206,28 @@ SplashError Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph) { return splashOk; } +void Splash::getImageBounds(SplashCoord xyMin, SplashCoord xyMax, + int *xyMinI, int *xyMaxI) { + if (state->strokeAdjust) { + splashStrokeAdjust(xyMin, xyMax, xyMinI, xyMaxI); + } else { + *xyMinI = splashFloor(xyMin); + *xyMaxI = splashFloor(xyMax); + if (*xyMaxI <= *xyMinI) { + *xyMaxI = *xyMinI + 1; + } + } +} + +// The glyphMode flag is not currently used, but may be useful if the +// stroke adjustment behavior is changed. SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData, int w, int h, SplashCoord *mat, - GBool glyphMode) { + GBool glyphMode, GBool interpolate) { SplashBitmap *scaledMask; SplashClipResult clipRes; GBool minorAxisZero; + SplashCoord wSize, hSize, t0, t1; int x0, y0, x1, y1, scaledWidth, scaledHeight; if (debugMode) { @@ -2406,68 +3241,269 @@ SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData, return splashErrSingularMatrix; } - minorAxisZero = mat[1] == 0 && mat[2] == 0; + minorAxisZero = splashAbs(mat[1]) <= 0.0001 && splashAbs(mat[2]) <= 0.0001; + + // rough estimate of size of scaled mask + t0 = splashAbs(mat[0]); + t1 = splashAbs(mat[1]); + wSize = t0 > t1 ? t0 : t1; + t0 = splashAbs(mat[2]); + t1 = splashAbs(mat[3]); + hSize = t0 > t1 ? t0 : t1; + + // stream-mode upscaling -- this is slower, so we only use it if the + // upscaled mask is large (in which case clipping should remove many + // pixels) + if (wSize > 2 * w && hSize > 2 * h && wSize * hSize > 1000000) { + upscaleMask(src, srcData, w, h, mat, glyphMode, interpolate); // scaling only - if (mat[0] > 0 && minorAxisZero && mat[3] > 0) { - x0 = imgCoordMungeLowerC(mat[4], glyphMode); - y0 = imgCoordMungeLowerC(mat[5], glyphMode); - x1 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode); - y1 = imgCoordMungeUpperC(mat[3] + mat[5], glyphMode); - // make sure narrow images cover at least one pixel - if (x0 == x1) { - ++x1; - } - if (y0 == y1) { - ++y1; - } - clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1); + } else if (mat[0] > 0 && minorAxisZero && mat[3] > 0) { + getImageBounds(mat[4], mat[0] + mat[4], &x0, &x1); + getImageBounds(mat[5], mat[3] + mat[5], &y0, &y1); + clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, + state->strokeAdjust); opClipRes = clipRes; if (clipRes != splashClipAllOutside) { scaledWidth = x1 - x0; scaledHeight = y1 - y0; - scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight); + scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight, + interpolate); blitMask(scaledMask, x0, y0, clipRes); delete scaledMask; } // scaling plus vertical flip } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) { - x0 = imgCoordMungeLowerC(mat[4], glyphMode); - y0 = imgCoordMungeLowerC(mat[3] + mat[5], glyphMode); - x1 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode); - y1 = imgCoordMungeUpperC(mat[5], glyphMode); - // make sure narrow images cover at least one pixel - if (x0 == x1) { - ++x1; - } - if (y0 == y1) { - ++y1; - } - clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1); + getImageBounds(mat[4], mat[0] + mat[4], &x0, &x1); + getImageBounds(mat[3] + mat[5], mat[5], &y0, &y1); + clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, + state->strokeAdjust); + opClipRes = clipRes; + if (clipRes != splashClipAllOutside) { + scaledWidth = x1 - x0; + scaledHeight = y1 - y0; + scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight, + interpolate); + vertFlipImage(scaledMask, scaledWidth, scaledHeight, 1); + blitMask(scaledMask, x0, y0, clipRes); + delete scaledMask; + } + + // scaling plus horizontal flip + } else if (mat[0] < 0 && minorAxisZero && mat[3] > 0) { + getImageBounds(mat[0] + mat[4], mat[4], &x0, &x1); + getImageBounds(mat[5], mat[3] + mat[5], &y0, &y1); + clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, + state->strokeAdjust); + opClipRes = clipRes; + if (clipRes != splashClipAllOutside) { + scaledWidth = x1 - x0; + scaledHeight = y1 - y0; + scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight, + interpolate); + horizFlipImage(scaledMask, scaledWidth, scaledHeight, 1); + blitMask(scaledMask, x0, y0, clipRes); + delete scaledMask; + } + + // scaling plus horizontal and vertical flips + } else if (mat[0] < 0 && minorAxisZero && mat[3] < 0) { + getImageBounds(mat[0] + mat[4], mat[4], &x0, &x1); + getImageBounds(mat[3] + mat[5], mat[5], &y0, &y1); + clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, + state->strokeAdjust); opClipRes = clipRes; if (clipRes != splashClipAllOutside) { scaledWidth = x1 - x0; scaledHeight = y1 - y0; - scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight); + scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight, + interpolate); vertFlipImage(scaledMask, scaledWidth, scaledHeight, 1); + horizFlipImage(scaledMask, scaledWidth, scaledHeight, 1); blitMask(scaledMask, x0, y0, clipRes); delete scaledMask; } // all other cases } else { - arbitraryTransformMask(src, srcData, w, h, mat, glyphMode); + arbitraryTransformMask(src, srcData, w, h, mat, glyphMode, interpolate); } return splashOk; } +// The glyphMode flag is not currently used, but may be useful if the +// stroke adjustment behavior is changed. +void Splash::upscaleMask(SplashImageMaskSource src, void *srcData, + int srcWidth, int srcHeight, + SplashCoord *mat, GBool glyphMode, + GBool interpolate) { + SplashClipResult clipRes; + SplashPipe pipe; + Guchar *unscaledImage, *p; + SplashCoord xMin, yMin, xMax, yMax, t; + SplashCoord mi0, mi1, mi2, mi3, mi4, mi5, det; + SplashCoord ix, iy, sx, sy, pix0, pix1; + int xMinI, yMinI, xMaxI, yMaxI, x, y, x0, y0, x1, y1, tt; + + // compute the bbox of the target quadrilateral + xMin = xMax = mat[4]; + t = mat[2] + mat[4]; + if (t < xMin) { + xMin = t; + } else if (t > xMax) { + xMax = t; + } + t = mat[0] + mat[2] + mat[4]; + if (t < xMin) { + xMin = t; + } else if (t > xMax) { + xMax = t; + } + t = mat[0] + mat[4]; + if (t < xMin) { + xMin = t; + } else if (t > xMax) { + xMax = t; + } + getImageBounds(xMin, xMax, &xMinI, &xMaxI); + yMin = yMax = mat[5]; + t = mat[3] + mat[5]; + if (t < yMin) { + yMin = t; + } else if (t > yMax) { + yMax = t; + } + t = mat[1] + mat[3] + mat[5]; + if (t < yMin) { + yMin = t; + } else if (t > yMax) { + yMax = t; + } + t = mat[1] + mat[5]; + if (t < yMin) { + yMin = t; + } else if (t > yMax) { + yMax = t; + } + getImageBounds(yMin, yMax, &yMinI, &yMaxI); + + // clipping + clipRes = state->clip->testRect(xMinI, yMinI, xMaxI - 1, yMaxI - 1, + state->strokeAdjust); + opClipRes = clipRes; + if (clipRes == splashClipAllOutside) { + return; + } + if (clipRes != splashClipAllInside) { + if ((tt = state->clip->getXMinI(state->strokeAdjust)) > xMinI) { + xMinI = tt; + } + if ((tt = state->clip->getXMaxI(state->strokeAdjust) + 1) < xMaxI) { + xMaxI = tt; + } + if ((tt = state->clip->getYMinI(state->strokeAdjust)) > yMinI) { + yMinI = tt; + } + if ((tt = state->clip->getYMaxI(state->strokeAdjust) + 1) < yMaxI) { + yMaxI = tt; + } + } + + // invert the matrix + det = mat[0] * mat[3] - mat[1] * mat[2]; + if (splashAbs(det) < 1e-6) { + // this should be caught by the singular matrix check in fillImageMask + return; + } + det = (SplashCoord)1 / det; + mi0 = det * mat[3] * srcWidth; + mi1 = -det * mat[1] * srcHeight; + mi2 = -det * mat[2] * srcWidth; + mi3 = det * mat[0] * srcHeight; + mi4 = det * (mat[2] * mat[5] - mat[3] * mat[4]) * srcWidth; + mi5 = -det * (mat[0] * mat[5] - mat[1] * mat[4]) * srcHeight; + + // grab the image + unscaledImage = (Guchar *)gmallocn(srcWidth, srcHeight); + for (y = 0, p = unscaledImage; y < srcHeight; ++y, p += srcWidth) { + (*src)(srcData, p); + for (x = 0; x < srcWidth; ++x) { + p[x] *= 255; + } + } + + // draw it + pipeInit(&pipe, state->fillPattern, + (Guchar)splashRound(state->fillAlpha * 255), + gTrue, gFalse); + for (y = yMinI; y < yMaxI; ++y) { + for (x = xMinI; x < xMaxI; ++x) { + ix = ((SplashCoord)x + 0.5) * mi0 + ((SplashCoord)y + 0.5) * mi2 + mi4; + iy = ((SplashCoord)x + 0.5) * mi1 + ((SplashCoord)y + 0.5) * mi3 + mi5; + if (interpolate) { + if (ix >= 0 && ix < srcWidth && iy >= 0 && iy < srcHeight) { + x0 = splashFloor(ix - 0.5); + x1 = x0 + 1; + sx = (ix - 0.5) - x0; + y0 = splashFloor(iy - 0.5); + y1 = y0 + 1; + sy = (iy - 0.5) - y0; + if (x0 < 0) { + x0 = 0; + } + if (x1 >= srcWidth) { + x1 = srcWidth - 1; + } + if (y0 < 0) { + y0 = 0; + } + if (y1 >= srcHeight) { + y1 = srcHeight - 1; + } + pix0 = ((SplashCoord)1 - sx) * unscaledImage[y0 * srcWidth + x0] + + sx * unscaledImage[y0 * srcWidth + x1]; + pix1 = ((SplashCoord)1 - sx) * unscaledImage[y1 * srcWidth + x0] + + sx * unscaledImage[y1 * srcWidth + x1]; + scanBuf[x] = (Guchar)splashRound(((SplashCoord)1 - sy) * pix0 + + sy * pix1); + } else { + scanBuf[x] = 0; + } + } else { + x0 = splashFloor(ix); + y0 = splashFloor(iy); + if (x0 >= 0 && x0 < srcWidth && y0 >= 0 && y0 < srcHeight) { + scanBuf[x] = unscaledImage[y0 * srcWidth + x0]; + } else { + scanBuf[x] = 0; + } + } + } + if (clipRes != splashClipAllInside) { + if (vectorAntialias) { + state->clip->clipSpan(scanBuf, y, xMinI, xMaxI - 1, + state->strokeAdjust); + } else { + state->clip->clipSpanBinary(scanBuf, y, xMinI, xMaxI - 1, + state->strokeAdjust); + } + } + (this->*pipe.run)(&pipe, xMinI, xMaxI - 1, y, scanBuf + xMinI, NULL); + } + + gfree(unscaledImage); +} + +// The glyphMode flag is not currently used, but may be useful if the +// stroke adjustment behavior is changed. void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, - SplashCoord *mat, GBool glyphMode) { + SplashCoord *mat, GBool glyphMode, + GBool interpolate) { SplashBitmap *scaledMask; - SplashClipResult clipRes, clipRes2; + SplashClipResult clipRes; SplashPipe pipe; int scaledWidth, scaledHeight, t0, t1; SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11; @@ -2484,29 +3520,26 @@ void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData, vx[3] = mat[0] + mat[4]; vy[3] = mat[1] + mat[5]; // clipping - xMin = imgCoordMungeLowerC(vx[0], glyphMode); - xMax = imgCoordMungeUpperC(vx[0], glyphMode); - yMin = imgCoordMungeLowerC(vy[0], glyphMode); - yMax = imgCoordMungeUpperC(vy[0], glyphMode); + xMin = splashRound(vx[0]); + xMax = splashRound(vx[0]); + yMin = splashRound(vy[0]); + yMax = splashRound(vy[0]); for (i = 1; i < 4; ++i) { - t0 = imgCoordMungeLowerC(vx[i], glyphMode); + t0 = splashRound(vx[i]); if (t0 < xMin) { xMin = t0; - } - t0 = imgCoordMungeUpperC(vx[i], glyphMode); - if (t0 > xMax) { + } else if (t0 > xMax) { xMax = t0; } - t1 = imgCoordMungeLowerC(vy[i], glyphMode); + t1 = splashRound(vy[i]); if (t1 < yMin) { yMin = t1; - } - t1 = imgCoordMungeUpperC(vy[i], glyphMode); - if (t1 > yMax) { + } else if (t1 > yMax) { yMax = t1; } } - clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1); + clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1, + state->strokeAdjust); opClipRes = clipRes; if (clipRes == splashClipAllOutside) { return; @@ -2514,33 +3547,25 @@ void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData, // compute the scale factors if (mat[0] >= 0) { - t0 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode) - - imgCoordMungeLowerC(mat[4], glyphMode); + t0 = splashRound(mat[0] + mat[4]) - splashRound(mat[4]); } else { - t0 = imgCoordMungeUpperC(mat[4], glyphMode) - - imgCoordMungeLowerC(mat[0] + mat[4], glyphMode); + t0 = splashRound(mat[4]) - splashRound(mat[0] + mat[4]); } if (mat[1] >= 0) { - t1 = imgCoordMungeUpperC(mat[1] + mat[5], glyphMode) - - imgCoordMungeLowerC(mat[5], glyphMode); + t1 = splashRound(mat[1] + mat[5]) - splashRound(mat[5]); } else { - t1 = imgCoordMungeUpperC(mat[5], glyphMode) - - imgCoordMungeLowerC(mat[1] + mat[5], glyphMode); + t1 = splashRound(mat[5]) - splashRound(mat[1] + mat[5]); } scaledWidth = t0 > t1 ? t0 : t1; if (mat[2] >= 0) { - t0 = imgCoordMungeUpperC(mat[2] + mat[4], glyphMode) - - imgCoordMungeLowerC(mat[4], glyphMode); + t0 = splashRound(mat[2] + mat[4]) - splashRound(mat[4]); } else { - t0 = imgCoordMungeUpperC(mat[4], glyphMode) - - imgCoordMungeLowerC(mat[2] + mat[4], glyphMode); + t0 = splashRound(mat[4]) - splashRound(mat[2] + mat[4]); } if (mat[3] >= 0) { - t1 = imgCoordMungeUpperC(mat[3] + mat[5], glyphMode) - - imgCoordMungeLowerC(mat[5], glyphMode); + t1 = splashRound(mat[3] + mat[5]) - splashRound(mat[5]); } else { - t1 = imgCoordMungeUpperC(mat[5], glyphMode) - - imgCoordMungeLowerC(mat[3] + mat[5], glyphMode); + t1 = splashRound(mat[5]) - splashRound(mat[3] + mat[5]); } scaledHeight = t0 > t1 ? t0 : t1; if (scaledWidth == 0) { @@ -2567,19 +3592,28 @@ void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData, // scale the input image scaledMask = scaleMask(src, srcData, srcWidth, srcHeight, - scaledWidth, scaledHeight); + scaledWidth, scaledHeight, interpolate); // construct the three sections - i = (vy[2] <= vy[3]) ? 2 : 3; - if (vy[1] <= vy[i]) { + i = 0; + if (vy[1] < vy[i]) { i = 1; } - if (vy[0] < vy[i] || (i != 3 && vy[0] == vy[i])) { - i = 0; + if (vy[2] < vy[i]) { + i = 2; } - 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 (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 = splashRound(vy[i]); + section[0].y1 = splashRound(vy[(i+2) & 3]) - 1; if (vx[i] < vx[(i+1) & 3]) { section[0].ia0 = i; section[0].ia1 = (i+3) & 3; @@ -2593,8 +3627,8 @@ void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData, } nSections = 1; } else { - section[0].y0 = imgCoordMungeLowerC(vy[i], glyphMode); - section[2].y1 = imgCoordMungeUpperC(vy[(i+2) & 3], glyphMode) - 1; + section[0].y0 = splashRound(vy[i]); + section[2].y1 = splashRound(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]) { @@ -2605,8 +3639,8 @@ void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData, 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); + section[1].y0 = splashRound(vy[(i+1) & 3]); + section[2].y0 = splashRound(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; @@ -2619,8 +3653,8 @@ void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData, 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); + section[1].y0 = splashRound(vy[(i+3) & 3]); + section[2].y0 = splashRound(vy[(i+1) & 3]); if (vx[(i+1) & 3] < vx[(i+3) & 3]) { section[1].ia0 = i; section[1].ia1 = (i+1) & 3; @@ -2653,11 +3687,9 @@ void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData, } // initialize the pixel pipe - pipeInit(&pipe, 0, 0, state->fillPattern, NULL, - (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); - if (vectorAntialias) { - drawAAPixelInit(); - } + pipeInit(&pipe, state->fillPattern, + (Guchar)splashRound(state->fillAlpha * 255), + gTrue, gFalse); // make sure narrow images cover at least one pixel if (nSections == 1) { @@ -2675,23 +3707,30 @@ void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData, // 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); + xa = splashRound(section[i].xa0 + + ((SplashCoord)y + 0.5 - section[i].ya0) * + section[i].dxdya); + xb = splashRound(section[i].xb0 + + ((SplashCoord)y + 0.5 - section[i].yb0) * + section[i].dxdyb); + if (xa > xb) { + continue; + } // 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; + // check the scanBuf bounds + if (xa >= bitmap->width || xb < 0) { + continue; + } + if (xa < 0) { + xa = 0; } + if (xb > bitmap->width) { + xb = bitmap->width; + } + // get the scan line 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 + @@ -2710,13 +3749,19 @@ void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData, } else if (yy >= scaledHeight) { yy = scaledHeight - 1; } - pipe.shape = scaledMask->data[yy * scaledWidth + xx]; - if (vectorAntialias && clipRes2 != splashClipAllInside) { - drawAAPixel(&pipe, x, y); + scanBuf[x] = scaledMask->data[yy * scaledWidth + xx]; + } + // clip the scan line + if (clipRes != splashClipAllInside) { + if (vectorAntialias) { + state->clip->clipSpan(scanBuf, y, xa, xb - 1, state->strokeAdjust); } else { - drawPixel(&pipe, x, y, clipRes2 == splashClipAllInside); + state->clip->clipSpanBinary(scanBuf, y, xa, xb - 1, + state->strokeAdjust); } } + // draw the scan line + (this->*pipe.run)(&pipe, xa, xb - 1, y, scanBuf + xa, NULL); } } @@ -2726,7 +3771,8 @@ void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData, // Scale an image mask into a SplashBitmap. SplashBitmap *Splash::scaleMask(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight) { + int scaledWidth, int scaledHeight, + GBool interpolate) { SplashBitmap *dest; dest = new SplashBitmap(scaledWidth, scaledHeight, 1, splashModeMono8, @@ -2744,8 +3790,13 @@ SplashBitmap *Splash::scaleMask(SplashImageMaskSource src, void *srcData, scaleMaskYuXd(src, srcData, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } else { - scaleMaskYuXu(src, srcData, srcWidth, srcHeight, - scaledWidth, scaledHeight, dest); + if (interpolate) { + scaleMaskYuXuI(src, srcData, srcWidth, srcHeight, + scaledWidth, scaledHeight, dest); + } else { + scaleMaskYuXu(src, srcData, srcWidth, srcHeight, + scaledWidth, scaledHeight, dest); + } } } return dest; @@ -3057,60 +4108,158 @@ void Splash::scaleMaskYuXu(SplashImageMaskSource src, void *srcData, gfree(lineBuf); } +void Splash::scaleMaskYuXuI(SplashImageMaskSource src, void *srcData, + int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight, + SplashBitmap *dest) { + Guchar *lineBuf0, *lineBuf1, *tBuf; + Guchar pix; + SplashCoord yr, xr, ys, xs, ySrc, xSrc; + int ySrc0, ySrc1, yBuf, xSrc0, xSrc1, y, x; + Guchar *destPtr; + + // ratios + yr = (SplashCoord)srcHeight / (SplashCoord)scaledHeight; + xr = (SplashCoord)srcWidth / (SplashCoord)scaledWidth; + + // allocate buffers + lineBuf0 = (Guchar *)gmalloc(scaledWidth); + lineBuf1 = (Guchar *)gmalloc(scaledWidth); + + // read first two rows + (*src)(srcData, lineBuf0); + if (srcHeight > 1) { + (*src)(srcData, lineBuf1); + yBuf = 1; + } else { + memcpy(lineBuf1, lineBuf0, srcWidth); + yBuf = 0; + } + + // interpolate first two rows + for (x = scaledWidth - 1; x >= 0; --x) { + xSrc = xr * x; + xSrc0 = splashFloor(xSrc + xr * 0.5 - 0.5); + xSrc1 = xSrc0 + 1; + xs = ((SplashCoord)xSrc1 + 0.5) - (xSrc + xr * 0.5); + if (xSrc0 < 0) { + xSrc0 = 0; + } + if (xSrc1 >= srcWidth) { + xSrc1 = srcWidth - 1; + } + lineBuf0[x] = (Guchar)(int) + ((xs * lineBuf0[xSrc0] + + ((SplashCoord)1 - xs) * lineBuf0[xSrc1]) * 255); + lineBuf1[x] = (Guchar)(int) + ((xs * lineBuf1[xSrc0] + + ((SplashCoord)1 - xs) * lineBuf1[xSrc1]) * 255); + } + + destPtr = dest->data; + for (y = 0; y < scaledHeight; ++y) { + + // compute vertical interpolation parameters + ySrc = yr * y; + ySrc0 = splashFloor(ySrc + yr * 0.5 - 0.5); + ySrc1 = ySrc0 + 1; + ys = ((SplashCoord)ySrc1 + 0.5) - (ySrc + yr * 0.5); + if (ySrc0 < 0) { + ySrc0 = 0; + ys = 1; + } + if (ySrc1 >= srcHeight) { + ySrc1 = srcHeight - 1; + ys = 0; + } + + // read another row (if necessary) + if (ySrc1 > yBuf) { + tBuf = lineBuf0; + lineBuf0 = lineBuf1; + lineBuf1 = tBuf; + (*src)(srcData, lineBuf1); + + // interpolate the row + for (x = scaledWidth - 1; x >= 0; --x) { + xSrc = xr * x; + xSrc0 = splashFloor(xSrc + xr * 0.5 - 0.5); + xSrc1 = xSrc0 + 1; + xs = ((SplashCoord)xSrc1 + 0.5) - (xSrc + xr * 0.5); + if (xSrc0 < 0) { + xSrc0 = 0; + } + if (xSrc1 >= srcWidth) { + xSrc1 = srcWidth - 1; + } + lineBuf1[x] = (Guchar)(int) + ((xs * lineBuf1[xSrc0] + + ((SplashCoord)1 - xs) * lineBuf1[xSrc1]) * 255); + } + + ++yBuf; + } + + // do the vertical interpolation + for (x = 0; x < scaledWidth; ++x) { + + pix = (Guchar)(int)(ys * lineBuf0[x] + + ((SplashCoord)1 - ys) * lineBuf1[x]); + + // store the pixel + *destPtr++ = pix; + } + } + + gfree(lineBuf1); + gfree(lineBuf0); +} + void Splash::blitMask(SplashBitmap *src, int xDest, int yDest, SplashClipResult clipRes) { SplashPipe pipe; - Guchar *p; - int w, h, x, y; + int w, h, x0, x1, y0, y1, y, t; 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(); + pipeInit(&pipe, state->fillPattern, + (Guchar)splashRound(state->fillAlpha * 255), + gTrue, gFalse); + if (clipRes == splashClipAllInside) { for (y = 0; y < h; ++y) { - for (x = 0; x < w; ++x) { - pipe.shape = *p++; - drawAAPixel(&pipe, xDest + x, yDest + y); - } + (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, + src->getDataPtr() + y * w, NULL); } } 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; + x0 = xDest; + if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) { + x0 = t; + } + x1 = xDest + w; + if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) { + x1 = t; + } + y0 = yDest; + if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) { + y0 = t; + } + y1 = yDest + h; + if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) { + y1 = t; + } + if (x0 < x1 && y0 < y1) { + for (y = y0; y < y1; ++y) { + memcpy(scanBuf + x0, + src->getDataPtr() + (y - yDest) * w + (x0 - xDest), + x1 - x0); + if (vectorAntialias) { + state->clip->clipSpan(scanBuf, y, x0, x1 - 1, + state->strokeAdjust); + } else { + state->clip->clipSpanBinary(scanBuf, y, x0, x1 - 1, + state->strokeAdjust); } + (this->*pipe.run)(&pipe, x0, x1 - 1, y, scanBuf + x0, NULL); } } } @@ -3118,11 +4267,13 @@ void Splash::blitMask(SplashBitmap *src, int xDest, int yDest, SplashError Splash::drawImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, GBool srcAlpha, - int w, int h, SplashCoord *mat) { + int w, int h, SplashCoord *mat, + GBool interpolate) { GBool ok; SplashBitmap *scaledImg; SplashClipResult clipRes; GBool minorAxisZero; + SplashCoord wSize, hSize, t0, t1; int x0, y0, x1, y1, scaledWidth, scaledHeight; int nComps; @@ -3142,11 +4293,8 @@ SplashError Splash::drawImage(SplashImageSource src, void *srcData, nComps = 1; break; case splashModeRGB8: - ok = srcMode == splashModeRGB8; - nComps = 3; - break; case splashModeBGR8: - ok = srcMode == splashModeBGR8; + ok = srcMode == splashModeRGB8; nComps = 3; break; #if SPLASH_CMYK @@ -3168,60 +4316,87 @@ SplashError Splash::drawImage(SplashImageSource src, void *srcData, return splashErrSingularMatrix; } - minorAxisZero = mat[1] == 0 && mat[2] == 0; + minorAxisZero = splashAbs(mat[1]) <= 0.0001 && splashAbs(mat[2]) <= 0.0001; + + // rough estimate of size of scaled image + t0 = splashAbs(mat[0]); + t1 = splashAbs(mat[1]); + wSize = t0 > t1 ? t0 : t1; + t0 = splashAbs(mat[2]); + t1 = splashAbs(mat[3]); + hSize = t0 > t1 ? t0 : t1; + + // stream-mode upscaling -- this is slower, so we only use it if the + // upscaled image is large (in which case clipping should remove + // many pixels) + if (wSize > 2 * w && hSize > 2 * h && wSize * hSize > 1000000) { + upscaleImage(src, srcData, srcMode, nComps, srcAlpha, + w, h, mat, interpolate); // 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); + } else if (mat[0] > 0 && minorAxisZero && mat[3] > 0) { + getImageBounds(mat[4], mat[0] + mat[4], &x0, &x1); + getImageBounds(mat[5], mat[3] + mat[5], &y0, &y1); + clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, + state->strokeAdjust); opClipRes = clipRes; if (clipRes != splashClipAllOutside) { scaledWidth = x1 - x0; scaledHeight = y1 - y0; scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, - scaledWidth, scaledHeight); + scaledWidth, scaledHeight, interpolate); 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; - } + getImageBounds(mat[4], mat[0] + mat[4], &x0, &x1); + getImageBounds(mat[3] + mat[5], mat[5], &y0, &y1); + clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, + state->strokeAdjust); + opClipRes = clipRes; + if (clipRes != splashClipAllOutside) { + scaledWidth = x1 - x0; + scaledHeight = y1 - y0; + scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, + scaledWidth, scaledHeight, interpolate); + vertFlipImage(scaledImg, scaledWidth, scaledHeight, nComps); + blitImage(scaledImg, srcAlpha, x0, y0, clipRes); + delete scaledImg; } - if (y0 == y1) { - if (mat[5] + mat[1] * 0.5 < y0) { - --y0; - } else { - ++y1; - } + + // scaling plus horizontal flip + } else if (mat[0] > 0 && minorAxisZero && mat[3] > 0) { + getImageBounds(mat[0] + mat[4], mat[4], &x0, &x1); + getImageBounds(mat[5], mat[3] + mat[5], &y0, &y1); + clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, + state->strokeAdjust); + opClipRes = clipRes; + if (clipRes != splashClipAllOutside) { + scaledWidth = x1 - x0; + scaledHeight = y1 - y0; + scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, + scaledWidth, scaledHeight, interpolate); + horizFlipImage(scaledImg, scaledWidth, scaledHeight, nComps); + blitImage(scaledImg, srcAlpha, x0, y0, clipRes); + delete scaledImg; } - clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1); + + // scaling plus horizontal and vertical flips + } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) { + getImageBounds(mat[0] + mat[4], mat[4], &x0, &x1); + getImageBounds(mat[3] + mat[5], mat[5], &y0, &y1); + clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, + state->strokeAdjust); opClipRes = clipRes; if (clipRes != splashClipAllOutside) { scaledWidth = x1 - x0; scaledHeight = y1 - y0; scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, - scaledWidth, scaledHeight); + scaledWidth, scaledHeight, interpolate); vertFlipImage(scaledImg, scaledWidth, scaledHeight, nComps); + horizFlipImage(scaledImg, scaledWidth, scaledHeight, nComps); blitImage(scaledImg, srcAlpha, x0, y0, clipRes); delete scaledImg; } @@ -3229,21 +4404,227 @@ SplashError Splash::drawImage(SplashImageSource src, void *srcData, // all other cases } else { arbitraryTransformImage(src, srcData, srcMode, nComps, srcAlpha, - w, h, mat); + w, h, mat, interpolate); } return splashOk; } +void Splash::upscaleImage(SplashImageSource src, void *srcData, + SplashColorMode srcMode, int nComps, + GBool srcAlpha, int srcWidth, int srcHeight, + SplashCoord *mat, GBool interpolate) { + SplashClipResult clipRes; + SplashPipe pipe; + SplashColorPtr unscaledImage, pixelBuf, p, q, q00, q01, q10, q11; + Guchar *unscaledAlpha, *alphaPtr; + SplashCoord xMin, yMin, xMax, yMax, t; + SplashCoord mi0, mi1, mi2, mi3, mi4, mi5, det; + SplashCoord ix, iy, sx, sy, pix0, pix1; + int rowSize, xMinI, yMinI, xMaxI, yMaxI, x, y, x0, y0, x1, y1, tt, i; + + // compute the bbox of the target quadrilateral + xMin = xMax = mat[4]; + t = mat[2] + mat[4]; + if (t < xMin) { + xMin = t; + } else if (t > xMax) { + xMax = t; + } + t = mat[0] + mat[2] + mat[4]; + if (t < xMin) { + xMin = t; + } else if (t > xMax) { + xMax = t; + } + t = mat[0] + mat[4]; + if (t < xMin) { + xMin = t; + } else if (t > xMax) { + xMax = t; + } + getImageBounds(xMin, xMax, &xMinI, &xMaxI); + yMin = yMax = mat[5]; + t = mat[3] + mat[5]; + if (t < yMin) { + yMin = t; + } else if (t > yMax) { + yMax = t; + } + t = mat[1] + mat[3] + mat[5]; + if (t < yMin) { + yMin = t; + } else if (t > yMax) { + yMax = t; + } + t = mat[1] + mat[5]; + if (t < yMin) { + yMin = t; + } else if (t > yMax) { + yMax = t; + } + getImageBounds(yMin, yMax, &yMinI, &yMaxI); + + // clipping + clipRes = state->clip->testRect(xMinI, yMinI, xMaxI - 1, yMaxI - 1, + state->strokeAdjust); + opClipRes = clipRes; + if (clipRes == splashClipAllOutside) { + return; + } + if (clipRes != splashClipAllInside) { + if ((tt = state->clip->getXMinI(state->strokeAdjust)) > xMinI) { + xMinI = tt; + } + if ((tt = state->clip->getXMaxI(state->strokeAdjust) + 1) < xMaxI) { + xMaxI = tt; + } + if ((tt = state->clip->getYMinI(state->strokeAdjust)) > yMinI) { + yMinI = tt; + } + if ((tt = state->clip->getYMaxI(state->strokeAdjust) + 1) < yMaxI) { + yMaxI = tt; + } + } + + // invert the matrix + det = mat[0] * mat[3] - mat[1] * mat[2]; + if (splashAbs(det) < 1e-6) { + // this should be caught by the singular matrix check in fillImageMask + return; + } + det = (SplashCoord)1 / det; + mi0 = det * mat[3] * srcWidth; + mi1 = -det * mat[1] * srcHeight; + mi2 = -det * mat[2] * srcWidth; + mi3 = det * mat[0] * srcHeight; + mi4 = det * (mat[2] * mat[5] - mat[3] * mat[4]) * srcWidth; + mi5 = -det * (mat[0] * mat[5] - mat[1] * mat[4]) * srcHeight; + + // grab the image + if (srcWidth > INT_MAX / nComps) { + rowSize = -1; + } else { + rowSize = srcWidth * nComps; + } + unscaledImage = (SplashColorPtr)gmallocn(srcHeight, rowSize); + if (srcAlpha) { + unscaledAlpha = (Guchar *)gmallocn(srcHeight, srcWidth); + for (y = 0, p = unscaledImage, alphaPtr = unscaledAlpha; + y < srcHeight; + ++y, p += rowSize, alphaPtr += srcWidth) { + (*src)(srcData, p, alphaPtr); + } + } else { + unscaledAlpha = NULL; + for (y = 0, p = unscaledImage; y < srcHeight; ++y, p += srcWidth * nComps) { + (*src)(srcData, p, NULL); + } + } + + // draw it + pixelBuf = (SplashColorPtr)gmallocn(xMaxI - xMinI, nComps); + pipeInit(&pipe, NULL, + (Guchar)splashRound(state->fillAlpha * 255), + gTrue, gFalse); + for (y = yMinI; y < yMaxI; ++y) { + p = pixelBuf; + for (x = xMinI; x < xMaxI; ++x) { + ix = ((SplashCoord)x + 0.5) * mi0 + ((SplashCoord)y + 0.5) * mi2 + mi4; + iy = ((SplashCoord)x + 0.5) * mi1 + ((SplashCoord)y + 0.5) * mi3 + mi5; + if (interpolate) { + if (ix >= 0 && ix < srcWidth && iy >= 0 && iy < srcHeight) { + x0 = splashFloor(ix - 0.5); + x1 = x0 + 1; + sx = (ix - 0.5) - x0; + y0 = splashFloor(iy - 0.5); + y1 = y0 + 1; + sy = (iy - 0.5) - y0; + if (x0 < 0) { + x0 = 0; + } + if (x1 >= srcWidth) { + x1 = srcWidth - 1; + } + if (y0 < 0) { + y0 = 0; + } + if (y1 >= srcHeight) { + y1 = srcHeight - 1; + } + q00 = &unscaledImage[(y0 * srcWidth + x0) * nComps]; + q01 = &unscaledImage[(y0 * srcWidth + x1) * nComps]; + q10 = &unscaledImage[(y1 * srcWidth + x0) * nComps]; + q11 = &unscaledImage[(y1 * srcWidth + x1) * nComps]; + for (i = 0; i < nComps; ++i) { + pix0 = ((SplashCoord)1 - sx) * *q00++ + sx * *q01++; + pix1 = ((SplashCoord)1 - sx) * *q10++ + sx * *q11++; + *p++ = (Guchar)splashRound(((SplashCoord)1 - sy) * pix0 + + sy * pix1); + } + if (srcAlpha) { + pix0 = ((SplashCoord)1 - sx) * unscaledAlpha[y0 * srcWidth + x0] + + sx * unscaledAlpha[y0 * srcWidth + x1]; + pix1 = ((SplashCoord)1 - sx) * unscaledAlpha[y1 * srcWidth + x0] + + sx * unscaledAlpha[y1 * srcWidth + x1]; + scanBuf[x] = (Guchar)splashRound(((SplashCoord)1 - sy) * pix0 + + sy * pix1); + } else { + scanBuf[x] = 0xff; + } + } else { + for (i = 0; i < nComps; ++i) { + *p++ = 0; + } + scanBuf[x] = 0; + } + } else { + x0 = splashFloor(ix); + y0 = splashFloor(iy); + if (x0 >= 0 && x0 < srcWidth && y0 >= 0 && y0 < srcHeight) { + q = &unscaledImage[(y0 * srcWidth + x0) * nComps]; + for (i = 0; i < nComps; ++i) { + *p++ = *q++; + } + if (srcAlpha) { + scanBuf[x] = unscaledAlpha[y0 * srcWidth + x0]; + } else { + scanBuf[x] = 0xff; + } + } else { + for (i = 0; i < nComps; ++i) { + *p++ = 0; + } + scanBuf[x] = 0; + } + } + } + if (clipRes != splashClipAllInside) { + if (vectorAntialias) { + state->clip->clipSpan(scanBuf, y, xMinI, xMaxI - 1, + state->strokeAdjust); + } else { + state->clip->clipSpanBinary(scanBuf, y, xMinI, xMaxI - 1, + state->strokeAdjust); + } + } + (this->*pipe.run)(&pipe, xMinI, xMaxI - 1, y, scanBuf + xMinI, pixelBuf); + } + + gfree(pixelBuf); + gfree(unscaledImage); + gfree(unscaledAlpha); +} + void Splash::arbitraryTransformImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, - SplashCoord *mat) { + SplashCoord *mat, GBool interpolate) { SplashBitmap *scaledImg; - SplashClipResult clipRes, clipRes2; + SplashClipResult clipRes; SplashPipe pipe; - SplashColor pixel; + SplashColorPtr pixelBuf; int scaledWidth, scaledHeight, t0, t1; SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11; SplashCoord vx[4], vy[4]; @@ -3259,29 +4640,26 @@ void Splash::arbitraryTransformImage(SplashImageSource src, void *srcData, vx[3] = mat[0] + mat[4]; vy[3] = mat[1] + mat[5]; // clipping - xMin = imgCoordMungeLower(vx[0]); - xMax = imgCoordMungeUpper(vx[0]); - yMin = imgCoordMungeLower(vy[0]); - yMax = imgCoordMungeUpper(vy[0]); + xMin = splashRound(vx[0]); + xMax = splashRound(vx[0]); + yMin = splashRound(vy[0]); + yMax = splashRound(vy[0]); for (i = 1; i < 4; ++i) { - t0 = imgCoordMungeLower(vx[i]); + t0 = splashRound(vx[i]); if (t0 < xMin) { xMin = t0; - } - t0 = imgCoordMungeUpper(vx[i]); - if (t0 > xMax) { + } else if (t0 > xMax) { xMax = t0; } - t1 = imgCoordMungeLower(vy[i]); + t1 = splashRound(vy[i]); if (t1 < yMin) { yMin = t1; - } - t1 = imgCoordMungeUpper(vy[i]); - if (t1 > yMax) { + } else if (t1 > yMax) { yMax = t1; } } - clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1); + clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1, + state->strokeAdjust); opClipRes = clipRes; if (clipRes == splashClipAllOutside) { return; @@ -3289,25 +4667,25 @@ void Splash::arbitraryTransformImage(SplashImageSource src, void *srcData, // compute the scale factors if (mat[0] >= 0) { - t0 = imgCoordMungeUpper(mat[0] + mat[4]) - imgCoordMungeLower(mat[4]); + t0 = splashRound(mat[0] + mat[4]) - splashRound(mat[4]); } else { - t0 = imgCoordMungeUpper(mat[4]) - imgCoordMungeLower(mat[0] + mat[4]); + t0 = splashRound(mat[4]) - splashRound(mat[0] + mat[4]); } if (mat[1] >= 0) { - t1 = imgCoordMungeUpper(mat[1] + mat[5]) - imgCoordMungeLower(mat[5]); + t1 = splashRound(mat[1] + mat[5]) - splashRound(mat[5]); } else { - t1 = imgCoordMungeUpper(mat[5]) - imgCoordMungeLower(mat[1] + mat[5]); + t1 = splashRound(mat[5]) - splashRound(mat[1] + mat[5]); } scaledWidth = t0 > t1 ? t0 : t1; if (mat[2] >= 0) { - t0 = imgCoordMungeUpper(mat[2] + mat[4]) - imgCoordMungeLower(mat[4]); + t0 = splashRound(mat[2] + mat[4]) - splashRound(mat[4]); } else { - t0 = imgCoordMungeUpper(mat[4]) - imgCoordMungeLower(mat[2] + mat[4]); + t0 = splashRound(mat[4]) - splashRound(mat[2] + mat[4]); } if (mat[3] >= 0) { - t1 = imgCoordMungeUpper(mat[3] + mat[5]) - imgCoordMungeLower(mat[5]); + t1 = splashRound(mat[3] + mat[5]) - splashRound(mat[5]); } else { - t1 = imgCoordMungeUpper(mat[5]) - imgCoordMungeLower(mat[3] + mat[5]); + t1 = splashRound(mat[5]) - splashRound(mat[3] + mat[5]); } scaledHeight = t0 > t1 ? t0 : t1; if (scaledWidth == 0) { @@ -3334,7 +4712,8 @@ void Splash::arbitraryTransformImage(SplashImageSource src, void *srcData, // scale the input image scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, - srcWidth, srcHeight, scaledWidth, scaledHeight); + srcWidth, srcHeight, scaledWidth, scaledHeight, + interpolate); // construct the three sections i = 0; @@ -3354,8 +4733,8 @@ void Splash::arbitraryTransformImage(SplashImageSource src, void *srcData, 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; + section[0].y0 = splashRound(vy[i]); + section[0].y1 = splashRound(vy[(i+2) & 3]) - 1; if (vx[i] < vx[(i+1) & 3]) { section[0].ia0 = i; section[0].ia1 = (i+3) & 3; @@ -3369,8 +4748,8 @@ void Splash::arbitraryTransformImage(SplashImageSource src, void *srcData, } nSections = 1; } else { - section[0].y0 = imgCoordMungeLower(vy[i]); - section[2].y1 = imgCoordMungeUpper(vy[(i+2) & 3]) - 1; + section[0].y0 = splashRound(vy[i]); + section[2].y1 = splashRound(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]) { @@ -3381,8 +4760,8 @@ void Splash::arbitraryTransformImage(SplashImageSource src, void *srcData, 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]); + section[1].y0 = splashRound(vy[(i+1) & 3]); + section[2].y0 = splashRound(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; @@ -3395,8 +4774,8 @@ void Splash::arbitraryTransformImage(SplashImageSource src, void *srcData, section[1].ib1 = (i+2) & 3; } } else { - section[1].y0 = imgCoordMungeLower(vy[(i+3) & 3]); - section[2].y0 = imgCoordMungeUpper(vy[(i+1) & 3]); + section[1].y0 = splashRound(vy[(i+3) & 3]); + section[2].y0 = splashRound(vy[(i+1) & 3]); if (vx[(i+1) & 3] < vx[(i+3) & 3]) { section[1].ia0 = i; section[1].ia1 = (i+1) & 3; @@ -3429,13 +4808,9 @@ void Splash::arbitraryTransformImage(SplashImageSource src, void *srcData, } // initialize the pixel pipe - pipeInit(&pipe, 0, 0, NULL, pixel, + pipeInit(&pipe, NULL, (Guchar)splashRound(state->fillAlpha * 255), - srcAlpha || (vectorAntialias && clipRes != splashClipAllInside), - gFalse); - if (vectorAntialias) { - drawAAPixelInit(); - } + gTrue, gFalse); // make sure narrow images cover at least one pixel if (nSections == 1) { @@ -3450,24 +4825,46 @@ void Splash::arbitraryTransformImage(SplashImageSource src, void *srcData, } } + pixelBuf = (SplashColorPtr)gmallocn(xMax - xMin + 1, bitmapComps); + // 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); + xa = splashRound(section[i].xa0 + + ((SplashCoord)y + 0.5 - section[i].ya0) * + section[i].dxdya); + xb = splashRound(section[i].xb0 + + ((SplashCoord)y + 0.5 - section[i].yb0) * + section[i].dxdyb); + if (xa > xb) { + continue; + } // make sure narrow images cover at least one pixel if (xa == xb) { ++xb; } + // check the scanBuf bounds + if (xa >= bitmap->width || xb < 0) { + continue; + } + if (xa < 0) { + xa = 0; + } + if (xb > bitmap->width) { + xb = bitmap->width; + } + // clip the scan line + memset(scanBuf + xa, 0xff, xb - xa); if (clipRes != splashClipAllInside) { - clipRes2 = state->clip->testSpan(xa, xb - 1, y); - } else { - clipRes2 = clipRes; + if (vectorAntialias) { + state->clip->clipSpan(scanBuf, y, xa, xb - 1, + state->strokeAdjust); + } else { + state->clip->clipSpanBinary(scanBuf, y, xa, xb - 1, + state->strokeAdjust); + } } + // draw the scan line 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 + @@ -3486,21 +4883,19 @@ void Splash::arbitraryTransformImage(SplashImageSource src, void *srcData, } else if (yy >= scaledHeight) { yy = scaledHeight - 1; } - scaledImg->getPixel(xx, yy, pixel); + // get the color + scaledImg->getPixel(xx, yy, pixelBuf + (x - xa) * bitmapComps); + // apply alpha if (srcAlpha) { - pipe.shape = scaledImg->alpha[yy * scaledWidth + xx]; - } else { - pipe.shape = 255; - } - if (vectorAntialias && clipRes2 != splashClipAllInside) { - drawAAPixel(&pipe, x, y); - } else { - drawPixel(&pipe, x, y, clipRes2 == splashClipAllInside); + scanBuf[x] = div255(scanBuf[x] * + scaledImg->alpha[yy * scaledWidth + xx]); } } + (this->*pipe.run)(&pipe, xa, xb - 1, y, scanBuf + xa, pixelBuf); } } + gfree(pixelBuf); delete scaledImg; } @@ -3508,7 +4903,8 @@ void Splash::arbitraryTransformImage(SplashImageSource src, void *srcData, SplashBitmap *Splash::scaleImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight) { + int scaledWidth, int scaledHeight, + GBool interpolate) { SplashBitmap *dest; dest = new SplashBitmap(scaledWidth, scaledHeight, 1, srcMode, srcAlpha); @@ -3525,8 +4921,13 @@ SplashBitmap *Splash::scaleImage(SplashImageSource src, void *srcData, scaleImageYuXd(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } else { - scaleImageYuXu(src, srcData, srcMode, nComps, srcAlpha, - srcWidth, srcHeight, scaledWidth, scaledHeight, dest); + if (interpolate) { + scaleImageYuXuI(src, srcData, srcMode, nComps, srcAlpha, + srcWidth, srcHeight, scaledWidth, scaledHeight, dest); + } else { + scaleImageYuXu(src, srcData, srcMode, nComps, srcAlpha, + srcWidth, srcHeight, scaledWidth, scaledHeight, dest); + } } } return dest; @@ -3654,27 +5055,6 @@ void Splash::scaleImageYdXd(SplashImageSource src, void *srcData, *destPtr++ = (Guchar)pix2; break; - case splashModeBGR8: - - // compute the final pixel - pix0 = pix1 = pix2 = 0; - for (i = 0; i < xStep; ++i) { - pix0 += pixBuf[xx]; - pix1 += pixBuf[xx+1]; - pix2 += pixBuf[xx+2]; - xx += 3; - } - // pix / xStep * yStep - pix0 = (pix0 * d) >> 23; - pix1 = (pix1 * d) >> 23; - pix2 = (pix2 * d) >> 23; - - // store the pixel - *destPtr++ = (Guchar)pix2; - *destPtr++ = (Guchar)pix1; - *destPtr++ = (Guchar)pix0; - break; - #if SPLASH_CMYK case splashModeCMYK8: @@ -3703,6 +5083,7 @@ void Splash::scaleImageYdXd(SplashImageSource src, void *srcData, case splashModeMono1: // mono1 is not allowed + case splashModeBGR8: // bgr8 is not allowed default: break; } @@ -3812,8 +5193,6 @@ void Splash::scaleImageYdXu(SplashImageSource src, void *srcData, // store the pixel switch (srcMode) { - case splashModeMono1: // mono1 is not allowed - break; case splashModeMono8: for (i = 0; i < xStep; ++i) { *destPtr++ = (Guchar)pix[0]; @@ -3826,13 +5205,6 @@ void Splash::scaleImageYdXu(SplashImageSource src, void *srcData, *destPtr++ = (Guchar)pix[2]; } 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) { @@ -3843,6 +5215,10 @@ void Splash::scaleImageYdXu(SplashImageSource src, void *srcData, } break; #endif + case splashModeMono1: // mono1 is not allowed + case splashModeBGR8: // BGR8 is not allowed + default: + break; } // process alpha @@ -3942,8 +5318,6 @@ void Splash::scaleImageYuXd(SplashImageSource src, void *srcData, // 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; @@ -3958,14 +5332,6 @@ void Splash::scaleImageYuXd(SplashImageSource src, void *srcData, *destPtr++ = (Guchar)pix[2]; } break; - case splashModeBGR8: - for (i = 0; i < yStep; ++i) { - destPtr = destPtr0 + (i * scaledWidth + x) * nComps; - *destPtr++ = (Guchar)pix[2]; - *destPtr++ = (Guchar)pix[1]; - *destPtr++ = (Guchar)pix[0]; - } - break; #if SPLASH_CMYK case splashModeCMYK8: for (i = 0; i < yStep; ++i) { @@ -3977,6 +5343,10 @@ void Splash::scaleImageYuXd(SplashImageSource src, void *srcData, } break; #endif + case splashModeMono1: // mono1 is not allowed + case splashModeBGR8: // BGR8 is not allowed + default: + break; } // process alpha @@ -4071,8 +5441,6 @@ void Splash::scaleImageYuXu(SplashImageSource src, void *srcData, // 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) { @@ -4091,16 +5459,6 @@ void Splash::scaleImageYuXu(SplashImageSource src, void *srcData, } } break; - case splashModeBGR8: - for (i = 0; i < yStep; ++i) { - for (j = 0; j < xStep; ++j) { - destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; - *destPtr++ = (Guchar)pix[2]; - *destPtr++ = (Guchar)pix[1]; - *destPtr++ = (Guchar)pix[0]; - } - } - break; #if SPLASH_CMYK case splashModeCMYK8: for (i = 0; i < yStep; ++i) { @@ -4114,6 +5472,10 @@ void Splash::scaleImageYuXu(SplashImageSource src, void *srcData, } break; #endif + case splashModeMono1: // mono1 is not allowed + case splashModeBGR8: // BGR8 is not allowed + default: + break; } // process alpha @@ -4140,6 +5502,177 @@ void Splash::scaleImageYuXu(SplashImageSource src, void *srcData, gfree(lineBuf); } +void Splash::scaleImageYuXuI(SplashImageSource src, void *srcData, + SplashColorMode srcMode, int nComps, + GBool srcAlpha, int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight, + SplashBitmap *dest) { + Guchar *lineBuf0, *lineBuf1, *alphaLineBuf0, *alphaLineBuf1, *tBuf; + Guchar pix[splashMaxColorComps]; + SplashCoord yr, xr, ys, xs, ySrc, xSrc; + int ySrc0, ySrc1, yBuf, xSrc0, xSrc1, y, x, i; + Guchar *destPtr, *destAlphaPtr; + + // ratios + yr = (SplashCoord)srcHeight / (SplashCoord)scaledHeight; + xr = (SplashCoord)srcWidth / (SplashCoord)scaledWidth; + + // allocate buffers + lineBuf0 = (Guchar *)gmallocn(scaledWidth, nComps); + lineBuf1 = (Guchar *)gmallocn(scaledWidth, nComps); + if (srcAlpha) { + alphaLineBuf0 = (Guchar *)gmalloc(scaledWidth); + alphaLineBuf1 = (Guchar *)gmalloc(scaledWidth); + } else { + alphaLineBuf0 = NULL; + alphaLineBuf1 = NULL; + } + + // read first two rows + (*src)(srcData, lineBuf0, alphaLineBuf0); + if (srcHeight > 1) { + (*src)(srcData, lineBuf1, alphaLineBuf1); + yBuf = 1; + } else { + memcpy(lineBuf1, lineBuf0, srcWidth * nComps); + if (srcAlpha) { + memcpy(alphaLineBuf1, alphaLineBuf0, srcWidth); + } + yBuf = 0; + } + + // interpolate first two rows + for (x = scaledWidth - 1; x >= 0; --x) { + xSrc = xr * x; + xSrc0 = splashFloor(xSrc + xr * 0.5 - 0.5); + xSrc1 = xSrc0 + 1; + xs = ((SplashCoord)xSrc1 + 0.5) - (xSrc + xr * 0.5); + if (xSrc0 < 0) { + xSrc0 = 0; + } + if (xSrc1 >= srcWidth) { + xSrc1 = srcWidth - 1; + } + for (i = 0; i < nComps; ++i) { + lineBuf0[x*nComps+i] = (Guchar)(int) + (xs * lineBuf0[xSrc0*nComps+i] + + ((SplashCoord)1 - xs) * lineBuf0[xSrc1*nComps+i]); + lineBuf1[x*nComps+i] = (Guchar)(int) + (xs * lineBuf1[xSrc0*nComps+i] + + ((SplashCoord)1 - xs) * lineBuf1[xSrc1*nComps+i]); + } + if (srcAlpha) { + alphaLineBuf0[x] = (Guchar)(int) + (xs * alphaLineBuf0[xSrc0] + + ((SplashCoord)1 - xs) * alphaLineBuf0[xSrc1]); + alphaLineBuf1[x] = (Guchar)(int) + (xs * alphaLineBuf1[xSrc0] + + ((SplashCoord)1 - xs) * alphaLineBuf1[xSrc1]); + } + } + + destPtr = dest->data; + destAlphaPtr = dest->alpha; + for (y = 0; y < scaledHeight; ++y) { + + // compute vertical interpolation parameters + ySrc = yr * y; + ySrc0 = splashFloor(ySrc + yr * 0.5 - 0.5); + ySrc1 = ySrc0 + 1; + ys = ((SplashCoord)ySrc1 + 0.5) - (ySrc + yr * 0.5); + if (ySrc0 < 0) { + ySrc0 = 0; + ys = 1; + } + if (ySrc1 >= srcHeight) { + ySrc1 = srcHeight - 1; + ys = 0; + } + + // read another row (if necessary) + if (ySrc1 > yBuf) { + tBuf = lineBuf0; + lineBuf0 = lineBuf1; + lineBuf1 = tBuf; + tBuf = alphaLineBuf0; + alphaLineBuf0 = alphaLineBuf1; + alphaLineBuf1 = tBuf; + (*src)(srcData, lineBuf1, alphaLineBuf1); + + // interpolate the row + for (x = scaledWidth - 1; x >= 0; --x) { + xSrc = xr * x; + xSrc0 = splashFloor(xSrc + xr * 0.5 - 0.5); + xSrc1 = xSrc0 + 1; + xs = ((SplashCoord)xSrc1 + 0.5) - (xSrc + xr * 0.5); + if (xSrc0 < 0) { + xSrc0 = 0; + } + if (xSrc1 >= srcWidth) { + xSrc1 = srcWidth - 1; + } + for (i = 0; i < nComps; ++i) { + lineBuf1[x*nComps+i] = + (Guchar)(int)(xs * lineBuf1[xSrc0*nComps+i] + + ((SplashCoord)1 - xs) * lineBuf1[xSrc1*nComps+i]); + } + if (srcAlpha) { + alphaLineBuf1[x] = + (Guchar)(int)(xs * alphaLineBuf1[xSrc0] + + ((SplashCoord)1 - xs) * alphaLineBuf1[xSrc1]); + } + } + + ++yBuf; + } + + // do the vertical interpolation + for (x = 0; x < scaledWidth; ++x) { + + for (i = 0; i < nComps; ++i) { + pix[i] = (Guchar)(int)(ys * lineBuf0[x*nComps+i] + + ((SplashCoord)1 - ys) * lineBuf1[x*nComps+i]); + } + + // store the pixel + switch (srcMode) { + case splashModeMono8: + *destPtr++ = pix[0]; + break; + case splashModeRGB8: + *destPtr++ = pix[0]; + *destPtr++ = pix[1]; + *destPtr++ = pix[2]; + break; +#if SPLASH_CMYK + case splashModeCMYK8: + *destPtr++ = pix[0]; + *destPtr++ = pix[1]; + *destPtr++ = pix[2]; + *destPtr++ = pix[3]; + break; +#endif + case splashModeMono1: // mono1 is not allowed + case splashModeBGR8: // BGR8 is not allowed + default: + break; + } + + // process alpha + if (srcAlpha) { + *destAlphaPtr++ = (Guchar)(int) + (ys * alphaLineBuf0[x] + + ((SplashCoord)1 - ys) * alphaLineBuf1[x]); + } + } + } + + gfree(alphaLineBuf1); + gfree(alphaLineBuf0); + gfree(lineBuf1); + gfree(lineBuf0); +} + void Splash::vertFlipImage(SplashBitmap *img, int width, int height, int nComps) { Guchar *lineBuf; @@ -4167,12 +5700,43 @@ void Splash::vertFlipImage(SplashBitmap *img, int width, int height, gfree(lineBuf); } +void Splash::horizFlipImage(SplashBitmap *img, int width, int height, + int nComps) { + Guchar *lineBuf; + SplashColorPtr p0, p1, p2; + int w, x, y, i; + + w = width * nComps; + lineBuf = (Guchar *)gmalloc(w); + for (y = 0, p0 = img->data; y < height; ++y, p0 += img->rowSize) { + memcpy(lineBuf, p0, w); + p1 = p0; + p2 = lineBuf + (w - nComps); + for (x = 0; x < width; ++x) { + for (i = 0; i < nComps; ++i) { + p1[i] = p2[i]; + } + p1 += nComps; + p2 -= nComps; + } + } + if (img->alpha) { + for (y = 0, p0 = img->alpha; y < height; ++y, p0 += width) { + memcpy(lineBuf, p0, width); + p1 = p0; + p2 = lineBuf + (width - 1); + for (x = 0; x < width; ++x) { + *p1++ = *p2--; + } + } + } + gfree(lineBuf); +} + 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; + int w, h, x0, y0, x1, y1, y; // split the image into clipped and unclipped regions w = src->getWidth(); @@ -4200,8 +5764,8 @@ void Splash::blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest, x1 = x0; } if ((y1 = splashFloor(state->clip->getYMax()) - yDest) > h) { - y1 = h; - } + y1 = h; + } if (y1 < y0) { y1 = y0; } @@ -4210,31 +5774,24 @@ void Splash::blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest, // 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); + pipeInit(&pipe, NULL, + (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); - } + (this->*pipe.run)(&pipe, xDest + x0, xDest + x1 - 1, yDest + y, + src->getAlphaPtr() + y * w + x0, + src->getDataPtr() + y * src->getRowSize() + + x0 * bitmapComps); } } 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); - } + (this->*pipe.run)(&pipe, xDest + x0, xDest + x1 - 1, yDest + y, + NULL, + src->getDataPtr() + y * src->getRowSize() + + x0 * bitmapComps); } } - updateModX(xDest + x0); - updateModX(xDest + x1 - 1); - updateModY(yDest + y0); - updateModY(yDest + y1 - 1); } // draw the clipped regions @@ -4257,66 +5814,62 @@ 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; + int y; - if (vectorAntialias) { - pipeInit(&pipe, xDest, yDest, NULL, pixel, - (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); - drawAAPixelInit(); - if (srcAlpha) { - for (y = 0; y < h; ++y) { - ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc; - for (x = 0; x < w; ++x) { - src->getPixel(xSrc + x, ySrc + y, pixel); - pipe.shape = *ap++; - drawAAPixel(&pipe, xDest + x, yDest + y); - } - } - } else { - for (y = 0; y < h; ++y) { - for (x = 0; x < w; ++x) { - src->getPixel(xSrc + x, ySrc + y, pixel); - pipe.shape = 255; - drawAAPixel(&pipe, xDest + x, yDest + y); - } + if (xDest < 0) { + xSrc -= xDest; + w += xDest; + xDest = 0; + } + if (xDest + w > bitmap->width) { + w = bitmap->width - xDest; + } + if (yDest < 0) { + ySrc -= yDest; + h += yDest; + yDest = 0; + } + if (yDest + h > bitmap->height) { + h = bitmap->height - yDest; + } + if (w <= 0 || h <= 0) { + return; + } + + pipeInit(&pipe, NULL, + (Guchar)splashRound(state->fillAlpha * 255), + gTrue, gFalse); + if (srcAlpha) { + for (y = 0; y < h; ++y) { + memcpy(scanBuf + xDest, + src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc, + w); + if (vectorAntialias) { + state->clip->clipSpan(scanBuf, yDest + y, xDest, xDest + w - 1, + state->strokeAdjust); + } else { + state->clip->clipSpanBinary(scanBuf, yDest + y, xDest, xDest + w - 1, + state->strokeAdjust); } + (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, + scanBuf + xDest, + src->getDataPtr() + (ySrc + y) * src->getRowSize() + + xSrc * bitmapComps); } } 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); - } - } + for (y = 0; y < h; ++y) { + memset(scanBuf + xDest, 0xff, w); + if (vectorAntialias) { + state->clip->clipSpan(scanBuf, yDest + y, xDest, xDest + w - 1, + state->strokeAdjust); + } else { + state->clip->clipSpanBinary(scanBuf, yDest + y, xDest, xDest + w - 1, + state->strokeAdjust); } + (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, + scanBuf + xDest, + src->getDataPtr() + (ySrc + y) * src->getRowSize() + + xSrc * bitmapComps); } } } @@ -4325,82 +5878,82 @@ SplashError Splash::composite(SplashBitmap *src, int xSrc, int ySrc, int xDest, int yDest, int w, int h, GBool noClip, GBool nonIsolated) { SplashPipe pipe; - SplashColor pixel; - Guchar alpha; - Guchar *ap; - int x, y; + int x0, x1, y0, y1, y, t; if (src->mode != bitmap->mode) { return splashErrModeMismatch; } - if (src->alpha) { - pipeInit(&pipe, xDest, yDest, NULL, pixel, - (Guchar)splashRound(state->fillAlpha * 255), gTrue, nonIsolated); - if (noClip) { + pipeInit(&pipe, NULL, + (Guchar)splashRound(state->fillAlpha * 255), + !noClip || src->alpha != NULL, nonIsolated); + if (noClip) { + if (src->alpha) { 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); - } + // this uses shape instead of alpha, which isn't technically + // correct, but works out the same + (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, + src->getAlphaPtr() + + (ySrc + y) * src->getWidth() + xSrc, + src->getDataPtr() + (ySrc + y) * src->getRowSize() + + xSrc * bitmapComps); } - updateModX(xDest); - updateModX(xDest + w - 1); - updateModY(yDest); - updateModY(yDest + h - 1); } else { for (y = 0; y < h; ++y) { - pipeSetXY(&pipe, xDest, yDest + y); - ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc; - for (x = 0; x < w; ++x) { - src->getPixel(xSrc + x, ySrc + y, pixel); - alpha = *ap++; - if (state->clip->test(xDest + x, yDest + y)) { - // this uses shape instead of alpha, which isn't technically - // correct, but works out the same - pipe.shape = alpha; - (this->*pipe.run)(&pipe); - updateModX(xDest + x); - updateModY(yDest + y); - } else { - pipeIncX(&pipe); - } - } + (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, + NULL, + src->getDataPtr() + (ySrc + y) * src->getRowSize() + + xSrc * bitmapComps); } } } else { - pipeInit(&pipe, xDest, yDest, NULL, pixel, - (Guchar)splashRound(state->fillAlpha * 255), gFalse, nonIsolated); - if (noClip) { - for (y = 0; y < h; ++y) { - pipeSetXY(&pipe, xDest, yDest + y); - for (x = 0; x < w; ++x) { - src->getPixel(xSrc + x, ySrc + y, pixel); - (this->*pipe.run)(&pipe); + x0 = xDest; + if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) { + x0 = t; + } + x1 = xDest + w; + if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) { + x1 = t; + } + y0 = yDest; + if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) { + y0 = t; + } + y1 = yDest + h; + if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) { + y1 = t; + } + if (x0 < x1 && y0 < y1) { + if (src->alpha) { + for (y = y0; y < y1; ++y) { + memcpy(scanBuf + x0, + src->getAlphaPtr() + (ySrc + y - yDest) * src->getWidth() + + (xSrc + x0 - xDest), + x1 - x0); + if (!state->clip->clipSpanBinary(scanBuf, y, x0, x1 - 1, + state->strokeAdjust)) { + continue; + } + // this uses shape instead of alpha, which isn't technically + // correct, but works out the same + (this->*pipe.run)(&pipe, x0, x1 - 1, y, + scanBuf + x0, + src->getDataPtr() + + (ySrc + y - yDest) * src->getRowSize() + + (xSrc + x0 - xDest) * bitmapComps); } - } - 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); + } else { + for (y = y0; y < y1; ++y) { + memset(scanBuf + x0, 0xff, x1 - x0); + if (!state->clip->clipSpanBinary(scanBuf, y, x0, x1 - 1, + state->strokeAdjust)) { + continue; } + (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, + scanBuf + x0, + src->getDataPtr() + + (ySrc + y - yDest) * src->getRowSize() + + (xSrc - xDest) * bitmapComps); } } } @@ -4535,9 +6088,7 @@ SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc, for (y = 0; y < h; ++y) { p = &bitmap->data[(yDest + y) * bitmap->rowSize + xDest]; q = &src->data[(ySrc + y) * src->rowSize + xSrc]; - for (x = 0; x < w; ++x) { - *p++ = *q++; - } + memcpy(p, q, w); } break; case splashModeRGB8: @@ -4545,11 +6096,7 @@ SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc, for (y = 0; y < h; ++y) { p = &bitmap->data[(yDest + y) * bitmap->rowSize + 3 * xDest]; q = &src->data[(ySrc + y) * src->rowSize + 3 * xSrc]; - for (x = 0; x < w; ++x) { - *p++ = *q++; - *p++ = *q++; - *p++ = *q++; - } + memcpy(p, q, 3 * w); } break; #if SPLASH_CMYK @@ -4557,12 +6104,7 @@ SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc, for (y = 0; y < h; ++y) { p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest]; q = &src->data[(ySrc + y) * src->rowSize + 4 * xSrc]; - for (x = 0; x < w; ++x) { - *p++ = *q++; - *p++ = *q++; - *p++ = *q++; - *p++ = *q++; - } + memcpy(p, q, 4 * w); } break; #endif @@ -4571,9 +6113,7 @@ SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc, if (bitmap->alpha) { for (y = 0; y < h; ++y) { q = &bitmap->alpha[(yDest + y) * bitmap->width + xDest]; - for (x = 0; x < w; ++x) { - *q++ = 0x00; - } + memset(q, 0, w); } } @@ -5002,11 +6542,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\n", + printf(" %4d: x0=%8.2f y0=%8.2f x1=%8.2f y1=%8.2f count=%d\n", i, (double)path->segs[i].x0, (double)path->segs[i].y0, (double)path->segs[i].x1, (double)path->segs[i].y1, - (path->segs[i].flags & splashXPathHoriz) ? "H" : " ", - (path->segs[i].flags & splashXPathVert) ? "V" : " ", - (path->segs[i].flags & splashXPathFlip) ? "P" : " "); + path->segs[i].count); } } diff --git a/splash/Splash.h b/splash/Splash.h index 30761fb..7409e0c 100644 --- a/splash/Splash.h +++ b/splash/Splash.h @@ -2,6 +2,8 @@ // // Splash.h // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #ifndef SPLASH_H @@ -97,6 +99,7 @@ public: SplashClip *getClip(); SplashBitmap *getSoftMask(); GBool getInNonIsolatedGroup(); + GBool getInKnockoutGroup(); //----- state write @@ -125,8 +128,9 @@ public: // NB: uses untransformed coordinates. SplashError clipToPath(SplashPath *path, GBool eo); void setSoftMask(SplashBitmap *softMask); - void setInNonIsolatedGroup(SplashBitmap *alpha0BitmapA, - int alpha0XA, int alpha0YA); + void setInTransparencyGroup(SplashBitmap *groupBackBitmapA, + int groupBackXA, int groupBackYA, + GBool nonIsolated, GBool knockout); void setTransfer(Guchar *red, Guchar *green, Guchar *blue, Guchar *gray); void setOverprintMask(Guint overprintMask); @@ -172,7 +176,7 @@ public: // top line. SplashError fillImageMask(SplashImageMaskSource src, void *srcData, int w, int h, SplashCoord *mat, - GBool glyphMode); + GBool glyphMode, GBool interpolate); // Draw an image. This will read <h> lines of <w> pixels from // <src>, starting with the top line. These pixels are assumed to @@ -182,16 +186,16 @@ public: // are supported: // source target // ------ ------ - // Mono1 Mono1 // Mono8 Mono1 -- with dithering // Mono8 Mono8 // RGB8 RGB8 - // BGR8 BGR8 + // BGR8 RGB8 // CMYK8 CMYK8 // The matrix behaves as for fillImageMask. SplashError drawImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, GBool srcAlpha, - int w, int h, SplashCoord *mat); + int w, int h, SplashCoord *mat, + GBool interpolate); // Composite a rectangular region from <src> onto this Splash // object. @@ -245,37 +249,53 @@ public: private: - void pipeInit(SplashPipe *pipe, int x, int y, - SplashPattern *pattern, SplashColorPtr cSrc, + void pipeInit(SplashPipe *pipe, SplashPattern *pattern, Guchar aInput, GBool usesShape, GBool nonIsolatedGroup); - void pipeRun(SplashPipe *pipe); - void pipeRunSimpleMono1(SplashPipe *pipe); - void pipeRunSimpleMono8(SplashPipe *pipe); - void pipeRunSimpleRGB8(SplashPipe *pipe); - void pipeRunSimpleBGR8(SplashPipe *pipe); + void pipeRun(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr); + void pipeRunSimpleMono1(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr); + void pipeRunSimpleMono8(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr); + void pipeRunSimpleRGB8(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr); + void pipeRunSimpleBGR8(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr); +#if SPLASH_CMYK + void pipeRunSimpleCMYK8(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr); +#endif + void pipeRunShapeMono1(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr); + void pipeRunShapeMono8(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr); + void pipeRunShapeRGB8(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr); + void pipeRunShapeBGR8(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr); #if SPLASH_CMYK - void pipeRunSimpleCMYK8(SplashPipe *pipe); + void pipeRunShapeCMYK8(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr); #endif - void pipeRunAAMono1(SplashPipe *pipe); - void pipeRunAAMono8(SplashPipe *pipe); - void pipeRunAARGB8(SplashPipe *pipe); - void pipeRunAABGR8(SplashPipe *pipe); + void pipeRunAAMono1(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr); + void pipeRunAAMono8(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr); + void pipeRunAARGB8(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr); + void pipeRunAABGR8(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr); #if SPLASH_CMYK - void pipeRunAACMYK8(SplashPipe *pipe); + void pipeRunAACMYK8(SplashPipe *pipe, int x0, int x1, int y, + Guchar *shapePtr, SplashColorPtr cSrcPtr); #endif - void pipeSetXY(SplashPipe *pipe, int x, int y); - void pipeIncX(SplashPipe *pipe); - void drawPixel(SplashPipe *pipe, int x, int y, GBool noClip); - void drawAAPixelInit(); - void drawAAPixel(SplashPipe *pipe, int x, int y); - void drawSpan(SplashPipe *pipe, int x0, int x1, int y, GBool noClip); - void drawAALine(SplashPipe *pipe, int x0, int x1, int y); void transform(SplashCoord *matrix, SplashCoord xi, SplashCoord yi, SplashCoord *xo, SplashCoord *yo); void updateModX(int x); void updateModY(int y); void strokeNarrow(SplashPath *path); + void drawStrokeSpan(SplashPipe *pipe, int x0, int x1, int y, GBool noClip); void strokeWide(SplashPath *path, SplashCoord w); SplashPath *flattenPath(SplashPath *path, SplashCoord *matrix, SplashCoord flatness); @@ -288,14 +308,23 @@ private: SplashPath *makeDashedPath(SplashPath *xPath); SplashError fillWithPattern(SplashPath *path, GBool eo, SplashPattern *pattern, SplashCoord alpha); + SplashPath *tweakFillPath(SplashPath *path); GBool pathAllOutside(SplashPath *path); SplashError fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph); + void getImageBounds(SplashCoord xyMin, SplashCoord xyMax, + int *xyMinI, int *xyMaxI); + void upscaleMask(SplashImageMaskSource src, void *srcData, + int srcWidth, int srcHeight, + SplashCoord *mat, GBool glyphMode, + GBool interpolate); void arbitraryTransformMask(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, - SplashCoord *mat, GBool glyphMode); + SplashCoord *mat, GBool glyphMode, + GBool interpolate); SplashBitmap *scaleMask(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight); + int scaledWidth, int scaledHeight, + GBool interpolate); void scaleMaskYdXd(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, @@ -312,17 +341,26 @@ private: int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); + void scaleMaskYuXuI(SplashImageMaskSource src, void *srcData, + int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight, + SplashBitmap *dest); void blitMask(SplashBitmap *src, int xDest, int yDest, SplashClipResult clipRes); + void upscaleImage(SplashImageSource src, void *srcData, + SplashColorMode srcMode, int nComps, + GBool srcAlpha, int srcWidth, int srcHeight, + SplashCoord *mat, GBool interpolate); void arbitraryTransformImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, - SplashCoord *mat); + SplashCoord *mat, GBool interpolate); SplashBitmap *scaleImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, - int scaledWidth, int scaledHeight); + int scaledWidth, int scaledHeight, + GBool interpolate); void scaleImageYdXd(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, @@ -343,8 +381,15 @@ private: GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); + void scaleImageYuXuI(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 horizFlipImage(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, @@ -359,13 +404,13 @@ private: static int pipeNonIsoGroupCorrection[]; SplashBitmap *bitmap; + int bitmapComps; SplashState *state; - SplashBitmap *aaBuf; - int aaBufY; - SplashBitmap *alpha0Bitmap; // for non-isolated groups, this is the - // bitmap containing the alpha0 values - int alpha0X, alpha0Y; // offset within alpha0Bitmap - Guchar aaGamma[splashAASize * splashAASize + 1]; + Guchar *scanBuf; + SplashBitmap // for transparency groups, this is the bitmap + *groupBackBitmap; // containing the alpha0/color0 values + int groupBackX, groupBackY; // offset within groupBackBitmap + Guchar aaGamma[256]; SplashCoord minLineWidth; int modXMin, modYMin, modXMax, modYMax; SplashClipResult opClipRes; diff --git a/splash/SplashBitmap.cc b/splash/SplashBitmap.cc index 9fc7b04..3606a91 100644 --- a/splash/SplashBitmap.cc +++ b/splash/SplashBitmap.cc @@ -2,6 +2,8 @@ // // SplashBitmap.cc // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #include <aconf.h> diff --git a/splash/SplashBitmap.h b/splash/SplashBitmap.h index 78f37d6..3dfc939 100644 --- a/splash/SplashBitmap.h +++ b/splash/SplashBitmap.h @@ -2,6 +2,8 @@ // // SplashBitmap.h // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #ifndef SPLASHBITMAP_H diff --git a/splash/SplashClip.cc b/splash/SplashClip.cc index da85771..6865c57 100644 --- a/splash/SplashClip.cc +++ b/splash/SplashClip.cc @@ -2,6 +2,8 @@ // // SplashClip.cc // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #include <aconf.h> @@ -17,52 +19,52 @@ #include "SplashPath.h" #include "SplashXPath.h" #include "SplashXPathScanner.h" -#include "SplashBitmap.h" #include "SplashClip.h" //------------------------------------------------------------------------ -// SplashClip.flags -//------------------------------------------------------------------------ -#define splashClipEO 0x01 // use even-odd rule +// Compute x * y / 255, where x and y are in [0, 255]. +static inline Guchar mul255(Guchar x, Guchar y) { + int z; + + z = (int)x * (int)y; + return (Guchar)((z + (z >> 8) + 0x80) >> 8); +} //------------------------------------------------------------------------ // SplashClip //------------------------------------------------------------------------ -SplashClip::SplashClip(SplashCoord x0, SplashCoord y0, - SplashCoord x1, SplashCoord y1, - GBool antialiasA) { - antialias = antialiasA; - if (x0 < x1) { - xMin = x0; - xMax = x1; - } else { - xMin = x1; - xMax = x0; - } - if (y0 < y1) { - yMin = y0; - yMax = y1; - } else { - yMin = y1; - yMax = y0; - } - xMinI = splashFloor(xMin); - yMinI = splashFloor(yMin); - xMaxI = splashCeil(xMax) - 1; - yMaxI = splashCeil(yMax) - 1; +SplashClip::SplashClip(int hardXMinA, int hardYMinA, + int hardXMaxA, int hardYMaxA) { + int w; + + hardXMin = hardXMinA; + hardYMin = hardYMinA; + hardXMax = hardXMaxA; + hardYMax = hardYMaxA; + xMin = hardXMin; + yMin = hardYMin; + xMax = hardXMax; + yMax = hardYMax; + intBoundsValid = gFalse; paths = NULL; - flags = NULL; + eo = NULL; scanners = NULL; length = size = 0; + if ((w = hardXMax + 1) <= 0) { + w = 1; + } + buf = (Guchar *)gmalloc(w); } SplashClip::SplashClip(SplashClip *clip) { - int yMinAA, yMaxAA; - int i; + int w, i; - antialias = clip->antialias; + hardXMin = clip->hardXMin; + hardYMin = clip->hardYMin; + hardXMax = clip->hardXMax; + hardYMax = clip->hardYMax; xMin = clip->xMin; yMin = clip->yMin; xMax = clip->xMax; @@ -71,25 +73,23 @@ SplashClip::SplashClip(SplashClip *clip) { yMinI = clip->yMinI; xMaxI = clip->xMaxI; yMaxI = clip->yMaxI; + intBoundsValid = clip->intBoundsValid; + intBoundsStrokeAdjust = clip->intBoundsStrokeAdjust; length = clip->length; size = clip->size; paths = (SplashXPath **)gmallocn(size, sizeof(SplashXPath *)); - flags = (Guchar *)gmallocn(size, sizeof(Guchar)); + eo = (Guchar *)gmallocn(size, sizeof(Guchar)); scanners = (SplashXPathScanner **) gmallocn(size, sizeof(SplashXPathScanner *)); for (i = 0; i < length; ++i) { paths[i] = clip->paths[i]->copy(); - flags[i] = clip->flags[i]; - 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); + eo[i] = clip->eo[i]; + scanners[i] = new SplashXPathScanner(paths[i], eo[i], yMinI, yMaxI); + } + if ((w = splashCeil(xMax)) <= 0) { + w = 1; } + buf = (Guchar *)gmalloc(w); } SplashClip::~SplashClip() { @@ -100,8 +100,9 @@ SplashClip::~SplashClip() { delete scanners[i]; } gfree(paths); - gfree(flags); + gfree(eo); gfree(scanners); + gfree(buf); } void SplashClip::grow(int nPaths) { @@ -113,7 +114,7 @@ void SplashClip::grow(int nPaths) { size *= 2; } paths = (SplashXPath **)greallocn(paths, size, sizeof(SplashXPath *)); - flags = (Guchar *)greallocn(flags, size, sizeof(Guchar)); + eo = (Guchar *)greallocn(eo, size, sizeof(Guchar)); scanners = (SplashXPathScanner **) greallocn(scanners, size, sizeof(SplashXPathScanner *)); } @@ -121,17 +122,18 @@ void SplashClip::grow(int nPaths) { void SplashClip::resetToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1) { - int i; + int w, i; for (i = 0; i < length; ++i) { delete paths[i]; delete scanners[i]; } gfree(paths); - gfree(flags); + gfree(eo); gfree(scanners); + gfree(buf); paths = NULL; - flags = NULL; + eo = NULL; scanners = NULL; length = size = 0; @@ -149,10 +151,11 @@ void SplashClip::resetToRect(SplashCoord x0, SplashCoord y0, yMin = y1; yMax = y0; } - xMinI = splashFloor(xMin); - yMinI = splashFloor(yMin); - xMaxI = splashCeil(xMax) - 1; - yMaxI = splashCeil(yMax) - 1; + intBoundsValid = gFalse; + if ((w = splashCeil(xMax)) <= 0) { + w = 1; + } + buf = (Guchar *)gmalloc(w); } SplashError SplashClip::clipToRect(SplashCoord x0, SplashCoord y0, @@ -160,240 +163,358 @@ SplashError SplashClip::clipToRect(SplashCoord x0, SplashCoord y0, if (x0 < x1) { if (x0 > xMin) { xMin = x0; - xMinI = splashFloor(xMin); + intBoundsValid = gFalse; } if (x1 < xMax) { xMax = x1; - xMaxI = splashCeil(xMax) - 1; + intBoundsValid = gFalse; } } else { if (x1 > xMin) { xMin = x1; - xMinI = splashFloor(xMin); + intBoundsValid = gFalse; } if (x0 < xMax) { xMax = x0; - xMaxI = splashCeil(xMax) - 1; + intBoundsValid = gFalse; } } if (y0 < y1) { if (y0 > yMin) { yMin = y0; - yMinI = splashFloor(yMin); + intBoundsValid = gFalse; } if (y1 < yMax) { yMax = y1; - yMaxI = splashCeil(yMax) - 1; + intBoundsValid = gFalse; } } else { if (y1 > yMin) { yMin = y1; - yMinI = splashFloor(yMin); + intBoundsValid = gFalse; } if (y0 < yMax) { yMax = y0; - yMaxI = splashCeil(yMax) - 1; + intBoundsValid = gFalse; } } return splashOk; } SplashError SplashClip::clipToPath(SplashPath *path, SplashCoord *matrix, - SplashCoord flatness, GBool eo) { + SplashCoord flatness, GBool eoA) { SplashXPath *xPath; - int yMinAA, yMaxAA; + SplashCoord t; xPath = new SplashXPath(path, matrix, flatness, gTrue); // check for an empty path if (xPath->length == 0) { - xMax = xMin - 1; - yMax = yMin - 1; - xMaxI = splashCeil(xMax) - 1; - yMaxI = splashCeil(yMax) - 1; + xMin = yMin = 1; + xMax = yMax = 0; + intBoundsValid = gFalse; delete xPath; + return splashOk; + } // check for a rectangle - } else if (xPath->length == 4 && - ((xPath->segs[0].x0 == xPath->segs[0].x1 && - xPath->segs[0].x0 == xPath->segs[1].x0 && - xPath->segs[0].x0 == xPath->segs[3].x1 && - xPath->segs[2].x0 == xPath->segs[2].x1 && - xPath->segs[2].x0 == xPath->segs[1].x1 && - xPath->segs[2].x0 == xPath->segs[3].x0 && - xPath->segs[1].y0 == xPath->segs[1].y1 && - xPath->segs[1].y0 == xPath->segs[0].y1 && - xPath->segs[1].y0 == xPath->segs[2].y0 && - xPath->segs[3].y0 == xPath->segs[3].y1 && - xPath->segs[3].y0 == xPath->segs[0].y0 && - xPath->segs[3].y0 == xPath->segs[2].y1) || - (xPath->segs[0].y0 == xPath->segs[0].y1 && - xPath->segs[0].y0 == xPath->segs[1].y0 && - xPath->segs[0].y0 == xPath->segs[3].y1 && - xPath->segs[2].y0 == xPath->segs[2].y1 && - xPath->segs[2].y0 == xPath->segs[1].y1 && - xPath->segs[2].y0 == xPath->segs[3].y0 && - xPath->segs[1].x0 == xPath->segs[1].x1 && - xPath->segs[1].x0 == xPath->segs[0].x1 && - xPath->segs[1].x0 == xPath->segs[2].x0 && - xPath->segs[3].x0 == xPath->segs[3].x1 && - xPath->segs[3].x0 == xPath->segs[0].x0 && - xPath->segs[3].x0 == xPath->segs[2].x1))) { - clipToRect(xPath->segs[0].x0, xPath->segs[0].y0, - xPath->segs[2].x0, xPath->segs[2].y0); + if (xPath->length == 4 && + xPath->segs[0].y0 == xPath->segs[0].y1 && + xPath->segs[1].x0 == xPath->segs[1].x1 && + xPath->segs[2].x0 == xPath->segs[2].x1 && + xPath->segs[3].y0 == xPath->segs[3].y1) { + clipToRect(xPath->segs[1].x0, xPath->segs[0].y0, + xPath->segs[2].x0, xPath->segs[3].y0); + delete xPath; + return splashOk; + } + if (xPath->length == 4 && + xPath->segs[0].x0 == xPath->segs[0].x1 && + xPath->segs[1].y0 == xPath->segs[1].y1 && + xPath->segs[2].x0 == xPath->segs[2].x1 && + xPath->segs[3].y0 == xPath->segs[3].y1) { + clipToRect(xPath->segs[0].x0, xPath->segs[1].y0, + xPath->segs[2].x0, xPath->segs[3].y0); delete xPath; + return splashOk; + } + if (xPath->length == 4 && + xPath->segs[0].x0 == xPath->segs[0].x1 && + xPath->segs[1].x0 == xPath->segs[1].x1 && + xPath->segs[2].y0 == xPath->segs[2].y1 && + xPath->segs[3].y0 == xPath->segs[3].y1) { + clipToRect(xPath->segs[0].x0, xPath->segs[2].y0, + xPath->segs[1].x0, xPath->segs[3].y0); + delete xPath; + return splashOk; + } + grow(1); + paths[length] = xPath; + eo[length] = (Guchar)eoA; + if ((t = xPath->getXMin()) > xMin) { + xMin = t; + } + if ((t = xPath->getYMin()) > yMin) { + yMin = t; + } + if ((t = xPath->getXMax() + 1) < xMax) { + xMax = t; + } + if ((t = xPath->getYMax() + 1) < yMax) { + yMax = t; + } + intBoundsValid = gFalse; + scanners[length] = new SplashXPathScanner(xPath, eoA, splashFloor(yMin), + splashCeil(yMax) - 1); + ++length; + + return splashOk; +} + +SplashClipResult SplashClip::testRect(int rectXMin, int rectYMin, + int rectXMax, int rectYMax, + GBool strokeAdjust) { + // In general, this function tests the rectangle: + // x = [rectXMin, rectXMax + 1) (note: coords are ints) + // y = [rectYMin, rectYMax + 1) + // against the clipping region: + // x = [xMin, xMax) (note: coords are fp) + // y = [yMin, yMax) + + if (strokeAdjust && length == 0) { + // special case for stroke adjustment with a simple clipping + // rectangle -- the clipping region is: + // x = [xMinI, xMaxI + 1) + // y = [yMinI, yMaxI + 1) + updateIntBounds(strokeAdjust); + if (xMinI > xMaxI || yMinI > yMaxI) { + return splashClipAllOutside; + } + if (rectXMax + 1 <= xMinI || + rectXMin >= xMaxI + 1 || + rectYMax + 1 <= yMinI || + rectYMin >= yMaxI + 1) { + return splashClipAllOutside; + } + if (rectXMin >= xMinI && + rectXMax <= xMaxI && + rectYMin >= yMinI && + rectYMax <= yMaxI) { + return splashClipAllInside; + } } else { - grow(1); - if (antialias) { - xPath->aaScale(); + if (xMin >= xMax || yMin >= yMax) { + return splashClipAllOutside; } - xPath->sort(); - paths[length] = xPath; - flags[length] = eo ? splashClipEO : 0; - if (antialias) { - yMinAA = yMinI * splashAASize; - yMaxAA = (yMaxI + 1) * splashAASize - 1; - } else { - yMinAA = yMinI; - yMaxAA = yMaxI; + if ((SplashCoord)(rectXMax + 1) <= xMin || + (SplashCoord)rectXMin >= xMax || + (SplashCoord)(rectYMax + 1) <= yMin || + (SplashCoord)rectYMin >= yMax) { + return splashClipAllOutside; + } + if (length == 0 && + (SplashCoord)rectXMin >= xMin && + (SplashCoord)(rectXMax + 1) <= xMax && + (SplashCoord)rectYMin >= yMin && + (SplashCoord)(rectYMax + 1) <= yMax) { + return splashClipAllInside; } - scanners[length] = new SplashXPathScanner(xPath, eo, yMinAA, yMaxAA); - ++length; } - - return splashOk; + return splashClipPartial; } -GBool SplashClip::test(int x, int y) { - int i; +void SplashClip::clipSpan(Guchar *line, int y, int x0, int x1, + GBool strokeAdjust) { + SplashCoord d; + int x0a, x1a, x, i; - // check the rectangle - if (x < xMinI || x > xMaxI || y < yMinI || y > yMaxI) { - return gFalse; + updateIntBounds(strokeAdjust); + + //--- clip to the integer rectangle + + if (y < yMinI || y > yMaxI || + x1 < xMinI || x0 > xMaxI) { + memset(line + x0, 0, x1 - x0 + 1); + return; + } + + if (x0 > xMinI) { + x0a = x0; + } else { + x0a = xMinI; + memset(line + x0, 0, x0a - x0); + } + + if (x1 < xMaxI) { + x1a = x1; + } else { + x1a = xMaxI; + memset(line + x1a + 1, 0, x1 - x1a); + } + + if (x0a > x1a) { + return; } - // check the paths - if (antialias) { - for (i = 0; i < length; ++i) { - if (!scanners[i]->test(x * splashAASize, y * splashAASize)) { - return gFalse; + //--- clip to the floating point rectangle + // (if stroke adjustment is disabled) + + if (!strokeAdjust) { + + // clip left edge (xMin) + if (x0a == xMinI) { + d = (SplashCoord)(xMinI + 1) - xMin; + line[x0a] = (Guchar)(int)((SplashCoord)line[x0a] * d); + } + + // clip right edge (xMax) + if (x1a == xMaxI) { + d = xMax - (SplashCoord)xMaxI; + line[x1a] = (Guchar)(int)((SplashCoord)line[x1a] * d); + } + + // clip top edge (yMin) + if (y == yMinI) { + d = (SplashCoord)(yMinI + 1) - yMin; + for (x = x0a; x <= x1a; ++x) { + line[x] = (Guchar)(int)((SplashCoord)line[x] * d); } } - } else { - for (i = 0; i < length; ++i) { - if (!scanners[i]->test(x, y)) { - return gFalse; + + // clip bottom edge (yMax) + if (y == yMaxI) { + d = yMax - (SplashCoord)yMaxI; + for (x = x0a; x <= x1a; ++x) { + line[x] = (Guchar)(int)((SplashCoord)line[x] * d); } } } - return gTrue; -} - -SplashClipResult SplashClip::testRect(int rectXMin, int rectYMin, - int rectXMax, int rectYMax) { - // This tests the rectangle: - // 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) { - return splashClipAllOutside; + if (length == 0) { + return; } - if ((SplashCoord)rectXMin >= xMin && (SplashCoord)(rectXMax + 1) <= xMax && - (SplashCoord)rectYMin >= yMin && (SplashCoord)(rectYMax + 1) <= yMax && - length == 0) { - return splashClipAllInside; + + //--- clip to the paths + + for (i = 0; i < length; ++i) { + scanners[i]->getSpan(buf, y, x0a, x1a); + for (x = x0a; x <= x1a; ++x) { + line[x] = mul255(line[x], buf[x]); + } } - return splashClipPartial; } -SplashClipResult SplashClip::testSpan(int spanXMin, int spanXMax, int spanY) { - int i; +GBool SplashClip::clipSpanBinary(Guchar *line, int y, int x0, int x1, + GBool strokeAdjust) { + int x0a, x1a, x0b, x1b, x, i; + Guchar any; - // This tests the rectangle: - // 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) { - return splashClipAllOutside; - } - if (!((SplashCoord)spanXMin >= xMin && (SplashCoord)(spanXMax + 1) <= xMax && - (SplashCoord)spanY >= yMin && (SplashCoord)(spanY + 1) <= yMax)) { - return splashClipPartial; - } - if (antialias) { - for (i = 0; i < length; ++i) { - if (!scanners[i]->testSpan(spanXMin * splashAASize, - spanXMax * splashAASize + (splashAASize - 1), - spanY * splashAASize)) { - return splashClipPartial; - } + updateIntBounds(strokeAdjust); + + if (y < yMinI || y > yMaxI || + x1 < xMinI || x0 > xMaxI) { + if (x0 <= x1) { + memset(line + x0, 0, x1 - x0 + 1); } + return gFalse; + } + + if (x0 > xMinI) { + x0a = x0; + } else { + x0a = xMinI; + memset(line + x0, 0, x0a - x0); + } + + if (x1 < xMaxI) { + x1a = x1; } else { - for (i = 0; i < length; ++i) { - if (!scanners[i]->testSpan(spanXMin, spanXMax, spanY)) { - return splashClipPartial; + x1a = xMaxI; + memset(line + x1a + 1, 0, x1 - x1a); + } + + if (x0a > x1a) { + return gFalse; + } + + if (length == 0) { + for (x = x0a; x <= x1a; ++x) { + if (line[x]) { + return gTrue; } } + return gFalse; } - return splashClipAllInside; -} -void SplashClip::clipAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y) { - int xx0, xx1, xx, yy, i; - SplashColorPtr p; - - // zero out pixels with x < xMin - xx0 = *x0 * splashAASize; - xx1 = splashFloor(xMin * splashAASize); - if (xx1 > aaBuf->getWidth()) { - xx1 = aaBuf->getWidth(); - } - if (xx0 < xx1) { - xx0 &= ~7; - for (yy = 0; yy < splashAASize; ++yy) { - p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + (xx0 >> 3); - for (xx = xx0; xx + 7 < xx1; xx += 8) { - *p++ = 0; - } - if (xx < xx1) { - *p &= 0xff >> (xx1 & 7); - } + any = 0; + for (i = 0; i < length; ++i) { + scanners[i]->getSpanBinary(buf, y, x0a, x1a); + for (x0b = x0a; x0b <= x1a && !buf[x0b]; ++x0b) ; + if (x0a < x0b) { + memset(line + x0a, 0, x0b - x0a); } - *x0 = splashFloor(xMin); - } - - // zero out pixels with x > xMax - xx0 = splashFloor(xMax * splashAASize) + 1; - if (xx0 < 0) { - xx0 = 0; - } - xx1 = (*x1 + 1) * splashAASize; - if (xx0 < xx1) { - for (yy = 0; yy < splashAASize; ++yy) { - p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + (xx0 >> 3); - xx = xx0; - if (xx & 7) { - *p &= 0xff00 >> (xx & 7); - xx = (xx & ~7) + 8; - ++p; - } - for (; xx < xx1; xx += 8) { - *p++ = 0; - } + for (x1b = x1a; x1b >= x0b && !buf[x1b]; --x1b) ; + if (x1b < x1a) { + memset(line + x1b + 1, 0, x1a - x1b); + } + for (x = x0b; x <= x1b; ++x) { + line[x] &= buf[x]; + any |= line[x]; } - *x1 = splashFloor(xMax); } - // check the paths - for (i = 0; i < length; ++i) { - scanners[i]->clipAALine(aaBuf, x0, x1, y); + return any != 0; +} + +int SplashClip::getXMinI(GBool strokeAdjust) { + updateIntBounds(strokeAdjust); + return xMinI; +} + +int SplashClip::getXMaxI(GBool strokeAdjust) { + updateIntBounds(strokeAdjust); + return xMaxI; +} + +int SplashClip::getYMinI(GBool strokeAdjust) { + updateIntBounds(strokeAdjust); + return yMinI; +} + +int SplashClip::getYMaxI(GBool strokeAdjust) { + updateIntBounds(strokeAdjust); + return yMaxI; +} + +void SplashClip::updateIntBounds(GBool strokeAdjust) { + if (intBoundsValid && strokeAdjust == intBoundsStrokeAdjust) { + return; + } + if (strokeAdjust && length == 0) { + splashStrokeAdjust(xMin, xMax, &xMinI, &xMaxI); + splashStrokeAdjust(yMin, yMax, &yMinI, &yMaxI); + } else { + xMinI = splashFloor(xMin); + yMinI = splashFloor(yMin); + xMaxI = splashCeil(xMax); + yMaxI = splashCeil(yMax); + } + if (xMinI < hardXMin) { + xMinI = hardXMin; + } + if (yMinI < hardYMin) { + yMinI = hardYMin; + } + if (xMaxI > hardXMax) { + xMaxI = hardXMax; + } + if (yMaxI > hardYMax) { + yMaxI = hardYMax; } + // the clipping code uses [xMinI, xMaxI] instead of [xMinI, xMaxI) + --xMaxI; + --yMaxI; + intBoundsValid = gTrue; + intBoundsStrokeAdjust = strokeAdjust; } diff --git a/splash/SplashClip.h b/splash/SplashClip.h index 6f580f8..4298622 100644 --- a/splash/SplashClip.h +++ b/splash/SplashClip.h @@ -2,6 +2,8 @@ // // SplashClip.h // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #ifndef SPLASHCLIP_H @@ -37,9 +39,8 @@ class SplashClip { public: // Create a clip, for the given rectangle. - SplashClip(SplashCoord x0, SplashCoord y0, - SplashCoord x1, SplashCoord y1, - GBool antialiasA); + SplashClip(int hardXMinA, int hardYMinA, + int hardXMaxA, int hardYMaxA); // Copy a clip. SplashClip *copy() { return new SplashClip(this); } @@ -56,10 +57,7 @@ public: // Interesect the clip with <path>. SplashError clipToPath(SplashPath *path, SplashCoord *matrix, - SplashCoord flatness, GBool eo); - - // Returns true if (<x>,<y>) is inside the clip. - GBool test(int x, int y); + SplashCoord flatness, GBool eoA); // Tests a rectangle against the clipping region. Returns one of: // - splashClipAllInside if the entire rectangle is inside the @@ -71,15 +69,19 @@ public: // - splashClipPartial if the rectangle is part inside and part // outside the clipping region SplashClipResult testRect(int rectXMin, int rectYMin, - int rectXMax, int rectYMax); + int rectXMax, int rectYMax, + GBool strokeAdjust); - // Similar to testRect, but tests a horizontal span. - SplashClipResult testSpan(int spanXMin, int spanXMax, int spanY); + // Clip a scan line. Modifies line[] by multiplying with clipping + // shape values for one scan line: ([x0, x1], y). + void clipSpan(Guchar *line, int y, int x0, int x1, + GBool strokeAdjust); - // Clips an anti-aliased line by setting pixels to zero. On entry, - // all non-zero pixels are between <x0> and <x1>. This function - // will update <x0> and <x1>. - void clipAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y); + // Like clipSpan(), but uses the values 0 and 255 only. + // Returns true if there are any non-zero values in the result + // (i.e., returns false if the entire line is clipped out). + GBool clipSpanBinary(Guchar *line, int y, int x0, int x1, + GBool strokeAdjust); // Get the rectangle part of the clip region. SplashCoord getXMin() { return xMin; } @@ -88,10 +90,10 @@ public: SplashCoord getYMax() { return yMax; } // Get the rectangle part of the clip region, in integer coordinates. - int getXMinI() { return xMinI; } - int getXMaxI() { return xMaxI; } - int getYMinI() { return yMinI; } - int getYMaxI() { return yMaxI; } + int getXMinI(GBool strokeAdjust); + int getXMaxI(GBool strokeAdjust); + int getYMinI(GBool strokeAdjust); + int getYMaxI(GBool strokeAdjust); // Get the number of arbitrary paths used by the clip region. int getNumPaths() { return length; } @@ -100,14 +102,25 @@ private: SplashClip(SplashClip *clip); void grow(int nPaths); + void updateIntBounds(GBool strokeAdjust); + + int hardXMin, hardYMin, // coordinates cannot fall outside of + hardXMax, hardYMax; // [hardXMin, hardXMax), [hardYMin, hardYMax) + + SplashCoord xMin, yMin, // current clip bounding rectangle + xMax, yMax; // (these coordinates may be adjusted if + // stroke adjustment is enabled) - GBool antialias; - SplashCoord xMin, yMin, xMax, yMax; int xMinI, yMinI, xMaxI, yMaxI; + GBool intBoundsValid; // true if xMinI, etc. are valid + GBool intBoundsStrokeAdjust; // value of strokeAdjust used to compute + // xMinI, etc. + SplashXPath **paths; - Guchar *flags; + Guchar *eo; SplashXPathScanner **scanners; int length, size; + Guchar *buf; }; #endif diff --git a/splash/SplashErrorCodes.h b/splash/SplashErrorCodes.h index 2a70d4b..ebe6091 100644 --- a/splash/SplashErrorCodes.h +++ b/splash/SplashErrorCodes.h @@ -2,6 +2,8 @@ // // SplashErrorCodes.h // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #ifndef SPLASHERRORCODES_H diff --git a/splash/SplashFTFont.cc b/splash/SplashFTFont.cc index 218f13e..6fc6b94 100644 --- a/splash/SplashFTFont.cc +++ b/splash/SplashFTFont.cc @@ -2,6 +2,8 @@ // // SplashFTFont.cc // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #include <aconf.h> @@ -242,23 +244,24 @@ GBool SplashFTFont::makeGlyph(int c, int xFrac, int yFrac, return gFalse; } - flags = 0; - if (aa) { - flags |= FT_LOAD_NO_BITMAP; - } + // Set up the load flags: + // * disable bitmaps because they look ugly when scaled, rotated, + // etc. + // * disable autohinting because it can fail badly with font subsets + // that use invalid glyph names (the FreeType autohinter depends + // on the glyph name to figure out how to autohint the glyph) + // * but enable light autohinting for Type 1 fonts because regular + // hinting looks pretty bad, and the invalid glyph name issue + // seems to be very rare (Type 1 fonts are mostly used for + // substitution, in which case the full font is being used, which + // means we have the glyph names) + flags = FT_LOAD_NO_BITMAP; if (ff->engine->flags & splashFTNoHinting) { flags |= FT_LOAD_NO_HINTING; - } else if (ff->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) { - flags |= FT_LOAD_NO_AUTOHINT; - } - } else if (ff->type1) { - // Type 1 fonts seem to look better with 'light' hinting mode + } else if (ff->useLightHinting) { flags |= FT_LOAD_TARGET_LIGHT; + } else { + flags |= FT_LOAD_NO_AUTOHINT; } if (FT_Load_Glyph(ff->face, gid, flags)) { return gFalse; diff --git a/splash/SplashFTFont.h b/splash/SplashFTFont.h index 8e31d14..560b74c 100644 --- a/splash/SplashFTFont.h +++ b/splash/SplashFTFont.h @@ -2,6 +2,8 @@ // // SplashFTFont.h // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #ifndef SPLASHFTFONT_H diff --git a/splash/SplashFTFontEngine.cc b/splash/SplashFTFontEngine.cc index 9a4533a..3ba033c 100644 --- a/splash/SplashFTFontEngine.cc +++ b/splash/SplashFTFontEngine.cc @@ -2,6 +2,8 @@ // // SplashFTFontEngine.cc // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #include <aconf.h> @@ -13,7 +15,7 @@ #endif #include <stdio.h> -#ifndef WIN32 +#ifndef _WIN32 # include <unistd.h> #endif #include "gmem.h" @@ -23,6 +25,10 @@ #include "FoFiType1C.h" #include "SplashFTFontFile.h" #include "SplashFTFontEngine.h" +#include FT_MODULE_H +#ifdef FT_CFF_DRIVER_H +# include FT_CFF_DRIVER_H +#endif #ifdef VMS #if (__VMS_VER < 70000000) @@ -36,6 +42,12 @@ static void fileWrite(void *stream, const char *data, int len) { fwrite(data, 1, len, (FILE *)stream); } +#if LOAD_FONTS_FROM_MEM +static void gstringWrite(void *stream, const char *data, int len) { + ((GString *)stream)->append(data, len); +} +#endif + //------------------------------------------------------------------------ // SplashFTFontEngine //------------------------------------------------------------------------ @@ -68,29 +80,117 @@ SplashFTFontEngine::~SplashFTFontEngine() { } SplashFontFile *SplashFTFontEngine::loadType1Font(SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBuf, +#else char *fileName, GBool deleteFile, +#endif const char **enc) { - return SplashFTFontFile::loadType1Font(this, idA, fileName, deleteFile, enc); + return SplashFTFontFile::loadType1Font(this, idA, +#if LOAD_FONTS_FROM_MEM + fontBuf, +#else + fileName, deleteFile, +#endif + enc, gTrue); } SplashFontFile *SplashFTFontEngine::loadType1CFont(SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBuf, +#else char *fileName, GBool deleteFile, +#endif const char **enc) { - return SplashFTFontFile::loadType1Font(this, idA, fileName, deleteFile, enc); + return SplashFTFontFile::loadType1Font(this, idA, +#if LOAD_FONTS_FROM_MEM + fontBuf, +#else + fileName, deleteFile, +#endif + enc, gFalse); } SplashFontFile *SplashFTFontEngine::loadOpenTypeT1CFont(SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBuf, +#else char *fileName, GBool deleteFile, +#endif const char **enc) { - return SplashFTFontFile::loadType1Font(this, idA, fileName, deleteFile, enc); + FoFiTrueType *ff; +#if LOAD_FONTS_FROM_MEM + GString *fontBuf2; +#else + GString *tmpFileName; + FILE *tmpFile; +#endif + SplashFontFile *ret; + +#if LOAD_FONTS_FROM_MEM + if (!(ff = FoFiTrueType::make(fontBuf->getCString(), fontBuf->getLength(), + 0, gTrue))) { +#else + if (!(ff = FoFiTrueType::load(fileName, 0, gTrue))) { +#endif + return NULL; + } + if (ff->isHeadlessCFF()) { +#if LOAD_FONTS_FROM_MEM + fontBuf2 = new GString(); + ff->convertToType1(NULL, enc, gFalse, &gstringWrite, fontBuf2); + delete ff; + ret = SplashFTFontFile::loadType1Font(this, idA, fontBuf2, enc, + gFalse); + if (ret) { + delete fontBuf; + } else { + delete fontBuf2; + } +#else + tmpFileName = NULL; + if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) { + delete ff; + return NULL; + } + ff->convertToType1(NULL, enc, gFalse, &fileWrite, tmpFile); + delete ff; + fclose(tmpFile); + ret = SplashFTFontFile::loadType1Font(this, idA, tmpFileName->getCString(), + gTrue, enc, gFalse); + if (ret) { + if (deleteFile) { + unlink(fileName); + } + } else { + unlink(tmpFileName->getCString()); + } + delete tmpFileName; +#endif + } else { + delete ff; + ret = SplashFTFontFile::loadType1Font(this, idA, +#if LOAD_FONTS_FROM_MEM + fontBuf, +#else + fileName, deleteFile, +#endif + enc, gFalse); + } + return ret; } SplashFontFile *SplashFTFontEngine::loadCIDFont(SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBuf +#else char *fileName, - GBool deleteFile) { + GBool deleteFile +#endif + ) { FoFiType1C *ff; int *cidToGIDMap; int nCIDs; @@ -100,14 +200,24 @@ SplashFontFile *SplashFTFontEngine::loadCIDFont(SplashFontFileID *idA, if (useCIDs) { cidToGIDMap = NULL; nCIDs = 0; +#if LOAD_FONTS_FROM_MEM + } else if ((ff = FoFiType1C::make(fontBuf->getCString(), + fontBuf->getLength()))) { +#else } else if ((ff = FoFiType1C::load(fileName))) { +#endif cidToGIDMap = ff->getCIDToGIDMap(&nCIDs); delete ff; } else { cidToGIDMap = NULL; nCIDs = 0; } - ret = SplashFTFontFile::loadCIDFont(this, idA, fileName, deleteFile, + ret = SplashFTFontFile::loadCIDFont(this, idA, +#if LOAD_FONTS_FROM_MEM + fontBuf, +#else + fileName, deleteFile, +#endif cidToGIDMap, nCIDs); if (!ret) { gfree(cidToGIDMap); @@ -116,32 +226,90 @@ SplashFontFile *SplashFTFontEngine::loadCIDFont(SplashFontFileID *idA, } SplashFontFile *SplashFTFontEngine::loadOpenTypeCFFFont(SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBuf, +#else char *fileName, GBool deleteFile, +#endif int *codeToGID, int codeToGIDLen) { FoFiTrueType *ff; - GBool isCID; +#if LOAD_FONTS_FROM_MEM + GString *fontBuf2; +#else + GString *tmpFileName; + FILE *tmpFile; +#endif + char *cffStart; + int cffLength; int *cidToGIDMap; int nCIDs; SplashFontFile *ret; +#if LOAD_FONTS_FROM_MEM + if (!(ff = FoFiTrueType::make(fontBuf->getCString(), fontBuf->getLength(), + 0, gTrue))) { +#else + if (!(ff = FoFiTrueType::load(fileName, 0, gTrue))) { +#endif + return NULL; + } cidToGIDMap = NULL; nCIDs = 0; - isCID = gFalse; - if (!codeToGID) { + if (ff->isHeadlessCFF()) { + if (!ff->getCFFBlock(&cffStart, &cffLength)) { + return NULL; + } +#if LOAD_FONTS_FROM_MEM + fontBuf2 = new GString(cffStart, cffLength); + if (!useCIDs) { + cidToGIDMap = ff->getCIDToGIDMap(&nCIDs); + } + ret = SplashFTFontFile::loadCIDFont(this, idA, fontBuf2, + cidToGIDMap, nCIDs); + if (ret) { + delete fontBuf; + } else { + delete fontBuf2; + } +#else + tmpFileName = NULL; + if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) { + delete ff; + return NULL; + } + fwrite(cffStart, 1, cffLength, tmpFile); + fclose(tmpFile); if (!useCIDs) { - if ((ff = FoFiTrueType::load(fileName))) { - if (ff->isOpenTypeCFF()) { - cidToGIDMap = ff->getCIDToGIDMap(&nCIDs); - } - delete ff; + cidToGIDMap = ff->getCIDToGIDMap(&nCIDs); + } + ret = SplashFTFontFile::loadCIDFont(this, idA, + tmpFileName->getCString(), gTrue, + cidToGIDMap, nCIDs); + if (ret) { + if (deleteFile) { + unlink(fileName); } + } else { + unlink(tmpFileName->getCString()); + } + delete tmpFileName; +#endif + } else { + if (!codeToGID && !useCIDs && ff->isOpenTypeCFF()) { + cidToGIDMap = ff->getCIDToGIDMap(&nCIDs); } + ret = SplashFTFontFile::loadCIDFont(this, idA, +#if LOAD_FONTS_FROM_MEM + fontBuf, +#else + fileName, deleteFile, +#endif + codeToGID ? codeToGID : cidToGIDMap, + codeToGID ? codeToGIDLen : nCIDs); } - ret = SplashFTFontFile::loadCIDFont(this, idA, fileName, deleteFile, - codeToGID ? codeToGID : cidToGIDMap, - codeToGID ? codeToGIDLen : nCIDs); + delete ff; if (!ret) { gfree(cidToGIDMap); } @@ -149,31 +317,59 @@ SplashFontFile *SplashFTFontEngine::loadOpenTypeCFFFont(SplashFontFileID *idA, } SplashFontFile *SplashFTFontEngine::loadTrueTypeFont(SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBuf, +#else char *fileName, - int fontNum, GBool deleteFile, +#endif + int fontNum, int *codeToGID, int codeToGIDLen) { FoFiTrueType *ff; +#if LOAD_FONTS_FROM_MEM + GString *fontBuf2; +#else GString *tmpFileName; FILE *tmpFile; +#endif SplashFontFile *ret; - //~ this should use fontNum to load the correct font - if (!(ff = FoFiTrueType::load(fileName))) { +#if LOAD_FONTS_FROM_MEM + if (!(ff = FoFiTrueType::make(fontBuf->getCString(), fontBuf->getLength(), + fontNum))) { +#else + if (!(ff = FoFiTrueType::load(fileName, fontNum))) { +#endif return NULL; } +#if LOAD_FONTS_FROM_MEM + fontBuf2 = new GString; + ff->writeTTF(&gstringWrite, fontBuf2); +#else tmpFileName = NULL; if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) { delete ff; return NULL; } ff->writeTTF(&fileWrite, tmpFile); - delete ff; fclose(tmpFile); +#endif + delete ff; ret = SplashFTFontFile::loadTrueTypeFont(this, idA, - tmpFileName->getCString(), fontNum, - gTrue, codeToGID, codeToGIDLen); +#if LOAD_FONTS_FROM_MEM + fontBuf2, +#else + tmpFileName->getCString(), gTrue, +#endif + 0, codeToGID, codeToGIDLen); +#if LOAD_FONTS_FROM_MEM + if (ret) { + delete fontBuf; + } else { + delete fontBuf2; + } +#else if (ret) { if (deleteFile) { unlink(fileName); @@ -182,6 +378,7 @@ SplashFontFile *SplashFTFontEngine::loadTrueTypeFont(SplashFontFileID *idA, unlink(tmpFileName->getCString()); } delete tmpFileName; +#endif return ret; } diff --git a/splash/SplashFTFontEngine.h b/splash/SplashFTFontEngine.h index 900bff2..37e8820 100644 --- a/splash/SplashFTFontEngine.h +++ b/splash/SplashFTFontEngine.h @@ -2,6 +2,8 @@ // // SplashFTFontEngine.h // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #ifndef SPLASHFTFONTENGINE_H @@ -18,6 +20,7 @@ #include <ft2build.h> #include FT_FREETYPE_H #include "gtypes.h" +class GString; class SplashFontFile; class SplashFontFileID; @@ -34,19 +37,48 @@ public: ~SplashFTFontEngine(); // Load fonts. - SplashFontFile *loadType1Font(SplashFontFileID *idA, char *fileName, - GBool deleteFile, const char **enc); - SplashFontFile *loadType1CFont(SplashFontFileID *idA, char *fileName, - GBool deleteFile, const char **enc); - SplashFontFile *loadOpenTypeT1CFont(SplashFontFileID *idA, char *fileName, - GBool deleteFile, const char **enc); - SplashFontFile *loadCIDFont(SplashFontFileID *idA, char *fileName, - GBool deleteFile); - SplashFontFile *loadOpenTypeCFFFont(SplashFontFileID *idA, char *fileName, - GBool deleteFile, + SplashFontFile *loadType1Font(SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBuf, +#else + char *fileName, GBool deleteFile, +#endif + const char **enc); + SplashFontFile *loadType1CFont(SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBuf, +#else + char *fileName, GBool deleteFile, +#endif + const char **enc); + SplashFontFile *loadOpenTypeT1CFont(SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBuf, +#else + char *fileName, GBool deleteFile, +#endif + const char **enc); + SplashFontFile *loadCIDFont(SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBuf +#else + char *fileName, GBool deleteFile +#endif + ); + SplashFontFile *loadOpenTypeCFFFont(SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBuf, +#else + char *fileName, GBool deleteFile, +#endif int *codeToGID, int codeToGIDLen); - SplashFontFile *loadTrueTypeFont(SplashFontFileID *idA, char *fileName, - int fontNum, GBool deleteFile, + SplashFontFile *loadTrueTypeFont(SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBuf, +#else + char *fileName, GBool deleteFile, +#endif + int fontNum, int *codeToGID, int codeToGIDLen); private: diff --git a/splash/SplashFTFontFile.cc b/splash/SplashFTFontFile.cc index 5761117..5b01fcf 100644 --- a/splash/SplashFTFontFile.cc +++ b/splash/SplashFTFontFile.cc @@ -2,6 +2,8 @@ // // SplashFTFontFile.cc // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #include <aconf.h> @@ -13,6 +15,7 @@ #endif #include "gmem.h" +#include "GString.h" #include "SplashFTFontEngine.h" #include "SplashFTFont.h" #include "SplashFTFontFile.h" @@ -23,15 +26,25 @@ SplashFontFile *SplashFTFontFile::loadType1Font(SplashFTFontEngine *engineA, SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBufA, +#else char *fileNameA, GBool deleteFileA, - const char **encA) { +#endif + const char **encA, + GBool useLightHintingA) { FT_Face faceA; int *codeToGIDA; const char *name; int i; +#if LOAD_FONTS_FROM_MEM + if (FT_New_Memory_Face(engineA->lib, (FT_Byte *)fontBufA->getCString(), + fontBufA->getLength(), 0, &faceA)) { +#else if (FT_New_Face(engineA->lib, fileNameA, 0, &faceA)) { +#endif return NULL; } codeToGIDA = (int *)gmallocn(256, sizeof(int)); @@ -42,59 +55,101 @@ SplashFontFile *SplashFTFontFile::loadType1Font(SplashFTFontEngine *engineA, } } - return new SplashFTFontFile(engineA, idA, fileNameA, deleteFileA, - faceA, codeToGIDA, 256, gFalse, gTrue); + return new SplashFTFontFile(engineA, idA, +#if LOAD_FONTS_FROM_MEM + fontBufA, +#else + fileNameA, deleteFileA, +#endif + faceA, codeToGIDA, 256, + gFalse, useLightHintingA); } SplashFontFile *SplashFTFontFile::loadCIDFont(SplashFTFontEngine *engineA, SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBufA, +#else char *fileNameA, GBool deleteFileA, +#endif int *codeToGIDA, int codeToGIDLenA) { FT_Face faceA; +#if LOAD_FONTS_FROM_MEM + if (FT_New_Memory_Face(engineA->lib, (FT_Byte *)fontBufA->getCString(), + fontBufA->getLength(), 0, &faceA)) { +#else if (FT_New_Face(engineA->lib, fileNameA, 0, &faceA)) { +#endif return NULL; } - return new SplashFTFontFile(engineA, idA, fileNameA, deleteFileA, + return new SplashFTFontFile(engineA, idA, +#if LOAD_FONTS_FROM_MEM + fontBufA, +#else + fileNameA, deleteFileA, +#endif faceA, codeToGIDA, codeToGIDLenA, gFalse, gFalse); } SplashFontFile *SplashFTFontFile::loadTrueTypeFont(SplashFTFontEngine *engineA, SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBufA, +#else char *fileNameA, - int fontNum, GBool deleteFileA, +#endif + int fontNum, int *codeToGIDA, int codeToGIDLenA) { FT_Face faceA; +#if LOAD_FONTS_FROM_MEM + if (FT_New_Memory_Face(engineA->lib, (FT_Byte *)fontBufA->getCString(), + fontBufA->getLength(), fontNum, &faceA)) { +#else if (FT_New_Face(engineA->lib, fileNameA, fontNum, &faceA)) { +#endif return NULL; } - return new SplashFTFontFile(engineA, idA, fileNameA, deleteFileA, + return new SplashFTFontFile(engineA, idA, +#if LOAD_FONTS_FROM_MEM + fontBufA, +#else + fileNameA, deleteFileA, +#endif faceA, codeToGIDA, codeToGIDLenA, gTrue, gFalse); } SplashFTFontFile::SplashFTFontFile(SplashFTFontEngine *engineA, SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBufA, +#else char *fileNameA, GBool deleteFileA, +#endif FT_Face faceA, int *codeToGIDA, int codeToGIDLenA, - GBool trueTypeA, GBool type1A): + GBool trueTypeA, GBool useLightHintingA): +#if LOAD_FONTS_FROM_MEM + SplashFontFile(idA, fontBufA) +#else SplashFontFile(idA, fileNameA, deleteFileA) +#endif { engine = engineA; face = faceA; codeToGID = codeToGIDA; codeToGIDLen = codeToGIDLenA; trueType = trueTypeA; - type1 = type1A; + useLightHinting = useLightHintingA; } SplashFTFontFile::~SplashFTFontFile() { diff --git a/splash/SplashFTFontFile.h b/splash/SplashFTFontFile.h index 0f45e4f..21fa517 100644 --- a/splash/SplashFTFontFile.h +++ b/splash/SplashFTFontFile.h @@ -2,6 +2,8 @@ // // SplashFTFontFile.h // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #ifndef SPLASHFTFONTFILE_H @@ -30,17 +32,31 @@ class SplashFTFontFile: public SplashFontFile { public: static SplashFontFile *loadType1Font(SplashFTFontEngine *engineA, - SplashFontFileID *idA, char *fileNameA, - GBool deleteFileA, const char **encA); + SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBufA, +#else + char *fileNameA, GBool deleteFileA, +#endif + const char **encA, + GBool useLightHintingA); static SplashFontFile *loadCIDFont(SplashFTFontEngine *engineA, - SplashFontFileID *idA, char *fileNameA, - GBool deleteFileA, + SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBufA, +#else + char *fileNameA, GBool deleteFileA, +#endif int *codeToGIDA, int codeToGIDLenA); static SplashFontFile *loadTrueTypeFont(SplashFTFontEngine *engineA, SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBufA, +#else char *fileNameA, - int fontNum, GBool deleteFileA, +#endif + int fontNum, int *codeToGIDA, int codeToGIDLenA); @@ -55,17 +71,21 @@ private: SplashFTFontFile(SplashFTFontEngine *engineA, SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBufA, +#else char *fileNameA, GBool deleteFileA, +#endif FT_Face faceA, int *codeToGIDA, int codeToGIDLenA, - GBool trueTypeA, GBool type1A); + GBool trueTypeA, GBool useLightHintingA); SplashFTFontEngine *engine; FT_Face face; int *codeToGID; int codeToGIDLen; GBool trueType; - GBool type1; + GBool useLightHinting; friend class SplashFTFont; }; diff --git a/splash/SplashFont.cc b/splash/SplashFont.cc index 6ab2ecf..37a3200 100644 --- a/splash/SplashFont.cc +++ b/splash/SplashFont.cc @@ -2,6 +2,8 @@ // // SplashFont.cc // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #include <aconf.h> diff --git a/splash/SplashFont.h b/splash/SplashFont.h index 60a2db7..3d15112 100644 --- a/splash/SplashFont.h +++ b/splash/SplashFont.h @@ -2,6 +2,8 @@ // // SplashFont.h // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #ifndef SPLASHFONT_H diff --git a/splash/SplashFontEngine.cc b/splash/SplashFontEngine.cc index 5f5ad46..d4673a7 100644 --- a/splash/SplashFontEngine.cc +++ b/splash/SplashFontEngine.cc @@ -2,6 +2,8 @@ // // SplashFontEngine.cc // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #include <aconf.h> @@ -10,19 +12,14 @@ #pragma implementation #endif -#if HAVE_T1LIB_H -#include <t1lib.h> -#endif - #include <stdlib.h> #include <stdio.h> -#ifndef WIN32 +#ifndef _WIN32 # include <unistd.h> #endif #include "gmem.h" #include "GString.h" #include "SplashMath.h" -#include "SplashT1FontEngine.h" #include "SplashFTFontEngine.h" #include "SplashFontFile.h" #include "SplashFontFileID.h" @@ -40,9 +37,6 @@ extern "C" int unlink(char *filename); //------------------------------------------------------------------------ SplashFontEngine::SplashFontEngine( -#if HAVE_T1LIB_H - GBool enableT1lib, -#endif #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H GBool enableFreeType, Guint freeTypeFlags, @@ -54,13 +48,6 @@ SplashFontEngine::SplashFontEngine( fontCache[i] = NULL; } -#if HAVE_T1LIB_H - if (enableT1lib) { - t1Engine = SplashT1FontEngine::init(aa); - } else { - t1Engine = NULL; - } -#endif #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H if (enableFreeType) { ftEngine = SplashFTFontEngine::init(aa, freeTypeFlags); @@ -79,11 +66,6 @@ SplashFontEngine::~SplashFontEngine() { } } -#if HAVE_T1LIB_H - if (t1Engine) { - delete t1Engine; - } -#endif #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H if (ftEngine) { delete ftEngine; @@ -107,24 +89,29 @@ SplashFontFile *SplashFontEngine::getFontFile(SplashFontFileID *id) { } SplashFontFile *SplashFontEngine::loadType1Font(SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBuf, +#else char *fileName, GBool deleteFile, +#endif const char **enc) { SplashFontFile *fontFile; fontFile = NULL; -#if HAVE_T1LIB_H - if (!fontFile && t1Engine) { - fontFile = t1Engine->loadType1Font(idA, fileName, deleteFile, enc); - } -#endif #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H if (!fontFile && ftEngine) { - fontFile = ftEngine->loadType1Font(idA, fileName, deleteFile, enc); + fontFile = ftEngine->loadType1Font(idA, +#if LOAD_FONTS_FROM_MEM + fontBuf, +#else + fileName, deleteFile, +#endif + enc); } #endif -#ifndef WIN32 +#if !LOAD_FONTS_FROM_MEM && !defined(_WIN32) // delete the (temporary) font file -- with Unix hard link // semantics, this will remove the last link; otherwise it will // return an error, leaving the file to be deleted later (if @@ -138,24 +125,29 @@ SplashFontFile *SplashFontEngine::loadType1Font(SplashFontFileID *idA, } SplashFontFile *SplashFontEngine::loadType1CFont(SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBuf, +#else char *fileName, GBool deleteFile, +#endif const char **enc) { SplashFontFile *fontFile; fontFile = NULL; -#if HAVE_T1LIB_H - if (!fontFile && t1Engine) { - fontFile = t1Engine->loadType1CFont(idA, fileName, deleteFile, enc); - } -#endif #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H if (!fontFile && ftEngine) { - fontFile = ftEngine->loadType1CFont(idA, fileName, deleteFile, enc); + fontFile = ftEngine->loadType1CFont(idA, +#if LOAD_FONTS_FROM_MEM + fontBuf, +#else + fileName, deleteFile, +#endif + enc); } #endif -#ifndef WIN32 +#if !LOAD_FONTS_FROM_MEM && !defined(_WIN32) // delete the (temporary) font file -- with Unix hard link // semantics, this will remove the last link; otherwise it will // return an error, leaving the file to be deleted later (if @@ -169,19 +161,29 @@ SplashFontFile *SplashFontEngine::loadType1CFont(SplashFontFileID *idA, } SplashFontFile *SplashFontEngine::loadOpenTypeT1CFont(SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBuf, +#else char *fileName, GBool deleteFile, +#endif const char **enc) { SplashFontFile *fontFile; fontFile = NULL; #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H if (!fontFile && ftEngine) { - fontFile = ftEngine->loadOpenTypeT1CFont(idA, fileName, deleteFile, enc); + fontFile = ftEngine->loadOpenTypeT1CFont(idA, +#if LOAD_FONTS_FROM_MEM + fontBuf, +#else + fileName, deleteFile, +#endif + enc); } #endif -#ifndef WIN32 +#if !LOAD_FONTS_FROM_MEM && !defined(_WIN32) // delete the (temporary) font file -- with Unix hard link // semantics, this will remove the last link; otherwise it will // return an error, leaving the file to be deleted later (if @@ -195,18 +197,29 @@ SplashFontFile *SplashFontEngine::loadOpenTypeT1CFont(SplashFontFileID *idA, } SplashFontFile *SplashFontEngine::loadCIDFont(SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBuf +#else char *fileName, - GBool deleteFile) { + GBool deleteFile +#endif + ) { SplashFontFile *fontFile; fontFile = NULL; #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H if (!fontFile && ftEngine) { - fontFile = ftEngine->loadCIDFont(idA, fileName, deleteFile); + fontFile = ftEngine->loadCIDFont(idA, +#if LOAD_FONTS_FROM_MEM + fontBuf +#else + fileName, deleteFile +#endif + ); } #endif -#ifndef WIN32 +#if !LOAD_FONTS_FROM_MEM && !defined(_WIN32) // delete the (temporary) font file -- with Unix hard link // semantics, this will remove the last link; otherwise it will // return an error, leaving the file to be deleted later (if @@ -220,8 +233,12 @@ SplashFontFile *SplashFontEngine::loadCIDFont(SplashFontFileID *idA, } SplashFontFile *SplashFontEngine::loadOpenTypeCFFFont(SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBuf, +#else char *fileName, GBool deleteFile, +#endif int *codeToGID, int codeToGIDLen) { SplashFontFile *fontFile; @@ -229,12 +246,17 @@ SplashFontFile *SplashFontEngine::loadOpenTypeCFFFont(SplashFontFileID *idA, fontFile = NULL; #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H if (!fontFile && ftEngine) { - fontFile = ftEngine->loadOpenTypeCFFFont(idA, fileName, deleteFile, + fontFile = ftEngine->loadOpenTypeCFFFont(idA, +#if LOAD_FONTS_FROM_MEM + fontBuf, +#else + fileName, deleteFile, +#endif codeToGID, codeToGIDLen); } #endif -#ifndef WIN32 +#if !LOAD_FONTS_FROM_MEM && !defined(_WIN32) // delete the (temporary) font file -- with Unix hard link // semantics, this will remove the last link; otherwise it will // return an error, leaving the file to be deleted later (if @@ -248,9 +270,13 @@ SplashFontFile *SplashFontEngine::loadOpenTypeCFFFont(SplashFontFileID *idA, } SplashFontFile *SplashFontEngine::loadTrueTypeFont(SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBuf, +#else char *fileName, - int fontNum, GBool deleteFile, +#endif + int fontNum, int *codeToGID, int codeToGIDLen, char *fontName) { @@ -259,8 +285,13 @@ SplashFontFile *SplashFontEngine::loadTrueTypeFont(SplashFontFileID *idA, fontFile = NULL; #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H if (!fontFile && ftEngine) { - fontFile = ftEngine->loadTrueTypeFont(idA, fileName, fontNum, deleteFile, - codeToGID, codeToGIDLen); + fontFile = ftEngine->loadTrueTypeFont(idA, +#if LOAD_FONTS_FROM_MEM + fontBuf, +#else + fileName, deleteFile, +#endif + fontNum, codeToGID, codeToGIDLen); } #endif @@ -268,7 +299,7 @@ SplashFontFile *SplashFontEngine::loadTrueTypeFont(SplashFontFileID *idA, gfree(codeToGID); } -#ifndef WIN32 +#if !LOAD_FONTS_FROM_MEM && !defined(_WIN32) // delete the (temporary) font file -- with Unix hard link // semantics, this will remove the last link; otherwise it will // return an error, leaving the file to be deleted later (if diff --git a/splash/SplashFontEngine.h b/splash/SplashFontEngine.h index a6d0e58..43eb6d3 100644 --- a/splash/SplashFontEngine.h +++ b/splash/SplashFontEngine.h @@ -2,6 +2,8 @@ // // SplashFontEngine.h // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #ifndef SPLASHFONTENGINE_H @@ -14,8 +16,8 @@ #endif #include "gtypes.h" +class GString; -class SplashT1FontEngine; class SplashFTFontEngine; class SplashDTFontEngine; class SplashDT4FontEngine; @@ -40,9 +42,6 @@ public: // Create a font engine. SplashFontEngine( -#if HAVE_T1LIB_H - GBool enableT1lib, -#endif #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H GBool enableFreeType, Guint freeTypeFlags, @@ -56,19 +55,48 @@ public: SplashFontFile *getFontFile(SplashFontFileID *id); // Load fonts - these create new SplashFontFile objects. - SplashFontFile *loadType1Font(SplashFontFileID *idA, char *fileName, - GBool deleteFile, const char **enc); - SplashFontFile *loadType1CFont(SplashFontFileID *idA, char *fileName, - GBool deleteFile, const char **enc); - SplashFontFile *loadOpenTypeT1CFont(SplashFontFileID *idA, char *fileName, - GBool deleteFile, const char **enc); - SplashFontFile *loadCIDFont(SplashFontFileID *idA, char *fileName, - GBool deleteFile); - SplashFontFile *loadOpenTypeCFFFont(SplashFontFileID *idA, char *fileName, - GBool deleteFile, + SplashFontFile *loadType1Font(SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBuf, +#else + char *fileName, GBool deleteFile, +#endif + const char **enc); + SplashFontFile *loadType1CFont(SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBuf, +#else + char *fileName, GBool deleteFile, +#endif + const char **enc); + SplashFontFile *loadOpenTypeT1CFont(SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBuf, +#else + char *fileName, GBool deleteFile, +#endif + const char **enc); + SplashFontFile *loadCIDFont(SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBuf +#else + char *fileName, GBool deleteFile +#endif + ); + SplashFontFile *loadOpenTypeCFFFont(SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBuf, +#else + char *fileName, GBool deleteFile, +#endif int *codeToGID, int codeToGIDLen); - SplashFontFile *loadTrueTypeFont(SplashFontFileID *idA, char *fileName, - int fontNum, GBool deleteFile, + SplashFontFile *loadTrueTypeFont(SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBuf, +#else + char *fileName, GBool deleteFile, +#endif + int fontNum, int *codeToGID, int codeToGIDLen, char *fontName); @@ -87,9 +115,6 @@ private: SplashFont *fontCache[splashFontCacheSize]; -#if HAVE_T1LIB_H - SplashT1FontEngine *t1Engine; -#endif #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H SplashFTFontEngine *ftEngine; #endif diff --git a/splash/SplashFontFile.cc b/splash/SplashFontFile.cc index acbc12a..d0e36a2 100644 --- a/splash/SplashFontFile.cc +++ b/splash/SplashFontFile.cc @@ -2,6 +2,8 @@ // // SplashFontFile.cc // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #include <aconf.h> @@ -11,7 +13,7 @@ #endif #include <stdio.h> -#ifndef WIN32 +#ifndef _WIN32 # include <unistd.h> #endif #include "GString.h" @@ -28,19 +30,32 @@ extern "C" int unlink(char *filename); // SplashFontFile //------------------------------------------------------------------------ -SplashFontFile::SplashFontFile(SplashFontFileID *idA, char *fileNameA, - GBool deleteFileA) { +SplashFontFile::SplashFontFile(SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBufA +#else + char *fileNameA, GBool deleteFileA +#endif + ) { id = idA; +#if LOAD_FONTS_FROM_MEM + fontBuf = fontBufA; +#else fileName = new GString(fileNameA); deleteFile = deleteFileA; +#endif refCnt = 0; } SplashFontFile::~SplashFontFile() { +#if LOAD_FONTS_FROM_MEM + delete fontBuf; +#else if (deleteFile) { unlink(fileName->getCString()); } delete fileName; +#endif delete id; } diff --git a/splash/SplashFontFile.h b/splash/SplashFontFile.h index 9f89312..5f53ece 100644 --- a/splash/SplashFontFile.h +++ b/splash/SplashFontFile.h @@ -2,6 +2,8 @@ // // SplashFontFile.h // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #ifndef SPLASHFONTFILE_H @@ -46,12 +48,21 @@ public: protected: - SplashFontFile(SplashFontFileID *idA, char *fileNameA, - GBool deleteFileA); + SplashFontFile(SplashFontFileID *idA, +#if LOAD_FONTS_FROM_MEM + GString *fontBufA +#else + char *fileNameA, GBool deleteFileA +#endif + ); SplashFontFileID *id; +#if LOAD_FONTS_FROM_MEM + GString *fontBuf; +#else GString *fileName; GBool deleteFile; +#endif int refCnt; friend class SplashFontEngine; diff --git a/splash/SplashFontFileID.cc b/splash/SplashFontFileID.cc index af37cb2..3274847 100644 --- a/splash/SplashFontFileID.cc +++ b/splash/SplashFontFileID.cc @@ -2,6 +2,8 @@ // // SplashFontFileID.cc // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #include <aconf.h> diff --git a/splash/SplashFontFileID.h b/splash/SplashFontFileID.h index bed11d3..384018a 100644 --- a/splash/SplashFontFileID.h +++ b/splash/SplashFontFileID.h @@ -2,6 +2,8 @@ // // SplashFontFileID.h // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #ifndef SPLASHFONTFILEID_H diff --git a/splash/SplashGlyphBitmap.h b/splash/SplashGlyphBitmap.h index 044ba4a..d375b3c 100644 --- a/splash/SplashGlyphBitmap.h +++ b/splash/SplashGlyphBitmap.h @@ -2,6 +2,8 @@ // // SplashGlyphBitmap.h // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #ifndef SPLASHGLYPHBITMAP_H diff --git a/splash/SplashMath.h b/splash/SplashMath.h index 3b82e7a..4b7ab1f 100644 --- a/splash/SplashMath.h +++ b/splash/SplashMath.h @@ -2,6 +2,8 @@ // // SplashMath.h // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #ifndef SPLASHMATH_H @@ -35,19 +37,18 @@ static inline int splashFloor(SplashCoord x) { Gushort oldCW, newCW, t; int result; - __asm__ volatile("fldl %4\n" - "fnstcw %0\n" + __asm__ volatile("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" + "fistl %2\n" "fldcw %0\n" : "=m" (oldCW), "=m" (newCW), "=m" (result), "=r" (t) - : "m" (x)); + : "t" (x)); return result; -#elif defined(WIN32) && defined(_M_IX86) +#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 @@ -81,19 +82,18 @@ static inline int splashCeil(SplashCoord x) { Gushort oldCW, newCW, t; int result; - __asm__ volatile("fldl %4\n" - "fnstcw %0\n" + __asm__ volatile("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" + "fistl %2\n" "fldcw %0\n" : "=m" (oldCW), "=m" (newCW), "=m" (result), "=r" (t) - : "m" (x)); + : "t" (x)); return result; -#elif defined(WIN32) && defined(_M_IX86) +#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 @@ -128,19 +128,18 @@ static inline int splashRound(SplashCoord x) { int result; x += 0.5; - __asm__ volatile("fldl %4\n" - "fnstcw %0\n" + __asm__ volatile("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" + "fistl %2\n" "fldcw %0\n" : "=m" (oldCW), "=m" (newCW), "=m" (result), "=r" (t) - : "m" (x)); + : "t" (x)); return result; -#elif defined(WIN32) && defined(_M_IX86) +#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) @@ -223,4 +222,68 @@ static inline GBool splashCheckDet(SplashCoord m11, SplashCoord m12, #endif } +// Perform stroke adjustment on a SplashCoord range [xMin, xMax), +// resulting in an int range [*xMinI, *xMaxI). +// +// There are several options: +// +// 1. Round both edge coordinates. +// Pro: adjacent strokes/fills line up without any gaps or +// overlaps +// Con: lines with the same original floating point width can +// end up with different integer widths, e.g.: +// xMin = 10.1 xMax = 11.3 (width = 1.2) +// --> xMinI = 10 xMaxI = 11 (width = 1) +// but +// xMin = 10.4 xMax = 11.6 (width = 1.2) +// --> xMinI = 10 xMaxI = 12 (width = 2) +// +// 2. Round the min coordinate; add the ceiling of the width. +// Pro: lines with the same original floating point width will +// always end up with the same integer width +// Con: adjacent strokes/fills can have overlaps (which is +// problematic with transparency) +// (This could use floor on the min coordinate, instead of +// rounding, with similar results.) +// (If the width is rounded instead of using ceiling, the results +// Are similar, except that adjacent strokes/fills can have gaps +// as well as overlaps.) +// +// 3. Use floor on the min coordinate and ceiling on the max +// coordinate. +// Pro: lines always end up at least as wide as the original +// floating point width +// Con: adjacent strokes/fills can have overlaps, and lines with +// the same original floating point width can end up with +// different integer widths; the integer width can be more +// than one pixel wider than the original width, e.g.: +// xMin = 10.9 xMax = 12.1 (width = 1.2) +// --> xMinI = 10 xMaxI = 13 (width = 3) +// but +// xMin = 10.1 xMax = 11.3 (width = 1.2) +// --> xMinI = 10 xMaxI = 12 (width = 2) +static inline void splashStrokeAdjust(SplashCoord xMin, SplashCoord xMax, + int *xMinI, int *xMaxI) { + int x0, x1; + + // NB: enable exactly one of these. +#if 1 // 1. Round both edge coordinates. + x0 = splashRound(xMin); + x1 = splashRound(xMax); +#endif +#if 0 // 2. Round the min coordinate; add the ceiling of the width. + x0 = splashRound(xMin); + x1 = x0 + splashCeil(xMax - xMin); +#endif +#if 0 // 3. Use floor on the min coord and ceiling on the max coord. + x0 = splashFloor(xMin); + x1 = splashCeil(xMax); +#endif + if (x1 == x0) { + ++x1; + } + *xMinI = x0; + *xMaxI = x1; +} + #endif diff --git a/splash/SplashPath.cc b/splash/SplashPath.cc index b4249ed..4ee8be0 100644 --- a/splash/SplashPath.cc +++ b/splash/SplashPath.cc @@ -2,6 +2,8 @@ // // SplashPath.cc // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #include <aconf.h> @@ -53,6 +55,7 @@ SplashPath::SplashPath(SplashPath *path) { memcpy(hints, path->hints, hintsLength * sizeof(SplashPathHint)); } else { hints = NULL; + hintsLength = hintsSize = 0; } } diff --git a/splash/SplashPath.h b/splash/SplashPath.h index aae51f2..a871530 100644 --- a/splash/SplashPath.h +++ b/splash/SplashPath.h @@ -2,6 +2,8 @@ // // SplashPath.h // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #ifndef SPLASHPATH_H @@ -94,7 +96,7 @@ public: // Get the points on the path. int getLength() { return length; } - void getPoint(int i, double *x, double *y, Guchar *f) + void getPoint(int i, SplashCoord *x, SplashCoord *y, Guchar *f) { *x = pts[i].x; *y = pts[i].y; *f = flags[i]; } // Get the current point. diff --git a/splash/SplashPattern.cc b/splash/SplashPattern.cc index b77658e..6ca4fb2 100644 --- a/splash/SplashPattern.cc +++ b/splash/SplashPattern.cc @@ -2,6 +2,8 @@ // // SplashPattern.cc // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #include <aconf.h> diff --git a/splash/SplashPattern.h b/splash/SplashPattern.h index 0a02e9c..01207bf 100644 --- a/splash/SplashPattern.h +++ b/splash/SplashPattern.h @@ -2,6 +2,8 @@ // // SplashPattern.h // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #ifndef SPLASHPATTERN_H diff --git a/splash/SplashScreen.cc b/splash/SplashScreen.cc index f717173..68a762f 100644 --- a/splash/SplashScreen.cc +++ b/splash/SplashScreen.cc @@ -2,6 +2,8 @@ // // SplashScreen.cc // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #include <aconf.h> diff --git a/splash/SplashScreen.h b/splash/SplashScreen.h index 708c37f..4e9767b 100644 --- a/splash/SplashScreen.h +++ b/splash/SplashScreen.h @@ -2,6 +2,8 @@ // // SplashScreen.h // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #ifndef SPLASHSCREEN_H diff --git a/splash/SplashState.cc b/splash/SplashState.cc index e6dde61..09c1949 100644 --- a/splash/SplashState.cc +++ b/splash/SplashState.cc @@ -2,6 +2,8 @@ // // SplashState.cc // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #include <aconf.h> @@ -45,7 +47,7 @@ SplashState::SplashState(int width, int height, GBool vectorAntialias, blendFunc = NULL; strokeAlpha = 1; fillAlpha = 1; - lineWidth = 0; + lineWidth = 1; lineCap = splashLineCapButt; lineJoin = splashLineJoinMiter; miterLimit = 10; @@ -54,10 +56,12 @@ SplashState::SplashState(int width, int height, GBool vectorAntialias, lineDashLength = 0; lineDashPhase = 0; strokeAdjust = gFalse; - clip = new SplashClip(0, 0, width, height, vectorAntialias); + clip = new SplashClip(0, 0, width, height); + clipIsShared = gFalse; softMask = NULL; deleteSoftMask = gFalse; inNonIsolatedGroup = gFalse; + inKnockoutGroup = gFalse; for (i = 0; i < 256; ++i) { rgbTransferR[i] = (Guchar)i; rgbTransferG[i] = (Guchar)i; @@ -87,7 +91,7 @@ SplashState::SplashState(int width, int height, GBool vectorAntialias, blendFunc = NULL; strokeAlpha = 1; fillAlpha = 1; - lineWidth = 0; + lineWidth = 1; lineCap = splashLineCapButt; lineJoin = splashLineJoinMiter; miterLimit = 10; @@ -96,10 +100,12 @@ SplashState::SplashState(int width, int height, GBool vectorAntialias, lineDashLength = 0; lineDashPhase = 0; strokeAdjust = gFalse; - clip = new SplashClip(0, 0, width, height, vectorAntialias); + clip = new SplashClip(0, 0, width, height); + clipIsShared = gFalse; softMask = NULL; deleteSoftMask = gFalse; inNonIsolatedGroup = gFalse; + inKnockoutGroup = gFalse; for (i = 0; i < 256; ++i) { rgbTransferR[i] = (Guchar)i; rgbTransferG[i] = (Guchar)i; @@ -137,10 +143,12 @@ SplashState::SplashState(SplashState *state) { } lineDashPhase = state->lineDashPhase; strokeAdjust = state->strokeAdjust; - clip = state->clip->copy(); + clip = state->clip; + clipIsShared = gTrue; softMask = state->softMask; deleteSoftMask = gFalse; inNonIsolatedGroup = state->inNonIsolatedGroup; + inKnockoutGroup = state->inKnockoutGroup; memcpy(rgbTransferR, state->rgbTransferR, 256); memcpy(rgbTransferG, state->rgbTransferG, 256); memcpy(rgbTransferB, state->rgbTransferB, 256); @@ -158,7 +166,9 @@ SplashState::~SplashState() { delete fillPattern; delete screen; gfree(lineDash); - delete clip; + if (!clipIsShared) { + delete clip; + } if (deleteSoftMask && softMask) { delete softMask; } @@ -192,6 +202,32 @@ void SplashState::setLineDash(SplashCoord *lineDashA, int lineDashLengthA, lineDashPhase = lineDashPhaseA; } +void SplashState::clipResetToRect(SplashCoord x0, SplashCoord y0, + SplashCoord x1, SplashCoord y1) { + if (clipIsShared) { + clip = clip->copy(); + clipIsShared = gFalse; + } + clip->resetToRect(x0, y0, x1, y1); +} + +SplashError SplashState::clipToRect(SplashCoord x0, SplashCoord y0, + SplashCoord x1, SplashCoord y1) { + if (clipIsShared) { + clip = clip->copy(); + clipIsShared = gFalse; + } + return clip->clipToRect(x0, y0, x1, y1); +} + +SplashError SplashState::clipToPath(SplashPath *path, GBool eo) { + if (clipIsShared) { + clip = clip->copy(); + clipIsShared = gFalse; + } + return clip->clipToPath(path, matrix, flatness, eo); +} + void SplashState::setSoftMask(SplashBitmap *softMaskA) { if (deleteSoftMask) { delete softMask; diff --git a/splash/SplashState.h b/splash/SplashState.h index 0d1b6c5..fe773a5 100644 --- a/splash/SplashState.h +++ b/splash/SplashState.h @@ -2,6 +2,8 @@ // // SplashState.h // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #ifndef SPLASHSTATE_H @@ -19,6 +21,7 @@ class SplashPattern; class SplashScreen; class SplashClip; class SplashBitmap; +class SplashPath; //------------------------------------------------------------------------ // line cap values @@ -67,6 +70,12 @@ public: void setLineDash(SplashCoord *lineDashA, int lineDashLengthA, SplashCoord lineDashPhaseA); + void clipResetToRect(SplashCoord x0, SplashCoord y0, + SplashCoord x1, SplashCoord y1); + SplashError clipToRect(SplashCoord x0, SplashCoord y0, + SplashCoord x1, SplashCoord y1); + SplashError clipToPath(SplashPath *path, GBool eo); + // Set the soft mask bitmap. void setSoftMask(SplashBitmap *softMaskA); @@ -94,9 +103,11 @@ private: SplashCoord lineDashPhase; GBool strokeAdjust; SplashClip *clip; + GBool clipIsShared; SplashBitmap *softMask; GBool deleteSoftMask; GBool inNonIsolatedGroup; + GBool inKnockoutGroup; Guchar rgbTransferR[256], rgbTransferG[256], rgbTransferB[256]; diff --git a/splash/SplashT1Font.cc b/splash/SplashT1Font.cc deleted file mode 100644 index 703aacf..0000000 --- a/splash/SplashT1Font.cc +++ /dev/null @@ -1,290 +0,0 @@ -//======================================================================== -// -// SplashT1Font.cc -// -//======================================================================== - -#include <aconf.h> - -#if HAVE_T1LIB_H - -#ifdef USE_GCC_PRAGMAS -#pragma implementation -#endif - -#include <stdlib.h> -#include <t1lib.h> -#include "gmem.h" -#include "SplashMath.h" -#include "SplashGlyphBitmap.h" -#include "SplashPath.h" -#include "SplashT1FontEngine.h" -#include "SplashT1FontFile.h" -#include "SplashT1Font.h" - -//------------------------------------------------------------------------ - -static Guchar bitReverse[256] = { - 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, - 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, - 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, - 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, - 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, - 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, - 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, - 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, - 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, - 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, - 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, - 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, - 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, - 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, - 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, - 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, - 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, - 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, - 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, - 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, - 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, - 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, - 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, - 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, - 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, - 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, - 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, - 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, - 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, - 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, - 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, - 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff -}; - -//------------------------------------------------------------------------ -// SplashT1Font -//------------------------------------------------------------------------ - -SplashT1Font::SplashT1Font(SplashT1FontFile *fontFileA, SplashCoord *matA, - SplashCoord *textMatA): - SplashFont(fontFileA, matA, textMatA, fontFileA->engine->aa) -{ - T1_TMATRIX matrix; - BBox bbox; - SplashCoord bbx0, bby0, bbx1, bby1; - int x, y; - - t1libID = T1_CopyFont(fontFileA->t1libID); - outlineID = -1; - - // compute font size - 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 - bbox = T1_GetFontBBox(t1libID); - bbx0 = 0.001 * bbox.llx; - bby0 = 0.001 * bbox.lly; - bbx1 = 0.001 * bbox.urx; - bby1 = 0.001 * bbox.ury; - // some fonts are completely broken, so we fake it (with values - // large enough that most glyphs should fit) - if (bbx0 == 0 && bby0 == 0 && bbx1 == 0 && bby1 == 0) { - bbx0 = bby0 = -0.5; - bbx1 = bby1 = 1.5; - } - x = (int)(mat[0] * bbx0 + mat[2] * bby0); - xMin = xMax = x; - y = (int)(mat[1] * bbx0 + mat[3] * bby0); - yMin = yMax = y; - x = (int)(mat[0] * bbx0 + mat[2] * bby1); - if (x < xMin) { - xMin = x; - } else if (x > xMax) { - xMax = x; - } - y = (int)(mat[1] * bbx0 + mat[3] * bby1); - if (y < yMin) { - yMin = y; - } else if (y > yMax) { - yMax = y; - } - x = (int)(mat[0] * bbx1 + mat[2] * bby0); - if (x < xMin) { - xMin = x; - } else if (x > xMax) { - xMax = x; - } - y = (int)(mat[1] * bbx1 + mat[3] * bby0); - if (y < yMin) { - yMin = y; - } else if (y > yMax) { - yMax = y; - } - x = (int)(mat[0] * bbx1 + mat[2] * bby1); - if (x < xMin) { - xMin = x; - } else if (x > xMax) { - xMax = x; - } - y = (int)(mat[1] * bbx1 + mat[3] * bby1); - if (y < yMin) { - yMin = y; - } else if (y > yMax) { - yMax = y; - } - // This is a kludge: some buggy PDF generators embed fonts with - // zero bounding boxes. - if (xMax == xMin) { - xMin = 0; - xMax = (int)size; - } - if (yMax == yMin) { - yMin = 0; - yMax = (int)(1.2 * size); - } - // Another kludge: an unusually large xMin or yMin coordinate is - // probably wrong. - if (xMin > 0) { - xMin = 0; - } - if (yMin > 0) { - yMin = 0; - } - // Another kludge: t1lib doesn't correctly handle fonts with - // real (non-integer) bounding box coordinates. - if (xMax - xMin > 5000) { - xMin = 0; - xMax = (int)size; - } - if (yMax - yMin > 5000) { - yMin = 0; - yMax = (int)(1.2 * size); - } - - // transform the font - matrix.cxx = (double)mat[0] / size; - matrix.cxy = (double)mat[1] / size; - matrix.cyx = (double)mat[2] / size; - matrix.cyy = (double)mat[3] / size; - T1_TransformFont(t1libID, &matrix); -} - -SplashT1Font::~SplashT1Font() { - T1_DeleteFont(t1libID); - if (outlineID >= 0) { - T1_DeleteFont(outlineID); - } -} - -GBool SplashT1Font::getGlyph(int c, int xFrac, int yFrac, - SplashGlyphBitmap *bitmap) { - return SplashFont::getGlyph(c, 0, 0, bitmap); -} - -GBool SplashT1Font::makeGlyph(int c, int xFrac, int yFrac, - SplashGlyphBitmap *bitmap) { - GLYPH *glyph; - int n, i; - - if (aa) { - glyph = T1_AASetChar(t1libID, c, size, NULL); - } else { - glyph = T1_SetChar(t1libID, c, size, NULL); - } - if (!glyph) { - return gFalse; - } - - bitmap->x = -glyph->metrics.leftSideBearing; - bitmap->y = glyph->metrics.ascent; - bitmap->w = glyph->metrics.rightSideBearing - glyph->metrics.leftSideBearing; - bitmap->h = glyph->metrics.ascent - glyph->metrics.descent; - bitmap->aa = aa; - if (aa) { - bitmap->data = (Guchar *)glyph->bits; - bitmap->freeData = gFalse; - } else { - n = bitmap->h * ((bitmap->w + 7) >> 3); - bitmap->data = (Guchar *)gmalloc(n); - for (i = 0; i < n; ++i) { - bitmap->data[i] = bitReverse[glyph->bits[i] & 0xff]; - } - bitmap->freeData = gTrue; - } - - return gTrue; -} - -SplashPath *SplashT1Font::getGlyphPath(int c) { - T1_TMATRIX matrix; - SplashPath *path; - T1_OUTLINE *outline; - T1_PATHSEGMENT *seg; - T1_BEZIERSEGMENT *bez; - int x, y, x1, y1; - GBool needClose; - - if (outlineID < 0) { - outlineID = T1_CopyFont(((SplashT1FontFile *)fontFile)->t1libID); - 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; - matrix.cyy = (double)textMat[3] / outlineSize; - // t1lib doesn't seem to handle small sizes correctly here, so set - // the size to 1000, and scale the resulting coordinates later - outlineMul = (float)(outlineSize / 65536000.0); - outlineSize = 1000; - T1_TransformFont(outlineID, &matrix); - } - - path = new SplashPath(); - if ((outline = T1_GetCharOutline(outlineID, c, outlineSize, NULL))) { - // 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) { - case T1_PATHTYPE_MOVE: - if (needClose) { - path->close(); - needClose = gFalse; - } - x += seg->dest.x; - y += seg->dest.y; - path->moveTo(outlineMul * x, -outlineMul * y); - break; - case T1_PATHTYPE_LINE: - 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 + 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; - break; - } - } - if (needClose) { - path->close(); - } - T1_FreeOutline(outline); - } - - return path; -} - -#endif // HAVE_T1LIB_H diff --git a/splash/SplashT1Font.h b/splash/SplashT1Font.h deleted file mode 100644 index 8ea74de..0000000 --- a/splash/SplashT1Font.h +++ /dev/null @@ -1,57 +0,0 @@ -//======================================================================== -// -// SplashT1Font.h -// -//======================================================================== - -#ifndef SPLASHT1FONT_H -#define SPLASHT1FONT_H - -#include <aconf.h> - -#if HAVE_T1LIB_H - -#ifdef USE_GCC_PRAGMAS -#pragma interface -#endif - -#include "SplashFont.h" - -class SplashT1FontFile; - -//------------------------------------------------------------------------ -// SplashT1Font -//------------------------------------------------------------------------ - -class SplashT1Font: public SplashFont { -public: - - SplashT1Font(SplashT1FontFile *fontFileA, SplashCoord *matA, - SplashCoord *textMatA); - - virtual ~SplashT1Font(); - - // Munge xFrac and yFrac before calling SplashFont::getGlyph. - virtual GBool getGlyph(int c, int xFrac, int yFrac, - SplashGlyphBitmap *bitmap); - - // Rasterize a glyph. The <xFrac> and <yFrac> values are the same - // as described for getGlyph. - virtual GBool makeGlyph(int c, int xFrac, int yFrac, - SplashGlyphBitmap *bitmap); - - // Return the path for a glyph. - virtual SplashPath *getGlyphPath(int c); - -private: - - int t1libID; // t1lib font ID - int outlineID; // t1lib font ID for glyph outlines - float size; - float outlineSize; // size for glyph outlines - float outlineMul; -}; - -#endif // HAVE_T1LIB_H - -#endif diff --git a/splash/SplashT1FontEngine.cc b/splash/SplashT1FontEngine.cc deleted file mode 100644 index 277e2fc..0000000 --- a/splash/SplashT1FontEngine.cc +++ /dev/null @@ -1,124 +0,0 @@ -//======================================================================== -// -// SplashT1FontEngine.cc -// -//======================================================================== - -#include <aconf.h> - -#if HAVE_T1LIB_H - -#ifdef USE_GCC_PRAGMAS -#pragma implementation -#endif - -#include <stdlib.h> -#include <stdio.h> -#ifndef WIN32 -# include <unistd.h> -#endif -#include <t1lib.h> -#include "GString.h" -#include "gfile.h" -#include "FoFiType1C.h" -#include "SplashT1FontFile.h" -#include "SplashT1FontEngine.h" - -#ifdef VMS -#if (__VMS_VER < 70000000) -extern "C" int unlink(char *filename); -#endif -#endif - -//------------------------------------------------------------------------ - -int SplashT1FontEngine::t1libInitCount = 0; - -//------------------------------------------------------------------------ - -static void fileWrite(void *stream, const char *data, int len) { - fwrite(data, 1, len, (FILE *)stream); -} - -//------------------------------------------------------------------------ -// SplashT1FontEngine -//------------------------------------------------------------------------ - -SplashT1FontEngine::SplashT1FontEngine(GBool aaA) { - aa = aaA; -} - -SplashT1FontEngine *SplashT1FontEngine::init(GBool aaA) { - // grayVals[i] = round(i * 255 / 16) - static unsigned long grayVals[17] = { - 0, 16, 32, 48, 64, 80, 96, 112, 128, 143, 159, 175, 191, 207, 223, 239, 255 - }; - - //~ for multithreading: need a mutex here - if (t1libInitCount == 0) { - T1_SetBitmapPad(8); - if (!T1_InitLib(NO_LOGFILE | IGNORE_CONFIGFILE | IGNORE_FONTDATABASE | - T1_NO_AFM)) { - return NULL; - } - if (aaA) { - T1_AASetBitsPerPixel(8); - T1_AASetLevel(T1_AA_HIGH); - T1_AAHSetGrayValues(grayVals); - } else { - T1_AANSetGrayValues(0, 1); - } - } - ++t1libInitCount; - - return new SplashT1FontEngine(aaA); -} - -SplashT1FontEngine::~SplashT1FontEngine() { - //~ for multithreading: need a mutex here - if (--t1libInitCount == 0) { - T1_CloseLib(); - } -} - -SplashFontFile *SplashT1FontEngine::loadType1Font(SplashFontFileID *idA, - char *fileName, - GBool deleteFile, - const char **enc) { - return SplashT1FontFile::loadType1Font(this, idA, fileName, deleteFile, enc); -} - -SplashFontFile *SplashT1FontEngine::loadType1CFont(SplashFontFileID *idA, - char *fileName, - GBool deleteFile, - const char **enc) { - FoFiType1C *ff; - GString *tmpFileName; - FILE *tmpFile; - SplashFontFile *ret; - - if (!(ff = FoFiType1C::load(fileName))) { - return NULL; - } - tmpFileName = NULL; - if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) { - delete ff; - return NULL; - } - ff->convertToType1(NULL, NULL, gTrue, &fileWrite, tmpFile); - delete ff; - fclose(tmpFile); - ret = SplashT1FontFile::loadType1Font(this, idA, tmpFileName->getCString(), - gTrue, enc); - if (ret) { - if (deleteFile) { - unlink(fileName); - } - } else { - unlink(tmpFileName->getCString()); - } - delete tmpFileName; - return ret; -} - -#endif // HAVE_T1LIB_H diff --git a/splash/SplashT1FontEngine.h b/splash/SplashT1FontEngine.h deleted file mode 100644 index 8942b85..0000000 --- a/splash/SplashT1FontEngine.h +++ /dev/null @@ -1,53 +0,0 @@ -//======================================================================== -// -// SplashT1FontEngine.h -// -//======================================================================== - -#ifndef SPLASHT1FONTENGINE_H -#define SPLASHT1FONTENGINE_H - -#include <aconf.h> - -#if HAVE_T1LIB_H - -#ifdef USE_GCC_PRAGMAS -#pragma interface -#endif - -#include "gtypes.h" - -class SplashFontFile; -class SplashFontFileID; - -//------------------------------------------------------------------------ -// SplashT1FontEngine -//------------------------------------------------------------------------ - -class SplashT1FontEngine { -public: - - static SplashT1FontEngine *init(GBool aaA); - - ~SplashT1FontEngine(); - - // Load fonts. - SplashFontFile *loadType1Font(SplashFontFileID *idA, char *fileName, - GBool deleteFile, const char **enc); - SplashFontFile *loadType1CFont(SplashFontFileID *idA, char *fileName, - GBool deleteFile, const char **enc); - -private: - - SplashT1FontEngine(GBool aaA); - - static int t1libInitCount; - GBool aa; - - friend class SplashT1FontFile; - friend class SplashT1Font; -}; - -#endif // HAVE_T1LIB_H - -#endif diff --git a/splash/SplashT1FontFile.cc b/splash/SplashT1FontFile.cc deleted file mode 100644 index 8cec742..0000000 --- a/splash/SplashT1FontFile.cc +++ /dev/null @@ -1,98 +0,0 @@ -//======================================================================== -// -// SplashT1FontFile.cc -// -//======================================================================== - -#include <aconf.h> - -#if HAVE_T1LIB_H - -#ifdef USE_GCC_PRAGMAS -#pragma implementation -#endif - -#include <string.h> -#include <t1lib.h> -#include "gmem.h" -#include "SplashT1FontEngine.h" -#include "SplashT1Font.h" -#include "SplashT1FontFile.h" - -//------------------------------------------------------------------------ -// SplashT1FontFile -//------------------------------------------------------------------------ - -SplashFontFile *SplashT1FontFile::loadType1Font(SplashT1FontEngine *engineA, - SplashFontFileID *idA, - char *fileNameA, - GBool deleteFileA, - const char **encA) { - int t1libIDA; - const char **encTmp; - char *encStrTmp; - int encStrSize; - char *encPtr; - int i; - - // load the font file - if ((t1libIDA = T1_AddFont(fileNameA)) < 0) { - return NULL; - } - T1_LoadFont(t1libIDA); - - // reencode it - encStrSize = 0; - for (i = 0; i < 256; ++i) { - if (encA[i]) { - encStrSize += strlen(encA[i]) + 1; - } - } - encTmp = (const char **)gmallocn(257, sizeof(char *)); - encStrTmp = (char *)gmallocn(encStrSize, sizeof(char)); - encPtr = encStrTmp; - for (i = 0; i < 256; ++i) { - if (encA[i]) { - strcpy(encPtr, encA[i]); - encTmp[i] = encPtr; - encPtr += strlen(encPtr) + 1; - } else { - encTmp[i] = ".notdef"; - } - } - encTmp[256] = "custom"; - T1_ReencodeFont(t1libIDA, (char **)encTmp); - - return new SplashT1FontFile(engineA, idA, fileNameA, deleteFileA, - t1libIDA, encTmp, encStrTmp); -} - -SplashT1FontFile::SplashT1FontFile(SplashT1FontEngine *engineA, - SplashFontFileID *idA, - char *fileNameA, GBool deleteFileA, - int t1libIDA, const char **encA, - char *encStrA): - SplashFontFile(idA, fileNameA, deleteFileA) -{ - engine = engineA; - t1libID = t1libIDA; - enc = encA; - encStr = encStrA; -} - -SplashT1FontFile::~SplashT1FontFile() { - gfree(encStr); - gfree(enc); - T1_DeleteFont(t1libID); -} - -SplashFont *SplashT1FontFile::makeFont(SplashCoord *mat, - SplashCoord *textMat) { - SplashFont *font; - - font = new SplashT1Font(this, mat, textMat); - font->initCache(); - return font; -} - -#endif // HAVE_T1LIB_H diff --git a/splash/SplashT1FontFile.h b/splash/SplashT1FontFile.h deleted file mode 100644 index 57de84d..0000000 --- a/splash/SplashT1FontFile.h +++ /dev/null @@ -1,58 +0,0 @@ -//======================================================================== -// -// SplashT1FontFile.h -// -//======================================================================== - -#ifndef SPLASHT1FONTFILE_H -#define SPLASHT1FONTFILE_H - -#include <aconf.h> - -#if HAVE_T1LIB_H - -#ifdef USE_GCC_PRAGMAS -#pragma interface -#endif - -#include "SplashFontFile.h" - -class SplashT1FontEngine; - -//------------------------------------------------------------------------ -// SplashT1FontFile -//------------------------------------------------------------------------ - -class SplashT1FontFile: public SplashFontFile { -public: - - static SplashFontFile *loadType1Font(SplashT1FontEngine *engineA, - SplashFontFileID *idA, - char *fileNameA, GBool deleteFileA, - const char **encA); - - virtual ~SplashT1FontFile(); - - // Create a new SplashT1Font, i.e., a scaled instance of this font - // file. - virtual SplashFont *makeFont(SplashCoord *mat, - SplashCoord *textMat); - -private: - - SplashT1FontFile(SplashT1FontEngine *engineA, - SplashFontFileID *idA, - char *fileNameA, GBool deleteFileA, - int t1libIDA, const char **encA, char *encStrA); - - SplashT1FontEngine *engine; - int t1libID; // t1lib font ID - const char **enc; - char *encStr; - - friend class SplashT1Font; -}; - -#endif // HAVE_T1LIB_H - -#endif diff --git a/splash/SplashTypes.h b/splash/SplashTypes.h index 6eb2607..240cb6f 100644 --- a/splash/SplashTypes.h +++ b/splash/SplashTypes.h @@ -2,6 +2,8 @@ // // SplashTypes.h // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #ifndef SPLASHTYPES_H diff --git a/splash/SplashXPath.cc b/splash/SplashXPath.cc index 6ed2f4b..a159988 100644 --- a/splash/SplashXPath.cc +++ b/splash/SplashXPath.cc @@ -2,6 +2,8 @@ // // SplashXPath.cc // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #include <aconf.h> @@ -54,12 +56,10 @@ inline void SplashXPath::transform(SplashCoord *matrix, 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; - int curSubpath, curSubpathX, i, j; + SplashCoord xMinFP, xMaxFP, yMinFP, yMaxFP; + int curSubpath, i; // transform the points pts = (SplashXPathPoint *)gmallocn(path->length, sizeof(SplashXPathPoint)); @@ -67,78 +67,16 @@ SplashXPath::SplashXPath(SplashPath *path, SplashCoord *matrix, transform(matrix, path->pts[i].x, path->pts[i].y, &pts[i].x, &pts[i].y); } - // set up the stroke adjustment hints + // do stroke adjustment if (path->hints) { - adjusts = (SplashXPathAdjust *)gmallocn(path->hintsLength, - sizeof(SplashXPathAdjust)); - for (i = 0; i < path->hintsLength; ++i) { - hint = &path->hints[i]; - x0 = pts[hint->ctrl0 ].x; y0 = pts[hint->ctrl0 ].y; - x1 = pts[hint->ctrl0 + 1].x; y1 = pts[hint->ctrl0 + 1].y; - x2 = pts[hint->ctrl1 ].x; y2 = pts[hint->ctrl1 ].y; - x3 = pts[hint->ctrl1 + 1].x; y3 = pts[hint->ctrl1 + 1].y; - if (x0 == x1 && x2 == x3) { - adjusts[i].vert = gTrue; - adj0 = x0; - adj1 = x2; - } else if (y0 == y1 && y2 == y3) { - adjusts[i].vert = gFalse; - adj0 = y0; - adj1 = y2; - } else { - gfree(adjusts); - adjusts = NULL; - break; - } - if (adj0 > adj1) { - x0 = adj0; - adj0 = adj1; - adj1 = x0; - } - 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; - // 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; - } - - } else { - adjusts = NULL; - } - - // perform stroke adjustment - if (adjusts) { - for (i = 0, adjust = adjusts; i < path->hintsLength; ++i, ++adjust) { - for (j = adjust->firstPt; j <= adjust->lastPt; ++j) { - strokeAdjust(adjust, &pts[j].x, &pts[j].y); - } - } - gfree(adjusts); + strokeAdjust(pts, path->hints, path->hintsLength); } segs = NULL; length = size = 0; x0 = y0 = xsp = ysp = 0; // make gcc happy - adj0 = adj1 = 0; // make gcc happy curSubpath = 0; - curSubpathX = 0; i = 0; while (i < path->length) { @@ -149,7 +87,6 @@ SplashXPath::SplashXPath(SplashPath *path, SplashCoord *matrix, xsp = x0; ysp = y0; curSubpath = i; - curSubpathX = length; ++i; } else { @@ -197,32 +134,130 @@ SplashXPath::SplashXPath(SplashPath *path, SplashCoord *matrix, } gfree(pts); + +#if HAVE_STD_SORT + std::sort(segs, segs + length, SplashXPathSeg::cmpY); +#else + qsort(segs, length, sizeof(SplashXPathSeg), &SplashXPathSeg::cmpY); +#endif + + if (length == 0) { + xMin = yMin = xMax = yMax = 0; + } else { + if (segs[0].x0 < segs[0].x1) { + xMinFP = segs[0].x0; + xMaxFP = segs[0].x1; + } else { + xMinFP = segs[0].x1; + xMaxFP = segs[0].x0; + } + yMinFP = segs[0].y0; + yMaxFP = segs[0].y1; + for (i = 1; i < length; ++i) { + if (segs[i].x0 < xMinFP) { + xMinFP = segs[i].x0; + } else if (segs[i].x0 > xMaxFP) { + xMaxFP = segs[i].x0; + } + if (segs[i].x1 < xMinFP) { + xMinFP = segs[i].x1; + } else if (segs[i].x1 > xMaxFP) { + xMaxFP = segs[i].x1; + } + if (segs[i].y1 > yMaxFP) { + yMaxFP = segs[i].y1; + } + } + xMin = splashFloor(xMinFP); + yMin = splashFloor(yMinFP); + xMax = splashFloor(xMaxFP); + yMax = splashFloor(yMaxFP); + } } -// Apply the stroke adjust hints to point <pt>: (*<xp>, *<yp>). -void SplashXPath::strokeAdjust(SplashXPathAdjust *adjust, - SplashCoord *xp, SplashCoord *yp) { - SplashCoord x, y; +void SplashXPath::strokeAdjust(SplashXPathPoint *pts, + SplashPathHint *hints, int nHints) { + SplashXPathAdjust *adjusts, *adjust; + SplashPathHint *hint; + SplashCoord x0, y0, x1, y1, x2, y2, x3, y3; + SplashCoord adj0, adj1, d; + int xi0, xi1; + int i, j; - if (adjust->vert) { - x = *xp; - if (x > adjust->x0a && x < adjust->x0b) { - *xp = adjust->x0; - } else if (x > adjust->xma && x < adjust->xmb) { - *xp = adjust->xm; - } else if (x > adjust->x1a && x < adjust->x1b) { - *xp = adjust->x1; + // set up the stroke adjustment hints + adjusts = (SplashXPathAdjust *)gmallocn(nHints, sizeof(SplashXPathAdjust)); + for (i = 0; i < nHints; ++i) { + hint = &hints[i]; + x0 = pts[hint->ctrl0 ].x; y0 = pts[hint->ctrl0 ].y; + x1 = pts[hint->ctrl0 + 1].x; y1 = pts[hint->ctrl0 + 1].y; + x2 = pts[hint->ctrl1 ].x; y2 = pts[hint->ctrl1 ].y; + x3 = pts[hint->ctrl1 + 1].x; y3 = pts[hint->ctrl1 + 1].y; + if (x0 == x1 && x2 == x3) { + adjusts[i].vert = gTrue; + adj0 = x0; + adj1 = x2; + } else if (y0 == y1 && y2 == y3) { + adjusts[i].vert = gFalse; + adj0 = y0; + adj1 = y2; + } else { + goto done; } - } else { - y = *yp; - if (y > adjust->x0a && y < adjust->x0b) { - *yp = adjust->x0; - } else if (y > adjust->xma && y < adjust->xmb) { - *yp = adjust->xm; - } else if (y > adjust->x1a && y < adjust->x1b) { - *yp = adjust->x1; + if (adj0 > adj1) { + x0 = adj0; + adj0 = adj1; + adj1 = x0; + } + d = adj1 - adj0; + if (d > 0.04) { + d = 0.01; + } else { + d *= 0.25; } + adjusts[i].x0a = adj0 - d; + adjusts[i].x0b = adj0 + d; + adjusts[i].xma = (SplashCoord)0.5 * (adj0 + adj1) - d; + adjusts[i].xmb = (SplashCoord)0.5 * (adj0 + adj1) + d; + adjusts[i].x1a = adj1 - d; + adjusts[i].x1b = adj1 + d; + splashStrokeAdjust(adj0, adj1, &xi0, &xi1); + adjusts[i].x0 = (SplashCoord)xi0; + // the "minus epsilon" thing here is needed when vector + // antialiasing is turned off -- otherwise stroke adjusted lines + // will touch an extra pixel on one edge + adjusts[i].x1 = (SplashCoord)xi1 - 0.001; + adjusts[i].xm = (SplashCoord)0.5 * (adjusts[i].x0 + adjusts[i].x1); + adjusts[i].firstPt = hint->firstPt; + adjusts[i].lastPt = hint->lastPt; } + + // perform stroke adjustment + for (i = 0, adjust = adjusts; i < nHints; ++i, ++adjust) { + for (j = adjust->firstPt; j <= adjust->lastPt; ++j) { + if (adjust->vert) { + x0 = pts[j].x; + if (x0 > adjust->x0a && x0 < adjust->x0b) { + pts[j].x = adjust->x0; + } else if (x0 > adjust->xma && x0 < adjust->xmb) { + pts[j].x = adjust->xm; + } else if (x0 > adjust->x1a && x0 < adjust->x1b) { + pts[j].x = adjust->x1; + } + } else { + y0 = pts[j].y; + if (y0 > adjust->x0a && y0 < adjust->x0b) { + pts[j].y = adjust->x0; + } else if (y0 > adjust->xma && y0 < adjust->xmb) { + pts[j].y = adjust->xm; + } else if (y0 > adjust->x1a && y0 < adjust->x1b) { + pts[j].y = adjust->x1; + } + } + } + } + + done: + gfree(adjusts); } SplashXPath::SplashXPath(SplashXPath *xPath) { @@ -230,6 +265,10 @@ SplashXPath::SplashXPath(SplashXPath *xPath) { size = xPath->size; segs = (SplashXPathSeg *)gmallocn(size, sizeof(SplashXPathSeg)); memcpy(segs, xPath->segs, length * sizeof(SplashXPathSeg)); + xMin = xPath->xMin; + yMin = xPath->yMin; + xMax = xPath->xMax; + yMax = xPath->yMax; } SplashXPath::~SplashXPath() { @@ -341,115 +380,38 @@ void SplashXPath::addCurve(SplashCoord x0, SplashCoord y0, void SplashXPath::addSegment(SplashCoord x0, SplashCoord y0, 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 (y1 == y0) { - segs[length].dxdy = segs[length].dydx = 0; - segs[length].flags |= splashXPathHoriz; - if (x1 == x0) { - segs[length].flags |= splashXPathVert; - } - } else if (x1 == x0) { - segs[length].dxdy = segs[length].dydx = 0; - segs[length].flags |= splashXPathVert; + if (y0 <= y1) { + segs[length].x0 = x0; + segs[length].y0 = y0; + segs[length].x1 = x1; + segs[length].y1 = y1; + segs[length].count = 1; } else { + segs[length].x0 = x1; + segs[length].y0 = y1; + segs[length].x1 = x0; + segs[length].y1 = y0; + segs[length].count = -1; + } #if USE_FIXEDPOINT - if (FixedPoint::divCheck(x1 - x0, y1 - y0, &segs[length].dxdy)) { - segs[length].dydx = (SplashCoord)1 / segs[length].dxdy; - } else { - segs[length].dxdy = segs[length].dydx = 0; - if (splashAbs(x1 - x0) > splashAbs(y1 - y0)) { - segs[length].flags |= splashXPathHoriz; - } else { - segs[length].flags |= splashXPathVert; - } - } + if (y0 == y1 || x0 == x1 || + !FixedPoint::divCheck(x1 - x0, y1 - y0, &segs[length].dxdy) || + !FixedPoint::divCheck(y1 - y0, x1 - x0, &segs[length].dydx)) { + segs[length].dxdy = 0; + segs[length].dydx = 0; + } #else + if (y0 == y1 || x0 == x1) { + segs[length].dxdy = 0; + segs[length].dydx = 0; + } else { segs[length].dxdy = (x1 - x0) / (y1 - y0); - segs[length].dydx = (SplashCoord)1 / segs[length].dxdy; -#endif - } - if (y0 > y1) { - segs[length].flags |= splashXPathFlip; - } - ++length; -} - -void SplashXPath::aaScale() { - SplashXPathSeg *seg; - int i; - - for (i = 0, seg = segs; i < length; ++i, ++seg) { - seg->x0 *= splashAASize; - seg->y0 *= splashAASize; - seg->x1 *= splashAASize; - seg->y1 *= splashAASize; - } -} - -#if HAVE_STD_SORT - -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; + if (segs[length].dxdy == 0) { + segs[length].dydx = 0; } else { - x0 = seg0.x0; - y0 = seg0.y0; + segs[length].dydx = 1 / segs[length].dxdy; } - if (seg1.flags & splashXPathFlip) { - x1 = seg1.x1; - y1 = seg1.y1; - } else { - x1 = seg1.x0; - y1 = seg1.y0; - } - return (y0 != y1) ? (y0 < y1) : (x0 < x1); - } -}; - -#else // HAVE_STD_SORT - -static int cmpXPathSegs(const void *arg0, const void *arg1) { - SplashXPathSeg *seg0 = (SplashXPathSeg *)arg0; - SplashXPathSeg *seg1 = (SplashXPathSeg *)arg1; - 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) ? 1 : -1; - } - if (x0 != x1) { - return (x0 > x1) ? 1 : -1; - } - return 0; -} - -#endif // HAVE_STD_SORT - -void SplashXPath::sort() { -#if HAVE_STD_SORT - std::sort(segs, segs + length, cmpXPathSegsFunctor()); -#else - qsort(segs, length, sizeof(SplashXPathSeg), &cmpXPathSegs); #endif + ++length; } diff --git a/splash/SplashXPath.h b/splash/SplashXPath.h index 4e06d82..5bcec61 100644 --- a/splash/SplashXPath.h +++ b/splash/SplashXPath.h @@ -2,6 +2,8 @@ // // SplashXPath.h // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #ifndef SPLASHXPATH_H @@ -16,7 +18,8 @@ #include "SplashTypes.h" class SplashPath; -struct SplashXPathAdjust; +struct SplashXPathPoint; +struct SplashPathHint; //------------------------------------------------------------------------ @@ -27,18 +30,44 @@ struct SplashXPathAdjust; //------------------------------------------------------------------------ struct SplashXPathSeg { - SplashCoord x0, y0; // first endpoint + SplashCoord x0, y0; // first endpoint (y0 <= y1) SplashCoord x1, y1; // second endpoint SplashCoord dxdy; // slope: delta-x / delta-y SplashCoord dydx; // slope: delta-y / delta-x - Guint flags; -}; + int count; // EO/NZWN counter increment + + //----- used by SplashXPathScanner + SplashCoord xCur0, xCur1; // current x values + +#if HAVE_STD_SORT + static bool cmpY(const SplashXPathSeg &seg0, + const SplashXPathSeg &seg1) { + return seg0.y0 < seg1.y0; + } +#else + static int cmpY(const void *seg0, const void *seg1) { + SplashCoord cmp; + + cmp = ((SplashXPathSeg *)seg0)->y0 + - ((SplashXPathSeg *)seg1)->y0; + return (cmp > 0) ? 1 : (cmp < 0) ? -1 : 0; + } +#endif + + static int cmpX(SplashXPathSeg *seg0, SplashXPathSeg *seg1) { + SplashCoord cmp; -#define splashXPathHoriz 0x01 // segment is vertical (y0 == y1) - // (dxdy is undef) -#define splashXPathVert 0x02 // segment is horizontal (x0 == x1) - // (dydx is undef) -#define splashXPathFlip 0x04 // y0 > y1 + if ((cmp = seg0->xCur0 - seg1->xCur0) == 0) { + cmp = seg0->dxdy - seg1->dxdy; + } + return (cmp > 0) ? 1 : (cmp < 0) ? -1 : 0; + } + + static int cmpXi(const void *p0, const void *p1) { + return cmpX(*(SplashXPathSeg **)p0, *(SplashXPathSeg **)p1); + } + +}; //------------------------------------------------------------------------ // SplashXPath @@ -59,20 +88,18 @@ public: ~SplashXPath(); - // Multiply all coordinates by splashAASize, in preparation for - // anti-aliased rendering. - void aaScale(); - - // Sort by upper coordinate (lower y), in y-major order. - void sort(); + int getXMin() { return xMin; } + int getXMax() { return xMax; } + int getYMin() { return yMin; } + int getYMax() { return yMax; } private: SplashXPath(SplashXPath *xPath); void transform(SplashCoord *matrix, SplashCoord xi, SplashCoord yi, SplashCoord *xo, SplashCoord *yo); - void strokeAdjust(SplashXPathAdjust *adjust, - SplashCoord *xp, SplashCoord *yp); + void strokeAdjust(SplashXPathPoint *pts, + SplashPathHint *hints, int nHints); void grow(int nSegs); void addCurve(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1, @@ -85,6 +112,8 @@ private: SplashXPathSeg *segs; int length, size; // length and size of segs array + int xMin, xMax; + int yMin, yMax; friend class SplashXPathScanner; friend class SplashClip; diff --git a/splash/SplashXPathScanner.cc b/splash/SplashXPathScanner.cc index abe4593..aee60cc 100644 --- a/splash/SplashXPathScanner.cc +++ b/splash/SplashXPathScanner.cc @@ -2,6 +2,8 @@ // // SplashXPathScanner.cc // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #include <aconf.h> @@ -16,523 +18,550 @@ #include <algorithm> #endif #include "gmem.h" +#include "GList.h" #include "SplashMath.h" #include "SplashXPath.h" -#include "SplashBitmap.h" #include "SplashXPathScanner.h" //------------------------------------------------------------------------ -struct SplashIntersect { - int y; - int x0, x1; // intersection of segment with [y, y+1) - int count; // EO/NZWN counter increment -}; - -#if HAVE_STD_SORT - -struct cmpIntersectFunctor { - bool operator()(const SplashIntersect &i0, const SplashIntersect &i1) { - return (i0.y != i1.y) ? (i0.y < i1.y) : (i0.x0 < i1.x0); - } -}; - -#else // HAVE_STD_SORT +#define minVertStep 0.05 -static int cmpIntersect(const void *p0, const void *p1) { - SplashIntersect *i0 = (SplashIntersect *)p0; - SplashIntersect *i1 = (SplashIntersect *)p1; - int cmp; - - if ((cmp = i0->y - i1->y) == 0) { - cmp = i0->x0 - i1->x0; - } - return cmp; -} - -#endif // HAVE_STD_SORT - -//------------------------------------------------------------------------ -// SplashXPathScanner //------------------------------------------------------------------------ SplashXPathScanner::SplashXPathScanner(SplashXPath *xPathA, GBool eoA, - int clipYMin, int clipYMax) { - SplashXPathSeg *seg; - SplashCoord xMinFP, yMinFP, xMaxFP, yMaxFP; - int i; - + int yMinA, int yMaxA) { xPath = xPathA; eo = eoA; - partialClip = gFalse; + yMin = yMinA; + yMax = yMaxA; - // compute the bbox - if (xPath->length == 0) { - xMin = yMin = 1; - xMax = yMax = 0; - } else { - seg = &xPath->segs[0]; - if (seg->x0 <= seg->x1) { - xMinFP = seg->x0; - xMaxFP = seg->x1; - } else { - xMinFP = seg->x1; - xMaxFP = seg->x0; - } - if (seg->flags & splashXPathFlip) { - yMinFP = seg->y1; - yMaxFP = seg->y0; - } else { - yMinFP = seg->y0; - yMaxFP = seg->y1; - } - for (i = 1; i < xPath->length; ++i) { - seg = &xPath->segs[i]; - if (seg->x0 < xMinFP) { - xMinFP = seg->x0; - } else if (seg->x0 > xMaxFP) { - xMaxFP = seg->x0; - } - if (seg->x1 < xMinFP) { - xMinFP = seg->x1; - } else if (seg->x1 > xMaxFP) { - xMaxFP = seg->x1; + activeSegs = new GList(); + nextSeg = 0; + yNext = xPath->yMin; +} + +SplashXPathScanner::~SplashXPathScanner() { + delete activeSegs; +} + +void SplashXPathScanner::getSpan(Guchar *line, int y, int x0, int x1) { + SplashXPathSeg *seg, *seg0; + SplashCoord y0, y1, y1p; + GBool intersect, last; + int eoMask, state0, state1, count, i; + + //--- clear the scan line buffer + memset(line + x0, 0, x1 - x0 + 1); + + //--- reset the path + if (yNext != y) { + delete activeSegs; + activeSegs = new GList(); + nextSeg = 0; + while (nextSeg < xPath->length) { + seg = &xPath->segs[nextSeg]; + if (seg->y0 >= y) { + break; } - if (seg->flags & splashXPathFlip) { - if (seg->y0 > yMaxFP) { - yMaxFP = seg->y0; - } - } else { - if (seg->y1 > yMaxFP) { - yMaxFP = seg->y1; + if (seg->y0 != seg->y1 && seg->y1 > y) { + if (seg->y0 == y) { + seg->xCur0 = seg->x0; + } else { + seg->xCur0 = seg->x0 + ((SplashCoord)y - seg->y0) * seg->dxdy; } + activeSegs->append(seg); } + ++nextSeg; } - xMin = splashFloor(xMinFP); - xMax = splashFloor(xMaxFP); - yMin = splashFloor(yMinFP); - yMax = splashFloor(yMaxFP); - if (clipYMin > yMin) { - yMin = clipYMin; - partialClip = gTrue; - } - if (clipYMax < yMax) { - yMax = clipYMax; - partialClip = gTrue; - } + activeSegs->sort(&SplashXPathSeg::cmpXi); } - allInter = NULL; - inter = NULL; - computeIntersections(); - interY = yMin - 1; -} + //--- process the scan line + y0 = y; + while (y0 < y + 1) { -SplashXPathScanner::~SplashXPathScanner() { - gfree(inter); - gfree(allInter); -} + //--- delete finished segs + i = 0; + while (i < activeSegs->getLength()) { + seg = (SplashXPathSeg *)activeSegs->get(i); + if (seg->y1 <= y0) { + activeSegs->del(i); + } else { + ++i; + } + } -void SplashXPathScanner::getBBoxAA(int *xMinA, int *yMinA, - int *xMaxA, int *yMaxA) { - *xMinA = xMin / splashAASize; - *yMinA = yMin / splashAASize; - *xMaxA = xMax / splashAASize; - *yMaxA = yMax / splashAASize; -} + //--- check for bottom of path + if (!activeSegs->getLength() && nextSeg >= xPath->length) { + break; + } -void SplashXPathScanner::getSpanBounds(int y, int *spanXMin, int *spanXMax) { - int interBegin, interEnd, xx, i; + //--- sort activeSegs + sortActiveSegs(); - if (y < yMin || y > yMax) { - interBegin = interEnd = 0; - } else { - interBegin = inter[y - yMin]; - interEnd = inter[y - yMin + 1]; - } - 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; + //--- add waiting segs + while (nextSeg < xPath->length) { + seg = &xPath->segs[nextSeg]; + if (seg->y0 > y0) { + break; + } + if (seg->y0 != seg->y1) { + seg->xCur0 = seg->x0; + insertActiveSeg(seg); } + ++nextSeg; } - *spanXMax = xx; - } else { - *spanXMin = xMax + 1; - *spanXMax = xMax; - } -} -GBool SplashXPathScanner::test(int x, int y) { - int interBegin, interEnd, count, i; - - if (y < yMin || y > yMax) { - return gFalse; - } - interBegin = inter[y - yMin]; - interEnd = inter[y - yMin + 1]; - count = 0; - for (i = interBegin; i < interEnd && allInter[i].x0 <= x; ++i) { - if (x <= allInter[i].x1) { - return gTrue; + //--- get the next "interesting" y value + y1 = y + 1; + if (nextSeg < xPath->length && xPath->segs[nextSeg].y0 < y1) { + y1 = xPath->segs[nextSeg].y0; + } + for (i = 0; i < activeSegs->getLength(); ++i) { + seg = (SplashXPathSeg *)activeSegs->get(i); + if (seg->y1 < y1) { + y1 = seg->y1; + } } - count += allInter[i].count; - } - return eo ? (count & 1) : (count != 0); -} -GBool SplashXPathScanner::testSpan(int x0, int x1, int y) { - int interBegin, interEnd, count, xx1, i; + //--- compute xCur1 values, check for intersections + seg0 = NULL; + intersect = gFalse; + for (i = 0; i < activeSegs->getLength(); ++i) { + seg = (SplashXPathSeg *)activeSegs->get(i); + if (seg->y1 == y1) { + seg->xCur1 = seg->x1; + } else { + seg->xCur1 = seg->x0 + (y1 - seg->y0) * seg->dxdy; + } + if (seg0 && seg0->xCur1 > seg->xCur1) { + intersect = gTrue; + } + seg0 = seg; + } - if (y < yMin || y > yMax) { - return gFalse; - } - interBegin = inter[y - yMin]; - interEnd = inter[y - yMin + 1]; - count = 0; - for (i = interBegin; i < interEnd && allInter[i].x1 < x0; ++i) { - count += allInter[i].count; - } + //--- draw rectangles + if (intersect) { + for (; y0 < y1; y0 += minVertStep) { + if ((y1p = y0 + minVertStep) >= y1) { + y1p = y1; + last = gTrue; + } else { + last = gFalse; + } + state0 = state1 = count = 0; + seg0 = NULL; + eoMask = eo ? 1 : 0xffffffff; + for (i = 0; i < activeSegs->getLength(); ++i) { + seg = (SplashXPathSeg *)activeSegs->get(i); + if (last && seg->y1 == y1) { + seg->xCur1 = seg->x1; + } else { + seg->xCur1 = seg->x0 + (y1p - seg->y0) * seg->dxdy; + } + count += seg->count; + state1 = count & eoMask; + if (!state0 && state1) { + seg0 = seg; + } else if (state0 && !state1) { + drawRectangle(line, x0, x1, y0, y1p, seg0->xCur0, seg->xCur0); + } + state0 = state1; + } + for (i = 0; i < activeSegs->getLength(); ++i) { + seg = (SplashXPathSeg *)activeSegs->get(i); + seg->xCur0 = seg->xCur1; + } + sortActiveSegs(); + } - // invariant: the subspan [x0,xx1] is inside the path - xx1 = x0 - 1; - while (xx1 < x1) { - if (i >= interEnd) { - return gFalse; - } - if (allInter[i].x0 > xx1 + 1 && - !(eo ? (count & 1) : (count != 0))) { - return gFalse; - } - if (allInter[i].x1 > xx1) { - xx1 = allInter[i].x1; + //--- draw trapezoids + } else { + state0 = state1 = count = 0; + seg0 = NULL; + eoMask = eo ? 1 : 0xffffffff; + for (i = 0; i < activeSegs->getLength(); ++i) { + seg = (SplashXPathSeg *)activeSegs->get(i); + count += seg->count; + state1 = count & eoMask; + if (!state0 && state1) { + seg0 = seg; + } else if (state0 && !state1) { + drawTrapezoid(line, x0, x1, y0, y1, + seg0->xCur0, seg0->xCur1, seg0->dydx, + seg->xCur0, seg->xCur1, seg->dydx); + } + state0 = state1; + } + for (i = 0; i < activeSegs->getLength(); ++i) { + seg = (SplashXPathSeg *)activeSegs->get(i); + seg->xCur0 = seg->xCur1; + } } - count += allInter[i].count; - ++i; + + //--- next slice + y0 = y1; } - return gTrue; + yNext = y + 1; } -GBool SplashXPathScanner::getNextSpan(int y, int *x0, int *x1) { - int interEnd, xx0, xx1; - - if (y < yMin || y > yMax) { - return gFalse; - } - if (interY != y) { - interY = y; - interIdx = inter[y - yMin]; - interCount = 0; - } - interEnd = inter[y - yMin + 1]; - if (interIdx >= interEnd) { - return gFalse; +void SplashXPathScanner::getSpanBinary(Guchar *line, int y, int x0, int x1) { + SplashXPathSeg *seg; + int xx0, xx1, xx; + int eoMask, state0, state1, count, i; + + //--- clear the scan line buffer + memset(line + x0, 0, x1 - x0 + 1); + + //--- reset the path + if (yNext != y) { + delete activeSegs; + activeSegs = new GList(); + nextSeg = 0; + while (nextSeg < xPath->length) { + seg = &xPath->segs[nextSeg]; + if (seg->y0 >= y) { + break; + } + if (seg->y1 > y) { + if (seg->y0 == y) { + seg->xCur0 = seg->x0; + } else { + seg->xCur0 = seg->x0 + ((SplashCoord)y - seg->y0) * seg->dxdy; + } + activeSegs->append(seg); + } + ++nextSeg; + } + activeSegs->sort(&SplashXPathSeg::cmpXi); } - 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; + + //--- delete finished segs + i = 0; + while (i < activeSegs->getLength()) { + seg = (SplashXPathSeg *)activeSegs->get(i); + if (seg->y1 <= y) { + activeSegs->del(i); + } else { + ++i; } - interCount += allInter[interIdx].count; - ++interIdx; } - *x0 = xx0; - *x1 = xx1; - return gTrue; -} -void SplashXPathScanner::computeIntersections() { - SplashXPathSeg *seg; - SplashCoord segXMin, segXMax, segYMin, segYMax, xx0, xx1; - int x, y, y0, y1, i; + //--- sort activeSegs + sortActiveSegs(); - if (yMin > yMax) { - return; + //--- add waiting segs + while (nextSeg < xPath->length) { + seg = &xPath->segs[nextSeg]; + if (seg->y0 >= y + 1) { + break; + } + seg->xCur0 = seg->x0; + insertActiveSeg(seg); + ++nextSeg; } - // 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) { - segYMin = seg->y1; - segYMax = seg->y0; + //--- compute xCur1 values + for (i = 0; i < activeSegs->getLength(); ++i) { + seg = (SplashXPathSeg *)activeSegs->get(i); + if (seg->y1 <= y + 1) { + seg->xCur1 = seg->x1; } else { - segYMin = seg->y0; - segYMax = seg->y1; + seg->xCur1 = seg->x0 + ((SplashCoord)(y + 1) - seg->y0) * seg->dxdy; } - if (seg->flags & splashXPathHoriz) { - 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) { - y0 = splashFloor(segYMin); - if (y0 < yMin) { - y0 = yMin; + } + + //--- draw spans + state0 = state1 = count = 0; + eoMask = eo ? 1 : 0xffffffff; + xx0 = xx1 = 0; // make gcc happy + for (i = 0; i < activeSegs->getLength(); ++i) { + seg = (SplashXPathSeg *)activeSegs->get(i); + if (seg->y0 <= y && seg->y0 < seg->y1) { + count += seg->count; + state1 = count & eoMask; + } + if (state0) { + xx = splashCeil(seg->xCur0) - 1; + if (xx > xx1) { + xx1 = xx; } - y1 = splashFloor(segYMax); - if (y1 > yMax) { - y1 = yMax; + xx = splashFloor(seg->xCur1); + if (xx < xx0) { + xx0 = xx; } - x = splashFloor(seg->x0); - for (y = y0; y <= y1; ++y) { - addIntersection(segYMin, segYMax, seg->flags, y, x, x); + xx = splashCeil(seg->xCur1) - 1; + if (xx > xx1) { + xx1 = xx; } } else { - if (seg->x0 < seg->x1) { - segXMin = seg->x0; - segXMax = seg->x1; + if (seg->xCur0 < seg->xCur1) { + xx0 = splashFloor(seg->xCur0); + xx1 = splashCeil(seg->xCur1) - 1; } else { - segXMin = seg->x1; - segXMax = seg->x0; + xx0 = splashFloor(seg->xCur1); + xx1 = splashCeil(seg->xCur0) - 1; } - y0 = splashFloor(segYMin); - if (y0 < yMin) { - y0 = yMin; + } + if (!state1) { + if (xx0 < x0) { + xx0 = x0; } - y1 = splashFloor(segYMax); - if (y1 > yMax) { - y1 = yMax; + if (xx1 > x1) { + xx1 = x1; } - // 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)); + for (xx = xx0; xx <= xx1; ++xx) { + line[xx] = 0xff; } } + state0 = state1; } -#if HAVE_STD_SORT - std::sort(allInter, allInter + allInterLen, cmpIntersectFunctor()); -#else - qsort(allInter, allInterLen, sizeof(SplashIntersect), cmpIntersect); -#endif - // 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; - } + //--- update xCur0 values + for (i = 0; i < activeSegs->getLength(); ++i) { + seg = (SplashXPathSeg *)activeSegs->get(i); + seg->xCur0 = seg->xCur1; } - inter[yMax - yMin + 1] = i; + + yNext = y + 1; } -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)); +inline void SplashXPathScanner::addArea(Guchar *line, int x, SplashCoord a) { + int a2, t; + + a2 = splashRound(a * 255); + if (a2 <= 0) { + return; + } + t = line[x] + a2; + if (t > 255) { + t = 255; + } + line[x] = t; +} + +// Draw a trapezoid with edges: +// top: (xa0, y0) - (xb0, y0) +// left: (xa0, y0) - (xa1, y1) +// right: (xb0, y0) - (xb1, y1) +// bottom: (xa1, y1) - (xb1, y1) +void SplashXPathScanner::drawTrapezoid(Guchar *line, int xMin, int xMax, + SplashCoord y0, SplashCoord y1, + SplashCoord xa0, SplashCoord xa1, + SplashCoord dydxa, + SplashCoord xb0, SplashCoord xb1, + SplashCoord dydxb) { + SplashCoord a, dy; + int x0, x1, x2, x3, x; + + // check for a rectangle + if (dydxa == 0 && dydxb == 0 && xa0 >= xMin && xb0 <= xMax) { + x0 = splashFloor(xa0); + x3 = splashFloor(xb0); + dy = y1 - y0; + if (x0 == x3) { + addArea(line, x0, (xb0 - xa0) * dy); + } else { + addArea(line, x0, ((SplashCoord)1 - (xa0 - x0)) * dy); + for (x = x0 + 1; x <= x3 - 1; ++x) { + addArea(line, x, y1 - y0); + } + addArea(line, x3, (xb0 - x3) * (y1 - y0)); + } + return; } - allInter[allInterLen].y = y; - if (x0 < x1) { - allInter[allInterLen].x0 = x0; - allInter[allInterLen].x1 = x1; + + if (dydxa > 0) { + x0 = splashFloor(xa0); + x1 = splashFloor(xa1); } else { - allInter[allInterLen].x0 = x1; - allInter[allInterLen].x1 = x0; + x0 = splashFloor(xa1); + x1 = splashFloor(xa0); + } + if (x0 < xMin) { + x0 = xMin; } - if (segYMin <= y && - (SplashCoord)y < segYMax && - !(segFlags & splashXPathHoriz)) { - allInter[allInterLen].count = eo ? 1 - : (segFlags & splashXPathFlip) ? 1 : -1; + if (dydxb > 0) { + x2 = splashFloor(xb0); + x3 = splashFloor(xb1); } else { - allInter[allInterLen].count = 0; + x2 = splashFloor(xb1); + x3 = splashFloor(xb0); + } + if (x3 > xMax) { + x3 = xMax; + } + for (x = x0; x <= x3; ++x) { + a = y1 - y0; + if (x <= x1) { + a -= areaLeft(x, xa0, y0, xa1, y1, dydxa); + } + if (x >= x2) { + a -= areaRight(x, xb0, y0, xb1, y1, dydxb); + } + addArea(line, x, a); } - ++allInterLen; } -void SplashXPathScanner::renderAALine(SplashBitmap *aaBuf, - int *x0, int *x1, int y) { - 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; - if (yMin <= yMax) { - if (splashAASize * y < yMin) { - interIdx = inter[0]; - } else if (splashAASize * y > yMax) { - interIdx = inter[yMax - yMin + 1]; +// Compute area within a pixel slice ((xp,y0)-(xp+1,y1)) to the left +// of a trapezoid edge ((x0,y0)-(x1,y1)). +SplashCoord SplashXPathScanner::areaLeft(int xp, + SplashCoord x0, SplashCoord y0, + SplashCoord x1, SplashCoord y1, + SplashCoord dydx) { + SplashCoord a, ya, yb; + + if (dydx >= 0) { + if (x0 >= xp) { + if (x1 <= xp + 1) { + a = ((x0 + x1) * 0.5 - xp) * (y1 - y0); + } else { + yb = y0 + ((SplashCoord)(xp + 1) - x0) * dydx; + a = (y1 - y0) - ((SplashCoord)(xp + 1) - x0) * (yb - y0) * 0.5; + } } else { - interIdx = inter[splashAASize * y - yMin]; + if (x1 <= xp + 1) { + ya = y0 + ((SplashCoord)xp - x0) * dydx; + a = (x1 - xp) * (y1 - ya) * 0.5; + } else { + // ya = y1 - (x1 - xp - 0.5) * dydx; + // a = y1 - ya; + a = (x1 - xp - 0.5) * dydx; + } } - 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 { + if (x0 <= xp + 1) { + if (x1 >= xp) { + a = ((x0 + x1) * 0.5 - xp) * (y1 - y0); } else { - interEnd = inter[splashAASize * y + yy - yMin + 1]; + ya = y0 + ((SplashCoord)xp - x0) * dydx; + a = (x0 - xp) * (ya - y0) * 0.5; } - 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; - } - interCount += allInter[interIdx].count; - ++interIdx; - } - if (xx0 < 0) { - xx0 = 0; - } - ++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; - } + } else { + if (x1 >= xp) { + yb = y0 + ((SplashCoord)(xp + 1) - x0) * dydx; + a = (y1 - y0) - ((SplashCoord)(xp + 1) - x1) * (y1 - yb) * 0.5; + } else { + // ya = y0 + (xp - x0 + 0.5) * dydx; + // a = ya - y0; + a = ((SplashCoord)xp - x0 + 0.5) * dydx; } } } - *x0 = xxMin / splashAASize; - *x1 = (xxMax - 1) / splashAASize; + return a; } -void SplashXPathScanner::clipAALine(SplashBitmap *aaBuf, - int *x0, int *x1, int y) { - int xx0, xx1, xx, yy, interEnd; - Guchar mask; - SplashColorPtr p; - - for (yy = 0; yy < splashAASize; ++yy) { - xx = *x0 * splashAASize; - if (yMin <= yMax) { - if (splashAASize * y + yy < yMin) { - interIdx = interEnd = inter[0]; - } else if (splashAASize * y + yy > yMax) { - interIdx = interEnd = inter[yMax - yMin + 1]; +// Compute area within a pixel slice ((xp,y0)-(xp+1,y1)) to the left +// of a trapezoid edge ((x0,y0)-(x1,y1)). +SplashCoord SplashXPathScanner::areaRight(int xp, + SplashCoord x0, SplashCoord y0, + SplashCoord x1, SplashCoord y1, + SplashCoord dydx) { + SplashCoord a, ya, yb; + + if (dydx >= 0) { + if (x0 >= xp) { + if (x1 <= xp + 1) { + a = ((SplashCoord)(xp + 1) - (x0 + x1) * 0.5) * (y1 - y0); } 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]; - } + yb = y0 + ((SplashCoord)(xp + 1) - x0) * dydx; + a = ((SplashCoord)(xp + 1) - x0) * (yb - y0) * 0.5; } - 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; - } - interCount += allInter[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); - } - *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; - } + } else { + if (x1 <= xp + 1) { + ya = y0 + ((SplashCoord)xp - x0) * dydx; + a = (y1 - y0) - (x1 - xp) * (y1 - ya) * 0.5; + } else { + // ya = y0 + (xp - x0 + 0.5) * dydx; + // a = ya - y0; + a = ((SplashCoord)xp + 0.5 - x0) * dydx; } } - xx0 = (*x1 + 1) * splashAASize; - // 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); - } - *p++ &= mask; - xx = (xx & ~7) + 8; + } else { + if (x0 <= xp + 1) { + if (x1 >= xp) { + a = ((SplashCoord)(xp + 1) - (x0 + x1) * 0.5) * (y1 - y0); + } else { + ya = y0 + ((SplashCoord)xp - x0) * dydx; + a = (y1 - y0) - (x0 - xp) * (ya - y0) * 0.5; } - for (; xx + 7 < xx0; xx += 8) { - *p++ = 0x00; + } else { + if (x1 >= xp) { + yb = y0 + ((SplashCoord)(xp + 1) - x0) * dydx; + a = ((SplashCoord)(xp + 1) - x1) * (y1 - yb) * 0.5; + } else { + // ya = y1 - (x1 - xp - 0.5) * dydx; + // a = y1 - ya; + a = (x1 - xp - 0.5) * dydx; } - if (xx < xx0) { - *p &= 0xff >> (xx0 & 7); + } + } + return a; +} + +void SplashXPathScanner::drawRectangle(Guchar *line, int xMin, int xMax, + SplashCoord y0, SplashCoord y1, + SplashCoord x0, SplashCoord x1) { + SplashCoord dy, a; + int xx0, xx1, x; + + xx0 = splashFloor(x0); + if (xx0 < xMin) { + xx0 = xMin; + } + xx1 = splashFloor(x1); + if (xx1 > xMax) { + xx1 = xMax; + } + dy = y1 - y0; + for (x = xx0; x <= xx1; ++x) { + a = dy; + if ((SplashCoord)x < x0) { + a -= (x0 - x) * dy; + } + if ((SplashCoord)(x + 1) > x1) { + a -= ((SplashCoord)(x + 1) - x1) * dy; + } + addArea(line, x, a); + } +} + +void SplashXPathScanner::sortActiveSegs() { + SplashXPathSeg *seg0, *seg1; + int i, j, k; + + if (activeSegs->getLength() < 2) { + return; + } + seg0 = (SplashXPathSeg *)activeSegs->get(0); + for (i = 1; i < activeSegs->getLength(); ++i) { + seg1 = (SplashXPathSeg *)activeSegs->get(i); + if (SplashXPathSeg::cmpX(seg0, seg1) > 0) { + for (j = i - 1; j > 0; --j) { + if (SplashXPathSeg::cmpX((SplashXPathSeg *)activeSegs->get(j - 1), + seg1) <= 0) { + break; + } } + for (k = i; k > j; --k) { + activeSegs->put(k, activeSegs->get(k-1)); + } + activeSegs->put(j, seg1); + } else { + seg0 = seg1; + } + } +} + +void SplashXPathScanner::insertActiveSeg(SplashXPathSeg *seg) { + int i; + + for (i = 0; i < activeSegs->getLength(); ++i) { + if (SplashXPathSeg::cmpX(seg, (SplashXPathSeg *)activeSegs->get(i)) < 0) { + break; } } + activeSegs->insert(i, seg); } diff --git a/splash/SplashXPathScanner.h b/splash/SplashXPathScanner.h index 5bb3aaf..519ff4d 100644 --- a/splash/SplashXPathScanner.h +++ b/splash/SplashXPathScanner.h @@ -2,6 +2,8 @@ // // SplashXPathScanner.h // +// Copyright 2003-2013 Glyph & Cog, LLC +// //======================================================================== #ifndef SPLASHXPATHSCANNER_H @@ -15,9 +17,8 @@ #include "SplashTypes.h" +class GList; class SplashXPath; -class SplashBitmap; -struct SplashIntersect; //------------------------------------------------------------------------ // SplashXPathScanner @@ -28,68 +29,47 @@ public: // Create a new SplashXPathScanner object. <xPathA> must be sorted. SplashXPathScanner(SplashXPath *xPathA, GBool eoA, - int clipYMin, int clipYMax); + int yMinA, int yMaxA); ~SplashXPathScanner(); - // Return the path's bounding box. - void getBBox(int *xMinA, int *yMinA, int *xMaxA, int *yMaxA) - { *xMinA = xMin; *yMinA = yMin; *xMaxA = xMax; *yMaxA = yMax; } - - // 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); - - // Returns true if (<x>,<y>) is inside the path. - GBool test(int x, int y); + // Compute shape values for a scan line. Fills in line[] with shape + // values for one scan line: ([x0, x1], y). The values are in [0, + // 255]. + void getSpan(Guchar *line, int y, int x0, int x1); - // Returns true if the entire span ([<x0>,<x1>], <y>) is inside the - // path. - GBool testSpan(int x0, int x1, int y); - - // Returns the next span inside the path at <y>. If <y> is - // different than the previous call to getNextSpan, this returns the - // first span at <y>; otherwise it returns the next span (relative - // to the previous call to getNextSpan). Returns false if there are - // no more spans at <y>. - GBool getNextSpan(int y, int *x0, int *x1); - - // Renders one anti-aliased line into <aaBuf>. Returns the min and - // max x coordinates with non-zero pixels in <x0> and <x1>. - void renderAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y); - - // Clips an anti-aliased line by setting pixels to zero. On entry, - // all non-zero pixels are between <x0> and <x1>. This function - // will update <x0> and <x1>. - void clipAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y); + // Like getSpan(), but uses the values 0 and 255 only. Writes 255 + // for all pixels which include non-zero area inside the path. + void getSpanBinary(Guchar *line, int y, int x0, int x1); private: - void computeIntersections(); - void addIntersection(double segYMin, double segYMax, - Guint segFlags, - int y, int x0, int x1); + inline void addArea(Guchar *line, int x, SplashCoord a); + void drawTrapezoid(Guchar *line, int xMin, int xMax, + SplashCoord y0, SplashCoord y1, + SplashCoord xa0, SplashCoord xa1, SplashCoord dydxa, + SplashCoord xb0, SplashCoord xb1, SplashCoord dydxb); + SplashCoord areaLeft(int xp, + SplashCoord x0, SplashCoord y0, + SplashCoord x1, SplashCoord y1, + SplashCoord dydx); + SplashCoord areaRight(int xp, + SplashCoord x0, SplashCoord y0, + SplashCoord x1, SplashCoord y1, + SplashCoord dydx); + void drawRectangle(Guchar *line, int xMin, int xMax, + SplashCoord y0, SplashCoord y1, + SplashCoord x0, SplashCoord x1); + void sortActiveSegs(); + void insertActiveSeg(SplashXPathSeg *seg); SplashXPath *xPath; GBool eo; - int xMin, yMin, xMax, yMax; - GBool partialClip; - - 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 yMin, yMax; + + GList *activeSegs; // [SplashXPathSeg] + int nextSeg; + int yNext; }; #endif diff --git a/splash/vms_make.com b/splash/vms_make.com deleted file mode 100644 index e69de29..0000000 --- a/splash/vms_make.com +++ /dev/null |