diff options
author | Albert Astals Cid <aacid@kde.org> | 2007-04-25 19:59:09 +0000 |
---|---|---|
committer | Albert Astals Cid <aacid@kde.org> | 2007-04-25 19:59:09 +0000 |
commit | bf7e0e980bf29994021cb1228f89f582adddf284 (patch) | |
tree | cec237ed204ceb988a6e74209c8beb2167a898fa /splash | |
parent | ba74bb3b0632593d1937911d73709fc870480efd (diff) |
Merge xpdf302branch in HEAD as noone vetoed it.
Testing more than welcome
Diffstat (limited to 'splash')
37 files changed, 3747 insertions, 2795 deletions
diff --git a/splash/Splash.cc b/splash/Splash.cc index 403766b0..0be4e581 100644 --- a/splash/Splash.cc +++ b/splash/Splash.cc @@ -28,23 +28,812 @@ //------------------------------------------------------------------------ -static void blendNormal(SplashColorPtr src, SplashColorPtr dest, - SplashColorPtr blend, SplashColorMode cm) { +// distance of Bezier control point from center for circle approximation +// = (4 * (sqrt(2) - 1) / 3) * r +#define bezierCircle ((SplashCoord)0.55228475) +#define bezierCircle2 ((SplashCoord)(0.5 * 0.55228475)) + +// Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result. +static inline Guchar div255(int x) { + return (Guchar)((x + (x >> 8) + 0x80) >> 8); +} + +//------------------------------------------------------------------------ +// SplashPipe +//------------------------------------------------------------------------ + +#define splashPipeMaxStages 9 + +struct SplashPipe { + // pixel coordinates + int x, y; + + // source pattern + SplashPattern *pattern; + + // source alpha and color + SplashCoord aInput; + GBool usesShape; + Guchar aSrc; + SplashColorPtr cSrc; + SplashColor cSrcVal; + + // non-isolated group alpha0 + Guchar *alpha0Ptr; + + // soft mask + SplashColorPtr softMaskPtr; + + // destination alpha and color + SplashColorPtr destColorPtr; + int destColorMask; + Guchar *destAlphaPtr; + + // shape + SplashCoord shape; + + // result alpha and color + GBool noTransparency; + SplashPipeResultColorCtrl resultColorCtrl; + + // non-isolated group correction + int nonIsolatedGroup; +}; + +SplashPipeResultColorCtrl Splash::pipeResultColorNoAlphaBlend[] = { + splashPipeResultColorNoAlphaBlendMono, + splashPipeResultColorNoAlphaBlendMono, + splashPipeResultColorNoAlphaBlendRGB, + splashPipeResultColorNoAlphaBlendRGB +#if SPLASH_CMYK + , + splashPipeResultColorNoAlphaBlendCMYK +#endif +}; + +SplashPipeResultColorCtrl Splash::pipeResultColorAlphaNoBlend[] = { + splashPipeResultColorAlphaNoBlendMono, + splashPipeResultColorAlphaNoBlendMono, + splashPipeResultColorAlphaNoBlendRGB, + splashPipeResultColorAlphaNoBlendRGB +#if SPLASH_CMYK + , + splashPipeResultColorAlphaNoBlendCMYK +#endif +}; + +SplashPipeResultColorCtrl Splash::pipeResultColorAlphaBlend[] = { + splashPipeResultColorAlphaBlendMono, + splashPipeResultColorAlphaBlendMono, + splashPipeResultColorAlphaBlendRGB, + splashPipeResultColorAlphaBlendRGB +#if SPLASH_CMYK + , + splashPipeResultColorAlphaBlendCMYK +#endif +}; + +//------------------------------------------------------------------------ + +static void blendXor(SplashColorPtr src, SplashColorPtr dest, + SplashColorPtr blend, SplashColorMode cm) { int i; for (i = 0; i < splashColorModeNComps[cm]; ++i) { - blend[i] = src[i]; + blend[i] = src[i] ^ dest[i]; + } +} + +//------------------------------------------------------------------------ +// modified region +//------------------------------------------------------------------------ + +void Splash::clearModRegion() { + modXMin = bitmap->getWidth(); + modYMin = bitmap->getHeight(); + modXMax = -1; + modYMax = -1; +} + +inline void Splash::updateModX(int x) { + if (x < modXMin) { + modXMin = x; + } + if (x > modXMax) { + modXMax = x; + } +} + +inline void Splash::updateModY(int y) { + if (y < modYMin) { + modYMin = y; + } + if (y > modYMax) { + modYMax = y; + } +} + +//------------------------------------------------------------------------ +// pipeline +//------------------------------------------------------------------------ + +inline void Splash::pipeInit(SplashPipe *pipe, int x, int y, + SplashPattern *pattern, SplashColorPtr cSrc, + SplashCoord aInput, GBool usesShape, + GBool nonIsolatedGroup) { + pipeSetXY(pipe, x, y); + pipe->pattern = NULL; + + // source color + if (pattern) { + if (pattern->isStatic()) { + pattern->getColor(x, y, pipe->cSrcVal); + } else { + pipe->pattern = pattern; + } + pipe->cSrc = pipe->cSrcVal; + } else { + pipe->cSrc = cSrc; + } + + // source alpha + pipe->aInput = aInput; + if (!state->softMask) { + if (usesShape) { + pipe->aInput *= 255; + } else { + pipe->aSrc = (Guchar)splashRound(pipe->aInput * 255); + } + } + pipe->usesShape = usesShape; + + // result alpha + if (aInput == 1 && !state->softMask && !usesShape && + !state->inNonIsolatedGroup) { + pipe->noTransparency = gTrue; + } else { + pipe->noTransparency = gFalse; + } + + // result color + if (pipe->noTransparency) { + // the !state->blendFunc case is handled separately in pipeRun + pipe->resultColorCtrl = pipeResultColorNoAlphaBlend[bitmap->mode]; + } else if (!state->blendFunc) { + pipe->resultColorCtrl = pipeResultColorAlphaNoBlend[bitmap->mode]; + } else { + pipe->resultColorCtrl = pipeResultColorAlphaBlend[bitmap->mode]; + } + + // non-isolated group correction + if (nonIsolatedGroup) { + pipe->nonIsolatedGroup = splashColorModeNComps[bitmap->mode]; + } else { + pipe->nonIsolatedGroup = 0; + } +} + +inline void Splash::pipeRun(SplashPipe *pipe) { + Guchar aSrc, aDest, alpha2, alpha0, aResult; + SplashColor cDest, cBlend; + Guchar cResult0, cResult1, cResult2, cResult3; + + //----- source color + + // static pattern: handled in pipeInit + // fixed color: handled in pipeInit + + // dynamic pattern + if (pipe->pattern) { + pipe->pattern->getColor(pipe->x, pipe->y, pipe->cSrcVal); + } + + if (pipe->noTransparency && !state->blendFunc) { + + //----- write destination pixel + + switch (bitmap->mode) { + case splashModeMono1: + cResult0 = pipe->cSrc[0]; + if (state->screen->test(pipe->x, pipe->y, cResult0)) { + *pipe->destColorPtr |= pipe->destColorMask; + } else { + *pipe->destColorPtr &= ~pipe->destColorMask; + } + if (!(pipe->destColorMask >>= 1)) { + pipe->destColorMask = 0x80; + ++pipe->destColorPtr; + } + break; + case splashModeMono8: + *pipe->destColorPtr++ = pipe->cSrc[0]; + break; + case splashModeRGB8: + *pipe->destColorPtr++ = pipe->cSrc[0]; + *pipe->destColorPtr++ = pipe->cSrc[1]; + *pipe->destColorPtr++ = pipe->cSrc[2]; + break; + case splashModeRGBX8: + *pipe->destColorPtr++ = pipe->cSrc[0]; + *pipe->destColorPtr++ = pipe->cSrc[1]; + *pipe->destColorPtr++ = pipe->cSrc[2]; + *pipe->destColorPtr++ = 255; + break; + case splashModeBGR8: + *pipe->destColorPtr++ = pipe->cSrc[2]; + *pipe->destColorPtr++ = pipe->cSrc[1]; + *pipe->destColorPtr++ = pipe->cSrc[0]; + break; +#if SPLASH_CMYK + case splashModeCMYK8: + *pipe->destColorPtr++ = pipe->cSrc[0]; + *pipe->destColorPtr++ = pipe->cSrc[1]; + *pipe->destColorPtr++ = pipe->cSrc[2]; + *pipe->destColorPtr++ = pipe->cSrc[3]; + break; +#endif + } + if (pipe->destAlphaPtr) { + *pipe->destAlphaPtr++ = 255; + } + + } else { + + //----- read destination pixel + + switch (bitmap->mode) { + case splashModeMono1: + cDest[0] = (*pipe->destColorPtr & pipe->destColorMask) ? 0xff : 0x00; + break; + case splashModeMono8: + cDest[0] = *pipe->destColorPtr; + break; + case splashModeRGB8: + cDest[0] = pipe->destColorPtr[0]; + cDest[1] = pipe->destColorPtr[1]; + cDest[2] = pipe->destColorPtr[2]; + break; + case splashModeRGBX8: + cDest[0] = pipe->destColorPtr[0]; + cDest[1] = pipe->destColorPtr[1]; + cDest[2] = pipe->destColorPtr[2]; + cDest[3] = 255; + break; + case splashModeBGR8: + cDest[0] = pipe->destColorPtr[2]; + cDest[1] = pipe->destColorPtr[1]; + cDest[2] = pipe->destColorPtr[0]; + break; +#if SPLASH_CMYK + case splashModeCMYK8: + cDest[0] = pipe->destColorPtr[0]; + cDest[1] = pipe->destColorPtr[1]; + cDest[2] = pipe->destColorPtr[2]; + cDest[3] = pipe->destColorPtr[3]; + break; +#endif + } + if (pipe->destAlphaPtr) { + aDest = *pipe->destAlphaPtr; + } else { + aDest = 0xff; + } + + //----- blend function + + if (state->blendFunc) { + (*state->blendFunc)(pipe->cSrc, cDest, cBlend, bitmap->mode); + } + + //----- source alpha + + if (state->softMask) { + if (pipe->usesShape) { + aSrc = (Guchar)splashRound(pipe->aInput * *pipe->softMaskPtr++ + * pipe->shape); + } else { + aSrc = (Guchar)splashRound(pipe->aInput * *pipe->softMaskPtr++); + } + } else if (pipe->usesShape) { + // pipe->aInput is premultiplied by 255 in pipeInit + aSrc = (Guchar)splashRound(pipe->aInput * pipe->shape); + } else { + // precomputed in pipeInit + aSrc = pipe->aSrc; + } + + //----- result alpha and non-isolated group element correction + + if (pipe->noTransparency) { + alpha2 = aResult = 255; + } else { + aResult = aSrc + aDest - div255(aSrc * aDest); + + if (pipe->alpha0Ptr) { + alpha0 = *pipe->alpha0Ptr++; + alpha2 = aResult + alpha0 - div255(aResult * alpha0); + } else { + alpha2 = aResult; + } + } + + //----- result color + + cResult0 = cResult1 = cResult2 = cResult3 = 0; // make gcc happy + + switch (pipe->resultColorCtrl) { + +#if SPLASH_CMYK + case splashPipeResultColorNoAlphaBlendCMYK: + cResult3 = div255((255 - aDest) * pipe->cSrc[3] + aDest * cBlend[3]); +#endif + case splashPipeResultColorNoAlphaBlendRGB: + cResult2 = div255((255 - aDest) * pipe->cSrc[2] + aDest * cBlend[2]); + cResult1 = div255((255 - aDest) * pipe->cSrc[1] + aDest * cBlend[1]); + case splashPipeResultColorNoAlphaBlendMono: + cResult0 = div255((255 - aDest) * pipe->cSrc[0] + aDest * cBlend[0]); + break; + + case splashPipeResultColorAlphaNoBlendMono: + if (alpha2 == 0) { + cResult0 = 0; + } else { + cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] + + aSrc * pipe->cSrc[0]) / alpha2); + } + break; + case splashPipeResultColorAlphaNoBlendRGB: + if (alpha2 == 0) { + cResult0 = 0; + cResult1 = 0; + cResult2 = 0; + } else { + cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] + + aSrc * pipe->cSrc[0]) / alpha2); + cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] + + aSrc * pipe->cSrc[1]) / alpha2); + cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] + + aSrc * pipe->cSrc[2]) / alpha2); + } + break; +#if SPLASH_CMYK + case splashPipeResultColorAlphaNoBlendCMYK: + if (alpha2 == 0) { + cResult0 = 0; + cResult1 = 0; + cResult2 = 0; + cResult3 = 0; + } else { + cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] + + aSrc * pipe->cSrc[0]) / alpha2); + cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] + + aSrc * pipe->cSrc[1]) / alpha2); + cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] + + aSrc * pipe->cSrc[2]) / alpha2); + cResult3 = (Guchar)(((alpha2 - aSrc) * cDest[3] + + aSrc * pipe->cSrc[3]) / alpha2); + } + break; +#endif + + case splashPipeResultColorAlphaBlendMono: + if (alpha2 == 0) { + cResult0 = 0; + } else { + cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] + + aSrc * ((255 - aDest) * pipe->cSrc[0] + + aDest * cBlend[0]) / 255) / + alpha2); + } + break; + case splashPipeResultColorAlphaBlendRGB: + if (alpha2 == 0) { + cResult0 = 0; + cResult1 = 0; + cResult2 = 0; + } else { + cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] + + aSrc * ((255 - aDest) * pipe->cSrc[0] + + aDest * cBlend[0]) / 255) / + alpha2); + cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] + + aSrc * ((255 - aDest) * pipe->cSrc[1] + + aDest * cBlend[1]) / 255) / + alpha2); + cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] + + aSrc * ((255 - aDest) * pipe->cSrc[2] + + aDest * cBlend[2]) / 255) / + alpha2); + } + break; +#if SPLASH_CMYK + case splashPipeResultColorAlphaBlendCMYK: + if (alpha2 == 0) { + cResult0 = 0; + cResult1 = 0; + cResult2 = 0; + cResult3 = 0; + } else { + cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] + + aSrc * ((255 - aDest) * pipe->cSrc[0] + + aDest * cBlend[0]) / 255) / + alpha2); + cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] + + aSrc * ((255 - aDest) * pipe->cSrc[1] + + aDest * cBlend[1]) / 255) / + alpha2); + cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] + + aSrc * ((255 - aDest) * pipe->cSrc[2] + + aDest * cBlend[2]) / 255) / + alpha2); + cResult3 = (Guchar)(((alpha2 - aSrc) * cDest[3] + + aSrc * ((255 - aDest) * pipe->cSrc[3] + + aDest * cBlend[3]) / 255) / + alpha2); + } + break; +#endif + } + + //----- non-isolated group correction + + if (aResult != 0) { + switch (pipe->nonIsolatedGroup) { +#if SPLASH_CMYK + case 4: + cResult3 += (cResult3 - cDest[3]) * aDest * + (255 - aResult) / (255 * aResult); +#endif + case 3: + cResult2 += (cResult2 - cDest[2]) * aDest * + (255 - aResult) / (255 * aResult); + cResult1 += (cResult1 - cDest[1]) * aDest * + (255 - aResult) / (255 * aResult); + case 1: + cResult0 += (cResult0 - cDest[0]) * aDest * + (255 - aResult) / (255 * aResult); + case 0: + break; + } + } + + //----- write destination pixel + + switch (bitmap->mode) { + case splashModeMono1: + if (state->screen->test(pipe->x, pipe->y, cResult0)) { + *pipe->destColorPtr |= pipe->destColorMask; + } else { + *pipe->destColorPtr &= ~pipe->destColorMask; + } + if (!(pipe->destColorMask >>= 1)) { + pipe->destColorMask = 0x80; + ++pipe->destColorPtr; + } + break; + case splashModeMono8: + *pipe->destColorPtr++ = cResult0; + break; + case splashModeRGB8: + *pipe->destColorPtr++ = cResult0; + *pipe->destColorPtr++ = cResult1; + *pipe->destColorPtr++ = cResult2; + break; + case splashModeRGBX8: + *pipe->destColorPtr++ = cResult0; + *pipe->destColorPtr++ = cResult1; + *pipe->destColorPtr++ = cResult2; + *pipe->destColorPtr++ = 255; + break; + case splashModeBGR8: + *pipe->destColorPtr++ = cResult2; + *pipe->destColorPtr++ = cResult1; + *pipe->destColorPtr++ = cResult0; + break; +#if SPLASH_CMYK + case splashModeCMYK8: + *pipe->destColorPtr++ = cResult0; + *pipe->destColorPtr++ = cResult1; + *pipe->destColorPtr++ = cResult2; + *pipe->destColorPtr++ = cResult3; + break; +#endif + } + if (pipe->destAlphaPtr) { + *pipe->destAlphaPtr++ = aResult; + } + + } + + ++pipe->x; +} + +inline void Splash::pipeSetXY(SplashPipe *pipe, int x, int y) { + pipe->x = x; + pipe->y = y; + if (state->softMask) { + pipe->softMaskPtr = + &state->softMask->data[y * state->softMask->rowSize + x]; + } + switch (bitmap->mode) { + case splashModeMono1: + pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + (x >> 3)]; + pipe->destColorMask = 0x80 >> (x & 7); + break; + case splashModeMono8: + pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + x]; + break; + case splashModeRGB8: + case splashModeBGR8: + pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x]; + break; + case splashModeRGBX8: + pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x]; + break; +#if SPLASH_CMYK + case splashModeCMYK8: + pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x]; + break; +#endif + } + if (bitmap->alpha) { + pipe->destAlphaPtr = &bitmap->alpha[y * bitmap->width + x]; + } else { + pipe->destAlphaPtr = NULL; + } + if (state->inNonIsolatedGroup && alpha0Bitmap->alpha) { + pipe->alpha0Ptr = + &alpha0Bitmap->alpha[(alpha0Y + y) * alpha0Bitmap->width + + (alpha0X + x)]; + } else { + pipe->alpha0Ptr = NULL; + } +} + +inline void Splash::pipeIncX(SplashPipe *pipe) { + ++pipe->x; + if (state->softMask) { + ++pipe->softMaskPtr; + } + switch (bitmap->mode) { + case splashModeMono1: + if (!(pipe->destColorMask >>= 1)) { + pipe->destColorMask = 0x80; + ++pipe->destColorPtr; + } + break; + case splashModeMono8: + ++pipe->destColorPtr; + break; + case splashModeRGB8: + case splashModeBGR8: + pipe->destColorPtr += 3; + break; + case splashModeRGBX8: + pipe->destColorPtr += 4; + break; +#if SPLASH_CMYK + case splashModeCMYK8: + pipe->destColorPtr += 4; + break; +#endif + } + if (pipe->destAlphaPtr) { + ++pipe->destAlphaPtr; + } + if (pipe->alpha0Ptr) { + ++pipe->alpha0Ptr; + } +} + +inline void Splash::drawPixel(SplashPipe *pipe, int x, int y, GBool noClip) { + if (noClip || state->clip->test(x, y)) { + pipeSetXY(pipe, x, y); + pipeRun(pipe); + updateModX(x); + updateModY(y); + } +} + +inline void Splash::drawAAPixelInit() { + aaBufY = -1; +} + +inline void Splash::drawAAPixel(SplashPipe *pipe, int x, int y) { +#if splashAASize == 4 + static int bitCount4[16] = { 0, 1, 1, 2, 1, 2, 2, 3, + 1, 2, 2, 3, 2, 3, 3, 4 }; + int w; +#else + int xx, yy; +#endif + SplashColorPtr p; + int x0, x1, t; + + if (x < 0 || x >= bitmap->width || + y < state->clip->getYMinI() || y > state->clip->getYMaxI()) { + return; + } + + // update aaBuf + if (y != aaBufY) { + memset(aaBuf->getDataPtr(), 0xff, + aaBuf->getRowSize() * aaBuf->getHeight()); + x0 = 0; + x1 = bitmap->width - 1; + state->clip->clipAALine(aaBuf, &x0, &x1, y); + aaBufY = y; + } + + // compute the shape value +#if splashAASize == 4 + p = aaBuf->getDataPtr() + (x >> 1); + w = aaBuf->getRowSize(); + if (x & 1) { + t = bitCount4[*p & 0x0f] + bitCount4[p[w] & 0x0f] + + bitCount4[p[2*w] & 0x0f] + bitCount4[p[3*w] & 0x0f]; + } else { + t = bitCount4[*p >> 4] + bitCount4[p[w] >> 4] + + bitCount4[p[2*w] >> 4] + bitCount4[p[3*w] >> 4]; + } +#else + t = 0; + for (yy = 0; yy < splashAASize; ++yy) { + for (xx = 0; xx < splashAASize; ++xx) { + p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + + ((x * splashAASize + xx) >> 3); + t += (*p >> (7 - ((x * splashAASize + xx) & 7))) & 1; + } + } +#endif + + // draw the pixel + if (t != 0) { + pipeSetXY(pipe, x, y); + pipe->shape *= aaGamma[t]; + pipeRun(pipe); + updateModX(x); + updateModY(y); + } +} + +inline void Splash::drawSpan(SplashPipe *pipe, int x0, int x1, int y, + GBool noClip) { + int x; + + pipeSetXY(pipe, x0, y); + if (noClip) { + for (x = x0; x <= x1; ++x) { + pipeRun(pipe); + } + updateModX(x0); + updateModX(x1); + updateModY(y); + } else { + for (x = x0; x <= x1; ++x) { + if (state->clip->test(x, y)) { + pipeRun(pipe); + updateModX(x); + updateModY(y); + } else { + pipeIncX(pipe); + } + } + } +} + +inline void Splash::drawAALine(SplashPipe *pipe, int x0, int x1, int y) { +#if splashAASize == 4 + static int bitCount4[16] = { 0, 1, 1, 2, 1, 2, 2, 3, + 1, 2, 2, 3, 2, 3, 3, 4 }; + SplashColorPtr p0, p1, p2, p3; + int t; +#else + SplashColorPtr p; + int xx, yy, t; +#endif + int x; + +#if splashAASize == 4 + p0 = aaBuf->getDataPtr() + (x0 >> 1); + p1 = p0 + aaBuf->getRowSize(); + p2 = p1 + aaBuf->getRowSize(); + p3 = p2 + aaBuf->getRowSize(); +#endif + pipeSetXY(pipe, x0, y); + for (x = x0; x <= x1; ++x) { + + // compute the shape value +#if splashAASize == 4 + if (x & 1) { + t = bitCount4[*p0 & 0x0f] + bitCount4[*p1 & 0x0f] + + bitCount4[*p2 & 0x0f] + bitCount4[*p3 & 0x0f]; + ++p0; ++p1; ++p2; ++p3; + } else { + t = bitCount4[*p0 >> 4] + bitCount4[*p1 >> 4] + + bitCount4[*p2 >> 4] + bitCount4[*p3 >> 4]; + } +#else + t = 0; + for (yy = 0; yy < splashAASize; ++yy) { + for (xx = 0; xx < splashAASize; ++xx) { + p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + + ((x * splashAASize + xx) >> 3); + t += (*p >> (7 - ((x * splashAASize + xx) & 7))) & 1; + } + } +#endif + + if (t != 0) { + pipe->shape = aaGamma[t]; + pipeRun(pipe); + updateModX(x); + updateModY(y); + } else { + pipeIncX(pipe); + } } } //------------------------------------------------------------------------ + +// Transform a point from user space to device space. +inline void Splash::transform(SplashCoord *matrix, + SplashCoord xi, SplashCoord yi, + SplashCoord *xo, SplashCoord *yo) { + // [ m[0] m[1] 0 ] + // [xo yo 1] = [xi yi 1] * [ m[2] m[3] 0 ] + // [ m[4] m[5] 1 ] + *xo = xi * matrix[0] + yi * matrix[2] + matrix[4]; + *yo = xi * matrix[1] + yi * matrix[3] + matrix[5]; +} + +//------------------------------------------------------------------------ // Splash //------------------------------------------------------------------------ -Splash::Splash(SplashBitmap *bitmapA) { +Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, + SplashScreenParams *screenParams) { + int i; + + bitmap = bitmapA; + vectorAntialias = vectorAntialiasA; + state = new SplashState(bitmap->width, bitmap->height, vectorAntialias, + screenParams); + if (vectorAntialias) { + aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize, + 1, splashModeMono1, gFalse); + for (i = 0; i <= splashAASize * splashAASize; ++i) { + aaGamma[i] = splashPow((SplashCoord)i / + (SplashCoord)(splashAASize * splashAASize), + 1.5); + } + } else { + aaBuf = NULL; + } + clearModRegion(); + debugMode = gFalse; +} + +Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, + SplashScreen *screenA) { + int i; + bitmap = bitmapA; - state = new SplashState(bitmap->width, bitmap->height); - softMask = NULL; + vectorAntialias = vectorAntialiasA; + state = new SplashState(bitmap->width, bitmap->height, vectorAntialias, + screenA); + if (vectorAntialias) { + aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize, + 1, splashModeMono1, gFalse); + for (i = 0; i <= splashAASize * splashAASize; ++i) { + aaGamma[i] = splashPow((SplashCoord)i / + (SplashCoord)(splashAASize * splashAASize), + 1.5); + } + } else { + aaBuf = NULL; + } clearModRegion(); debugMode = gFalse; } @@ -54,8 +843,8 @@ Splash::~Splash() { restoreState(); } delete state; - if (softMask) { - delete softMask; + if (vectorAntialias) { + delete aaBuf; } } @@ -63,6 +852,10 @@ Splash::~Splash() { // state read //------------------------------------------------------------------------ +SplashCoord *Splash::getMatrix() { + return state->matrix; +} + SplashPattern *Splash::getStrokePattern() { return state->strokePattern; } @@ -123,10 +916,22 @@ SplashClip *Splash::getClip() { return state->clip; } +SplashBitmap *Splash::getSoftMask() { + return state->softMask; +} + +GBool Splash::getInNonIsolatedGroup() { + return state->inNonIsolatedGroup; +} + //------------------------------------------------------------------------ // state write //------------------------------------------------------------------------ +void Splash::setMatrix(SplashCoord *matrix) { + memcpy(state->matrix, matrix, 6 * sizeof(SplashCoord)); +} + void Splash::setStrokePattern(SplashPattern *strokePattern) { state->setStrokePattern(strokePattern); } @@ -180,6 +985,10 @@ void Splash::setLineDash(SplashCoord *lineDash, int lineDashLength, state->setLineDash(lineDash, lineDashLength, lineDashPhase); } +void Splash::setStrokeAdjust(GBool strokeAdjust) { + state->strokeAdjust = strokeAdjust; +} + void Splash::clipResetToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1) { state->clip->resetToRect(x0, y0, x1, y1); @@ -191,7 +1000,19 @@ SplashError Splash::clipToRect(SplashCoord x0, SplashCoord y0, } SplashError Splash::clipToPath(SplashPath *path, GBool eo) { - return state->clip->clipToPath(path, state->flatness, eo); + return state->clip->clipToPath(path, state->matrix, state->flatness, eo); +} + +void Splash::setSoftMask(SplashBitmap *softMask) { + state->setSoftMask(softMask); +} + +void Splash::setInNonIsolatedGroup(SplashBitmap *alpha0BitmapA, + int alpha0XA, int alpha0YA) { + alpha0Bitmap = alpha0BitmapA; + alpha0X = alpha0XA; + alpha0Y = alpha0YA; + state->inNonIsolatedGroup = gTrue; } //------------------------------------------------------------------------ @@ -219,57 +1040,17 @@ SplashError Splash::restoreState() { } //------------------------------------------------------------------------ -// soft mask -//------------------------------------------------------------------------ - -void Splash::setSoftMask(SplashBitmap *softMaskA) { - if (softMask) { - delete softMask; - } - softMask = softMaskA; -} - -//------------------------------------------------------------------------ -// modified region -//------------------------------------------------------------------------ - -void Splash::clearModRegion() { - modXMin = bitmap->getWidth(); - modYMin = bitmap->getHeight(); - modXMax = -1; - modYMax = -1; -} - -inline void Splash::updateModX(int x) { - if (x < modXMin) { - modXMin = x; - } - if (x > modXMax) { - modXMax = x; - } -} - -inline void Splash::updateModY(int y) { - if (y < modYMin) { - modYMin = y; - } - if (y > modYMax) { - modYMax = y; - } -} - -//------------------------------------------------------------------------ // drawing operations //------------------------------------------------------------------------ -void Splash::clear(SplashColorPtr color) { +void Splash::clear(SplashColorPtr color, Guchar alpha) { SplashColorPtr row, p; Guchar mono; int x, y; switch (bitmap->mode) { case splashModeMono1: - mono = color[0] ? 0xff : 0x00; + mono = (color[0] & 0x80) ? 0xff : 0x00; if (bitmap->rowSize < 0) { memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), mono, -bitmap->rowSize * bitmap->height); @@ -285,28 +1066,7 @@ void Splash::clear(SplashColorPtr color) { memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); } break; - case splashModeAMono8: - if (color[0] == color[1]) { - if (bitmap->rowSize < 0) { - memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), - color[0], -bitmap->rowSize * bitmap->height); - } else { - memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); - } - } else { - row = bitmap->data; - for (y = 0; y < bitmap->height; ++y) { - p = row; - for (x = 0; x < bitmap->width; ++x) { - *p++ = color[0]; - *p++ = color[1]; - } - row += bitmap->rowSize; - } - } - break; case splashModeRGB8: - case splashModeBGR8: if (color[0] == color[1] && color[1] == color[2]) { if (bitmap->rowSize < 0) { memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), @@ -319,15 +1079,15 @@ void Splash::clear(SplashColorPtr color) { 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; } } break; - case splashModeRGB8Qt: + case splashModeRGBX8: if (color[0] == color[1] && color[1] == color[2]) { if (bitmap->rowSize < 0) { memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), @@ -349,12 +1109,8 @@ void Splash::clear(SplashColorPtr color) { } } break; - case splashModeARGB8: - case splashModeBGRA8: -#if SPLASH_CMYK - case splashModeCMYK8: -#endif - if (color[0] == color[1] && color[1] == color[2] && color[2] == color[3]) { + case splashModeBGR8: + if (color[0] == color[1] && color[1] == color[2]) { if (bitmap->rowSize < 0) { memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), color[0], -bitmap->rowSize * bitmap->height); @@ -369,16 +1125,14 @@ void Splash::clear(SplashColorPtr color) { *p++ = color[0]; *p++ = color[1]; *p++ = color[2]; - *p++ = color[3]; } row += bitmap->rowSize; } } break; #if SPLASH_CMYK - case splashModeACMYK8: - if (color[0] == color[1] && color[1] == color[2] && - color[2] == color[3] && color[3] == color[4]) { + case splashModeCMYK8: + if (color[0] == color[1] && color[1] == color[2] && color[2] == color[3]) { if (bitmap->rowSize < 0) { memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), color[0], -bitmap->rowSize * bitmap->height); @@ -394,7 +1148,6 @@ void Splash::clear(SplashColorPtr color) { *p++ = color[1]; *p++ = color[2]; *p++ = color[3]; - *p++ = color[4]; } row += bitmap->rowSize; } @@ -403,6 +1156,10 @@ void Splash::clear(SplashColorPtr color) { #endif } + if (bitmap->alpha) { + memset(bitmap->alpha, alpha, bitmap->width * bitmap->height); + } + updateModX(0); updateModY(0); updateModX(bitmap->width - 1); @@ -410,7 +1167,7 @@ void Splash::clear(SplashColorPtr color) { } SplashError Splash::stroke(SplashPath *path) { - SplashXPath *xPath, *xPath2; + SplashPath *path2, *dPath; if (debugMode) { printf("stroke [dash:%d] [width:%.2f]:\n", @@ -421,33 +1178,38 @@ SplashError Splash::stroke(SplashPath *path) { if (path->length == 0) { return splashErrEmptyPath; } - xPath = new SplashXPath(path, state->flatness, gFalse); - if (xPath->length == 0) { - delete xPath; - return splashErrEmptyPath; - } + path2 = flattenPath(path, state->matrix, state->flatness); if (state->lineDashLength > 0) { - xPath2 = makeDashedPath(xPath); - delete xPath; - xPath = xPath2; + dPath = makeDashedPath(path2); + delete path2; + path2 = dPath; } - if (state->lineWidth <= 1) { - strokeNarrow(xPath); + if (state->lineWidth == 0) { + strokeNarrow(path2); } else { - strokeWide(xPath); + strokeWide(path2); } - delete xPath; + delete path2; return splashOk; } -void Splash::strokeNarrow(SplashXPath *xPath) { +void Splash::strokeNarrow(SplashPath *path) { + SplashPipe pipe; + SplashXPath *xPath; SplashXPathSeg *seg; int x0, x1, x2, x3, y0, y1, x, y, t; SplashCoord dx, dy, dxdy; SplashClipResult clipRes; - int nClipRes[3] = {0, 0, 0}; + int nClipRes[3]; int i; + nClipRes[0] = nClipRes[1] = nClipRes[2] = 0; + + xPath = new SplashXPath(path, state->matrix, state->flatness, gFalse); + + pipeInit(&pipe, 0, 0, state->strokePattern, NULL, state->strokeAlpha, + gFalse, gFalse); + for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) { x0 = splashFloor(seg->x0); @@ -462,8 +1224,7 @@ void Splash::strokeNarrow(SplashXPath *xPath) { } if ((clipRes = state->clip->testSpan(x0, x1, y0)) != splashClipAllOutside) { - drawSpan(x0, x1, y0, state->strokePattern, state->strokeAlpha, - clipRes == splashClipAllInside); + drawSpan(&pipe, x0, x1, y0, clipRes == splashClipAllInside); } // segment with |dx| > |dy| @@ -483,31 +1244,29 @@ void Splash::strokeNarrow(SplashXPath *xPath) { if (dx > 0) { x2 = x0; x3 = splashFloor(seg->x0 + ((SplashCoord)y0 + 1 - seg->y0) * dxdy); - drawSpan(x2, (x2 <= x3 - 1) ? x3 - 1 : x2, y0, state->strokePattern, - state->strokeAlpha, clipRes == splashClipAllInside); + drawSpan(&pipe, x2, (x2 <= x3 - 1) ? x3 - 1 : x2, y0, + clipRes == splashClipAllInside); x2 = x3; for (y = y0 + 1; y <= y1 - 1; ++y) { x3 = splashFloor(seg->x0 + ((SplashCoord)y + 1 - seg->y0) * dxdy); - drawSpan(x2, x3 - 1, y, state->strokePattern, - state->strokeAlpha, clipRes == splashClipAllInside); + drawSpan(&pipe, x2, x3 - 1, y, clipRes == splashClipAllInside); x2 = x3; } - drawSpan(x2, x2 <= x1 ? x1 : x2, y1, state->strokePattern, - state->strokeAlpha, clipRes == splashClipAllInside); + drawSpan(&pipe, x2, x2 <= x1 ? x1 : x2, y1, + clipRes == splashClipAllInside); } else { x2 = x0; x3 = splashFloor(seg->x0 + ((SplashCoord)y0 + 1 - seg->y0) * dxdy); - drawSpan((x3 + 1 <= x2) ? x3 + 1 : x2, x2, y0, state->strokePattern, - state->strokeAlpha, clipRes == splashClipAllInside); + drawSpan(&pipe, (x3 + 1 <= x2) ? x3 + 1 : x2, x2, y0, + clipRes == splashClipAllInside); x2 = x3; for (y = y0 + 1; y <= y1 - 1; ++y) { x3 = splashFloor(seg->x0 + ((SplashCoord)y + 1 - seg->y0) * dxdy); - drawSpan(x3 + 1, x2, y, state->strokePattern, - state->strokeAlpha, clipRes == splashClipAllInside); + drawSpan(&pipe, x3 + 1, x2, y, clipRes == splashClipAllInside); x2 = x3; } - drawSpan(x1, (x1 <= x2) ? x2 : x1, y1, state->strokePattern, - state->strokeAlpha, clipRes == splashClipAllInside); + drawSpan(&pipe, x1, (x1 <= x2) ? x2 : x1, y1, + clipRes == splashClipAllInside); } } @@ -521,16 +1280,13 @@ void Splash::strokeNarrow(SplashXPath *xPath) { if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0, x0 <= x1 ? x1 : x0, y1)) != splashClipAllOutside) { - drawPixel(x0, y0, state->strokePattern, state->strokeAlpha, - clipRes == splashClipAllInside); + drawPixel(&pipe, x0, y0, clipRes == splashClipAllInside); for (y = y0 + 1; y <= y1 - 1; ++y) { x = splashFloor(seg->x0 + ((SplashCoord)y - seg->y0) * dxdy); - drawPixel(x, y, state->strokePattern, state->strokeAlpha, - clipRes == splashClipAllInside); + drawPixel(&pipe, x, y, clipRes == splashClipAllInside); } - drawPixel(x1, y1, state->strokePattern, state->strokeAlpha, - clipRes == splashClipAllInside); - } + drawPixel(&pipe, x1, y1, clipRes == splashClipAllInside); + } } ++nClipRes[clipRes]; } @@ -542,187 +1298,140 @@ void Splash::strokeNarrow(SplashXPath *xPath) { } else { opClipRes = splashClipAllOutside; } -} -void Splash::strokeWide(SplashXPath *xPath) { - SplashXPathSeg *seg, *seg2; - SplashPath *widePath; - SplashCoord d, dx, dy, wdx, wdy, dxPrev, dyPrev, wdxPrev, wdyPrev; - SplashCoord dotprod, miter; - int i, j; + delete xPath; +} - dx = dy = wdx = wdy = 0; // make gcc happy - dxPrev = dyPrev = wdxPrev = wdyPrev = 0; // make gcc happy +void Splash::strokeWide(SplashPath *path) { + SplashPath *path2; - for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) { + path2 = makeStrokePath(path, gFalse); + fillWithPattern(path2, gFalse, state->strokePattern, state->strokeAlpha); + delete path2; +} - // save the deltas for the previous segment; if this is the first - // segment on a subpath, compute the deltas for the last segment - // on the subpath (which may be used to draw a line join) - if (seg->flags & splashXPathFirst) { - for (j = i + 1, seg2 = &xPath->segs[j]; j < xPath->length; ++j, ++seg2) { - if (seg2->flags & splashXPathLast) { - d = splashDist(seg2->x0, seg2->y0, seg2->x1, seg2->y1); - if (d == 0) { - //~ not clear what the behavior should be for joins with d==0 - dxPrev = 0; - dyPrev = 1; - } else { - d = (SplashCoord)1 / d; - dxPrev = d * (seg2->x1 - seg2->x0); - dyPrev = d * (seg2->y1 - seg2->y0); - } - wdxPrev = (SplashCoord)0.5 * state->lineWidth * dxPrev; - wdyPrev = (SplashCoord)0.5 * state->lineWidth * dyPrev; - break; - } - } - } else { - dxPrev = dx; - dyPrev = dy; - wdxPrev = wdx; - wdyPrev = wdy; - } +SplashPath *Splash::flattenPath(SplashPath *path, SplashCoord *matrix, + SplashCoord flatness) { + SplashPath *fPath; + SplashCoord flatness2; + Guchar flag; + int i; - // compute deltas for this line segment - d = splashDist(seg->x0, seg->y0, seg->x1, seg->y1); - if (d == 0) { - // we need to draw end caps on zero-length lines - //~ not clear what the behavior should be for splashLineCapButt with d==0 - dx = 0; - dy = 1; + fPath = new SplashPath(); + flatness2 = flatness * flatness; + i = 0; + while (i < path->length) { + flag = path->flags[i]; + if (flag & splashPathFirst) { + fPath->moveTo(path->pts[i].x, path->pts[i].y); + ++i; } else { - d = (SplashCoord)1 / d; - dx = d * (seg->x1 - seg->x0); - dy = d * (seg->y1 - seg->y0); - } - wdx = (SplashCoord)0.5 * state->lineWidth * dx; - wdy = (SplashCoord)0.5 * state->lineWidth * dy; - - // initialize the path (which will be filled) - widePath = new SplashPath(); - widePath->moveTo(seg->x0 - wdy, seg->y0 + wdx); - - // draw the start cap - if (seg->flags & splashXPathEnd0) { - switch (state->lineCap) { - case splashLineCapButt: - widePath->lineTo(seg->x0 + wdy, seg->y0 - wdx); - break; - case splashLineCapRound: - widePath->arcCWTo(seg->x0 + wdy, seg->y0 - wdx, seg->x0, seg->y0); - break; - case splashLineCapProjecting: - widePath->lineTo(seg->x0 - wdx - wdy, seg->y0 + wdx - wdy); - widePath->lineTo(seg->x0 - wdx + wdy, seg->y0 - wdx - wdy); - widePath->lineTo(seg->x0 + wdy, seg->y0 - wdx); - break; + if (flag & splashPathCurve) { + flattenCurve(path->pts[i-1].x, path->pts[i-1].y, + path->pts[i ].x, path->pts[i ].y, + path->pts[i+1].x, path->pts[i+1].y, + path->pts[i+2].x, path->pts[i+2].y, + matrix, flatness2, fPath); + i += 3; + } else { + fPath->lineTo(path->pts[i].x, path->pts[i].y); + ++i; + } + if (path->flags[i-1] & splashPathClosed) { + fPath->close(); } - } else { - widePath->lineTo(seg->x0 + wdy, seg->y0 - wdx); } + } + return fPath; +} - // draw the left side of the segment - widePath->lineTo(seg->x1 + wdy, seg->y1 - wdx); - - // draw the end cap - if (seg->flags & splashXPathEnd1) { - switch (state->lineCap) { - case splashLineCapButt: - widePath->lineTo(seg->x1 - wdy, seg->y1 + wdx); - break; - case splashLineCapRound: - widePath->arcCWTo(seg->x1 - wdy, seg->y1 + wdx, seg->x1, seg->y1); - break; - case splashLineCapProjecting: - widePath->lineTo(seg->x1 + wdx + wdy, seg->y1 - wdx + wdy); - widePath->lineTo(seg->x1 + wdx - wdy, seg->y1 + wdx + wdy); - widePath->lineTo(seg->x1 - wdy, seg->y1 + wdx); - break; - } +void Splash::flattenCurve(SplashCoord x0, SplashCoord y0, + SplashCoord x1, SplashCoord y1, + SplashCoord x2, SplashCoord y2, + SplashCoord x3, SplashCoord y3, + SplashCoord *matrix, SplashCoord flatness2, + SplashPath *fPath) { + SplashCoord cx[splashMaxCurveSplits + 1][3]; + SplashCoord cy[splashMaxCurveSplits + 1][3]; + int cNext[splashMaxCurveSplits + 1]; + SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh; + SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh; + SplashCoord dx, dy, mx, my, tx, ty, d1, d2; + int p1, p2, p3; + + // initial segment + p1 = 0; + p2 = splashMaxCurveSplits; + cx[p1][0] = x0; cy[p1][0] = y0; + cx[p1][1] = x1; cy[p1][1] = y1; + cx[p1][2] = x2; cy[p1][2] = y2; + cx[p2][0] = x3; cy[p2][0] = y3; + cNext[p1] = p2; + + while (p1 < splashMaxCurveSplits) { + + // get the next segment + xl0 = cx[p1][0]; yl0 = cy[p1][0]; + xx1 = cx[p1][1]; yy1 = cy[p1][1]; + xx2 = cx[p1][2]; yy2 = cy[p1][2]; + p2 = cNext[p1]; + xr3 = cx[p2][0]; yr3 = cy[p2][0]; + + // compute the distances (in device space) from the control points + // to the midpoint of the straight line (this is a bit of a hack, + // but it's much faster than computing the actual distances to the + // line) + transform(matrix, (xl0 + xr3) * 0.5, (yl0 + yr3) * 0.5, &mx, &my); + transform(matrix, xx1, yy1, &tx, &ty); + dx = tx - mx; + dy = ty - my; + d1 = dx*dx + dy*dy; + transform(matrix, xx2, yy2, &tx, &ty); + dx = tx - mx; + dy = ty - my; + d2 = dx*dx + dy*dy; + + // if the curve is flat enough, or no more subdivisions are + // allowed, add the straight line segment + if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) { + fPath->lineTo(xr3, yr3); + p1 = p2; + + // otherwise, subdivide the curve } else { - widePath->lineTo(seg->x1 - wdy, seg->y1 + wdx); - } - - // draw the right side of the segment - widePath->lineTo(seg->x0 - wdy, seg->y0 + wdx); - - // fill the segment - fillWithPattern(widePath, gTrue, state->strokePattern, state->strokeAlpha); - delete widePath; - - // draw the line join - if (!(seg->flags & splashXPathEnd0)) { - widePath = NULL; - switch (state->lineJoin) { - case splashLineJoinMiter: - dotprod = -(dx * dxPrev + dy * dyPrev); - if (splashAbs(splashAbs(dotprod) - 1) > 0.01) { - widePath = new SplashPath(); - widePath->moveTo(seg->x0, seg->y0); - miter = (SplashCoord)2 / ((SplashCoord)1 - dotprod); - if (splashSqrt(miter) <= state->miterLimit) { - miter = splashSqrt(miter - 1); - if (dy * dxPrev > dx * dyPrev) { - widePath->lineTo(seg->x0 + wdyPrev, seg->y0 - wdxPrev); - widePath->lineTo(seg->x0 + wdy - miter * wdx, - seg->y0 - wdx - miter * wdy); - widePath->lineTo(seg->x0 + wdy, seg->y0 - wdx); - } else { - widePath->lineTo(seg->x0 - wdyPrev, seg->y0 + wdxPrev); - widePath->lineTo(seg->x0 - wdy - miter * wdx, - seg->y0 + wdx - miter * wdy); - widePath->lineTo(seg->x0 - wdy, seg->y0 + wdx); - } - } else { - if (dy * dxPrev > dx * dyPrev) { - widePath->lineTo(seg->x0 + wdyPrev, seg->y0 - wdxPrev); - widePath->lineTo(seg->x0 + wdy, seg->y0 - wdx); - } else { - widePath->lineTo(seg->x0 - wdyPrev, seg->y0 + wdxPrev); - widePath->lineTo(seg->x0 - wdy, seg->y0 + wdx); - } - } - } - break; - case splashLineJoinRound: - widePath = new SplashPath(); - widePath->moveTo(seg->x0 + wdy, seg->y0 - wdx); - widePath->arcCWTo(seg->x0 + wdy, seg->y0 - wdx, seg->x0, seg->y0); - break; - case splashLineJoinBevel: - widePath = new SplashPath(); - widePath->moveTo(seg->x0, seg->y0); - if (dy * dxPrev > dx * dyPrev) { - widePath->lineTo(seg->x0 + wdyPrev, seg->y0 - wdxPrev); - widePath->lineTo(seg->x0 + wdy, seg->y0 - wdx); - } else { - widePath->lineTo(seg->x0 - wdyPrev, seg->y0 + wdxPrev); - widePath->lineTo(seg->x0 - wdy, seg->y0 + wdx); - } - break; - } - if (widePath) { - fillWithPattern(widePath, gTrue, state->strokePattern, - state->strokeAlpha); - delete widePath; - } + xl1 = (xl0 + xx1) * 0.5; + yl1 = (yl0 + yy1) * 0.5; + xh = (xx1 + xx2) * 0.5; + yh = (yy1 + yy2) * 0.5; + xl2 = (xl1 + xh) * 0.5; + yl2 = (yl1 + yh) * 0.5; + xr2 = (xx2 + xr3) * 0.5; + yr2 = (yy2 + yr3) * 0.5; + xr1 = (xh + xr2) * 0.5; + yr1 = (yh + yr2) * 0.5; + xr0 = (xl2 + xr1) * 0.5; + yr0 = (yl2 + yr1) * 0.5; + // add the new subdivision points + p3 = (p1 + p2) / 2; + cx[p1][1] = xl1; cy[p1][1] = yl1; + cx[p1][2] = xl2; cy[p1][2] = yl2; + cNext[p1] = p3; + cx[p3][0] = xr0; cy[p3][0] = yr0; + cx[p3][1] = xr1; cy[p3][1] = yr1; + cx[p3][2] = xr2; cy[p3][2] = yr2; + cNext[p3] = p2; } } } -SplashXPath *Splash::makeDashedPath(SplashXPath *xPath) { - SplashXPath *dPath; - GBool lineDashStartOn, lineDashOn; - GBool atSegStart, atSegEnd, atDashStart, atDashEnd; - int lineDashStartIdx, lineDashIdx, subpathStart; - SplashCoord lineDashTotal, lineDashStartPhase, lineDashDist; - int segIdx; - SplashXPathSeg *seg; - SplashCoord sx0, sy0, sx1, sy1, ax0, ay0, ax1, ay1, dist; - int i; - - dPath = new SplashXPath(); +SplashPath *Splash::makeDashedPath(SplashPath *path) { + SplashPath *dPath; + SplashCoord lineDashTotal; + SplashCoord lineDashStartPhase, lineDashDist, segLen; + SplashCoord x0, y0, x1, y1, xa, ya; + GBool lineDashStartOn, lineDashOn, newPath; + int lineDashStartIdx, lineDashIdx; + int i, j, k; lineDashTotal = 0; for (i = 0; i < state->lineDashLength; ++i) { @@ -739,85 +1448,75 @@ SplashXPath *Splash::makeDashedPath(SplashXPath *xPath) { ++lineDashStartIdx; } - segIdx = 0; - seg = xPath->segs; - sx0 = seg->x0; - sy0 = seg->y0; - sx1 = seg->x1; - sy1 = seg->y1; - dist = splashDist(sx0, sy0, sx1, sy1); - lineDashOn = lineDashStartOn; - lineDashIdx = lineDashStartIdx; - lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase; - atSegStart = gTrue; - atDashStart = gTrue; - subpathStart = dPath->length; - - while (segIdx < xPath->length) { - - ax0 = sx0; - ay0 = sy0; - if (dist <= lineDashDist) { - ax1 = sx1; - ay1 = sy1; - lineDashDist -= dist; - dist = 0; - atSegEnd = gTrue; - atDashEnd = lineDashDist == 0 || (seg->flags & splashXPathLast); - } else { - ax1 = sx0 + (lineDashDist / dist) * (sx1 - sx0); - ay1 = sy0 + (lineDashDist / dist) * (sy1 - sy0); - sx0 = ax1; - sy0 = ay1; - dist -= lineDashDist; - lineDashDist = 0; - atSegEnd = gFalse; - atDashEnd = gTrue; - } - - if (lineDashOn) { - dPath->addSegment(ax0, ay0, ax1, ay1, - atDashStart, atDashEnd, - atDashStart, atDashEnd); - // end of closed subpath - if (atSegEnd && - (seg->flags & splashXPathLast) && - !(seg->flags & splashXPathEnd1)) { - dPath->segs[subpathStart].flags &= ~splashXPathEnd0; - dPath->segs[dPath->length - 1].flags &= ~splashXPathEnd1; - } - } - - if (atDashEnd) { - lineDashOn = !lineDashOn; - if (++lineDashIdx == state->lineDashLength) { - lineDashIdx = 0; - } - lineDashDist = state->lineDash[lineDashIdx]; - atDashStart = gTrue; - } else { - atDashStart = gFalse; - } - if (atSegEnd) { - if (++segIdx < xPath->length) { - ++seg; - sx0 = seg->x0; - sy0 = seg->y0; - sx1 = seg->x1; - sy1 = seg->y1; - dist = splashDist(sx0, sy0, sx1, sy1); - if (seg->flags & splashXPathFirst) { - lineDashOn = lineDashStartOn; - lineDashIdx = lineDashStartIdx; - lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase; - atDashStart = gTrue; - subpathStart = dPath->length; + dPath = new SplashPath(); + + // process each subpath + i = 0; + while (i < path->length) { + + // find the end of the subpath + for (j = i; + j < path->length - 1 && !(path->flags[j] & splashPathLast); + ++j) ; + + // initialize the dash parameters + lineDashOn = lineDashStartOn; + lineDashIdx = lineDashStartIdx; + lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase; + + // process each segment of the subpath + newPath = gTrue; + for (k = i; k < j; ++k) { + + // grab the segment + x0 = path->pts[k].x; + y0 = path->pts[k].y; + x1 = path->pts[k+1].x; + y1 = path->pts[k+1].y; + segLen = splashDist(x0, y0, x1, y1); + + // process the segment + while (segLen > 0) { + + if (lineDashDist >= segLen) { + if (lineDashOn) { + if (newPath) { + dPath->moveTo(x0, y0); + newPath = gFalse; + } + dPath->lineTo(x1, y1); + } + lineDashDist -= segLen; + segLen = 0; + + } else { + xa = x0 + (lineDashDist / segLen) * (x1 - x0); + ya = y0 + (lineDashDist / segLen) * (y1 - y0); + if (lineDashOn) { + if (newPath) { + dPath->moveTo(x0, y0); + newPath = gFalse; + } + dPath->lineTo(xa, ya); + } + x0 = xa; + y0 = ya; + segLen -= lineDashDist; + lineDashDist = 0; + } + + // get the next entry in the dash array + if (lineDashDist <= 0) { + lineDashOn = !lineDashOn; + if (++lineDashIdx == state->lineDashLength) { + lineDashIdx = 0; + } + lineDashDist = state->lineDash[lineDashIdx]; + newPath = gTrue; } } - atSegStart = gTrue; - } else { - atSegStart = gFalse; } + i = j + 1; } return dPath; @@ -834,6 +1533,7 @@ SplashError Splash::fill(SplashPath *path, GBool eo) { SplashError Splash::fillWithPattern(SplashPath *path, GBool eo, SplashPattern *pattern, SplashCoord alpha) { + SplashPipe pipe; SplashXPath *xPath; SplashXPathScanner *scanner; int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y; @@ -842,40 +1542,59 @@ SplashError Splash::fillWithPattern(SplashPath *path, GBool eo, if (path->length == 0) { return splashErrEmptyPath; } - xPath = new SplashXPath(path, state->flatness, gTrue); + xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue); + if (vectorAntialias) { + xPath->aaScale(); + } xPath->sort(); scanner = new SplashXPathScanner(xPath, eo); // get the min and max x and y values - scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI); + if (vectorAntialias) { + scanner->getBBoxAA(&xMinI, &yMinI, &xMaxI, &yMaxI); + } else { + scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI); + } // check clipping if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI)) != splashClipAllOutside) { // limit the y range - if (yMinI < state->clip->getYMin()) { - yMinI = state->clip->getYMin(); + if (yMinI < state->clip->getYMinI()) { + yMinI = state->clip->getYMinI(); } - if (yMaxI > state->clip->getYMax()) { - yMaxI = state->clip->getYMax(); + if (yMaxI > state->clip->getYMaxI()) { + yMaxI = state->clip->getYMaxI(); } + pipeInit(&pipe, 0, yMinI, pattern, NULL, alpha, vectorAntialias, gFalse); + // draw the spans - for (y = yMinI; y <= yMaxI; ++y) { - while (scanner->getNextSpan(y, &x0, &x1)) { - if (clipRes == splashClipAllInside) { - drawSpan(x0, x1, y, pattern, alpha, gTrue); - } else { - // limit the x range - if (x0 < state->clip->getXMin()) { - x0 = state->clip->getXMin(); - } - if (x1 > state->clip->getXMax()) { - x1 = state->clip->getXMax(); + if (vectorAntialias) { + for (y = yMinI; y <= yMaxI; ++y) { + scanner->renderAALine(aaBuf, &x0, &x1, y); + if (clipRes != splashClipAllInside) { + state->clip->clipAALine(aaBuf, &x0, &x1, y); + } + drawAALine(&pipe, x0, x1, y); + } + } else { + for (y = yMinI; y <= yMaxI; ++y) { + while (scanner->getNextSpan(y, &x0, &x1)) { + if (clipRes == splashClipAllInside) { + drawSpan(&pipe, x0, x1, y, gTrue); + } else { + // limit the x range + if (x0 < state->clip->getXMinI()) { + x0 = state->clip->getXMinI(); + } + if (x1 > state->clip->getXMaxI()) { + x1 = state->clip->getXMaxI(); + } + clipRes2 = state->clip->testSpan(x0, x1, y); + drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside); } - clipRes2 = state->clip->testSpan(x0, x1, y); - drawSpan(x0, x1, y, pattern, alpha, clipRes2 == splashClipAllInside); } } } @@ -888,15 +1607,17 @@ SplashError Splash::fillWithPattern(SplashPath *path, GBool eo, } SplashError Splash::xorFill(SplashPath *path, GBool eo) { + SplashPipe pipe; SplashXPath *xPath; SplashXPathScanner *scanner; int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y; SplashClipResult clipRes, clipRes2; + SplashBlendFunc origBlendFunc; if (path->length == 0) { return splashErrEmptyPath; } - xPath = new SplashXPath(path, state->flatness, gTrue); + xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue); xPath->sort(); scanner = new SplashXPathScanner(xPath, eo); @@ -908,32 +1629,36 @@ SplashError Splash::xorFill(SplashPath *path, GBool eo) { != splashClipAllOutside) { // limit the y range - if (yMinI < state->clip->getYMin()) { - yMinI = state->clip->getYMin(); + if (yMinI < state->clip->getYMinI()) { + yMinI = state->clip->getYMinI(); } - if (yMaxI > state->clip->getYMax()) { - yMaxI = state->clip->getYMax(); + if (yMaxI > state->clip->getYMaxI()) { + yMaxI = state->clip->getYMaxI(); } + origBlendFunc = state->blendFunc; + state->blendFunc = &blendXor; + pipeInit(&pipe, 0, yMinI, state->fillPattern, NULL, 1, gFalse, gFalse); + // draw the spans for (y = yMinI; y <= yMaxI; ++y) { while (scanner->getNextSpan(y, &x0, &x1)) { if (clipRes == splashClipAllInside) { - xorSpan(x0, x1, y, state->fillPattern, gTrue); + drawSpan(&pipe, x0, x1, y, gTrue); } else { // limit the x range - if (x0 < state->clip->getXMin()) { - x0 = state->clip->getXMin(); + if (x0 < state->clip->getXMinI()) { + x0 = state->clip->getXMinI(); } - if (x1 > state->clip->getXMax()) { - x1 = state->clip->getXMax(); + if (x1 > state->clip->getXMaxI()) { + x1 = state->clip->getXMaxI(); } clipRes2 = state->clip->testSpan(x0, x1, y); - xorSpan(x0, x1, y, state->fillPattern, - clipRes2 == splashClipAllInside); + drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside); } } } + state->blendFunc = origBlendFunc; } opClipRes = clipRes; @@ -942,1168 +1667,10 @@ SplashError Splash::xorFill(SplashPath *path, GBool eo) { return splashOk; } -void Splash::drawPixel(int x, int y, SplashColorPtr color, - SplashCoord alpha, GBool noClip) { - SplashBlendFunc blendFunc; - SplashColorPtr p; - SplashColor dest, blend; - int alpha2, ialpha2; - Guchar t; - - if (noClip || state->clip->test(x, y)) { - if (alpha != 1 || softMask || state->blendFunc) { - blendFunc = state->blendFunc ? state->blendFunc : &blendNormal; - if (softMask) { - alpha2 = (int)(alpha * softMask->data[y * softMask->rowSize + x]); - } else { - alpha2 = (int)(alpha * 255); - } - ialpha2 = 255 - alpha2; - switch (bitmap->mode) { - case splashModeMono1: - p = &bitmap->data[y * bitmap->rowSize + (x >> 3)]; - dest[0] = (*p >> (7 - (x & 7))) & 1; - (*blendFunc)(color, dest, blend, bitmap->mode); - t = (alpha2 * blend[0] + ialpha2 * dest[0]) >> 8; - if (t) { - *p |= 0x80 >> (x & 7); - } else { - *p &= ~(0x80 >> (x & 7)); - } - break; - case splashModeMono8: - p = &bitmap->data[y * bitmap->rowSize + x]; - (*blendFunc)(color, p, blend, bitmap->mode); - // note: floor(x / 255) = x >> 8 (for 16-bit x) - p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; - break; - case splashModeAMono8: - p = &bitmap->data[y * bitmap->rowSize + 2 * x]; - (*blendFunc)(color, p, blend, bitmap->mode); - p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; - p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; - break; - case splashModeRGB8: - case splashModeBGR8: - p = &bitmap->data[y * bitmap->rowSize + 3 * x]; - (*blendFunc)(color, p, blend, bitmap->mode); - p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; - p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; - p[2] = (alpha2 * blend[2] + ialpha2 * p[2]) >> 8; - break; - case splashModeRGB8Qt: - p = &bitmap->data[y * bitmap->rowSize + 4 * x]; - (*blendFunc)(color, p, blend, bitmap->mode); - p[0] = (alpha2 * blend[2] + ialpha2 * p[0]) >> 8; - p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; - p[2] = (alpha2 * blend[0] + ialpha2 * p[2]) >> 8; - break; - case splashModeARGB8: - case splashModeBGRA8: -#if SPLASH_CMYK - case splashModeCMYK8: -#endif - p = &bitmap->data[y * bitmap->rowSize + 4 * x]; - (*blendFunc)(color, p, blend, bitmap->mode); - p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; - p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; - p[2] = (alpha2 * blend[2] + ialpha2 * p[2]) >> 8; - p[3] = (alpha2 * blend[3] + ialpha2 * p[3]) >> 8; - break; -#if SPLASH_CMYK - case splashModeACMYK8: - p = &bitmap->data[y * bitmap->rowSize + 5 * x]; - (*blendFunc)(color, p, blend, bitmap->mode); - p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; - p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; - p[2] = (alpha2 * blend[2] + ialpha2 * p[2]) >> 8; - p[3] = (alpha2 * blend[3] + ialpha2 * p[3]) >> 8; - p[4] = (alpha2 * blend[4] + ialpha2 * p[4]) >> 8; - break; -#endif - } - } else { - switch (bitmap->mode) { - case splashModeMono1: - p = &bitmap->data[y * bitmap->rowSize + (x >> 3)]; - if (color[0]) { - *p |= 0x80 >> (x & 7); - } else { - *p &= ~(0x80 >> (x & 7)); - } - break; - case splashModeMono8: - p = &bitmap->data[y * bitmap->rowSize + x]; - p[0] = color[0]; - break; - case splashModeAMono8: - p = &bitmap->data[y * bitmap->rowSize + 2 * x]; - p[0] = color[0]; - p[1] = color[1]; - break; - case splashModeRGB8: - case splashModeBGR8: - p = &bitmap->data[y * bitmap->rowSize + 3 * x]; - p[0] = color[0]; - p[1] = color[1]; - p[2] = color[2]; - break; - case splashModeRGB8Qt: - p = &bitmap->data[y * bitmap->rowSize + 4 * x]; - p[0] = color[2]; - p[1] = color[1]; - p[2] = color[0]; - break; - case splashModeARGB8: - case splashModeBGRA8: -#if SPLASH_CMYK - case splashModeCMYK8: -#endif - p = &bitmap->data[y * bitmap->rowSize + 4 * x]; - p[0] = color[0]; - p[1] = color[1]; - p[2] = color[2]; - p[3] = color[3]; - break; -#if SPLASH_CMYK - case splashModeACMYK8: - p = &bitmap->data[y * bitmap->rowSize + 5 * x]; - p[0] = color[0]; - p[1] = color[1]; - p[2] = color[2]; - p[3] = color[3]; - p[4] = color[4]; - break; -#endif - } - } - updateModX(x); - updateModY(y); - } -} - -void Splash::drawPixel(int x, int y, SplashPattern *pattern, - SplashCoord alpha, GBool noClip) { - SplashBlendFunc blendFunc; - SplashColor color; - SplashColorPtr p; - SplashColor dest, blend; - int alpha2, ialpha2; - Guchar t; - - if (noClip || state->clip->test(x, y)) { - if (alpha != 1 || softMask || state->blendFunc) { - blendFunc = state->blendFunc ? state->blendFunc : &blendNormal; - pattern->getColor(x, y, color); - if (softMask) { - alpha2 = (int)(alpha * softMask->data[y * softMask->rowSize + x]); - } else { - alpha2 = (int)(alpha * 255); - } - ialpha2 = 255 - alpha2; - switch (bitmap->mode) { - case splashModeMono1: - p = &bitmap->data[y * bitmap->rowSize + (x >> 3)]; - dest[0] = (*p >> (7 - (x & 7))) & 1; - (*blendFunc)(color, dest, blend, bitmap->mode); - t = (alpha2 * blend[0] + ialpha2 * dest[0]) >> 8; - if (t) { - *p |= 0x80 >> (x & 7); - } else { - *p &= ~(0x80 >> (x & 7)); - } - break; - case splashModeMono8: - p = &bitmap->data[y * bitmap->rowSize + x]; - (*blendFunc)(color, p, blend, bitmap->mode); - // note: floor(x / 255) = x >> 8 (for 16-bit x) - p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; - break; - case splashModeAMono8: - p = &bitmap->data[y * bitmap->rowSize + 2 * x]; - (*blendFunc)(color, p, blend, bitmap->mode); - p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; - p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; - break; - case splashModeRGB8: - case splashModeBGR8: - p = &bitmap->data[y * bitmap->rowSize + 3 * x]; - (*blendFunc)(color, p, blend, bitmap->mode); - p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; - p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; - p[2] = (alpha2 * blend[2] + ialpha2 * p[2]) >> 8; - break; - case splashModeRGB8Qt: - p = &bitmap->data[y * bitmap->rowSize + 4 * x]; - (*blendFunc)(color, p, blend, bitmap->mode); - p[0] = (alpha2 * blend[2] + ialpha2 * p[0]) >> 8; - p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; - p[2] = (alpha2 * blend[0] + ialpha2 * p[2]) >> 8; - break; - case splashModeARGB8: - case splashModeBGRA8: -#if SPLASH_CMYK - case splashModeCMYK8: -#endif - p = &bitmap->data[y * bitmap->rowSize + 4 * x]; - (*blendFunc)(color, p, blend, bitmap->mode); - p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; - p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; - p[2] = (alpha2 * blend[2] + ialpha2 * p[2]) >> 8; - p[3] = (alpha2 * blend[3] + ialpha2 * p[3]) >> 8; - break; -#if SPLASH_CMYK - case splashModeACMYK8: - p = &bitmap->data[y * bitmap->rowSize + 5 * x]; - (*blendFunc)(color, p, blend, bitmap->mode); - p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; - p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; - p[2] = (alpha2 * blend[2] + ialpha2 * p[2]) >> 8; - p[3] = (alpha2 * blend[3] + ialpha2 * p[3]) >> 8; - p[4] = (alpha2 * blend[4] + ialpha2 * p[4]) >> 8; - break; -#endif - } - } else { - pattern->getColor(x, y, color); - switch (bitmap->mode) { - case splashModeMono1: - p = &bitmap->data[y * bitmap->rowSize + (x >> 3)]; - if (color[0]) { - *p |= 0x80 >> (x & 7); - } else { - *p &= ~(0x80 >> (x & 7)); - } - break; - case splashModeMono8: - p = &bitmap->data[y * bitmap->rowSize + x]; - p[0] = color[0]; - break; - case splashModeAMono8: - p = &bitmap->data[y * bitmap->rowSize + 2 * x]; - p[0] = color[0]; - p[1] = color[1]; - break; - case splashModeRGB8: - case splashModeBGR8: - p = &bitmap->data[y * bitmap->rowSize + 3 * x]; - p[0] = color[0]; - p[1] = color[1]; - p[2] = color[2]; - break; - case splashModeRGB8Qt: - p = &bitmap->data[y * bitmap->rowSize + 4 * x]; - p[0] = color[2]; - p[1] = color[1]; - p[2] = color[0]; - break; - case splashModeARGB8: - case splashModeBGRA8: -#if SPLASH_CMYK - case splashModeCMYK8: -#endif - p = &bitmap->data[y * bitmap->rowSize + 4 * x]; - p[0] = color[0]; - p[1] = color[1]; - p[2] = color[2]; - p[3] = color[3]; - break; -#if SPLASH_CMYK - case splashModeACMYK8: - p = &bitmap->data[y * bitmap->rowSize + 5 * x]; - p[0] = color[0]; - p[1] = color[1]; - p[2] = color[2]; - p[3] = color[3]; - p[4] = color[4]; - break; -#endif - } - } - updateModX(x); - updateModY(y); - } -} - -void Splash::drawSpan(int x0, int x1, int y, SplashPattern *pattern, - SplashCoord alpha, GBool noClip) { - SplashBlendFunc blendFunc; - SplashColor color; - SplashColorPtr p; - SplashColor dest, blend; - Guchar mask, t; - int alpha2, ialpha2; - int i, j, n; - - n = x1 - x0 + 1; - - if (noClip) { - updateModX(x0); - updateModX(x1); - updateModY(y); - } - - if (alpha != 1 || softMask || state->blendFunc) { - blendFunc = state->blendFunc ? state->blendFunc : &blendNormal; - if (softMask) { - alpha2 = ialpha2 = 0; // make gcc happy - } else { - alpha2 = (int)(alpha * 255); - ialpha2 = 255 - alpha2; - } - switch (bitmap->mode) { - case splashModeMono1: - p = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)]; - i = 0; - if (pattern->isStatic()) { - pattern->getColor(0, 0, color); - if ((j = x0 & 7)) { - mask = 0x80 >> j; - for (; j < 8 && i < n; ++i, ++j) { - if (noClip || state->clip->test(x0 + i, y)) { - if (softMask) { - alpha2 = (int)(alpha * - softMask->data[y * softMask->rowSize + x0 + i]); - ialpha2 = 255 - alpha2; - } - dest[0] = (*p >> (7 - j)) & 1; - (*blendFunc)(color, dest, blend, bitmap->mode); - t = (alpha2 * blend[0] + ialpha2 * dest[0]) >> 8; - if (t) { - *p |= mask; - } else { - *p &= ~mask; - } - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - mask >>= 1; - } - ++p; - } - while (i < n) { - mask = 0x80; - for (j = 0; j < 8 && i < n; ++i, ++j) { - if (noClip || state->clip->test(x0 + i, y)) { - if (softMask) { - alpha2 = (int)(alpha * - softMask->data[y * softMask->rowSize + x0 + i]); - ialpha2 = 255 - alpha2; - } - dest[0] = (*p >> (7 - j)) & 1; - (*blendFunc)(color, dest, blend, bitmap->mode); - t = (alpha2 * blend[0] + ialpha2 * dest[0]) >> 8; - if (t) { - *p |= mask; - } else { - *p &= ~mask; - } - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - mask >>= 1; - } - ++p; - } - } else { - if ((j = x0 & 7)) { - mask = 0x80 >> j; - for (; j < 8 && i < n; ++i, ++j) { - if (noClip || state->clip->test(x0 + i, y)) { - pattern->getColor(x0 + i, y, color); - if (softMask) { - alpha2 = (int)(alpha * - softMask->data[y * softMask->rowSize + x0 + i]); - ialpha2 = 255 - alpha2; - } - dest[0] = (*p >> (7 - j)) & 1; - (*blendFunc)(color, dest, blend, bitmap->mode); - t = (alpha2 * blend[0] + ialpha2 * dest[0]) >> 8; - if (t) { - *p |= mask; - } else { - *p &= ~mask; - } - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - mask >>= 1; - } - ++p; - } - while (i < n) { - mask = 0x80; - for (j = 0; j < 8 && i < n; ++i, ++j) { - if (noClip || state->clip->test(x0 + i, y)) { - pattern->getColor(x0 + i, y, color); - if (softMask) { - alpha2 = (int)(alpha * - softMask->data[y * softMask->rowSize + x0 + i]); - ialpha2 = 255 - alpha2; - } - dest[0] = (*p >> (7 - j)) & 1; - (*blendFunc)(color, dest, blend, bitmap->mode); - t = (alpha2 * blend[0] + ialpha2 * dest[0]) >> 8; - if (t) { - *p |= mask; - } else { - *p &= ~mask; - } - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - mask >>= 1; - } - ++p; - } - } - break; - - case splashModeMono8: - p = &bitmap->data[y * bitmap->rowSize + x0]; - if (pattern->isStatic()) { - pattern->getColor(0, 0, color); - for (i = 0; i < n; ++i) { - if (noClip || state->clip->test(x0 + i, y)) { - if (softMask) { - alpha2 = (int)(alpha * - softMask->data[y * softMask->rowSize + x0 + i]); - ialpha2 = 255 - alpha2; - } - (*blendFunc)(color, p, blend, bitmap->mode); - *p = (alpha2 * blend[0] + ialpha2 * *p) >> 8; - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - ++p; - } - } else { - for (i = 0; i < n; ++i) { - if (noClip || state->clip->test(x0 + i, y)) { - pattern->getColor(x0 + i, y, color); - if (softMask) { - alpha2 = (int)(alpha * - softMask->data[y * softMask->rowSize + x0 + i]); - ialpha2 = 255 - alpha2; - } - (*blendFunc)(color, p, blend, bitmap->mode); - *p = (alpha2 * blend[0] + ialpha2 * *p) >> 8; - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - ++p; - } - } - break; - - case splashModeAMono8: - p = &bitmap->data[y * bitmap->rowSize + 2 * x0]; - if (pattern->isStatic()) { - pattern->getColor(0, 0, color); - for (i = 0; i < n; ++i) { - if (noClip || state->clip->test(x0 + i, y)) { - if (softMask) { - alpha2 = (int)(alpha * - softMask->data[y * softMask->rowSize + x0 + i]); - ialpha2 = 255 - alpha2; - } - (*blendFunc)(color, p, blend, bitmap->mode); - p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; - p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - p += 2; - } - } else { - for (i = 0; i < n; ++i) { - if (noClip || state->clip->test(x0 + i, y)) { - pattern->getColor(x0 + i, y, color); - if (softMask) { - alpha2 = (int)(alpha * - softMask->data[y * softMask->rowSize + x0 + i]); - ialpha2 = 255 - alpha2; - } - (*blendFunc)(color, p, blend, bitmap->mode); - p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; - p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - p += 2; - } - } - break; - - case splashModeRGB8: - case splashModeBGR8: - p = &bitmap->data[y * bitmap->rowSize + 3 * x0]; - if (pattern->isStatic()) { - pattern->getColor(0, 0, color); - for (i = 0; i < n; ++i) { - if (noClip || state->clip->test(x0 + i, y)) { - if (softMask) { - alpha2 = (int)(alpha * - softMask->data[y * softMask->rowSize + x0 + i]); - ialpha2 = 255 - alpha2; - } - (*blendFunc)(color, p, blend, bitmap->mode); - p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; - p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; - p[2] = (alpha2 * blend[2] + ialpha2 * p[2]) >> 8; - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - p += 3; - } - } else { - for (i = 0; i < n; ++i) { - if (noClip || state->clip->test(x0 + i, y)) { - pattern->getColor(x0 + i, y, color); - if (softMask) { - alpha2 = (int)(alpha * - softMask->data[y * softMask->rowSize + x0 + i]); - ialpha2 = 255 - alpha2; - } - (*blendFunc)(color, p, blend, bitmap->mode); - p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; - p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; - p[2] = (alpha2 * blend[2] + ialpha2 * p[2]) >> 8; - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - p += 3; - } - } - break; - - case splashModeRGB8Qt: - p = &bitmap->data[y * bitmap->rowSize + 4 * x0]; - if (pattern->isStatic()) { - pattern->getColor(0, 0, color); - for (i = 0; i < n; ++i) { - if (noClip || state->clip->test(x0 + i, y)) { - if (softMask) { - alpha2 = (int)(alpha * - softMask->data[y * softMask->rowSize + x0 + i]); - ialpha2 = 255 - alpha2; - } - (*blendFunc)(color, p, blend, bitmap->mode); - p[0] = (alpha2 * blend[2] + ialpha2 * p[0]) >> 8; - p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; - p[2] = (alpha2 * blend[0] + ialpha2 * p[2]) >> 8; - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - p += 4; - } - } else { - for (i = 0; i < n; ++i) { - if (noClip || state->clip->test(x0 + i, y)) { - pattern->getColor(x0 + i, y, color); - if (softMask) { - alpha2 = (int)(alpha * - softMask->data[y * softMask->rowSize + x0 + i]); - ialpha2 = 255 - alpha2; - } - (*blendFunc)(color, p, blend, bitmap->mode); - p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; - p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; - p[2] = (alpha2 * blend[2] + ialpha2 * p[2]) >> 8; - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - p += 4; - } - } - break; - - case splashModeARGB8: - case splashModeBGRA8: -#if SPLASH_CMYK - case splashModeCMYK8: -#endif - p = &bitmap->data[y * bitmap->rowSize + 4 * x0]; - if (pattern->isStatic()) { - pattern->getColor(0, 0, color); - for (i = 0; i < n; ++i) { - if (noClip || state->clip->test(x0 + i, y)) { - if (softMask) { - alpha2 = (int)(alpha * - softMask->data[y * softMask->rowSize + x0 + i]); - ialpha2 = 255 - alpha2; - } - (*blendFunc)(color, p, blend, bitmap->mode); - p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; - p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; - p[2] = (alpha2 * blend[2] + ialpha2 * p[2]) >> 8; - p[3] = (alpha2 * blend[3] + ialpha2 * p[3]) >> 8; - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - p += 4; - } - } else { - for (i = 0; i < n; ++i) { - if (noClip || state->clip->test(x0 + i, y)) { - pattern->getColor(x0 + i, y, color); - if (softMask) { - alpha2 = (int)(alpha * - softMask->data[y * softMask->rowSize + x0 + i]); - ialpha2 = 255 - alpha2; - } - (*blendFunc)(color, p, blend, bitmap->mode); - p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; - p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; - p[2] = (alpha2 * blend[2] + ialpha2 * p[2]) >> 8; - p[3] = (alpha2 * blend[3] + ialpha2 * p[3]) >> 8; - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - p += 4; - } - } - break; -#if SPLASH_CMYK - case splashModeACMYK8: - p = &bitmap->data[y * bitmap->rowSize + 5 * x0]; - if (pattern->isStatic()) { - pattern->getColor(0, 0, color); - for (i = 0; i < n; ++i) { - if (noClip || state->clip->test(x0 + i, y)) { - if (softMask) { - alpha2 = (int)(alpha * - softMask->data[y * softMask->rowSize + x0 + i]); - ialpha2 = 255 - alpha2; - } - (*blendFunc)(color, p, blend, bitmap->mode); - p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; - p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; - p[2] = (alpha2 * blend[2] + ialpha2 * p[2]) >> 8; - p[3] = (alpha2 * blend[3] + ialpha2 * p[3]) >> 8; - p[4] = (alpha2 * blend[4] + ialpha2 * p[4]) >> 8; - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - p += 4; - } - } else { - for (i = 0; i < n; ++i) { - if (noClip || state->clip->test(x0 + i, y)) { - pattern->getColor(x0 + i, y, color); - if (softMask) { - alpha2 = (int)(alpha * - softMask->data[y * softMask->rowSize + x0 + i]); - ialpha2 = 255 - alpha2; - } - (*blendFunc)(color, p, blend, bitmap->mode); - p[0] = (alpha2 * blend[0] + ialpha2 * p[0]) >> 8; - p[1] = (alpha2 * blend[1] + ialpha2 * p[1]) >> 8; - p[2] = (alpha2 * blend[2] + ialpha2 * p[2]) >> 8; - p[3] = (alpha2 * blend[3] + ialpha2 * p[3]) >> 8; - p[4] = (alpha2 * blend[4] + ialpha2 * p[4]) >> 8; - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - p += 4; - } - } - break; -#endif - } - - } else { - switch (bitmap->mode) { - case splashModeMono1: - p = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)]; - i = 0; - if (pattern->isStatic()) { - pattern->getColor(0, 0, color); - if ((j = x0 & 7)) { - mask = 0x80 >> j; - for (; j < 8 && i < n; ++i, ++j) { - if (noClip || state->clip->test(x0 + i, y)) { - if (color[0]) { - *p |= mask; - } else { - *p &= ~mask; - } - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - mask >>= 1; - } - ++p; - } - while (i < n) { - mask = 0x80; - for (j = 0; j < 8 && i < n; ++i, ++j) { - if (noClip || state->clip->test(x0 + i, y)) { - if (color[0]) { - *p |= mask; - } else { - *p &= ~mask; - } - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - mask >>= 1; - } - ++p; - } - } else { - if ((j = x0 & 7)) { - mask = 0x80 >> j; - for (; j < 8 && i < n; ++i, ++j) { - if (noClip || state->clip->test(x0 + i, y)) { - pattern->getColor(x0 + i, y, color); - if (color[0]) { - *p |= mask; - } else { - *p &= ~mask; - } - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - mask >>= 1; - } - ++p; - } - while (i < n) { - mask = 0x80; - for (j = 0; j < 8 && i < n; ++i, ++j) { - if (noClip || state->clip->test(x0 + i, y)) { - pattern->getColor(x0 + i, y, color); - if (color[0]) { - *p |= mask; - } else { - *p &= ~mask; - } - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - mask >>= 1; - } - ++p; - } - } - break; - - case splashModeMono8: - p = &bitmap->data[y * bitmap->rowSize + x0]; - if (pattern->isStatic()) { - pattern->getColor(0, 0, color); - for (i = 0; i < n; ++i) { - if (noClip || state->clip->test(x0 + i, y)) { - *p = color[0]; - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - ++p; - } - } else { - for (i = 0; i < n; ++i) { - if (noClip || state->clip->test(x0 + i, y)) { - pattern->getColor(x0 + i, y, color); - *p = color[0]; - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - ++p; - } - } - break; - - case splashModeAMono8: - p = &bitmap->data[y * bitmap->rowSize + 2 * x0]; - if (pattern->isStatic()) { - pattern->getColor(0, 0, color); - for (i = 0; i < n; ++i) { - if (noClip || state->clip->test(x0 + i, y)) { - p[0] = color[0]; - p[1] = color[1]; - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - p += 2; - } - } else { - for (i = 0; i < n; ++i) { - if (noClip || state->clip->test(x0 + i, y)) { - pattern->getColor(x0 + i, y, color); - p[0] = color[0]; - p[1] = color[1]; - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - p += 2; - } - } - break; - - case splashModeRGB8: - case splashModeBGR8: - p = &bitmap->data[y * bitmap->rowSize + 3 * x0]; - if (pattern->isStatic()) { - pattern->getColor(0, 0, color); - for (i = 0; i < n; ++i) { - if (noClip || state->clip->test(x0 + i, y)) { - p[0] = color[0]; - p[1] = color[1]; - p[2] = color[2]; - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - p += 3; - } - } else { - for (i = 0; i < n; ++i) { - if (noClip || state->clip->test(x0 + i, y)) { - pattern->getColor(x0 + i, y, color); - p[0] = color[0]; - p[1] = color[1]; - p[2] = color[2]; - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - p += 3; - } - } - break; - - case splashModeRGB8Qt: - p = &bitmap->data[y * bitmap->rowSize + 4 * x0]; - if (pattern->isStatic()) { - pattern->getColor(0, 0, color); - for (i = 0; i < n; ++i) { - if (noClip || state->clip->test(x0 + i, y)) { - p[0] = color[2]; - p[1] = color[1]; - p[2] = color[0]; - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - p += 4; - } - } else { - for (i = 0; i < n; ++i) { - if (noClip || state->clip->test(x0 + i, y)) { - pattern->getColor(x0 + i, y, color); - p[0] = color[0]; - p[1] = color[1]; - p[2] = color[2]; - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - p += 4; - } - } - break; - - case splashModeARGB8: - case splashModeBGRA8: -#if SPLASH_CMYK - case splashModeCMYK8: -#endif - p = &bitmap->data[y * bitmap->rowSize + 4 * x0]; - if (pattern->isStatic()) { - pattern->getColor(0, 0, color); - for (i = 0; i < n; ++i) { - if (noClip || state->clip->test(x0 + i, y)) { - p[0] = color[0]; - p[1] = color[1]; - p[2] = color[2]; - p[3] = color[3]; - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - p += 4; - } - } else { - for (i = 0; i < n; ++i) { - if (noClip || state->clip->test(x0 + i, y)) { - pattern->getColor(x0 + i, y, color); - p[0] = color[0]; - p[1] = color[1]; - p[2] = color[2]; - p[3] = color[3]; - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - p += 4; - } - } - break; -#if SPLASH_CMYK - case splashModeACMYK8: - p = &bitmap->data[y * bitmap->rowSize + 5 * x0]; - if (pattern->isStatic()) { - pattern->getColor(0, 0, color); - for (i = 0; i < n; ++i) { - if (noClip || state->clip->test(x0 + i, y)) { - p[0] = color[0]; - p[1] = color[1]; - p[2] = color[2]; - p[3] = color[3]; - p[4] = color[4]; - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - p += 4; - } - } else { - for (i = 0; i < n; ++i) { - if (noClip || state->clip->test(x0 + i, y)) { - pattern->getColor(x0 + i, y, color); - p[0] = color[0]; - p[1] = color[1]; - p[2] = color[2]; - p[3] = color[3]; - p[4] = color[4]; - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - p += 4; - } - } - break; -#endif - } - } -} - -void Splash::xorSpan(int x0, int x1, int y, SplashPattern *pattern, - GBool noClip) { - SplashColor color; - SplashColorPtr p; - Guchar mask; - int i, j, n; - - n = x1 - x0 + 1; - - if (noClip) { - updateModX(x0); - updateModX(x1); - updateModY(y); - } - - switch (bitmap->mode) { - case splashModeMono1: - p = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)]; - i = 0; - if ((j = x0 & 7)) { - mask = 0x80 >> j; - for (; j < 8 && i < n; ++i, ++j) { - if (noClip || state->clip->test(x0 + i, y)) { - pattern->getColor(x0 + i, y, color); - if (color[0]) { - *p ^= mask; - } - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - mask >>= 1; - } - ++p; - } - while (i < n) { - mask = 0x80; - for (j = 0; j < 8 && i < n; ++i, ++j) { - if (noClip || state->clip->test(x0 + i, y)) { - pattern->getColor(x0 + i, y, color); - if (color[0]) { - *p ^= mask; - } - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - mask >>= 1; - } - ++p; - } - break; - - case splashModeMono8: - p = &bitmap->data[y * bitmap->rowSize + x0]; - for (i = 0; i < n; ++i) { - if (noClip || state->clip->test(x0 + i, y)) { - pattern->getColor(x0 + i, y, color); - *p ^= color[0]; - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - ++p; - } - break; - - case splashModeAMono8: - p = &bitmap->data[y * bitmap->rowSize + 2 * x0]; - for (i = 0; i < n; ++i) { - if (noClip || state->clip->test(x0 + i, y)) { - pattern->getColor(x0 + i, y, color); - p[0] ^= color[0]; - p[1] ^= color[1]; - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - p += 2; - } - break; - - case splashModeRGB8: - case splashModeBGR8: - p = &bitmap->data[y * bitmap->rowSize + 3 * x0]; - for (i = 0; i < n; ++i) { - if (noClip || state->clip->test(x0 + i, y)) { - pattern->getColor(x0 + i, y, color); - p[0] ^= color[0]; - p[1] ^= color[1]; - p[2] ^= color[2]; - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - p += 3; - } - break; - - case splashModeRGB8Qt: - p = &bitmap->data[y * bitmap->rowSize + 4 * x0]; - for (i = 0; i < n; ++i) { - if (noClip || state->clip->test(x0 + i, y)) { - pattern->getColor(x0 + i, y, color); - p[0] ^= color[2]; - p[1] ^= color[1]; - p[2] ^= color[0]; - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - p += 4; - } - break; - - case splashModeARGB8: - case splashModeBGRA8: -#if SPLASH_CMYK - case splashModeCMYK8: -#endif - p = &bitmap->data[y * bitmap->rowSize + 4 * x0]; - for (i = 0; i < n; ++i) { - if (noClip || state->clip->test(x0 + i, y)) { - pattern->getColor(x0 + i, y, color); - p[0] ^= color[0]; - p[1] ^= color[1]; - p[2] ^= color[2]; - p[3] ^= color[3]; - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - p += 4; - } - break; -#if SPLASH_CMYK - case splashModeACMYK8: - p = &bitmap->data[y * bitmap->rowSize + 5 * x0]; - for (i = 0; i < n; ++i) { - if (noClip || state->clip->test(x0 + i, y)) { - pattern->getColor(x0 + i, y, color); - p[0] ^= color[0]; - p[1] ^= color[1]; - p[2] ^= color[2]; - p[3] ^= color[3]; - p[4] ^= color[4]; - if (!noClip) { - updateModX(x0 + i); - updateModY(y); - } - } - p += 4; - } - break; -#endif - } -} - SplashError Splash::fillChar(SplashCoord x, SplashCoord y, int c, SplashFont *font) { SplashGlyphBitmap glyph; + SplashCoord xt, yt; int x0, y0, xFrac, yFrac; SplashError err; @@ -2111,14 +1678,15 @@ SplashError Splash::fillChar(SplashCoord x, SplashCoord y, printf("fillChar: x=%.2f y=%.2f c=%3d=0x%02x='%c'\n", (double)x, (double)y, c, c, c); } - x0 = splashFloor(x); - xFrac = splashFloor((x - x0) * splashFontFraction); - y0 = splashFloor(y); - yFrac = splashFloor((y - y0) * splashFontFraction); + transform(state->matrix, x, y, &xt, &yt); + x0 = splashFloor(xt); + xFrac = splashFloor((xt - x0) * splashFontFraction); + y0 = splashFloor(yt); + yFrac = splashFloor((yt - y0) * splashFontFraction); if (!font->getGlyph(c, xFrac, yFrac, &glyph)) { return splashErrNoGlyph; } - err = fillGlyph(x, y, &glyph); + err = fillGlyph2(x0, y0, &glyph); if (glyph.freeData) { gfree(glyph.data); } @@ -2127,18 +1695,22 @@ SplashError Splash::fillChar(SplashCoord x, SplashCoord y, SplashError Splash::fillGlyph(SplashCoord x, SplashCoord y, SplashGlyphBitmap *glyph) { - SplashBlendFunc blendFunc; - int alpha0, alpha, ialpha; - Guchar *p; - SplashColor fg, dest, blend; - SplashColorPtr pix; + SplashCoord xt, yt; + int x0, y0; + + transform(state->matrix, x, y, &xt, &yt); + x0 = splashFloor(xt); + y0 = splashFloor(yt); + return fillGlyph2(x0, y0, glyph); +} + +SplashError Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph) { + SplashPipe pipe; SplashClipResult clipRes; GBool noClip; - Guchar t; - int x0, y0, x1, y1, xx, xx1, yy; - - x0 = splashFloor(x); - y0 = splashFloor(y); + int alpha0, alpha; + Guchar *p; + int x1, y1, xx, xx1, yy; if ((clipRes = state->clip->testRect(x0 - glyph->x, y0 - glyph->y, @@ -2148,335 +1720,88 @@ SplashError Splash::fillGlyph(SplashCoord x, SplashCoord y, noClip = clipRes == splashClipAllInside; if (noClip) { - updateModX(x0 - glyph->x); - updateModX(x0 - glyph->x + glyph->w - 1); - updateModY(y0 - glyph->y); - updateModY(y0 - glyph->y + glyph->h - 1); - } - - //~ optimize this - if (state->fillAlpha != 1 || softMask || state->blendFunc) { - blendFunc = state->blendFunc ? state->blendFunc : &blendNormal; if (glyph->aa) { + pipeInit(&pipe, x0 - glyph->x, y0 - glyph->y, + state->fillPattern, NULL, state->fillAlpha, 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 (softMask) { - alpha = (int)(alpha * (float)state->fillAlpha * - softMask->data[y1 * softMask->rowSize + x1]); + if (alpha != 0) { + pipe.shape = (SplashCoord)(alpha / 255.0); + pipeRun(&pipe); + updateModX(x1); + updateModY(y1); } else { - alpha = (int)(alpha * (float)state->fillAlpha); - } - if (alpha > 0) { - if (noClip || state->clip->test(x1, y1)) { - ialpha = 255 - alpha; - state->fillPattern->getColor(x1, y1, fg); - switch (bitmap->mode) { - case splashModeMono1: - pix = &bitmap->data[y1 * bitmap->rowSize + (x1 >> 3)]; - dest[0] = (*pix >> (7 - (x1 & 7))) & 1; - (*blendFunc)(fg, dest, blend, bitmap->mode); - t = (alpha * blend[0] + ialpha * dest[0]) >> 8; - if (t) { - *pix |= 0x80 >> (x1 & 7); - } else { - *pix &= ~(0x80 >> (x1 & 7)); - } - break; - case splashModeMono8: - pix = &bitmap->data[y1 * bitmap->rowSize + x1]; - (*blendFunc)(fg, pix, blend, bitmap->mode); - // note: floor(x / 255) = x >> 8 (for 16-bit x) - pix[0] = (alpha * blend[0] + ialpha * pix[0]) >> 8; - break; - case splashModeAMono8: - pix = &bitmap->data[y1 * bitmap->rowSize + 2 * x1]; - (*blendFunc)(fg, pix, blend, bitmap->mode); - pix[0] = (alpha * blend[0] + ialpha * pix[0]) >> 8; - pix[1] = (alpha * blend[1] + ialpha * pix[1]) >> 8; - break; - case splashModeRGB8: - case splashModeBGR8: - pix = &bitmap->data[y1 * bitmap->rowSize + 3 * x1]; - (*blendFunc)(fg, pix, blend, bitmap->mode); - pix[0] = (alpha * blend[0] + ialpha * pix[0]) >> 8; - pix[1] = (alpha * blend[1] + ialpha * pix[1]) >> 8; - pix[2] = (alpha * blend[2] + ialpha * pix[2]) >> 8; - break; - case splashModeRGB8Qt: - pix = &bitmap->data[y1 * bitmap->rowSize + 4 * x1]; - (*blendFunc)(fg, pix, blend, bitmap->mode); - pix[0] = (alpha * blend[2] + ialpha * pix[0]) >> 8; - pix[1] = (alpha * blend[1] + ialpha * pix[1]) >> 8; - pix[2] = (alpha * blend[0] + ialpha * pix[2]) >> 8; - break; - case splashModeARGB8: - case splashModeBGRA8: -#if SPLASH_CMYK - case splashModeCMYK8: -#endif - pix = &bitmap->data[y1 * bitmap->rowSize + 4 * x1]; - (*blendFunc)(fg, pix, blend, bitmap->mode); - pix[0] = (alpha * blend[0] + ialpha * pix[0]) >> 8; - pix[1] = (alpha * blend[1] + ialpha * pix[1]) >> 8; - pix[2] = (alpha * blend[2] + ialpha * pix[2]) >> 8; - pix[3] = (alpha * blend[3] + ialpha * pix[3]) >> 8; - break; -#if SPLASH_CMYK - case splashModeACMYK8: - pix = &bitmap->data[y1 * bitmap->rowSize + 5 * x1]; - (*blendFunc)(fg, pix, blend, bitmap->mode); - pix[0] = (alpha * blend[0] + ialpha * pix[0]) >> 8; - pix[1] = (alpha * blend[1] + ialpha * pix[1]) >> 8; - pix[2] = (alpha * blend[2] + ialpha * pix[2]) >> 8; - pix[3] = (alpha * blend[3] + ialpha * pix[3]) >> 8; - pix[4] = (alpha * blend[4] + ialpha * pix[4]) >> 8; - break; -#endif - } - if (!noClip) { - updateModX(x1); - updateModY(y1); - } - } + pipeIncX(&pipe); } } } - } else { + pipeInit(&pipe, x0 - glyph->x, y0 - glyph->y, + state->fillPattern, NULL, state->fillAlpha, 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) { - if (noClip || state->clip->test(x1, y1)) { - if (softMask) { - alpha = (int)(state->fillAlpha * - softMask->data[y1 * softMask->rowSize + x1]); - } else { - alpha = (int)(state->fillAlpha * 255); - } - ialpha = 255 - alpha; - state->fillPattern->getColor(x1, y1, fg); - switch (bitmap->mode) { - case splashModeMono1: - pix = &bitmap->data[y1 * bitmap->rowSize + (x1 >> 3)]; - dest[0] = (*pix >> (7 - (x1 & 7))) & 1; - (*blendFunc)(fg, dest, blend, bitmap->mode); - t = (alpha * blend[0] + ialpha * dest[0]) >> 8; - if (t) { - *pix |= 0x80 >> (x1 & 7); - } else { - *pix &= ~(0x80 >> (x1 & 7)); - } - break; - case splashModeMono8: - pix = &bitmap->data[y1 * bitmap->rowSize + x1]; - (*blendFunc)(fg, pix, blend, bitmap->mode); - // note: floor(x / 255) = x >> 8 (for 16-bit x) - pix[0] = (alpha * blend[0] + ialpha * pix[0]) >> 8; - break; - case splashModeAMono8: - pix = &bitmap->data[y1 * bitmap->rowSize + 2 * x1]; - (*blendFunc)(fg, pix, blend, bitmap->mode); - pix[0] = (alpha * blend[0] + ialpha * pix[0]) >> 8; - pix[1] = (alpha * blend[1] + ialpha * pix[1]) >> 8; - break; - case splashModeRGB8: - case splashModeBGR8: - pix = &bitmap->data[y1 * bitmap->rowSize + 3 * x1]; - (*blendFunc)(fg, pix, blend, bitmap->mode); - pix[0] = (alpha * blend[0] + ialpha * pix[0]) >> 8; - pix[1] = (alpha * blend[1] + ialpha * pix[1]) >> 8; - pix[2] = (alpha * blend[2] + ialpha * pix[2]) >> 8; - break; - case splashModeRGB8Qt: - pix = &bitmap->data[y1 * bitmap->rowSize + 4 * x1]; - (*blendFunc)(fg, pix, blend, bitmap->mode); - pix[0] = (alpha * blend[2] + ialpha * pix[0]) >> 8; - pix[1] = (alpha * blend[1] + ialpha * pix[1]) >> 8; - pix[2] = (alpha * blend[0] + ialpha * pix[2]) >> 8; - break; - case splashModeARGB8: - case splashModeBGRA8: -#if SPLASH_CMYK - case splashModeCMYK8: -#endif - pix = &bitmap->data[y1 * bitmap->rowSize + 4 * x1]; - (*blendFunc)(fg, pix, blend, bitmap->mode); - pix[0] = (alpha * blend[0] + ialpha * pix[0]) >> 8; - pix[1] = (alpha * blend[1] + ialpha * pix[1]) >> 8; - pix[2] = (alpha * blend[2] + ialpha * pix[2]) >> 8; - pix[3] = (alpha * blend[3] + ialpha * pix[3]) >> 8; - break; -#if SPLASH_CMYK - case splashModeACMYK8: - pix = &bitmap->data[y1 * bitmap->rowSize + 5 * x1]; - (*blendFunc)(fg, pix, blend, bitmap->mode); - pix[0] = (alpha * blend[0] + ialpha * pix[0]) >> 8; - pix[1] = (alpha * blend[1] + ialpha * pix[1]) >> 8; - pix[2] = (alpha * blend[2] + ialpha * pix[2]) >> 8; - pix[3] = (alpha * blend[3] + ialpha * pix[3]) >> 8; - pix[4] = (alpha * blend[4] + ialpha * pix[4]) >> 8; - break; -#endif - } - if (!noClip) { - updateModX(x1); - updateModY(y1); - } - } + pipeRun(&pipe); + updateModX(x1); + updateModY(y1); + } else { + pipeIncX(&pipe); } alpha0 <<= 1; } } } } - } else { if (glyph->aa) { + pipeInit(&pipe, x0 - glyph->x, y0 - glyph->y, + state->fillPattern, NULL, state->fillAlpha, 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) { - if (noClip || state->clip->test(x1, y1)) { - ialpha = 255 - alpha; - state->fillPattern->getColor(x1, y1, fg); - switch (bitmap->mode) { - case splashModeMono1: - if (alpha >= 0x80) { - pix = &bitmap->data[y1 * bitmap->rowSize + (x1 >> 3)]; - if (fg[0]) { - *pix |= 0x80 >> (x1 & 7); - } else { - *pix &= ~(0x80 >> (x1 & 7)); - } - } - break; - case splashModeMono8: - pix = &bitmap->data[y1 * bitmap->rowSize + x1]; - // note: floor(x / 255) = x >> 8 (for 16-bit x) - pix[0] = (alpha * fg[0] + ialpha * pix[0]) >> 8; - break; - case splashModeAMono8: - pix = &bitmap->data[y1 * bitmap->rowSize + 2 * x1]; - pix[0] = (alpha * fg[0] + ialpha * pix[0]) >> 8; - pix[1] = (alpha * fg[1] + ialpha * pix[1]) >> 8; - break; - case splashModeRGB8: - case splashModeBGR8: - pix = &bitmap->data[y1 * bitmap->rowSize + 3 * x1]; - pix[0] = (alpha * fg[0] + ialpha * pix[0]) >> 8; - pix[1] = (alpha * fg[1] + ialpha * pix[1]) >> 8; - pix[2] = (alpha * fg[2] + ialpha * pix[2]) >> 8; - break; - case splashModeRGB8Qt: - pix = &bitmap->data[y1 * bitmap->rowSize + 4 * x1]; - pix[0] = (alpha * fg[2] + ialpha * pix[0]) >> 8; - pix[1] = (alpha * fg[1] + ialpha * pix[1]) >> 8; - pix[2] = (alpha * fg[0] + ialpha * pix[2]) >> 8; - break; - case splashModeARGB8: - case splashModeBGRA8: -#if SPLASH_CMYK - case splashModeCMYK8: -#endif - pix = &bitmap->data[y1 * bitmap->rowSize + 4 * x1]; - pix[0] = (alpha * fg[0] + ialpha * pix[0]) >> 8; - pix[1] = (alpha * fg[1] + ialpha * pix[1]) >> 8; - pix[2] = (alpha * fg[2] + ialpha * pix[2]) >> 8; - pix[3] = (alpha * fg[3] + ialpha * pix[3]) >> 8; - break; -#if SPLASH_CMYK - case splashModeACMYK8: - pix = &bitmap->data[y1 * bitmap->rowSize + 5 * x1]; - pix[0] = (alpha * fg[0] + ialpha * pix[0]) >> 8; - pix[1] = (alpha * fg[1] + ialpha * pix[1]) >> 8; - pix[2] = (alpha * fg[2] + ialpha * pix[2]) >> 8; - pix[3] = (alpha * fg[3] + ialpha * pix[3]) >> 8; - pix[4] = (alpha * fg[4] + ialpha * pix[4]) >> 8; - break; -#endif - } - if (!noClip) { - updateModX(x1); - updateModY(y1); - } + if (state->clip->test(x1, y1)) { + alpha = *p++; + if (alpha != 0) { + pipe.shape = (SplashCoord)(alpha / 255.0); + pipeRun(&pipe); + updateModX(x1); + updateModY(y1); + } else { + pipeIncX(&pipe); } + } else { + pipeIncX(&pipe); + ++p; } } } - } else { + pipeInit(&pipe, x0 - glyph->x, y0 - glyph->y, + state->fillPattern, NULL, state->fillAlpha, 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) { - if (noClip || state->clip->test(x1, y1)) { - state->fillPattern->getColor(x1, y1, fg); - switch (bitmap->mode) { - case splashModeMono1: - pix = &bitmap->data[y1 * bitmap->rowSize + (x1 >> 3)]; - if (fg[0]) { - *pix |= 0x80 >> (x1 & 7); - } else { - *pix &= ~(0x80 >> (x1 & 7)); - } - break; - case splashModeMono8: - pix = &bitmap->data[y1 * bitmap->rowSize + x1]; - pix[0] = fg[0]; - break; - case splashModeAMono8: - pix = &bitmap->data[y1 * bitmap->rowSize + 2 * x1]; - pix[0] = fg[0]; - pix[1] = fg[1]; - break; - case splashModeRGB8: - case splashModeBGR8: - pix = &bitmap->data[y1 * bitmap->rowSize + 3 * x1]; - pix[0] = fg[0]; - pix[1] = fg[1]; - pix[2] = fg[2]; - break; - case splashModeRGB8Qt: - pix = &bitmap->data[y1 * bitmap->rowSize + 4 * x1]; - pix[0] = fg[2]; - pix[1] = fg[1]; - pix[2] = fg[0]; - break; - case splashModeARGB8: - case splashModeBGRA8: -#if SPLASH_CMYK - case splashModeCMYK8: -#endif - pix = &bitmap->data[y1 * bitmap->rowSize + 4 * x1]; - pix[0] = fg[0]; - pix[1] = fg[1]; - pix[2] = fg[2]; - pix[3] = fg[3]; - break; -#if SPLASH_CMYK - case splashModeACMYK8: - pix = &bitmap->data[y1 * bitmap->rowSize + 5 * x1]; - pix[0] = fg[0]; - pix[1] = fg[1]; - pix[2] = fg[2]; - pix[3] = fg[3]; - pix[4] = fg[4]; - break; -#endif - } - if (!noClip) { - updateModX(x1); - updateModY(y1); - } + if (state->clip->test(x1, y1)) { + if (alpha0 & 0x80) { + pipeRun(&pipe); + updateModX(x1); + updateModY(y1); + } else { + pipeIncX(&pipe); } + } else { + pipeIncX(&pipe); } alpha0 <<= 1; } @@ -2491,7 +1816,9 @@ SplashError Splash::fillGlyph(SplashCoord x, SplashCoord y, } SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData, - int w, int h, SplashCoord *mat) { + int w, int h, SplashCoord *mat, + GBool glyphMode) { + SplashPipe pipe; GBool rot; SplashCoord xScale, yScale, xShear, yShear, yShear1; int tx, tx2, ty, ty2, scaledWidth, scaledHeight, xSign, ySign; @@ -2504,7 +1831,6 @@ SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData, int k1, spanXMin, spanXMax, spanY; SplashColorPtr pixBuf, p; int pixAcc; - SplashCoord alpha; int x, y, x1, x2, y2; SplashCoord y1; int n, m, i, j; @@ -2535,38 +1861,54 @@ SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData, xShear = mat[2] / yScale; yShear = mat[1] / mat[0]; } - // the +/-0.01 in these computations is to avoid floating point - // precision problems which can lead to gaps between image stripes - // (it can cause image stripes to overlap, but that's a much less - // visible problem) - if (xScale >= 0) { - tx = splashRound(mat[4] - 0.01); - tx2 = splashRound(mat[4] + xScale + 0.01) - 1; + // Note 1: The PDF spec says that all pixels whose *centers* lie + // within the region get painted -- but that doesn't seem to match + // up with what Acrobat actually does: it ends up leaving gaps + // between image stripes. So we use the same rule here as for + // fills: any pixel that overlaps the region gets painted. + // Note 2: The "glyphMode" flag is a kludge: it switches back to + // "correct" behavior (matching the spec), for use in rendering Type + // 3 fonts. + // Note 3: The +/-0.01 in these computations is to avoid floating + // point precision problems which can lead to gaps between image + // stripes (it can cause image stripes to overlap, but that's a much + // less visible problem). + if (glyphMode) { + if (xScale >= 0) { + tx = splashRound(mat[4]); + tx2 = splashRound(mat[4] + xScale) - 1; + } else { + tx = splashRound(mat[4]) - 1; + tx2 = splashRound(mat[4] + xScale); + } } else { - tx = splashRound(mat[4] + 0.01) - 1; - tx2 = splashRound(mat[4] + xScale - 0.01); + if (xScale >= 0) { + tx = splashFloor(mat[4] - 0.01); + tx2 = splashFloor(mat[4] + xScale + 0.01); + } else { + tx = splashFloor(mat[4] + 0.01); + tx2 = splashFloor(mat[4] + xScale - 0.01); + } } scaledWidth = abs(tx2 - tx) + 1; - if (scaledWidth == 0) { - // technically, this should draw nothing, but it generally seems - // better to draw a one-pixel-wide stripe rather than throwing it - // away - scaledWidth = 1; - } - if (yScale >= 0) { - ty = splashRound(mat[5] - 0.01); - ty2 = splashRound(mat[5] + yScale + 0.01) - 1; + if (glyphMode) { + if (yScale >= 0) { + ty = splashRound(mat[5]); + ty2 = splashRound(mat[5] + yScale) - 1; + } else { + ty = splashRound(mat[5]) - 1; + ty2 = splashRound(mat[5] + yScale); + } } else { - ty = splashRound(mat[5] + 0.01) - 1; - ty2 = splashRound(mat[5] + yScale - 0.01); + if (yScale >= 0) { + ty = splashFloor(mat[5] - 0.01); + ty2 = splashFloor(mat[5] + yScale + 0.01); + } else { + ty = splashFloor(mat[5] + 0.01); + ty2 = splashFloor(mat[5] + yScale - 0.01); + } } scaledHeight = abs(ty2 - ty) + 1; - if (scaledHeight == 0) { - // technically, this should draw nothing, but it generally seems - // better to draw a one-pixel-wide stripe rather than throwing it - // away - scaledHeight = 1; - } xSign = (xScale < 0) ? -1 : 1; ySign = (yScale < 0) ? -1 : 1; yShear1 = (SplashCoord)xSign * yShear; @@ -2620,6 +1962,13 @@ SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData, // allocate pixel buffer pixBuf = (SplashColorPtr)gmalloc((yp + 1) * w); + // initialize the pixel pipe + pipeInit(&pipe, 0, 0, state->fillPattern, NULL, state->fillAlpha, + gTrue, gFalse); + if (vectorAntialias) { + drawAAPixelInit(); + } + // init y scale Bresenham yt = 0; lastYStep = 1; @@ -2721,14 +2070,13 @@ SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData, // blend fill color with background if (pixAcc != 0) { - if (pixAcc == n * m) { - drawPixel(tx + x2, ty + y2, state->fillPattern, state->fillAlpha, - clipRes2 == splashClipAllInside); + pipe.shape = (pixAcc == n * m) + ? (SplashCoord)1 + : (SplashCoord)pixAcc / (SplashCoord)(n * m); + if (vectorAntialias && clipRes2 != splashClipAllInside) { + drawAAPixel(&pipe, tx + x2, ty + y2); } else { - alpha = (SplashCoord)pixAcc / (SplashCoord)(n * m); - drawPixel(tx + x2, ty + y2, state->fillPattern, - state->fillAlpha * alpha, - clipRes2 == splashClipAllInside); + drawPixel(&pipe, tx + x2, ty + y2, clipRes2 == splashClipAllInside); } } @@ -2750,9 +2098,10 @@ SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData, } SplashError Splash::drawImage(SplashImageSource src, void *srcData, - SplashColorMode srcMode, + SplashColorMode srcMode, GBool srcAlpha, int w, int h, SplashCoord *mat) { - GBool ok, rot, halftone, srcAlpha; + SplashPipe pipe; + GBool ok, rot; SplashCoord xScale, yScale, xShear, yShear, yShear1; int tx, tx2, ty, ty2, scaledWidth, scaledHeight, xSign, ySign; int ulx, uly, llx, lly, urx, ury, lrx, lry; @@ -2762,8 +2111,9 @@ SplashError Splash::drawImage(SplashImageSource src, void *srcData, int yp, yq, yt, yStep, lastYStep; int xp, xq, xt, xStep, xSrc; int k1, spanXMin, spanXMax, spanY; - SplashColorPtr pixBuf, p; + SplashColorPtr colorBuf, p; SplashColor pix; + Guchar *alphaBuf, *q; #if SPLASH_CMYK int pixAcc0, pixAcc1, pixAcc2, pixAcc3; #else @@ -2776,61 +2126,37 @@ SplashError Splash::drawImage(SplashImageSource src, void *srcData, int nComps, n, m, i, j; if (debugMode) { - printf("drawImage: srcMode=%d w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n", - srcMode, w, h, (double)mat[0], (double)mat[1], (double)mat[2], + printf("drawImage: srcMode=%d srcAlpha=%d w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n", + srcMode, srcAlpha, w, h, (double)mat[0], (double)mat[1], (double)mat[2], (double)mat[3], (double)mat[4], (double)mat[5]); } // check color modes ok = gFalse; // make gcc happy nComps = 0; // make gcc happy - halftone = gFalse; - srcAlpha = gFalse; switch (bitmap->mode) { case splashModeMono1: - ok = srcMode == splashModeMono1 || srcMode == splashModeMono8 || - srcMode == splashModeAMono8; - halftone = srcMode == splashModeMono8 || srcMode == splashModeAMono8; - srcAlpha = srcMode == splashModeAMono8; - nComps = srcAlpha ? 2 : 1; - break; case splashModeMono8: - ok = srcMode == splashModeMono8 || srcMode == splashModeAMono8; - srcAlpha = srcMode == splashModeAMono8; - nComps = srcAlpha ? 2 : 1; - break; - case splashModeAMono8: - //~ not implemented yet - ok = gFalse; - nComps = 2; - break; - case splashModeRGB8Qt: + ok = srcMode == splashModeMono8; + nComps = 1; case splashModeRGB8: - ok = srcMode == splashModeRGB8 || srcMode == splashModeARGB8; - srcAlpha = srcMode == splashModeARGB8; - nComps = srcAlpha ? 4 : 3; + ok = srcMode == splashModeRGB8; + nComps = 3; + break; + case splashModeRGBX8: + ok = srcMode == splashModeRGBX8; + nComps = 4; break; case splashModeBGR8: - ok = srcMode == splashModeBGR8 || srcMode == splashModeBGRA8; - srcAlpha = srcMode == splashModeBGRA8; - nComps = srcAlpha ? 4 : 3; + ok = srcMode == splashModeBGR8; + nComps = 3; break; #if SPLASH_CMYK case splashModeCMYK8: - ok = srcMode == splashModeCMYK8 || srcMode == splashModeACMYK8; - srcAlpha = srcMode == splashModeACMYK8; - nComps = srcAlpha ? 5 : 4; - break; -#endif - case splashModeARGB8: - case splashModeBGRA8: -#if SPLASH_CMYK - case splashModeACMYK8: -#endif - //~ not implemented yet - ok = gFalse; + ok = srcMode == splashModeCMYK8; nComps = 4; break; +#endif } if (!ok) { return splashErrModeMismatch; @@ -2854,38 +2180,31 @@ SplashError Splash::drawImage(SplashImageSource src, void *srcData, xShear = mat[2] / yScale; yShear = mat[1] / mat[0]; } - // the +/-0.01 in these computations is to avoid floating point - // precision problems which can lead to gaps between image stripes - // (it can cause image stripes to overlap, but that's a much less - // visible problem) + // Note 1: The PDF spec says that all pixels whose *centers* lie + // within the region get painted -- but that doesn't seem to match + // up with what Acrobat actually does: it ends up leaving gaps + // between image stripes. So we use the same rule here as for + // fills: any pixel that overlaps the region gets painted. + // Note 2: The +/-0.01 in these computations is to avoid floating + // point precision problems which can lead to gaps between image + // stripes (it can cause image stripes to overlap, but that's a much + // less visible problem). if (xScale >= 0) { - tx = splashRound(mat[4] - 0.01); - tx2 = splashRound(mat[4] + xScale + 0.01) - 1; + tx = splashFloor(mat[4] - 0.01); + tx2 = splashFloor(mat[4] + xScale + 0.01); } else { - tx = splashRound(mat[4] + 0.01) - 1; - tx2 = splashRound(mat[4] + xScale - 0.01); + tx = splashFloor(mat[4] + 0.01); + tx2 = splashFloor(mat[4] + xScale - 0.01); } scaledWidth = abs(tx2 - tx) + 1; - if (scaledWidth == 0) { - // technically, this should draw nothing, but it generally seems - // better to draw a one-pixel-wide stripe rather than throwing it - // away - scaledWidth = 1; - } if (yScale >= 0) { - ty = splashRound(mat[5] - 0.01); - ty2 = splashRound(mat[5] + yScale + 0.01) - 1; + ty = splashFloor(mat[5] - 0.01); + ty2 = splashFloor(mat[5] + yScale + 0.01); } else { - ty = splashRound(mat[5] + 0.01) - 1; - ty2 = splashRound(mat[5] + yScale - 0.01); + ty = splashFloor(mat[5] + 0.01); + ty2 = splashFloor(mat[5] + yScale - 0.01); } scaledHeight = abs(ty2 - ty) + 1; - if (scaledHeight == 0) { - // technically, this should draw nothing, but it generally seems - // better to draw a one-pixel-wide stripe rather than throwing it - // away - scaledHeight = 1; - } xSign = (xScale < 0) ? -1 : 1; ySign = (yScale < 0) ? -1 : 1; yShear1 = (SplashCoord)xSign * yShear; @@ -2939,14 +2258,27 @@ SplashError Splash::drawImage(SplashImageSource src, void *srcData, xp = w / scaledWidth; xq = w % scaledWidth; - // allocate pixel buffer - pixBuf = (SplashColorPtr)gmalloc((yp + 1) * w * nComps); + // allocate pixel buffers + colorBuf = (SplashColorPtr)gmalloc((yp + 1) * w * nComps); + if (srcAlpha) { + alphaBuf = (Guchar *)gmalloc((yp + 1) * w); + } else { + alphaBuf = NULL; + } pixAcc0 = pixAcc1 = pixAcc2 = 0; // make gcc happy #if SPLASH_CMYK pixAcc3 = 0; // make gcc happy #endif + // initialize the pixel pipe + pipeInit(&pipe, 0, 0, NULL, pix, state->fillAlpha, + srcAlpha || (vectorAntialias && clipRes != splashClipAllInside), + gFalse); + if (vectorAntialias) { + drawAAPixelInit(); + } + if (srcAlpha) { // init y scale Bresenham @@ -2966,17 +2298,19 @@ SplashError Splash::drawImage(SplashImageSource src, void *srcData, // read row(s) from image n = (yp > 0) ? yStep : lastYStep; if (n > 0) { - p = pixBuf; + p = colorBuf; + q = alphaBuf; for (i = 0; i < n; ++i) { - (*src)(srcData, p); + (*src)(srcData, p, q); p += w * nComps; + q += w; } } lastYStep = yStep; // loop-invariant constants k1 = splashRound(xShear * ySign * y); - + // clipping test if (clipRes != splashClipAllInside && !rot && @@ -3017,132 +2351,280 @@ SplashError Splash::drawImage(SplashImageSource src, void *srcData, // loop-invariant constants n = yStep > 0 ? yStep : 1; - for (x = 0; x < scaledWidth; ++x) { + switch (srcMode) { - // x scale Bresenham - xStep = xp; - xt += xq; - if (xt >= scaledWidth) { - xt -= scaledWidth; - ++xStep; - } + case splashModeMono1: + case splashModeMono8: + for (x = 0; x < scaledWidth; ++x) { + + // x scale Bresenham + xStep = xp; + xt += xq; + if (xt >= scaledWidth) { + xt -= scaledWidth; + ++xStep; + } - // rotation - if (rot) { - x2 = (int)y1; - y2 = -x1; - } else { - x2 = x1; - y2 = (int)y1; - } + // rotation + if (rot) { + x2 = (int)y1; + y2 = -x1; + } else { + x2 = x1; + y2 = (int)y1; + } - // compute the filtered pixel at (x,y) after the x and y scaling - // operations - m = xStep > 0 ? xStep : 1; - alphaAcc = 0; - switch (srcMode) { - case splashModeAMono8: - p = pixBuf + xSrc * 2; + // compute the filtered pixel at (x,y) after the x and y scaling + // operations + m = xStep > 0 ? xStep : 1; + alphaAcc = 0; + p = colorBuf + xSrc; + q = alphaBuf + xSrc; pixAcc0 = 0; for (i = 0; i < n; ++i) { for (j = 0; j < m; ++j) { - alphaAcc += *p++; pixAcc0 += *p++; + alphaAcc += *q++; } - p += 2 * (w - m); + p += w - m; + q += w - m; } - break; - case splashModeARGB8: - p = pixBuf + xSrc * 4; + pixMul = (SplashCoord)1 / (SplashCoord)(n * m); + alphaMul = pixMul * (1.0 / 255.0); + alpha = (SplashCoord)alphaAcc * alphaMul; + + if (alpha > 0) { + pix[0] = (int)((SplashCoord)pixAcc0 * pixMul); + + // set pixel + pipe.shape = alpha; + if (vectorAntialias && clipRes != splashClipAllInside) { + drawAAPixel(&pipe, tx + x2, ty + y2); + } else { + drawPixel(&pipe, tx + x2, ty + y2, + clipRes2 == splashClipAllInside); + } + } + + // x scale Bresenham + xSrc += xStep; + + // x shear + x1 += xSign; + + // y shear + y1 += yShear1; + } + break; + + case splashModeRGB8: + case splashModeBGR8: + for (x = 0; x < scaledWidth; ++x) { + + // x scale Bresenham + xStep = xp; + xt += xq; + if (xt >= scaledWidth) { + xt -= scaledWidth; + ++xStep; + } + + // rotation + if (rot) { + x2 = (int)y1; + y2 = -x1; + } else { + x2 = x1; + y2 = (int)y1; + } + + // compute the filtered pixel at (x,y) after the x and y scaling + // operations + m = xStep > 0 ? xStep : 1; + alphaAcc = 0; + p = colorBuf + xSrc * 3; + q = alphaBuf + xSrc; pixAcc0 = pixAcc1 = pixAcc2 = 0; for (i = 0; i < n; ++i) { for (j = 0; j < m; ++j) { - alphaAcc += *p++; pixAcc0 += *p++; pixAcc1 += *p++; pixAcc2 += *p++; + alphaAcc += *q++; } - p += 4 * (w - m); + p += 3 * (w - m); + q += w - m; } - break; - case splashModeBGRA8: - p = pixBuf + xSrc * 4; + pixMul = (SplashCoord)1 / (SplashCoord)(n * m); + alphaMul = pixMul * (1.0 / 255.0); + alpha = (SplashCoord)alphaAcc * alphaMul; + + if (alpha > 0) { + pix[0] = (int)((SplashCoord)pixAcc0 * pixMul); + pix[1] = (int)((SplashCoord)pixAcc1 * pixMul); + pix[2] = (int)((SplashCoord)pixAcc2 * pixMul); + + // set pixel + pipe.shape = alpha; + if (vectorAntialias && clipRes != splashClipAllInside) { + drawAAPixel(&pipe, tx + x2, ty + y2); + } else { + drawPixel(&pipe, tx + x2, ty + y2, + clipRes2 == splashClipAllInside); + } + } + + // x scale Bresenham + xSrc += xStep; + + // x shear + x1 += xSign; + + // y shear + y1 += yShear1; + } + break; + + case splashModeRGBX8: + for (x = 0; x < scaledWidth; ++x) { + + // x scale Bresenham + xStep = xp; + xt += xq; + if (xt >= scaledWidth) { + xt -= scaledWidth; + ++xStep; + } + + // rotation + if (rot) { + x2 = (int)y1; + y2 = -x1; + } else { + x2 = x1; + y2 = (int)y1; + } + + // compute the filtered pixel at (x,y) after the x and y scaling + // operations + m = xStep > 0 ? xStep : 1; + alphaAcc = 0; + p = colorBuf + xSrc * 4; + q = alphaBuf + xSrc; pixAcc0 = pixAcc1 = pixAcc2 = 0; for (i = 0; i < n; ++i) { for (j = 0; j < m; ++j) { pixAcc0 += *p++; pixAcc1 += *p++; pixAcc2 += *p++; - alphaAcc += *p++; + *p++; + alphaAcc += *q++; } p += 4 * (w - m); + q += w - m; } - break; + pixMul = (SplashCoord)1 / (SplashCoord)(n * m); + alphaMul = pixMul * (1.0 / 255.0); + alpha = (SplashCoord)alphaAcc * alphaMul; + + if (alpha > 0) { + pix[0] = (int)((SplashCoord)pixAcc0 * pixMul); + pix[1] = (int)((SplashCoord)pixAcc1 * pixMul); + pix[2] = (int)((SplashCoord)pixAcc2 * pixMul); + pix[3] = 255; + + // set pixel + pipe.shape = alpha; + if (vectorAntialias && clipRes != splashClipAllInside) { + drawAAPixel(&pipe, tx + x2, ty + y2); + } else { + drawPixel(&pipe, tx + x2, ty + y2, + clipRes2 == splashClipAllInside); + } + } + + // x scale Bresenham + xSrc += xStep; + + // x shear + x1 += xSign; + + // y shear + y1 += yShear1; + } + break; + + #if SPLASH_CMYK - case splashModeACMYK8: - p = pixBuf + xSrc * 5; + case splashModeCMYK8: + for (x = 0; x < scaledWidth; ++x) { + + // x scale Bresenham + xStep = xp; + xt += xq; + if (xt >= scaledWidth) { + xt -= scaledWidth; + ++xStep; + } + + // rotation + if (rot) { + x2 = (int)y1; + y2 = -x1; + } else { + x2 = x1; + y2 = (int)y1; + } + + // compute the filtered pixel at (x,y) after the x and y scaling + // operations + m = xStep > 0 ? xStep : 1; + alphaAcc = 0; + p = colorBuf + xSrc * 4; + q = alphaBuf + xSrc; pixAcc0 = pixAcc1 = pixAcc2 = pixAcc3 = 0; for (i = 0; i < n; ++i) { for (j = 0; j < m; ++j) { - alphaAcc += *p++; pixAcc0 += *p++; pixAcc1 += *p++; pixAcc2 += *p++; pixAcc3 += *p++; + alphaAcc += *q++; } - p += 5 * (w - m); + p += 4 * (w - m); + q += w - m; } - break; -#endif - default: // make gcc happy - break; - } - pixMul = (SplashCoord)1 / (SplashCoord)(n * m); - alphaMul = pixMul * (1.0 / 256.0); - alpha = (SplashCoord)alphaAcc * alphaMul; + pixMul = (SplashCoord)1 / (SplashCoord)(n * m); + alphaMul = pixMul * (1.0 / 255.0); + alpha = (SplashCoord)alphaAcc * alphaMul; - if (alpha > 0) { - // mono8 -> mono1 conversion, with halftoning - if (halftone) { - pix[0] = state->screen->test(tx + x2, ty + y2, - (SplashCoord)pixAcc0 * pixMul * (1.0 / 256.0)); + if (alpha > 0) { + pix[0] = (int)((SplashCoord)pixAcc0 * pixMul); + pix[1] = (int)((SplashCoord)pixAcc1 * pixMul); + pix[2] = (int)((SplashCoord)pixAcc2 * pixMul); + pix[3] = (int)((SplashCoord)pixAcc3 * pixMul); - // no conversion, no halftoning - } else { - switch (bitmap->mode) { -#if SPLASH_CMYK - case splashModeCMYK8: - pix[3] = (int)((SplashCoord)pixAcc3 * pixMul); - // fall through -#endif - case splashModeRGB8: - case splashModeBGR8: - case splashModeRGB8Qt: - pix[2] = (int)((SplashCoord)pixAcc2 * pixMul); - pix[1] = (int)((SplashCoord)pixAcc1 * pixMul); - // fall through - case splashModeMono1: - case splashModeMono8: - pix[0] = (int)((SplashCoord)pixAcc0 * pixMul); - break; - default: // make gcc happy - break; + // set pixel + pipe.shape = alpha; + if (vectorAntialias && clipRes != splashClipAllInside) { + drawAAPixel(&pipe, tx + x2, ty + y2); + } else { + drawPixel(&pipe, tx + x2, ty + y2, + clipRes2 == splashClipAllInside); } } - // set pixel - drawPixel(tx + x2, ty + y2, pix, alpha * state->fillAlpha, - clipRes2 == splashClipAllInside); - } - - // x scale Bresenham - xSrc += xStep; + // x scale Bresenham + xSrc += xStep; - // x shear - x1 += xSign; + // x shear + x1 += xSign; - // y shear - y1 += yShear1; + // y shear + y1 += yShear1; + } + break; +#endif // SPLASH_CMYK } } @@ -3165,9 +2647,9 @@ SplashError Splash::drawImage(SplashImageSource src, void *srcData, // read row(s) from image n = (yp > 0) ? yStep : lastYStep; if (n > 0) { - p = pixBuf; + p = colorBuf; for (i = 0; i < n; ++i) { - (*src)(srcData, p); + (*src)(srcData, p, NULL); p += w * nComps; } } @@ -3216,32 +2698,33 @@ SplashError Splash::drawImage(SplashImageSource src, void *srcData, // loop-invariant constants n = yStep > 0 ? yStep : 1; - for (x = 0; x < scaledWidth; ++x) { + switch (srcMode) { - // x scale Bresenham - xStep = xp; - xt += xq; - if (xt >= scaledWidth) { - xt -= scaledWidth; - ++xStep; - } + case splashModeMono1: + case splashModeMono8: + for (x = 0; x < scaledWidth; ++x) { + + // x scale Bresenham + xStep = xp; + xt += xq; + if (xt >= scaledWidth) { + xt -= scaledWidth; + ++xStep; + } - // rotation - if (rot) { - x2 = (int)y1; - y2 = -x1; - } else { - x2 = x1; - y2 = (int)y1; - } + // rotation + if (rot) { + x2 = (int)y1; + y2 = -x1; + } else { + x2 = x1; + y2 = (int)y1; + } - // compute the filtered pixel at (x,y) after the x and y scaling - // operations - m = xStep > 0 ? xStep : 1; - switch (srcMode) { - case splashModeMono1: - case splashModeMono8: - p = pixBuf + xSrc; + // compute the filtered pixel at (x,y) after the x and y scaling + // operations + m = xStep > 0 ? xStep : 1; + p = colorBuf + xSrc; pixAcc0 = 0; for (i = 0; i < n; ++i) { for (j = 0; j < m; ++j) { @@ -3249,11 +2732,55 @@ SplashError Splash::drawImage(SplashImageSource src, void *srcData, } p += w - m; } - break; - case splashModeRGB8: - case splashModeBGR8: - case splashModeRGB8Qt: - p = pixBuf + xSrc * 3; + pixMul = (SplashCoord)1 / (SplashCoord)(n * m); + + pix[0] = (int)((SplashCoord)pixAcc0 * pixMul); + + // set pixel + if (vectorAntialias && clipRes != splashClipAllInside) { + pipe.shape = (SplashCoord)1; + drawAAPixel(&pipe, tx + x2, ty + y2); + } else { + drawPixel(&pipe, tx + x2, ty + y2, + clipRes2 == splashClipAllInside); + } + + // x scale Bresenham + xSrc += xStep; + + // x shear + x1 += xSign; + + // y shear + y1 += yShear1; + } + break; + + case splashModeRGB8: + case splashModeBGR8: + for (x = 0; x < scaledWidth; ++x) { + + // x scale Bresenham + xStep = xp; + xt += xq; + if (xt >= scaledWidth) { + xt -= scaledWidth; + ++xStep; + } + + // rotation + if (rot) { + x2 = (int)y1; + y2 = -x1; + } else { + x2 = x1; + y2 = (int)y1; + } + + // compute the filtered pixel at (x,y) after the x and y scaling + // operations + m = xStep > 0 ? xStep : 1; + p = colorBuf + xSrc * 3; pixAcc0 = pixAcc1 = pixAcc2 = 0; for (i = 0; i < n; ++i) { for (j = 0; j < m; ++j) { @@ -3263,10 +2790,118 @@ SplashError Splash::drawImage(SplashImageSource src, void *srcData, } p += 3 * (w - m); } - break; + pixMul = (SplashCoord)1 / (SplashCoord)(n * m); + + pix[0] = (int)((SplashCoord)pixAcc0 * pixMul); + pix[1] = (int)((SplashCoord)pixAcc1 * pixMul); + pix[2] = (int)((SplashCoord)pixAcc2 * pixMul); + + // set pixel + if (vectorAntialias && clipRes != splashClipAllInside) { + pipe.shape = (SplashCoord)1; + drawAAPixel(&pipe, tx + x2, ty + y2); + } else { + drawPixel(&pipe, tx + x2, ty + y2, + clipRes2 == splashClipAllInside); + } + + // x scale Bresenham + xSrc += xStep; + + // x shear + x1 += xSign; + + // y shear + y1 += yShear1; + } + break; + + case splashModeRGBX8: + for (x = 0; x < scaledWidth; ++x) { + + // x scale Bresenham + xStep = xp; + xt += xq; + if (xt >= scaledWidth) { + xt -= scaledWidth; + ++xStep; + } + + // rotation + if (rot) { + x2 = (int)y1; + y2 = -x1; + } else { + x2 = x1; + y2 = (int)y1; + } + + // compute the filtered pixel at (x,y) after the x and y scaling + // operations + m = xStep > 0 ? xStep : 1; + p = colorBuf + xSrc * 4; + pixAcc0 = pixAcc1 = pixAcc2 = 0; + for (i = 0; i < n; ++i) { + for (j = 0; j < m; ++j) { + pixAcc0 += *p++; + pixAcc1 += *p++; + pixAcc2 += *p++; + *p++; + } + p += 4 * (w - m); + } + pixMul = (SplashCoord)1 / (SplashCoord)(n * m); + + pix[0] = (int)((SplashCoord)pixAcc0 * pixMul); + pix[1] = (int)((SplashCoord)pixAcc1 * pixMul); + pix[2] = (int)((SplashCoord)pixAcc2 * pixMul); + pix[3] = 255; + + // set pixel + if (vectorAntialias && clipRes != splashClipAllInside) { + pipe.shape = (SplashCoord)1; + drawAAPixel(&pipe, tx + x2, ty + y2); + } else { + drawPixel(&pipe, tx + x2, ty + y2, + clipRes2 == splashClipAllInside); + } + + // x scale Bresenham + xSrc += xStep; + + // x shear + x1 += xSign; + + // y shear + y1 += yShear1; + } + break; + #if SPLASH_CMYK - case splashModeCMYK8: - p = pixBuf + xSrc * 4; + case splashModeCMYK8: + for (x = 0; x < scaledWidth; ++x) { + + // x scale Bresenham + xStep = xp; + xt += xq; + if (xt >= scaledWidth) { + xt -= scaledWidth; + ++xStep; + } + + // rotation + if (rot) { + x2 = (int)y1; + y2 = -x1; + } else { + x2 = x1; + y2 = (int)y1; + } + + // compute the filtered pixel at (x,y) after the x and y scaling + // operations + m = xStep > 0 ? xStep : 1; + p = colorBuf + xSrc * 4; pixAcc0 = pixAcc1 = pixAcc2 = pixAcc3 = 0; for (i = 0; i < n; ++i) { for (j = 0; j < m; ++j) { @@ -3277,74 +2912,595 @@ SplashError Splash::drawImage(SplashImageSource src, void *srcData, } p += 4 * (w - m); } - break; -#endif - default: // make gcc happy - break; + pixMul = (SplashCoord)1 / (SplashCoord)(n * m); + + pix[0] = (int)((SplashCoord)pixAcc0 * pixMul); + pix[1] = (int)((SplashCoord)pixAcc1 * pixMul); + pix[2] = (int)((SplashCoord)pixAcc2 * pixMul); + pix[3] = (int)((SplashCoord)pixAcc3 * pixMul); + + // set pixel + if (vectorAntialias && clipRes != splashClipAllInside) { + pipe.shape = (SplashCoord)1; + drawAAPixel(&pipe, tx + x2, ty + y2); + } else { + drawPixel(&pipe, tx + x2, ty + y2, + clipRes2 == splashClipAllInside); + } + + // x scale Bresenham + xSrc += xStep; + + // x shear + x1 += xSign; + + // y shear + y1 += yShear1; } - pixMul = (SplashCoord)1 / (SplashCoord)(n * m); + break; +#endif // SPLASH_CMYK + } + } + + } + + gfree(colorBuf); + gfree(alphaBuf); + + return splashOk; +} + +SplashError Splash::composite(SplashBitmap *src, int xSrc, int ySrc, + int xDest, int yDest, int w, int h, + GBool noClip, GBool nonIsolated) { + SplashPipe pipe; + SplashColor pixel; + Guchar alpha; + Guchar *ap; + int x, y; + + if (src->mode != bitmap->mode) { + return splashErrModeMismatch; + } + + if (src->alpha) { + pipeInit(&pipe, xDest, yDest, NULL, pixel, state->fillAlpha, + gTrue, nonIsolated); + for (y = 0; y < h; ++y) { + pipeSetXY(&pipe, xDest, yDest + y); + ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc; + for (x = 0; x < w; ++x) { + src->getPixel(xSrc + x, ySrc + y, pixel); + alpha = *ap++; + if (noClip || state->clip->test(xDest + x, yDest + y)) { + // this uses shape instead of alpha, which isn't technically + // correct, but works out the same + pipe.shape = (SplashCoord)(alpha / 255.0); + pipeRun(&pipe); + updateModX(xDest + x); + updateModY(yDest + y); + } else { + pipeIncX(&pipe); + } + } + } + } else { + pipeInit(&pipe, xDest, yDest, NULL, pixel, state->fillAlpha, + gFalse, nonIsolated); + for (y = 0; y < h; ++y) { + pipeSetXY(&pipe, xDest, yDest + y); + for (x = 0; x < w; ++x) { + src->getPixel(xSrc + x, ySrc + y, pixel); + if (noClip || state->clip->test(xDest + x, yDest + y)) { + pipeRun(&pipe); + updateModX(xDest + x); + updateModY(yDest + y); + } else { + pipeIncX(&pipe); + } + } + } + } - // mono8 -> mono1 conversion, with halftoning - if (halftone) { - pix[0] = state->screen->test(tx + x2, ty + y2, - (SplashCoord)pixAcc0 * pixMul * (1.0 / 256.0)); + return splashOk; +} - // no conversion, no halftoning +void Splash::compositeBackground(SplashColorPtr color) { + SplashColorPtr p; + Guchar *q; + Guchar alpha, alpha1, c, color0, color1, color2, color3; + int x, y, mask; + + switch (bitmap->mode) { + case splashModeMono1: + color0 = color[0]; + for (y = 0; y < bitmap->height; ++y) { + p = &bitmap->data[y * bitmap->rowSize]; + q = &bitmap->alpha[y * bitmap->width]; + mask = 0x80; + for (x = 0; x < bitmap->width; ++x) { + alpha = *q++; + alpha1 = 255 - alpha; + c = (*p & mask) ? 0xff : 0x00; + c = div255(alpha1 * color0 + alpha * c); + if (c & 0x80) { + *p |= mask; } else { - switch (bitmap->mode) { + *p &= ~mask; + } + if (!(mask >>= 1)) { + mask = 0x80; + ++p; + } + } + } + break; + case splashModeMono8: + color0 = color[0]; + for (y = 0; y < bitmap->height; ++y) { + p = &bitmap->data[y * bitmap->rowSize]; + q = &bitmap->alpha[y * bitmap->width]; + for (x = 0; x < bitmap->width; ++x) { + alpha = *q++; + alpha1 = 255 - alpha; + p[0] = div255(alpha1 * color0 + alpha * p[0]); + ++p; + } + } + break; + case splashModeRGB8: + case splashModeBGR8: + color0 = color[0]; + color1 = color[1]; + color2 = color[2]; + for (y = 0; y < bitmap->height; ++y) { + p = &bitmap->data[y * bitmap->rowSize]; + q = &bitmap->alpha[y * bitmap->width]; + for (x = 0; x < bitmap->width; ++x) { + alpha = *q++; + alpha1 = 255 - alpha; + p[0] = div255(alpha1 * color0 + alpha * p[0]); + p[1] = div255(alpha1 * color1 + alpha * p[1]); + p[2] = div255(alpha1 * color2 + alpha * p[2]); + p += 3; + } + } + break; + case splashModeRGBX8: + color0 = color[0]; + color1 = color[1]; + color2 = color[2]; + for (y = 0; y < bitmap->height; ++y) { + p = &bitmap->data[y * bitmap->rowSize]; + q = &bitmap->alpha[y * bitmap->width]; + for (x = 0; x < bitmap->width; ++x) { + alpha = *q++; + alpha1 = 255 - alpha; + p[0] = div255(alpha1 * color0 + alpha * p[0]); + p[1] = div255(alpha1 * color1 + alpha * p[1]); + p[2] = div255(alpha1 * color2 + alpha * p[2]); + p[3] = 255; + p += 4; + } + } + break; #if SPLASH_CMYK - case splashModeCMYK8: - pix[3] = (int)((SplashCoord)pixAcc3 * pixMul); - // fall through + case splashModeCMYK8: + color0 = color[0]; + color1 = color[1]; + color2 = color[2]; + color3 = color[3]; + for (y = 0; y < bitmap->height; ++y) { + p = &bitmap->data[y * bitmap->rowSize]; + q = &bitmap->alpha[y * bitmap->width]; + for (x = 0; x < bitmap->width; ++x) { + alpha = *q++; + alpha1 = 255 - alpha; + p[0] = div255(alpha1 * color0 + alpha * p[0]); + p[1] = div255(alpha1 * color1 + alpha * p[1]); + p[2] = div255(alpha1 * color2 + alpha * p[2]); + p[3] = div255(alpha1 * color3 + alpha * p[3]); + p += 4; + } + } + break; #endif - case splashModeRGB8: - case splashModeRGB8Qt: - case splashModeBGR8: - pix[2] = (int)((SplashCoord)pixAcc2 * pixMul); - pix[1] = (int)((SplashCoord)pixAcc1 * pixMul); - // fall through - case splashModeMono1: - case splashModeMono8: - pix[0] = (int)((SplashCoord)pixAcc0 * pixMul); - break; - default: // make gcc happy - break; - } + } + memset(bitmap->alpha, 255, bitmap->width * bitmap->height); +} + +SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc, + int xDest, int yDest, int w, int h) { + SplashColor pixel; + SplashColorPtr p; + Guchar *q; + int x, y, mask; + + if (src->mode != bitmap->mode) { + return splashErrModeMismatch; + } + + switch (bitmap->mode) { + case splashModeMono1: + for (y = 0; y < h; ++y) { + p = &bitmap->data[(yDest + y) * bitmap->rowSize + (xDest >> 3)]; + mask = 0x80 >> (xDest & 7); + for (x = 0; x < w; ++x) { + src->getPixel(xSrc + x, ySrc + y, pixel); + if (pixel[0]) { + *p |= mask; + } else { + *p &= ~mask; } + if (!(mask >>= 1)) { + mask = 0x80; + ++p; + } + } + } + break; + case splashModeMono8: + for (y = 0; y < h; ++y) { + p = &bitmap->data[(yDest + y) * bitmap->rowSize + xDest]; + for (x = 0; x < w; ++x) { + src->getPixel(xSrc + x, ySrc + y, pixel); + *p++ = pixel[0]; + } + } + break; + case splashModeRGB8: + case splashModeBGR8: + for (y = 0; y < h; ++y) { + p = &bitmap->data[(yDest + y) * bitmap->rowSize + 3 * xDest]; + for (x = 0; x < w; ++x) { + src->getPixel(xSrc + x, ySrc + y, pixel); + *p++ = pixel[0]; + *p++ = pixel[1]; + *p++ = pixel[2]; + } + } + break; + case splashModeRGBX8: + for (y = 0; y < h; ++y) { + p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest]; + for (x = 0; x < w; ++x) { + src->getPixel(xSrc + x, ySrc + y, pixel); + *p++ = pixel[0]; + *p++ = pixel[1]; + *p++ = pixel[2]; + *p++ = 255; + } + } + break; +#if SPLASH_CMYK + case splashModeCMYK8: + for (y = 0; y < h; ++y) { + p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest]; + for (x = 0; x < w; ++x) { + src->getPixel(xSrc + x, ySrc + y, pixel); + *p++ = pixel[0]; + *p++ = pixel[1]; + *p++ = pixel[2]; + *p++ = pixel[3]; + } + } + break; +#endif + } + + if (bitmap->alpha) { + for (y = 0; y < h; ++y) { + q = &bitmap->alpha[(yDest + y) * bitmap->width + xDest]; + for (x = 0; x < w; ++x) { + *q++ = 0x00; + } + } + } + + return splashOk; +} + +SplashPath *Splash::makeStrokePath(SplashPath *path, GBool flatten) { + SplashPath *pathIn, *pathOut; + SplashCoord w, d, dx, dy, wdx, wdy, dxNext, dyNext, wdxNext, wdyNext; + SplashCoord crossprod, dotprod, miter, m; + GBool first, last, closed; + int subpathStart, next, i; + int left0, left1, left2, right0, right1, right2, join0, join1, join2; + int leftFirst, rightFirst, firstPt; + + if (flatten) { + pathIn = flattenPath(path, state->matrix, state->flatness); + if (state->lineDashLength > 0) { + pathOut = makeDashedPath(pathIn); + delete pathIn; + pathIn = pathOut; + } + } else { + pathIn = path; + } + + subpathStart = 0; // make gcc happy + closed = gFalse; // make gcc happy + left0 = left1 = right0 = right1 = join0 = join1 = 0; // make gcc happy + leftFirst = rightFirst = firstPt = 0; // make gcc happy + + pathOut = new SplashPath(); + w = state->lineWidth; + + for (i = 0; i < pathIn->length - 1; ++i) { + if (pathIn->flags[i] & splashPathLast) { + continue; + } + if ((first = pathIn->flags[i] & splashPathFirst)) { + subpathStart = i; + closed = pathIn->flags[i] & splashPathClosed; + } + last = pathIn->flags[i+1] & splashPathLast; + + // compute the deltas for segment (i, i+1) + d = splashDist(pathIn->pts[i].x, pathIn->pts[i].y, + pathIn->pts[i+1].x, pathIn->pts[i+1].y); + if (d == 0) { + // we need to draw end caps on zero-length lines + //~ not clear what the behavior should be for splashLineCapButt + //~ with d==0 + dx = 0; + dy = 1; + } else { + d = (SplashCoord)1 / d; + dx = d * (pathIn->pts[i+1].x - pathIn->pts[i].x); + dy = d * (pathIn->pts[i+1].y - pathIn->pts[i].y); + } + wdx = (SplashCoord)0.5 * w * dx; + wdy = (SplashCoord)0.5 * w * dy; + + // compute the deltas for segment (i+1, next) + next = last ? subpathStart + 1 : i + 2; + d = splashDist(pathIn->pts[i+1].x, pathIn->pts[i+1].y, + pathIn->pts[next].x, pathIn->pts[next].y); + if (d == 0) { + // we need to draw end caps on zero-length lines + //~ not clear what the behavior should be for splashLineCapButt + //~ with d==0 + dxNext = 0; + dyNext = 1; + } else { + d = (SplashCoord)1 / d; + dxNext = d * (pathIn->pts[next].x - pathIn->pts[i+1].x); + dyNext = d * (pathIn->pts[next].y - pathIn->pts[i+1].y); + } + wdxNext = (SplashCoord)0.5 * w * dxNext; + wdyNext = (SplashCoord)0.5 * w * dyNext; + + // draw the start cap + pathOut->moveTo(pathIn->pts[i].x - wdy, pathIn->pts[i].y + wdx); + if (i == subpathStart) { + firstPt = pathOut->length - 1; + } + if (first && !closed) { + switch (state->lineCap) { + case splashLineCapButt: + pathOut->lineTo(pathIn->pts[i].x + wdy, pathIn->pts[i].y - wdx); + break; + case splashLineCapRound: + pathOut->curveTo(pathIn->pts[i].x - wdy - bezierCircle * wdx, + pathIn->pts[i].y + wdx - bezierCircle * wdy, + pathIn->pts[i].x - wdx - bezierCircle * wdy, + pathIn->pts[i].y - wdy + bezierCircle * wdx, + pathIn->pts[i].x - wdx, + pathIn->pts[i].y - wdy); + pathOut->curveTo(pathIn->pts[i].x - wdx + bezierCircle * wdy, + pathIn->pts[i].y - wdy - bezierCircle * wdx, + pathIn->pts[i].x + wdy - bezierCircle * wdx, + pathIn->pts[i].y - wdx - bezierCircle * wdy, + pathIn->pts[i].x + wdy, + pathIn->pts[i].y - wdx); + break; + case splashLineCapProjecting: + pathOut->lineTo(pathIn->pts[i].x - wdx - wdy, + pathIn->pts[i].y + wdx - wdy); + pathOut->lineTo(pathIn->pts[i].x - wdx + wdy, + pathIn->pts[i].y - wdx - wdy); + pathOut->lineTo(pathIn->pts[i].x + wdy, + pathIn->pts[i].y - wdx); + break; + } + } else { + pathOut->lineTo(pathIn->pts[i].x + wdy, pathIn->pts[i].y - wdx); + } + + // draw the left side of the segment rectangle + left2 = pathOut->length - 1; + pathOut->lineTo(pathIn->pts[i+1].x + wdy, pathIn->pts[i+1].y - wdx); - // set pixel - drawPixel(tx + x2, ty + y2, pix, state->fillAlpha, - clipRes2 == splashClipAllInside); + // draw the end cap + if (last && !closed) { + switch (state->lineCap) { + case splashLineCapButt: + pathOut->lineTo(pathIn->pts[i+1].x - wdy, pathIn->pts[i+1].y + wdx); + break; + case splashLineCapRound: + pathOut->curveTo(pathIn->pts[i+1].x + wdy + bezierCircle * wdx, + pathIn->pts[i+1].y - wdx + bezierCircle * wdy, + pathIn->pts[i+1].x + wdx + bezierCircle * wdy, + pathIn->pts[i+1].y + wdy - bezierCircle * wdx, + pathIn->pts[i+1].x + wdx, + pathIn->pts[i+1].y + wdy); + pathOut->curveTo(pathIn->pts[i+1].x + wdx - bezierCircle * wdy, + pathIn->pts[i+1].y + wdy + bezierCircle * wdx, + pathIn->pts[i+1].x - wdy + bezierCircle * wdx, + pathIn->pts[i+1].y + wdx + bezierCircle * wdy, + pathIn->pts[i+1].x - wdy, + pathIn->pts[i+1].y + wdx); + break; + case splashLineCapProjecting: + pathOut->lineTo(pathIn->pts[i+1].x + wdy + wdx, + pathIn->pts[i+1].y - wdx + wdy); + pathOut->lineTo(pathIn->pts[i+1].x - wdy + wdx, + pathIn->pts[i+1].y + wdx + wdy); + pathOut->lineTo(pathIn->pts[i+1].x - wdy, + pathIn->pts[i+1].y + wdx); + break; + } + } else { + pathOut->lineTo(pathIn->pts[i+1].x - wdy, pathIn->pts[i+1].y + wdx); + } - // x scale Bresenham - xSrc += xStep; + // draw the right side of the segment rectangle + right2 = pathOut->length - 1; + pathOut->close(); + + // draw the join + join2 = pathOut->length; + if (!last || closed) { + crossprod = dx * dyNext - dy * dxNext; + dotprod = -(dx * dxNext + dy * dyNext); + if (dotprod > 0.99999) { + // avoid a divide-by-zero -- set miter to something arbitrary + // such that sqrt(miter) will exceed miterLimit (and m is never + // used in that situation) + miter = (state->miterLimit + 1) * (state->miterLimit + 1); + m = 0; + } else { + miter = (SplashCoord)2 / ((SplashCoord)1 - dotprod); + if (miter < 1) { + // this can happen because of floating point inaccuracies + miter = 1; + } + m = splashSqrt(miter - 1); + } + + // round join + if (state->lineJoin == splashLineJoinRound) { + pathOut->moveTo(pathIn->pts[i+1].x + (SplashCoord)0.5 * w, + pathIn->pts[i+1].y); + pathOut->curveTo(pathIn->pts[i+1].x + (SplashCoord)0.5 * w, + pathIn->pts[i+1].y + bezierCircle2 * w, + pathIn->pts[i+1].x + bezierCircle2 * w, + pathIn->pts[i+1].y + (SplashCoord)0.5 * w, + pathIn->pts[i+1].x, + pathIn->pts[i+1].y + (SplashCoord)0.5 * w); + pathOut->curveTo(pathIn->pts[i+1].x - bezierCircle2 * w, + pathIn->pts[i+1].y + (SplashCoord)0.5 * w, + pathIn->pts[i+1].x - (SplashCoord)0.5 * w, + pathIn->pts[i+1].y + bezierCircle2 * w, + pathIn->pts[i+1].x - (SplashCoord)0.5 * w, + pathIn->pts[i+1].y); + pathOut->curveTo(pathIn->pts[i+1].x - (SplashCoord)0.5 * w, + pathIn->pts[i+1].y - bezierCircle2 * w, + pathIn->pts[i+1].x - bezierCircle2 * w, + pathIn->pts[i+1].y - (SplashCoord)0.5 * w, + pathIn->pts[i+1].x, + pathIn->pts[i+1].y - (SplashCoord)0.5 * w); + pathOut->curveTo(pathIn->pts[i+1].x + bezierCircle2 * w, + pathIn->pts[i+1].y - (SplashCoord)0.5 * w, + pathIn->pts[i+1].x + (SplashCoord)0.5 * w, + pathIn->pts[i+1].y - bezierCircle2 * w, + pathIn->pts[i+1].x + (SplashCoord)0.5 * w, + pathIn->pts[i+1].y); - // x shear - x1 += xSign; + } else { + pathOut->moveTo(pathIn->pts[i+1].x, pathIn->pts[i+1].y); + + // angle < 180 + if (crossprod < 0) { + pathOut->lineTo(pathIn->pts[i+1].x - wdyNext, + pathIn->pts[i+1].y + wdxNext); + // miter join inside limit + if (state->lineJoin == splashLineJoinMiter && + splashSqrt(miter) <= state->miterLimit) { + pathOut->lineTo(pathIn->pts[i+1].x - wdy + wdx * m, + pathIn->pts[i+1].y + wdx + wdy * m); + pathOut->lineTo(pathIn->pts[i+1].x - wdy, + pathIn->pts[i+1].y + wdx); + // bevel join or miter join outside limit + } else { + pathOut->lineTo(pathIn->pts[i+1].x - wdy, pathIn->pts[i+1].y + wdx); + } - // y shear - y1 += yShear1; + // angle >= 180 + } else { + pathOut->lineTo(pathIn->pts[i+1].x + wdy, + pathIn->pts[i+1].y - wdx); + // miter join inside limit + if (state->lineJoin == splashLineJoinMiter && + splashSqrt(miter) <= state->miterLimit) { + pathOut->lineTo(pathIn->pts[i+1].x + wdy + wdx * m, + pathIn->pts[i+1].y - wdx + wdy * m); + pathOut->lineTo(pathIn->pts[i+1].x + wdyNext, + pathIn->pts[i+1].y - wdxNext); + // bevel join or miter join outside limit + } else { + pathOut->lineTo(pathIn->pts[i+1].x + wdyNext, + pathIn->pts[i+1].y - wdxNext); + } + } } + + pathOut->close(); } + // add stroke adjustment hints + if (state->strokeAdjust) { + if (i >= subpathStart + 1) { + if (i >= subpathStart + 2) { + pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0); + pathOut->addStrokeAdjustHint(left1, right1, join0, left2); + } else { + pathOut->addStrokeAdjustHint(left1, right1, firstPt, left2); + } + pathOut->addStrokeAdjustHint(left1, right1, right2 + 1, right2 + 1); + } + left0 = left1; + left1 = left2; + right0 = right1; + right1 = right2; + join0 = join1; + join1 = join2; + if (i == subpathStart) { + leftFirst = left2; + rightFirst = right2; + } + if (last) { + if (i >= subpathStart + 2) { + pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0); + pathOut->addStrokeAdjustHint(left1, right1, + join0, pathOut->length - 1); + } else { + pathOut->addStrokeAdjustHint(left1, right1, + firstPt, pathOut->length - 1); + } + if (closed) { + pathOut->addStrokeAdjustHint(left1, right1, firstPt, leftFirst); + pathOut->addStrokeAdjustHint(left1, right1, + rightFirst + 1, rightFirst + 1); + pathOut->addStrokeAdjustHint(leftFirst, rightFirst, + left1 + 1, right1); + pathOut->addStrokeAdjustHint(leftFirst, rightFirst, + join1, pathOut->length - 1); + } + } + } } - gfree(pixBuf); + if (pathIn != path) { + delete pathIn; + } - return splashOk; + return pathOut; } void Splash::dumpPath(SplashPath *path) { int i; for (i = 0; i < path->length; ++i) { - printf(" %3d: x=%8.2f y=%8.2f%s%s%s%s%s\n", + printf(" %3d: x=%8.2f y=%8.2f%s%s%s%s\n", i, (double)path->pts[i].x, (double)path->pts[i].y, (path->flags[i] & splashPathFirst) ? " first" : "", (path->flags[i] & splashPathLast) ? " last" : "", (path->flags[i] & splashPathClosed) ? " closed" : "", - (path->flags[i] & splashPathCurve) ? " curve" : "", - (path->flags[i] & splashPathArcCW) ? " arcCW" : ""); + (path->flags[i] & splashPathCurve) ? " curve" : ""); } } diff --git a/splash/Splash.h b/splash/Splash.h index 783da8fc..99203b2c 100644 --- a/splash/Splash.h +++ b/splash/Splash.h @@ -14,6 +14,7 @@ #include "SplashTypes.h" #include "SplashClip.h" +class Splash; class SplashBitmap; struct SplashGlyphBitmap; class SplashState; @@ -22,6 +23,7 @@ class SplashScreen; class SplashPath; class SplashXPath; class SplashFont; +struct SplashPipe; //------------------------------------------------------------------------ @@ -33,7 +35,29 @@ typedef GBool (*SplashImageMaskSource)(void *data, SplashColorPtr pixel); // Retrieves the next line of pixels in an image. Normally, fills in // *<line> and returns true. If the image stream is exhausted, // returns false. -typedef GBool (*SplashImageSource)(void *data, SplashColorPtr line); +typedef GBool (*SplashImageSource)(void *data, SplashColorPtr colorLine, + Guchar *alphaLine); + +//------------------------------------------------------------------------ + +enum SplashPipeResultColorCtrl { +#if SPLASH_CMYK + splashPipeResultColorNoAlphaBlendCMYK, +#endif + splashPipeResultColorNoAlphaBlendRGB, + splashPipeResultColorNoAlphaBlendMono, + splashPipeResultColorAlphaNoBlendMono, + splashPipeResultColorAlphaNoBlendRGB, +#if SPLASH_CMYK + splashPipeResultColorAlphaNoBlendCMYK, +#endif + splashPipeResultColorAlphaBlendMono, + splashPipeResultColorAlphaBlendRGB +#if SPLASH_CMYK + , + splashPipeResultColorAlphaBlendCMYK +#endif +}; //------------------------------------------------------------------------ // Splash @@ -43,12 +67,16 @@ class Splash { public: // Create a new rasterizer object. - Splash(SplashBitmap *bitmapA); + Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, + SplashScreenParams *screenParams = NULL); + Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, + SplashScreen *screenA); ~Splash(); //----- state read + SplashCoord *getMatrix(); SplashPattern *getStrokePattern(); SplashPattern *getFillPattern(); SplashScreen *getScreen(); @@ -64,9 +92,12 @@ public: int getLineDashLength(); SplashCoord getLineDashPhase(); SplashClip *getClip(); + SplashBitmap *getSoftMask(); + GBool getInNonIsolatedGroup(); //----- state write + void setMatrix(SplashCoord *matrix); void setStrokePattern(SplashPattern *strokeColor); void setFillPattern(SplashPattern *fillColor); void setScreen(SplashScreen *screen); @@ -81,25 +112,28 @@ public: // the <lineDash> array will be copied void setLineDash(SplashCoord *lineDash, int lineDashLength, SplashCoord lineDashPhase); + void setStrokeAdjust(GBool strokeAdjust); + // NB: uses transformed coordinates. void clipResetToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1); + // NB: uses transformed coordinates. SplashError clipToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1); + // NB: uses untransformed coordinates. SplashError clipToPath(SplashPath *path, GBool eo); + void setSoftMask(SplashBitmap *softMask); + void setInNonIsolatedGroup(SplashBitmap *alpha0BitmapA, + int alpha0XA, int alpha0YA); //----- state save/restore void saveState(); SplashError restoreState(); - //----- soft mask - - void setSoftMask(SplashBitmap *softMaskA); - //----- drawing operations // Fill the bitmap with <color>. This is not subject to clipping. - void clear(SplashColorPtr color); + void clear(SplashColorPtr color, Guchar alpha = 0x00); // Stroke a path using the current stroke pattern. SplashError stroke(SplashPath *path); @@ -132,12 +166,15 @@ public: // is assumed to produce pixels in raster order, starting from the // top line. SplashError fillImageMask(SplashImageMaskSource src, void *srcData, - int w, int h, SplashCoord *mat); + int w, int h, SplashCoord *mat, + GBool glyphMode); // Draw an image. This will read <h> lines of <w> pixels from // <src>, starting with the top line. These pixels are assumed to - // be in the source mode, <srcMode>. The following combinations of - // source and target modes are supported: + // be in the source mode, <srcMode>. If <srcAlpha> is true, the + // alpha values returned by <src> are used; otherwise they are + // ignored. The following combinations of source and target modes + // are supported: // source target // ------ ------ // Mono1 Mono1 @@ -145,15 +182,35 @@ public: // Mono8 Mono8 // RGB8 RGB8 // BGR8 BGR8 - // ARGB8 RGB8 -- with source alpha (masking) - // BGRA8 BGR8 -- with source alpha (masking) + // CMYK8 CMYK8 // The matrix behaves as for fillImageMask. SplashError drawImage(SplashImageSource src, void *srcData, - SplashColorMode srcMode, + SplashColorMode srcMode, GBool srcAlpha, int w, int h, SplashCoord *mat); + // Composite a rectangular region from <src> onto this Splash + // object. + SplashError composite(SplashBitmap *src, int xSrc, int ySrc, + int xDest, int yDest, int w, int h, + GBool noClip, GBool nonIsolated); + + // Composite this Splash object onto a background color. The + // background alpha is assumed to be 1. + void compositeBackground(SplashColorPtr color); + + // Copy a rectangular region from <src> onto the bitmap belonging to + // this Splash object. The destination alpha values are all set to + // zero. + SplashError blitTransparent(SplashBitmap *src, int xSrc, int ySrc, + int xDest, int yDest, int w, int h); + //----- misc + // Construct a path for a stroke, given the path to be stroked, and + // using the current line parameters. If <flatten> is true, this + // function will first flatten the path and handle the linedash. + SplashPath *makeStrokePath(SplashPath *path, GBool flatten = gTrue); + // Return the associated bitmap. SplashBitmap *getBitmap() { return bitmap; } @@ -172,30 +229,62 @@ public: // Toggle debug mode on or off. void setDebugMode(GBool debugModeA) { debugMode = debugModeA; } +#if 1 //~tmp: turn off anti-aliasing temporarily + GBool getVectorAntialias() { return vectorAntialias; } + void setVectorAntialias(GBool vaa) { vectorAntialias = vaa; } +#endif + private: + void pipeInit(SplashPipe *pipe, int x, int y, + SplashPattern *pattern, SplashColorPtr cSrc, + SplashCoord aInput, GBool usesShape, + GBool nonIsolatedGroup); + void pipeRun(SplashPipe *pipe); + 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(SplashXPath *xPath); - void strokeWide(SplashXPath *xPath); - SplashXPath *makeDashedPath(SplashXPath *xPath); + void strokeNarrow(SplashPath *path); + void strokeWide(SplashPath *path); + SplashPath *flattenPath(SplashPath *path, SplashCoord *matrix, + SplashCoord flatness); + void flattenCurve(SplashCoord x0, SplashCoord y0, + SplashCoord x1, SplashCoord y1, + SplashCoord x2, SplashCoord y2, + SplashCoord x3, SplashCoord y3, + SplashCoord *matrix, SplashCoord flatness2, + SplashPath *fPath); + SplashPath *makeDashedPath(SplashPath *xPath); SplashError fillWithPattern(SplashPath *path, GBool eo, SplashPattern *pattern, SplashCoord alpha); - void drawPixel(int x, int y, SplashColorPtr color, - SplashCoord alpha, GBool noClip); - void drawPixel(int x, int y, SplashPattern *pattern, - SplashCoord alpha, GBool noClip); - void drawSpan(int x0, int x1, int y, SplashPattern *pattern, - SplashCoord alpha, GBool noClip); - void xorSpan(int x0, int x1, int y, SplashPattern *pattern, GBool noClip); + SplashError fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph); void dumpPath(SplashPath *path); void dumpXPath(SplashXPath *path); + static SplashPipeResultColorCtrl pipeResultColorNoAlphaBlend[]; + static SplashPipeResultColorCtrl pipeResultColorAlphaNoBlend[]; + static SplashPipeResultColorCtrl pipeResultColorAlphaBlend[]; + static int pipeNonIsoGroupCorrection[]; + SplashBitmap *bitmap; SplashState *state; - SplashBitmap *softMask; + SplashBitmap *aaBuf; + int aaBufY; + SplashBitmap *alpha0Bitmap; // for non-isolated groups, this is the + // bitmap containing the alpha0 values + int alpha0X, alpha0Y; // offset within alpha0Bitmap + SplashCoord aaGamma[splashAASize * splashAASize + 1]; int modXMin, modYMin, modXMax, modYMax; SplashClipResult opClipRes; + GBool vectorAntialias; GBool debugMode; }; diff --git a/splash/SplashBitmap.cc b/splash/SplashBitmap.cc index 989e318d..9420bbed 100644 --- a/splash/SplashBitmap.cc +++ b/splash/SplashBitmap.cc @@ -20,7 +20,8 @@ //------------------------------------------------------------------------ SplashBitmap::SplashBitmap(int widthA, int heightA, int rowPad, - SplashColorMode modeA, GBool topDown) { + SplashColorMode modeA, GBool alphaA, + GBool topDown) { width = widthA; height = heightA; mode = modeA; @@ -31,24 +32,16 @@ SplashBitmap::SplashBitmap(int widthA, int heightA, int rowPad, case splashModeMono8: rowSize = width; break; - case splashModeAMono8: - rowSize = width * 2; - break; case splashModeRGB8: case splashModeBGR8: rowSize = width * 3; break; - case splashModeRGB8Qt: - case splashModeARGB8: - case splashModeBGRA8: -#if SPLASH_CMYK - case splashModeCMYK8: -#endif + case splashModeRGBX8: rowSize = width * 4; break; #if SPLASH_CMYK - case splashModeACMYK8: - rowSize = width * 5; + case splashModeCMYK8: + rowSize = width * 4; break; #endif } @@ -59,6 +52,11 @@ SplashBitmap::SplashBitmap(int widthA, int heightA, int rowPad, data += (height - 1) * rowSize; rowSize = -rowSize; } + if (alphaA) { + alpha = (Guchar *)gmalloc(width * height); + } else { + alpha = NULL; + } } @@ -68,6 +66,7 @@ SplashBitmap::~SplashBitmap() { } else { gfree(data); } + gfree(alpha); } SplashError SplashBitmap::writePNMFile(char *fileName) { @@ -107,19 +106,6 @@ SplashError SplashBitmap::writePNMFile(char *fileName) { } break; - case splashModeAMono8: - fprintf(f, "P5\n%d %d\n255\n", width, height); - row = data; - for (y = 0; y < height; ++y) { - p = row; - for (x = 0; x < width; ++x) { - fputc(splashAMono8M(p), f); - p += 2; - } - row += rowSize; - } - break; - case splashModeRGB8: fprintf(f, "P6\n%d %d\n255\n", width, height); row = data; @@ -135,22 +121,7 @@ SplashError SplashBitmap::writePNMFile(char *fileName) { } break; - case splashModeBGR8: - fprintf(f, "P6\n%d %d\n255\n", width, height); - row = data; - for (y = 0; y < height; ++y) { - p = row; - for (x = 0; x < width; ++x) { - fputc(splashBGR8R(p), f); - fputc(splashBGR8G(p), f); - fputc(splashBGR8B(p), f); - p += 3; - } - row += rowSize; - } - break; - - case splashModeRGB8Qt: + case splashModeRGBX8: fprintf(f, "P6\n%d %d\n255\n", width, height); row = data; for (y = 0; y < height; ++y) { @@ -165,31 +136,17 @@ SplashError SplashBitmap::writePNMFile(char *fileName) { } break; - case splashModeARGB8: - fprintf(f, "P6\n%d %d\n255\n", width, height); - row = data; - for (y = 0; y < height; ++y) { - p = row; - for (x = 0; x < width; ++x) { - fputc(splashARGB8R(p), f); - fputc(splashARGB8G(p), f); - fputc(splashARGB8B(p), f); - p += 4; - } - row += rowSize; - } - break; - case splashModeBGRA8: + case splashModeBGR8: fprintf(f, "P6\n%d %d\n255\n", width, height); row = data; for (y = 0; y < height; ++y) { p = row; for (x = 0; x < width; ++x) { - fputc(splashBGRA8R(p), f); - fputc(splashBGRA8G(p), f); - fputc(splashBGRA8B(p), f); - p += 4; + fputc(splashBGR8R(p), f); + fputc(splashBGR8G(p), f); + fputc(splashBGR8B(p), f); + p += 3; } row += rowSize; } @@ -197,7 +154,6 @@ SplashError SplashBitmap::writePNMFile(char *fileName) { #if SPLASH_CMYK case splashModeCMYK8: - case splashModeACMYK8: // PNM doesn't support CMYK break; #endif @@ -217,49 +173,43 @@ void SplashBitmap::getPixel(int x, int y, SplashColorPtr pixel) { case splashModeMono1: p = &data[y * rowSize + (x >> 3)]; pixel[0] = (p[0] >> (7 - (x & 7))) & 1; + pixel[0] = (p[0] & (0x80 >> (x & 7))) ? 0xff : 0x00; break; case splashModeMono8: p = &data[y * rowSize + x]; pixel[0] = p[0]; break; - case splashModeAMono8: - p = &data[y * rowSize + 2 * x]; - pixel[0] = p[0]; - pixel[1] = p[1]; - break; case splashModeRGB8: - case splashModeBGR8: p = &data[y * rowSize + 3 * x]; pixel[0] = p[0]; pixel[1] = p[1]; pixel[2] = p[2]; break; - case splashModeRGB8Qt: + case splashModeRGBX8: p = &data[y * rowSize + 4 * x]; + pixel[0] = p[0]; + pixel[1] = p[1]; + pixel[2] = p[2]; + pixel[3] = p[3]; + break; + case splashModeBGR8: + p = &data[y * rowSize + 3 * x]; pixel[0] = p[2]; pixel[1] = p[1]; pixel[2] = p[0]; break; - case splashModeARGB8: - case splashModeBGRA8: #if SPLASH_CMYK case splashModeCMYK8: -#endif p = &data[y * rowSize + 4 * x]; pixel[0] = p[0]; pixel[1] = p[1]; pixel[2] = p[2]; pixel[3] = p[3]; break; -#if SPLASH_CMYK - case splashModeACMYK8: - p = &data[y * rowSize + 5 * x]; - pixel[0] = p[0]; - pixel[1] = p[1]; - pixel[2] = p[2]; - pixel[3] = p[3]; - pixel[4] = p[4]; - break; #endif } } + +Guchar SplashBitmap::getAlpha(int x, int y) { + return alpha[y * width + x]; +} diff --git a/splash/SplashBitmap.h b/splash/SplashBitmap.h index 83f1539f..37022d77 100644 --- a/splash/SplashBitmap.h +++ b/splash/SplashBitmap.h @@ -25,19 +25,23 @@ public: // <rowPad> bytes. If <topDown> is false, the bitmap will be stored // upside-down, i.e., with the last row first in memory. SplashBitmap(int widthA, int heightA, int rowPad, - SplashColorMode modeA, GBool topDown = gTrue); + SplashColorMode modeA, GBool alphaA, + GBool topDown = gTrue); ~SplashBitmap(); int getWidth() { return width; } int getHeight() { return height; } int getRowSize() { return rowSize; } + int getAlphaRowSize() { return width; } SplashColorMode getMode() { return mode; } SplashColorPtr getDataPtr() { return data; } + Guchar *getAlphaPtr() { return alpha; } SplashError writePNMFile(char *fileName); void getPixel(int x, int y, SplashColorPtr pixel); + Guchar getAlpha(int x, int y); private: @@ -45,7 +49,9 @@ private: int rowSize; // size of one row of data, in bytes // - negative for bottom-up bitmaps SplashColorMode mode; // color mode - SplashColorPtr data; // pointer to row zero of the bitmap data + SplashColorPtr data; // pointer to row zero of the color data + Guchar *alpha; // pointer to row zero of the alpha data + // (always top-down) friend class Splash; }; diff --git a/splash/SplashClip.cc b/splash/SplashClip.cc index 724c65c3..87b0d7ef 100644 --- a/splash/SplashClip.cc +++ b/splash/SplashClip.cc @@ -14,10 +14,10 @@ #include <string.h> #include "goo/gmem.h" #include "SplashErrorCodes.h" -#include "SplashMath.h" #include "SplashPath.h" #include "SplashXPath.h" #include "SplashXPathScanner.h" +#include "SplashBitmap.h" #include "SplashClip.h" //------------------------------------------------------------------------ @@ -31,21 +31,27 @@ //------------------------------------------------------------------------ SplashClip::SplashClip(SplashCoord x0, SplashCoord y0, - SplashCoord x1, SplashCoord y1) { + SplashCoord x1, SplashCoord y1, + GBool antialiasA) { + antialias = antialiasA; if (x0 < x1) { - xMin = splashFloor(x0); - xMax = splashFloor(x1); + xMin = x0; + xMax = x1; } else { - xMin = splashFloor(x1); - xMax = splashFloor(x0); + xMin = x1; + xMax = x0; } if (y0 < y1) { - yMin = splashFloor(y0); - yMax = splashFloor(y1); + yMin = y0; + yMax = y1; } else { - yMin = splashFloor(y1); - yMax = splashFloor(y0); + yMin = y1; + yMax = y0; } + xMinI = splashFloor(xMin); + yMinI = splashFloor(yMin); + xMaxI = splashFloor(xMax); + yMaxI = splashFloor(yMax); paths = NULL; flags = NULL; scanners = NULL; @@ -55,10 +61,15 @@ SplashClip::SplashClip(SplashCoord x0, SplashCoord y0, SplashClip::SplashClip(SplashClip *clip) { int i; + antialias = clip->antialias; xMin = clip->xMin; yMin = clip->yMin; xMax = clip->xMax; yMax = clip->yMax; + xMinI = clip->xMinI; + yMinI = clip->yMinI; + xMaxI = clip->xMaxI; + yMaxI = clip->yMaxI; length = clip->length; size = clip->size; paths = (SplashXPath **)gmallocn(size, sizeof(SplashXPath *)); @@ -116,98 +127,117 @@ void SplashClip::resetToRect(SplashCoord x0, SplashCoord y0, length = size = 0; if (x0 < x1) { - xMin = splashFloor(x0); - xMax = splashFloor(x1); + xMin = x0; + xMax = x1; } else { - xMin = splashFloor(x1); - xMax = splashFloor(x0); + xMin = x1; + xMax = x0; } if (y0 < y1) { - yMin = splashFloor(y0); - yMax = splashFloor(y1); + yMin = y0; + yMax = y1; } else { - yMin = splashFloor(y1); - yMax = splashFloor(y0); + yMin = y1; + yMax = y0; } + xMinI = splashFloor(xMin); + yMinI = splashFloor(yMin); + xMaxI = splashFloor(xMax); + yMaxI = splashFloor(yMax); } SplashError SplashClip::clipToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1) { - int x0I, y0I, x1I, y1I; - if (x0 < x1) { - x0I = splashFloor(x0); - x1I = splashFloor(x1); + if (x0 > xMin) { + xMin = x0; + xMinI = splashFloor(xMin); + } + if (x1 < xMax) { + xMax = x1; + xMaxI = splashFloor(xMax); + } } else { - x0I = splashFloor(x1); - x1I = splashFloor(x0); - } - if (x0I > xMin) { - xMin = x0I; - } - if (x1I < xMax) { - xMax = x1I; + if (x1 > xMin) { + xMin = x1; + xMinI = splashFloor(xMin); + } + if (x0 < xMax) { + xMax = x0; + xMaxI = splashFloor(xMax); + } } if (y0 < y1) { - y0I = splashFloor(y0); - y1I = splashFloor(y1); + if (y0 > yMin) { + yMin = y0; + yMinI = splashFloor(yMin); + } + if (y1 < yMax) { + yMax = y1; + yMaxI = splashFloor(yMax); + } } else { - y0I = splashFloor(y1); - y1I = splashFloor(y0); - } - if (y0I > yMin) { - yMin = y0I; - } - if (y1I < yMax) { - yMax = y1I; + if (y1 > yMin) { + yMin = y1; + yMinI = splashFloor(yMin); + } + if (y0 < yMax) { + yMax = y0; + yMaxI = splashFloor(yMax); + } } return splashOk; } -SplashError SplashClip::clipToPath(SplashPath *path, SplashCoord flatness, - GBool eo) { +SplashError SplashClip::clipToPath(SplashPath *path, SplashCoord *matrix, + SplashCoord flatness, GBool eo) { SplashXPath *xPath; - xPath = new SplashXPath(path, flatness, gTrue); + xPath = new SplashXPath(path, matrix, flatness, gTrue); // check for an empty path if (xPath->length == 0) { xMax = xMin - 1; yMax = yMin - 1; + xMaxI = splashFloor(xMax); + yMaxI = splashFloor(yMax); delete xPath; // 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))) { + ((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); delete xPath; } else { grow(1); + if (antialias) { + xPath->aaScale(); + } xPath->sort(); paths[length] = xPath; flags[length] = eo ? splashClipEO : 0; @@ -222,14 +252,22 @@ GBool SplashClip::test(int x, int y) { int i; // check the rectangle - if (x < xMin || x > xMax || y < yMin || y > yMax) { + if (x < xMinI || x > xMaxI || y < yMinI || y > yMaxI) { return gFalse; } // check the paths - for (i = 0; i < length; ++i) { - if (!scanners[i]->test(x, y)) { - return gFalse; + if (antialias) { + for (i = 0; i < length; ++i) { + if (!scanners[i]->test(x * splashAASize, y * splashAASize)) { + return gFalse; + } + } + } else { + for (i = 0; i < length; ++i) { + if (!scanners[i]->test(x, y)) { + return gFalse; + } } } @@ -238,12 +276,18 @@ GBool SplashClip::test(int x, int y) { SplashClipResult SplashClip::testRect(int rectXMin, int rectYMin, int rectXMax, int rectYMax) { - if (rectXMax < xMin || rectXMin > xMax || - rectYMax < yMin || rectYMin > yMax) { + // 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 (rectXMin >= xMin && rectXMax <= xMax && - rectYMin >= yMin && rectYMax <= yMax && + if ((SplashCoord)rectXMin >= xMin && (SplashCoord)(rectXMax + 1) <= xMax && + (SplashCoord)rectYMin >= yMin && (SplashCoord)(rectYMax + 1) <= yMax && length == 0) { return splashClipAllInside; } @@ -253,18 +297,86 @@ SplashClipResult SplashClip::testRect(int rectXMin, int rectYMin, SplashClipResult SplashClip::testSpan(int spanXMin, int spanXMax, int spanY) { int i; - if (spanXMax < xMin || spanXMin > xMax || - spanY < yMin || spanY > yMax) { + // 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 (!(spanXMin >= xMin && spanXMax <= xMax && - spanY >= yMin && spanY <= yMax)) { + if (!((SplashCoord)spanXMin >= xMin && (SplashCoord)(spanXMax + 1) <= xMax && + (SplashCoord)spanY >= yMin && (SplashCoord)(spanY + 1) <= yMax)) { return splashClipPartial; } - for (i = 0; i < length; ++i) { - if (!scanners[i]->testSpan(xMin, xMax, spanY)) { - return splashClipPartial; + if (antialias) { + for (i = 0; i < length; ++i) { + if (!scanners[i]->testSpan(spanXMin * splashAASize, + spanXMax * splashAASize + (splashAASize - 1), + spanY * splashAASize)) { + return splashClipPartial; + } + } + } else { + for (i = 0; i < length; ++i) { + if (!scanners[i]->testSpan(spanXMin, spanXMax, spanY)) { + return splashClipPartial; + } } } 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); + } + } + *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; + } + } + *x1 = splashFloor(xMax); + } + + // check the paths + for (i = 0; i < length; ++i) { + scanners[i]->clipAALine(aaBuf, x0, x1, y); + } +} diff --git a/splash/SplashClip.h b/splash/SplashClip.h index d27d7ae3..2420f9c4 100644 --- a/splash/SplashClip.h +++ b/splash/SplashClip.h @@ -12,10 +12,12 @@ #endif #include "SplashTypes.h" +#include "SplashMath.h" class SplashPath; class SplashXPath; class SplashXPathScanner; +class SplashBitmap; //------------------------------------------------------------------------ @@ -34,7 +36,8 @@ public: // Create a clip, for the given rectangle. SplashClip(SplashCoord x0, SplashCoord y0, - SplashCoord x1, SplashCoord y1); + SplashCoord x1, SplashCoord y1, + GBool antialiasA); // Copy a clip. SplashClip *copy() { return new SplashClip(this); } @@ -50,8 +53,8 @@ public: SplashCoord x1, SplashCoord y1); // Interesect the clip with <path>. - SplashError clipToPath(SplashPath *path, SplashCoord flatness, - GBool eo); + 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); @@ -71,11 +74,16 @@ public: // Similar to testRect, but tests a horizontal span. SplashClipResult testSpan(int spanXMin, int spanXMax, int spanY); - // Get the rectangle part of the clip region. - int getXMin() { return xMin; } - int getXMax() { return xMax; } - int getYMin() { return yMin; } - int getYMax() { return yMax; } + // 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); + + // 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; } // Get the number of arbitrary paths used by the clip region. int getNumPaths() { return length; } @@ -85,7 +93,9 @@ private: SplashClip(SplashClip *clip); void grow(int nPaths); - int xMin, yMin, xMax, yMax; + GBool antialias; + SplashCoord xMin, yMin, xMax, yMax; + int xMinI, yMinI, xMaxI, yMaxI; SplashXPath **paths; Guchar *flags; SplashXPathScanner **scanners; diff --git a/splash/SplashFTFont.cc b/splash/SplashFTFont.cc index 8ffff8eb..3c7da792 100644 --- a/splash/SplashFTFont.cc +++ b/splash/SplashFTFont.cc @@ -12,11 +12,6 @@ #pragma implementation #endif -#define MAKE_VERSION( a,b,c ) (((a) << 16) | ((b) << 8) | (c)) - -#define FREETYPE_VERSION \ - MAKE_VERSION(FREETYPE_MAJOR,FREETYPE_MINOR,FREETYPE_PATCH) - #include <ft2build.h> #include FT_OUTLINE_H #include FT_SIZES_H @@ -31,26 +26,20 @@ //------------------------------------------------------------------------ -#if ( FREETYPE_VERSION >= MAKE_VERSION(2,2,0) ) static int glyphPathMoveTo(const FT_Vector *pt, void *path); static int glyphPathLineTo(const FT_Vector *pt, void *path); -static int glyphPathConicTo(const FT_Vector *ctrl, const FT_Vector *pt, void *path); +static int glyphPathConicTo(const FT_Vector *ctrl, const FT_Vector *pt, + void *path); static int glyphPathCubicTo(const FT_Vector *ctrl1, const FT_Vector *ctrl2, const FT_Vector *pt, void *path); -#else -static int glyphPathMoveTo(FT_Vector *pt, void *path); -static int glyphPathLineTo(FT_Vector *pt, void *path); -static int glyphPathConicTo(FT_Vector *ctrl, FT_Vector *pt, void *path); -static int glyphPathCubicTo(FT_Vector *ctrl1, FT_Vector *ctrl2, - FT_Vector *pt, void *path); -#endif //------------------------------------------------------------------------ // SplashFTFont //------------------------------------------------------------------------ -SplashFTFont::SplashFTFont(SplashFTFontFile *fontFileA, SplashCoord *matA): - SplashFont(fontFileA, matA, fontFileA->engine->aa) +SplashFTFont::SplashFTFont(SplashFTFontFile *fontFileA, SplashCoord *matA, + SplashCoord *textMatA): + SplashFont(fontFileA, matA, textMatA, fontFileA->engine->aa) { FT_Face face; double size, div; @@ -65,6 +54,9 @@ SplashFTFont::SplashFTFont(SplashFTFontFile *fontFileA, SplashCoord *matA): if (FT_Set_Pixel_Sizes(face, 0, (int)size)) { return; } + // if the textMat values are too small, FreeType's fixed point + // arithmetic doesn't work so well + textScale = splashSqrt(textMat[2]*textMat[2] + textMat[3]*textMat[3]) / size; div = face->bbox.xMax > 20000 ? 65536 : 1; @@ -135,11 +127,19 @@ SplashFTFont::SplashFTFont(SplashFTFontFile *fontFileA, SplashCoord *matA): matrix.yx = (FT_Fixed)((mat[1] / size).getRaw()); matrix.xy = (FT_Fixed)((mat[2] / size).getRaw()); matrix.yy = (FT_Fixed)((mat[3] / size).getRaw()); + textMatrix.xx = (FT_Fixed)((textMat[0] / (size * textScale)).getRaw()); + textMatrix.yx = (FT_Fixed)((textMat[1] / (size * textScale)).getRaw()); + textMatrix.xy = (FT_Fixed)((textMat[2] / (size * textScale)).getRaw()); + textMatrix.yy = (FT_Fixed)((textMat[3] / (size * textScale)).getRaw()); #else matrix.xx = (FT_Fixed)((mat[0] / size) * 65536); matrix.yx = (FT_Fixed)((mat[1] / size) * 65536); matrix.xy = (FT_Fixed)((mat[2] / size) * 65536); matrix.yy = (FT_Fixed)((mat[3] / size) * 65536); + textMatrix.xx = (FT_Fixed)((textMat[0] / (size * textScale)) * 65536); + textMatrix.yx = (FT_Fixed)((textMat[1] / (size * textScale)) * 65536); + textMatrix.xy = (FT_Fixed)((textMat[2] / (size * textScale)) * 65536); + textMatrix.yy = (FT_Fixed)((textMat[3] / (size * textScale)) * 65536); #endif } @@ -174,6 +174,10 @@ GBool SplashFTFont::makeGlyph(int c, int xFrac, int yFrac, } else { gid = (FT_UInt)c; } + if (ff->trueType && gid == 0) { + // skip the TrueType notdef glyph + return gFalse; + } // if we have the FT2 bytecode interpreter, autohinting won't be used #ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER @@ -220,15 +224,23 @@ GBool SplashFTFont::makeGlyph(int c, int xFrac, int yFrac, struct SplashFTFontPath { SplashPath *path; + SplashCoord textScale; GBool needClose; }; SplashPath *SplashFTFont::getGlyphPath(int c) { static FT_Outline_Funcs outlineFuncs = { +#if FREETYPE_MINOR <= 1 + (int (*)(FT_Vector *, void *))&glyphPathMoveTo, + (int (*)(FT_Vector *, void *))&glyphPathLineTo, + (int (*)(FT_Vector *, FT_Vector *, void *))&glyphPathConicTo, + (int (*)(FT_Vector *, FT_Vector *, FT_Vector *, void *))&glyphPathCubicTo, +#else &glyphPathMoveTo, &glyphPathLineTo, &glyphPathConicTo, &glyphPathCubicTo, +#endif 0, 0 }; SplashFTFontFile *ff; @@ -239,13 +251,17 @@ SplashPath *SplashFTFont::getGlyphPath(int c) { ff = (SplashFTFontFile *)fontFile; ff->face->size = sizeObj; - FT_Set_Transform(ff->face, &matrix, NULL); + FT_Set_Transform(ff->face, &textMatrix, NULL); slot = ff->face->glyph; if (ff->codeToGID && c < ff->codeToGIDLen) { gid = ff->codeToGID[c]; } else { gid = (FT_UInt)c; } + if (ff->trueType && gid == 0) { + // skip the TrueType notdef glyph + return NULL; + } if (FT_Load_Glyph(ff->face, gid, FT_LOAD_NO_BITMAP)) { return NULL; } @@ -253,6 +269,7 @@ SplashPath *SplashFTFont::getGlyphPath(int c) { return NULL; } path.path = new SplashPath(); + path.textScale = textScale; path.needClose = gFalse; FT_Outline_Decompose(&((FT_OutlineGlyph)glyph)->outline, &outlineFuncs, &path); @@ -263,51 +280,39 @@ SplashPath *SplashFTFont::getGlyphPath(int c) { return path.path; } -#if ( FREETYPE_VERSION >= MAKE_VERSION(2,2,0) ) -static int glyphPathMoveTo(const FT_Vector *pt, void *path) -#else -static int glyphPathMoveTo(FT_Vector *pt, void *path) -#endif -{ +static int glyphPathMoveTo(const FT_Vector *pt, void *path) { SplashFTFontPath *p = (SplashFTFontPath *)path; if (p->needClose) { p->path->close(); p->needClose = gFalse; } - p->path->moveTo(pt->x / 64.0, -pt->y / 64.0); + p->path->moveTo((SplashCoord)pt->x * p->textScale / 64.0, + (SplashCoord)pt->y * p->textScale / 64.0); return 0; } -#if ( FREETYPE_VERSION >= MAKE_VERSION(2,2,0) ) -static int glyphPathLineTo(const FT_Vector *pt, void *path) -#else -static int glyphPathLineTo(FT_Vector *pt, void *path) -#endif -{ +static int glyphPathLineTo(const FT_Vector *pt, void *path) { SplashFTFontPath *p = (SplashFTFontPath *)path; - p->path->lineTo(pt->x / 64.0, -pt->y / 64.0); + p->path->lineTo((SplashCoord)pt->x * p->textScale / 64.0, + (SplashCoord)pt->y * p->textScale / 64.0); p->needClose = gTrue; return 0; } -#if ( FREETYPE_VERSION >= MAKE_VERSION(2,2,0) ) -static int glyphPathConicTo(const FT_Vector *ctrl, const FT_Vector *pt, void *path) -#else -static int glyphPathConicTo(FT_Vector *ctrl, FT_Vector *pt, void *path) -#endif -{ +static int glyphPathConicTo(const FT_Vector *ctrl, const FT_Vector *pt, + void *path) { SplashFTFontPath *p = (SplashFTFontPath *)path; SplashCoord x0, y0, x1, y1, x2, y2, x3, y3, xc, yc; if (!p->path->getCurPt(&x0, &y0)) { return 0; } - xc = ctrl->x / 64.0; - yc = -ctrl->y / 64.0; - x3 = pt->x / 64.0; - y3 = -pt->y / 64.0; + xc = (SplashCoord)ctrl->x * p->textScale / 64.0; + yc = (SplashCoord)ctrl->y * p->textScale / 64.0; + x3 = (SplashCoord)pt->x * p->textScale / 64.0; + y3 = (SplashCoord)pt->y * p->textScale / 64.0; // A second-order Bezier curve is defined by two endpoints, p0 and // p3, and one control point, pc: @@ -335,17 +340,16 @@ static int glyphPathConicTo(FT_Vector *ctrl, FT_Vector *pt, void *path) return 0; } -#if ( FREETYPE_VERSION >= MAKE_VERSION(2,2,0) ) -static int glyphPathCubicTo(const FT_Vector *ctrl1, const FT_Vector *ctrl2, const FT_Vector *pt, void *path) -#else -static int glyphPathCubicTo(FT_Vector *ctrl1, FT_Vector *ctrl2, FT_Vector *pt, void *path) -#endif -{ +static int glyphPathCubicTo(const FT_Vector *ctrl1, const FT_Vector *ctrl2, + const FT_Vector *pt, void *path) { SplashFTFontPath *p = (SplashFTFontPath *)path; - p->path->curveTo(ctrl1->x / 64.0, -ctrl1->y / 64.0, - ctrl2->x / 64.0, -ctrl2->y / 64.0, - pt->x / 64.0, -pt->y / 64.0); + p->path->curveTo((SplashCoord)ctrl1->x * p->textScale / 64.0, + (SplashCoord)ctrl1->y * p->textScale / 64.0, + (SplashCoord)ctrl2->x * p->textScale / 64.0, + (SplashCoord)ctrl2->y * p->textScale / 64.0, + (SplashCoord)pt->x * p->textScale / 64.0, + (SplashCoord)pt->y * p->textScale / 64.0); p->needClose = gTrue; return 0; } diff --git a/splash/SplashFTFont.h b/splash/SplashFTFont.h index ae5b5561..f351b2ff 100644 --- a/splash/SplashFTFont.h +++ b/splash/SplashFTFont.h @@ -26,7 +26,8 @@ class SplashFTFontFile; class SplashFTFont: public SplashFont { public: - SplashFTFont(SplashFTFontFile *fontFileA, SplashCoord *matA); + SplashFTFont(SplashFTFontFile *fontFileA, SplashCoord *matA, + SplashCoord *textMatA); virtual ~SplashFTFont(); @@ -46,6 +47,8 @@ private: FT_Size sizeObj; FT_Matrix matrix; + FT_Matrix textMatrix; + SplashCoord textScale; }; #endif // HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H diff --git a/splash/SplashFTFontEngine.cc b/splash/SplashFTFontEngine.cc index b4121bf2..066d48db 100644 --- a/splash/SplashFTFontEngine.cc +++ b/splash/SplashFTFontEngine.cc @@ -75,6 +75,12 @@ SplashFontFile *SplashFTFontEngine::loadType1CFont(SplashFontFileID *idA, return SplashFTFontFile::loadType1Font(this, idA, src, enc); } +SplashFontFile *SplashFTFontEngine::loadOpenTypeT1CFont(SplashFontFileID *idA, + SplashFontSrc *src, + char **enc) { + return SplashFTFontFile::loadType1Font(this, idA, src, enc); +} + SplashFontFile *SplashFTFontEngine::loadCIDFont(SplashFontFileID *idA, SplashFontSrc *src) { FoFiType1C *ff; @@ -90,15 +96,15 @@ SplashFontFile *SplashFTFontEngine::loadCIDFont(SplashFontFileID *idA, if (src->isFile) { ff = FoFiType1C::load(src->fileName->getCString()); } else { - ff = new FoFiType1C(src->buf, src->bufLen, gFalse); + ff = FoFiType1C::make(src->buf, src->bufLen); } if (ff) { - cidToGIDMap = ff->getCIDToGIDMap(&nCIDs); - delete ff; - } else { - cidToGIDMap = NULL; - nCIDs = 0; - } + cidToGIDMap = ff->getCIDToGIDMap(&nCIDs); + delete ff; + } else { + cidToGIDMap = NULL; + nCIDs = 0; + } } ret = SplashFTFontFile::loadCIDFont(this, idA, src, cidToGIDMap, nCIDs); if (!ret) { @@ -107,6 +113,38 @@ SplashFontFile *SplashFTFontEngine::loadCIDFont(SplashFontFileID *idA, return ret; } +SplashFontFile *SplashFTFontEngine::loadOpenTypeCFFFont(SplashFontFileID *idA, + SplashFontSrc *src) { + FoFiTrueType *ff; + GBool isCID; + Gushort *cidToGIDMap; + int nCIDs; + SplashFontFile *ret; + + cidToGIDMap = NULL; + nCIDs = 0; + isCID = gFalse; + if (!useCIDs) { + if (src->isFile) { + ff = FoFiTrueType::load(src->fileName->getCString()); + } else { + ff = FoFiTrueType::make(src->buf, src->bufLen); + } + if (ff) { + if (ff->isOpenTypeCFF()) { + cidToGIDMap = ff->getCIDToGIDMap(&nCIDs); + } + delete ff; + } + } + ret = SplashFTFontFile::loadCIDFont(this, idA, src, + cidToGIDMap, nCIDs); + if (!ret) { + gfree(cidToGIDMap); + } + return ret; +} + SplashFontFile *SplashFTFontEngine::loadTrueTypeFont(SplashFontFileID *idA, SplashFontSrc *src, Gushort *codeToGID, @@ -118,7 +156,7 @@ SplashFontFile *SplashFTFontEngine::loadTrueTypeFont(SplashFontFileID *idA, FILE *tmpFile; SplashFontFile *ret; - if (!(ff = FoFiTrueType::load(fileName, faceIndex))) { + if (!(ff = FoFiTrueType::load(fileName))) { return NULL; } tmpFileName = NULL; diff --git a/splash/SplashFTFontEngine.h b/splash/SplashFTFontEngine.h index e6600f58..a0c0b7c6 100644 --- a/splash/SplashFTFontEngine.h +++ b/splash/SplashFTFontEngine.h @@ -33,12 +33,13 @@ public: ~SplashFTFontEngine(); // Load fonts. - SplashFontFile *loadType1Font(SplashFontFileID *idA, SplashFontSrc *src, char **enc); - SplashFontFile *loadType1CFont(SplashFontFileID *idA, SplashFontSrc *src, char **enc); + SplashFontFile *loadType1Font(SplashFontFileID *idA, SplashFontSrc *src, char **enc); + SplashFontFile *loadType1CFont(SplashFontFileID *idA, SplashFontSrc *src, char **enc); + SplashFontFile *loadOpenTypeT1CFont(SplashFontFileID *idA, SplashFontSrc *src, char **enc); SplashFontFile *loadCIDFont(SplashFontFileID *idA, SplashFontSrc *src); + SplashFontFile *loadOpenTypeCFFFont(SplashFontFileID *idA, SplashFontSrc *src); SplashFontFile *loadTrueTypeFont(SplashFontFileID *idA, SplashFontSrc *src, - Gushort *codeToGID, int codeToGIDLen, - int faceIndex=0); + Gushort *codeToGID, int codeToGIDLen, int faceIndex = 0); private: diff --git a/splash/SplashFTFontFile.cc b/splash/SplashFTFontFile.cc index 76927660..c6d06593 100644 --- a/splash/SplashFTFontFile.cc +++ b/splash/SplashFTFontFile.cc @@ -47,7 +47,7 @@ SplashFontFile *SplashFTFontFile::loadType1Font(SplashFTFontEngine *engineA, } return new SplashFTFontFile(engineA, idA, src, - faceA, codeToGIDA, 256); + faceA, codeToGIDA, 256, gFalse); } SplashFontFile *SplashFTFontFile::loadCIDFont(SplashFTFontEngine *engineA, @@ -66,7 +66,7 @@ SplashFontFile *SplashFTFontFile::loadCIDFont(SplashFTFontEngine *engineA, } return new SplashFTFontFile(engineA, idA, src, - faceA, codeToGIDA, codeToGIDLenA); + faceA, codeToGIDA, codeToGIDLenA, gFalse); } SplashFontFile *SplashFTFontFile::loadTrueTypeFont(SplashFTFontEngine *engineA, @@ -86,20 +86,22 @@ SplashFontFile *SplashFTFontFile::loadTrueTypeFont(SplashFTFontEngine *engineA, } return new SplashFTFontFile(engineA, idA, src, - faceA, codeToGIDA, codeToGIDLenA); + faceA, codeToGIDA, codeToGIDLenA, gTrue); } SplashFTFontFile::SplashFTFontFile(SplashFTFontEngine *engineA, SplashFontFileID *idA, - SplashFontSrc *srcA, + SplashFontSrc *src, FT_Face faceA, - Gushort *codeToGIDA, int codeToGIDLenA): - SplashFontFile(idA, srcA) + Gushort *codeToGIDA, int codeToGIDLenA, + GBool trueTypeA): + SplashFontFile(idA, src) { engine = engineA; face = faceA; codeToGID = codeToGIDA; codeToGIDLen = codeToGIDLenA; + trueType = trueTypeA; } SplashFTFontFile::~SplashFTFontFile() { @@ -111,10 +113,11 @@ SplashFTFontFile::~SplashFTFontFile() { } } -SplashFont *SplashFTFontFile::makeFont(SplashCoord *mat) { +SplashFont *SplashFTFontFile::makeFont(SplashCoord *mat, + SplashCoord *textMat) { SplashFont *font; - font = new SplashFTFont(this, mat); + font = new SplashFTFont(this, mat, textMat); font->initCache(); return font; } diff --git a/splash/SplashFTFontFile.h b/splash/SplashFTFontFile.h index b1d0f41b..19ff2dee 100644 --- a/splash/SplashFTFontFile.h +++ b/splash/SplashFTFontFile.h @@ -45,20 +45,23 @@ public: // Create a new SplashFTFont, i.e., a scaled instance of this font // file. - virtual SplashFont *makeFont(SplashCoord *mat); + virtual SplashFont *makeFont(SplashCoord *mat, + SplashCoord *textMat); private: SplashFTFontFile(SplashFTFontEngine *engineA, SplashFontFileID *idA, - SplashFontSrc *srcA, + SplashFontSrc *src, FT_Face faceA, - Gushort *codeToGIDA, int codeToGIDLenA); + Gushort *codeToGIDA, int codeToGIDLenA, + GBool trueTypeA); SplashFTFontEngine *engine; FT_Face face; Gushort *codeToGID; int codeToGIDLen; + GBool trueType; friend class SplashFTFont; }; diff --git a/splash/SplashFont.cc b/splash/SplashFont.cc index 570a9c16..bea6fc94 100644 --- a/splash/SplashFont.cc +++ b/splash/SplashFont.cc @@ -31,13 +31,17 @@ struct SplashFontCacheTag { //------------------------------------------------------------------------ SplashFont::SplashFont(SplashFontFile *fontFileA, SplashCoord *matA, - GBool aaA) { + SplashCoord *textMatA, GBool aaA) { fontFile = fontFileA; fontFile->incRefCnt(); mat[0] = matA[0]; mat[1] = matA[1]; mat[2] = matA[2]; mat[3] = matA[3]; + textMat[0] = textMatA[0]; + textMat[1] = textMatA[1]; + textMat[2] = textMatA[2]; + textMat[3] = textMatA[3]; aa = aaA; cache = NULL; diff --git a/splash/SplashFont.h b/splash/SplashFont.h index 59bc14e4..d3b03538 100644 --- a/splash/SplashFont.h +++ b/splash/SplashFont.h @@ -35,7 +35,8 @@ class SplashPath; class SplashFont { public: - SplashFont(SplashFontFile *fontFileA, SplashCoord *matA, GBool aaA); + SplashFont(SplashFontFile *fontFileA, SplashCoord *matA, + SplashCoord *textMatA, GBool aaA); // This must be called after the constructor, so that the subclass // constructor has a chance to compute the bbox. @@ -46,10 +47,13 @@ public: SplashFontFile *getFontFile() { return fontFile; } // Return true if <this> matches the specified font file and matrix. - GBool matches(SplashFontFile *fontFileA, SplashCoord *matA) { + GBool matches(SplashFontFile *fontFileA, SplashCoord *matA, + SplashCoord *textMatA) { return fontFileA == fontFile && matA[0] == mat[0] && matA[1] == mat[1] && - matA[2] == mat[2] && matA[3] == mat[3]; + matA[2] == mat[2] && matA[3] == mat[3] && + textMatA[0] == textMat[0] && textMatA[1] == textMat[1] && + textMatA[2] == textMat[2] && textMatA[3] == textMat[3]; } // Get a glyph - this does a cache lookup first, and if not found, @@ -81,6 +85,9 @@ protected: SplashFontFile *fontFile; SplashCoord mat[4]; // font transform matrix + // (text space -> device space) + SplashCoord textMat[4]; // text transform matrix + // (text space -> user space) GBool aa; // anti-aliasing int xMin, yMin, xMax, yMax; // glyph bounding box Guchar *cache; // glyph bitmap cache diff --git a/splash/SplashFontEngine.cc b/splash/SplashFontEngine.cc index 01b1b6e9..9301b664 100644 --- a/splash/SplashFontEngine.cc +++ b/splash/SplashFontEngine.cc @@ -21,6 +21,7 @@ #endif #include "goo/gmem.h" #include "goo/GooString.h" +#include "SplashMath.h" #include "SplashT1FontEngine.h" #include "SplashFTFontEngine.h" #include "SplashFontFile.h" @@ -34,6 +35,12 @@ extern "C" int unlink(char *filename); #endif #endif +#ifdef VMS +#if (__VMS_VER < 70000000) +extern "C" int unlink(char *filename); +#endif +#endif + //------------------------------------------------------------------------ // SplashFontEngine //------------------------------------------------------------------------ @@ -141,7 +148,7 @@ SplashFontFile *SplashFontEngine::loadType1CFont(SplashFontFileID *idA, fontFile = NULL; #if HAVE_T1LIB_H if (!fontFile && t1Engine) { - fontFile = t1Engine->loadType1CFont(idA, src, enc); + fontFile = t1Engine->loadType1CFont(idA, sec, enc); } #endif #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H @@ -162,6 +169,28 @@ SplashFontFile *SplashFontEngine::loadType1CFont(SplashFontFileID *idA, return fontFile; } +SplashFontFile *SplashFontEngine::loadOpenTypeT1CFont(SplashFontFileID *idA, + SplashFontSrc *src, + char **enc) { + SplashFontFile *fontFile; + + fontFile = NULL; +#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H + if (!fontFile && ftEngine) { + fontFile = ftEngine->loadOpenTypeT1CFont(idA, src, enc); + } +#endif + + // 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 + // loadXYZFont failed, the file will always be deleted) + if (src->isFile) + src->unref(); + + return fontFile; +} + SplashFontFile *SplashFontEngine::loadCIDFont(SplashFontFileID *idA, SplashFontSrc *src) { SplashFontFile *fontFile; @@ -185,6 +214,27 @@ SplashFontFile *SplashFontEngine::loadCIDFont(SplashFontFileID *idA, return fontFile; } +SplashFontFile *SplashFontEngine::loadOpenTypeCFFFont(SplashFontFileID *idA, + SplashFontSrc *src) { + SplashFontFile *fontFile; + + fontFile = NULL; +#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H + if (!fontFile && ftEngine) { + fontFile = ftEngine->loadOpenTypeCFFFont(idA, src); + } +#endif + + // 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 + // loadXYZFont failed, the file will always be deleted) + if (src->isFile) + src->unref(); + + return fontFile; +} + SplashFontFile *SplashFontEngine::loadTrueTypeFont(SplashFontFileID *idA, SplashFontSrc *src, Gushort *codeToGID, @@ -217,17 +267,29 @@ SplashFontFile *SplashFontEngine::loadTrueTypeFont(SplashFontFileID *idA, } SplashFont *SplashFontEngine::getFont(SplashFontFile *fontFile, - SplashCoord *mat) { + SplashCoord *textMat, + SplashCoord *ctm) { + SplashCoord mat[4]; SplashFont *font; int i, j; + mat[0] = textMat[0] * ctm[0] + textMat[1] * ctm[2]; + mat[1] = -(textMat[0] * ctm[1] + textMat[1] * ctm[3]); + mat[2] = textMat[2] * ctm[0] + textMat[3] * ctm[2]; + mat[3] = -(textMat[2] * ctm[1] + textMat[3] * ctm[3]); + if (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.01) { + // avoid a singular (or close-to-singular) matrix + mat[0] = 0.01; mat[1] = 0; + mat[2] = 0; mat[3] = 0.01; + } + font = fontCache[0]; - if (font && font->matches(fontFile, mat)) { + if (font && font->matches(fontFile, mat, textMat)) { return font; } for (i = 1; i < splashFontCacheSize; ++i) { font = fontCache[i]; - if (font && font->matches(fontFile, mat)) { + if (font && font->matches(fontFile, mat, textMat)) { for (j = i; j > 0; --j) { fontCache[j] = fontCache[j-1]; } @@ -235,7 +297,7 @@ SplashFont *SplashFontEngine::getFont(SplashFontFile *fontFile, return font; } } - font = fontFile->makeFont(mat); + font = fontFile->makeFont(mat, textMat); if (fontCache[splashFontCacheSize - 1]) { delete fontCache[splashFontCacheSize - 1]; } diff --git a/splash/SplashFontEngine.h b/splash/SplashFontEngine.h index 23c49acd..b87d667b 100644 --- a/splash/SplashFontEngine.h +++ b/splash/SplashFontEngine.h @@ -16,6 +16,7 @@ class SplashT1FontEngine; class SplashFTFontEngine; class SplashDTFontEngine; +class SplashDT4FontEngine; class SplashFontFile; class SplashFontFileID; class SplashFont; @@ -51,20 +52,22 @@ public: // Load fonts - these create new SplashFontFile objects. SplashFontFile *loadType1Font(SplashFontFileID *idA, SplashFontSrc *src, char **enc); SplashFontFile *loadType1CFont(SplashFontFileID *idA, SplashFontSrc *src, char **enc); + SplashFontFile *loadOpenTypeT1CFont(SplashFontFileID *idA, SplashFontSrc *src, char **enc); SplashFontFile *loadCIDFont(SplashFontFileID *idA, SplashFontSrc *src); + SplashFontFile *loadOpenTypeCFFFont(SplashFontFileID *idA, SplashFontSrc *src); SplashFontFile *loadTrueTypeFont(SplashFontFileID *idA, SplashFontSrc *src, - Gushort *codeToGID, int codeToGIDLen, - int faceIndex=0); + Gushort *codeToGID, int codeToGIDLen, int faceIndex = 0); // Get a font - this does a cache lookup first, and if not found, // creates a new SplashFont object and adds it to the cache. The - // matrix: + // matrix, mat = textMat * ctm: // [ mat[0] mat[1] ] // [ mat[2] mat[3] ] // specifies the font transform in PostScript style: // [x' y'] = [x y] * mat // Note that the Splash y axis points downward. - SplashFont *getFont(SplashFontFile *fontFile, SplashCoord *mat); + SplashFont *getFont(SplashFontFile *fontFile, + SplashCoord *textMat, SplashCoord *ctm); private: diff --git a/splash/SplashFontFile.cc b/splash/SplashFontFile.cc index b92dd1b2..9dabd1fd 100644 --- a/splash/SplashFontFile.cc +++ b/splash/SplashFontFile.cc @@ -104,4 +104,3 @@ void SplashFontSrc::setBuf(char *bufA, int bufLenA, GBool del) bufLen = bufLenA; deleteSrc = del; } - diff --git a/splash/SplashFontFile.h b/splash/SplashFontFile.h index e434b9b2..ad2bc89b 100644 --- a/splash/SplashFontFile.h +++ b/splash/SplashFontFile.h @@ -49,7 +49,7 @@ public: // Create a new SplashFont, i.e., a scaled instance of this font // file. - virtual SplashFont *makeFont(SplashCoord *mat) = 0; + virtual SplashFont *makeFont(SplashCoord *mat, SplashCoord *textMat) = 0; // Get the font file ID. SplashFontFileID *getID() { return id; } diff --git a/splash/SplashMath.h b/splash/SplashMath.h index 1d4e2264..17d05cda 100644 --- a/splash/SplashMath.h +++ b/splash/SplashMath.h @@ -7,7 +7,7 @@ #ifndef SPLASHMATH_H #define SPLASHMATH_H -#if USE_FIXEDPONT +#if USE_FIXEDPOINT #include "FixedPoint.h" #else #include <math.h> @@ -68,7 +68,18 @@ static inline SplashCoord splashDist(SplashCoord x0, SplashCoord y0, dx = x1 - x0; dy = y1 - y0; #if USE_FIXEDPOINT - return FixedPoint::sqrt(dx * dx + dy * dy); + // this handles the situation where dx*dx or dy*dy is too large to + // fit in the 16.16 fixed point format + SplashCoord dxa, dya; + dxa = splashAbs(dx); + dya = splashAbs(dy); + if (dxa == 0 && dya == 0) { + return 0; + } else if (dxa > dya) { + return dxa * FixedPoint::sqrt(dya / dxa + 1); + } else { + return dya * FixedPoint::sqrt(dxa / dya + 1); + } #else return sqrt(dx * dx + dy * dy); #endif diff --git a/splash/SplashPath.cc b/splash/SplashPath.cc index 1f0f80dd..261f7788 100644 --- a/splash/SplashPath.cc +++ b/splash/SplashPath.cc @@ -35,6 +35,8 @@ SplashPath::SplashPath() { flags = NULL; length = size = 0; curSubpath = 0; + hints = NULL; + hintsLength = hintsSize = 0; } SplashPath::SplashPath(SplashPath *path) { @@ -45,11 +47,19 @@ SplashPath::SplashPath(SplashPath *path) { memcpy(pts, path->pts, length * sizeof(SplashPathPoint)); memcpy(flags, path->flags, length * sizeof(Guchar)); curSubpath = path->curSubpath; + if (path->hints) { + hintsLength = hintsSize = path->hintsLength; + hints = (SplashPathHint *)gmallocn(hintsSize, sizeof(SplashPathHint)); + memcpy(hints, path->hints, hintsLength * sizeof(SplashPathHint)); + } else { + hints = NULL; + } } SplashPath::~SplashPath() { gfree(pts); gfree(flags); + gfree(hints); } // Add space for <nPts> more points. @@ -126,24 +136,6 @@ SplashError SplashPath::curveTo(SplashCoord x1, SplashCoord y1, return splashOk; } -SplashError SplashPath::arcCWTo(SplashCoord x1, SplashCoord y1, - SplashCoord xc, SplashCoord yc) { - if (noCurrentPoint()) { - return splashErrNoCurPt; - } - flags[length-1] &= ~splashPathLast; - grow(2); - pts[length].x = xc; - pts[length].y = yc; - flags[length] = splashPathArcCW; - ++length; - pts[length].x = x1; - pts[length].y = y1; - flags[length] = splashPathLast; - ++length; - return splashOk; -} - SplashError SplashPath::close() { if (noCurrentPoint()) { return splashErrNoCurPt; @@ -159,6 +151,20 @@ SplashError SplashPath::close() { return splashOk; } +void SplashPath::addStrokeAdjustHint(int ctrl0, int ctrl1, + int firstPt, int lastPt) { + if (hintsLength == hintsSize) { + hintsSize = hintsLength ? 2 * hintsLength : 8; + hints = (SplashPathHint *)greallocn(hints, hintsSize, + sizeof(SplashPathHint)); + } + hints[hintsLength].ctrl0 = ctrl0; + hints[hintsLength].ctrl1 = ctrl1; + hints[hintsLength].firstPt = firstPt; + hints[hintsLength].lastPt = lastPt; + ++hintsLength; +} + void SplashPath::offset(SplashCoord dx, SplashCoord dy) { int i; diff --git a/splash/SplashPath.h b/splash/SplashPath.h index 1b7362a5..ea58af0c 100644 --- a/splash/SplashPath.h +++ b/splash/SplashPath.h @@ -26,20 +26,26 @@ struct SplashPathPoint { //------------------------------------------------------------------------ // first point on each subpath sets this flag -#define splashPathFirst 0x01 +#define splashPathFirst 0x01 // last point on each subpath sets this flag -#define splashPathLast 0x02 +#define splashPathLast 0x02 // if the subpath is closed, its first and last points must be // identical, and must set this flag -#define splashPathClosed 0x04 +#define splashPathClosed 0x04 // curve control points set this flag -#define splashPathCurve 0x08 +#define splashPathCurve 0x08 -// clockwise arc center points set this flag -#define splashPathArcCW 0x10 +//------------------------------------------------------------------------ +// SplashPathHint +//------------------------------------------------------------------------ + +struct SplashPathHint { + int ctrl0, ctrl1; + int firstPt, lastPt; +}; //------------------------------------------------------------------------ // SplashPath @@ -71,14 +77,14 @@ public: SplashCoord x2, SplashCoord y2, SplashCoord x3, SplashCoord y3); - // Add a clockwise circular arc with center (xc, yc) and endpoint - // (x1, y1). - SplashError arcCWTo(SplashCoord x1, SplashCoord y1, - SplashCoord xc, SplashCoord yc); - // Close the last subpath, adding a line segment if necessary. SplashError close(); + // Add a stroke adjustment hint. The controlling segments are + // <ctrl0> and <ctrl1> (where segments are identified by their first + // point), and the points to be adjusted are <firstPt> .. <lastPt>. + void addStrokeAdjustHint(int ctrl0, int ctrl1, int firstPt, int lastPt); + // Add (<dx>, <dy>) to every point on this path. void offset(SplashCoord dx, SplashCoord dy); @@ -103,6 +109,9 @@ private: int length, size; // length/size of the pts and flags arrays int curSubpath; // index of first point in last subpath + SplashPathHint *hints; // list of hints + int hintsLength, hintsSize; + friend class SplashXPath; friend class Splash; // this is a temporary hack, until we read FreeType paths directly diff --git a/splash/SplashPattern.cc b/splash/SplashPattern.cc index 38f39943..ce490785 100644 --- a/splash/SplashPattern.cc +++ b/splash/SplashPattern.cc @@ -38,31 +38,3 @@ SplashSolidColor::~SplashSolidColor() { void SplashSolidColor::getColor(int x, int y, SplashColorPtr c) { splashColorCopy(c, color); } - -//------------------------------------------------------------------------ -// SplashHalftone -//------------------------------------------------------------------------ - -SplashHalftone::SplashHalftone(SplashColorPtr color0A, SplashColorPtr color1A, - SplashScreen *screenA, SplashCoord valueA) { - splashColorCopy(color0, color0A); - splashColorCopy(color1, color1A); - screen = screenA; - value = valueA; -} - -SplashPattern *SplashHalftone::copy() { - return new SplashHalftone(color0, color1, screen->copy(), value); -} - -SplashHalftone::~SplashHalftone() { - delete screen; -} - -void SplashHalftone::getColor(int x, int y, SplashColorPtr c) { - splashColorCopy(c, screen->test(x, y, value) ? color1 : color0); -} - -GBool SplashHalftone::isStatic() { - return screen->isStatic(value); -} diff --git a/splash/SplashPattern.h b/splash/SplashPattern.h index 2e50c43b..4e3d9abb 100644 --- a/splash/SplashPattern.h +++ b/splash/SplashPattern.h @@ -60,29 +60,4 @@ private: SplashColor color; }; -//------------------------------------------------------------------------ -// SplashHalftone -//------------------------------------------------------------------------ - -class SplashHalftone: public SplashPattern { -public: - - SplashHalftone(SplashColorPtr color0A, SplashColorPtr color1A, - SplashScreen *screenA, SplashCoord valueA); - - virtual SplashPattern *copy(); - - virtual ~SplashHalftone(); - - virtual void getColor(int x, int y, SplashColorPtr c); - - virtual GBool isStatic(); - -private: - - SplashColor color0, color1; - SplashScreen *screen; - SplashCoord value; -}; - #endif diff --git a/splash/SplashScreen.cc b/splash/SplashScreen.cc index 221296fd..4fa84883 100644 --- a/splash/SplashScreen.cc +++ b/splash/SplashScreen.cc @@ -10,34 +10,139 @@ #pragma implementation #endif +#include <stdlib.h> #include <string.h> #include "goo/gmem.h" #include "SplashMath.h" #include "SplashScreen.h" +static SplashScreenParams defaultParams = { + splashScreenDispersed, // type + 2, // size + 2, // dotRadius + 1.0, // gamma + 0.0, // blackThreshold + 1.0 // whiteThreshold +}; + +//------------------------------------------------------------------------ + +struct SplashScreenPoint { + int x, y; + int dist; +}; + +static int cmpDistances(const void *p0, const void *p1) { + return ((SplashScreenPoint *)p0)->dist - ((SplashScreenPoint *)p1)->dist; +} + //------------------------------------------------------------------------ // SplashScreen //------------------------------------------------------------------------ -// This generates a 45 degree screen using a circular dot spot -// function. DPI = resolution / ((size / 2) * sqrt(2)). -// Gamma correction (gamma = 1 / 1.33) is also computed here. -SplashScreen::SplashScreen(int sizeA) { +// If <clustered> is true, this generates a 45 degree screen using a +// circular dot spot function. DPI = resolution / ((size / 2) * +// sqrt(2)). If <clustered> is false, this generates an optimal +// threshold matrix using recursive tesselation. Gamma correction +// (gamma = 1 / 1.33) is also computed here. +SplashScreen::SplashScreen(SplashScreenParams *params) { + Guchar u, black, white; + int i; + + if (!params) { + params = &defaultParams; + } + + switch (params->type) { + + case splashScreenDispersed: + // size must be a power of 2 + for (size = 1; size < params->size; size <<= 1) ; + mat = (Guchar *)gmallocn(size * size, sizeof(Guchar)); + buildDispersedMatrix(size/2, size/2, 1, size/2, 1); + break; + + case splashScreenClustered: + // size must be even + size = (params->size >> 1) << 1; + if (size < 2) { + size = 2; + } + mat = (Guchar *)gmallocn(size * size, sizeof(Guchar)); + buildClusteredMatrix(); + break; + + case splashScreenStochasticClustered: + // size must be at least 2*r + if (params->size < 2 * params->dotRadius) { + size = 2 * params->dotRadius; + } else { + size = params->size; + } + mat = (Guchar *)gmallocn(size * size, sizeof(Guchar)); + buildSCDMatrix(params->dotRadius); + break; + } + + // do gamma correction and compute minVal/maxVal + minVal = 255; + maxVal = 0; + black = splashRound((SplashCoord)255.0 * params->blackThreshold); + if (black < 1) { + black = 1; + } + int whiteAux = splashRound((SplashCoord)255.0 * params->whiteThreshold); + if (whiteAux > 255) { + white = 255; + } else { + white = whiteAux; + } + for (i = 0; i < size * size; ++i) { + u = splashRound((SplashCoord)255.0 * + splashPow((SplashCoord)mat[i] / 255.0, params->gamma)); + if (u < black) { + u = black; + } else if (u >= white) { + u = white; + } + mat[i] = u; + if (u < minVal) { + minVal = u; + } else if (u > maxVal) { + maxVal = u; + } + } +} + +void SplashScreen::buildDispersedMatrix(int i, int j, int val, + int delta, int offset) { + if (delta == 0) { + // map values in [1, size^2] --> [1, 255] + mat[i * size + j] = 1 + (254 * (val - 1)) / (size * size - 1); + } else { + buildDispersedMatrix(i, j, + val, delta / 2, 4*offset); + buildDispersedMatrix((i + delta) % size, (j + delta) % size, + val + offset, delta / 2, 4*offset); + buildDispersedMatrix((i + delta) % size, j, + val + 2*offset, delta / 2, 4*offset); + buildDispersedMatrix((i + 2*delta) % size, (j + delta) % size, + val + 3*offset, delta / 2, 4*offset); + } +} + +void SplashScreen::buildClusteredMatrix() { SplashCoord *dist; - SplashCoord u, v, d, val; + SplashCoord u, v, d; + Guchar val; int size2, x, y, x1, y1, i; - size2 = sizeA >> 1; - if (size2 < 1) { - size2 = 1; - } - size = size2 << 1; + size2 = size >> 1; // initialize the threshold matrix - mat = (SplashCoord *)gmallocn(size * size, sizeof(SplashCoord)); for (y = 0; y < size; ++y) { for (x = 0; x < size; ++x) { - mat[y * size + x] = -1; + mat[y * size + x] = 0; } } @@ -72,27 +177,22 @@ SplashScreen::SplashScreen(int sizeA) { minVal = 1; maxVal = 0; x1 = y1 = 0; // make gcc happy - for (i = 1; i <= size * size2; ++i) { - d = size * size2; + for (i = 0; i < size * size2; ++i) { + d = -1; for (y = 0; y < size; ++y) { for (x = 0; x < size2; ++x) { - if (mat[y * size + x] < 0 && - dist[y * size2 + x] < d) { + if (mat[y * size + x] == 0 && + dist[y * size2 + x] > d) { x1 = x; y1 = y; d = dist[y1 * size2 + x1]; } } } - u = (SplashCoord)1 - (SplashCoord)i / (SplashCoord)(size * size2 + 1); - val = splashPow(u, 1.33); - if (val < minVal) { - minVal = val; - } - if (val > maxVal) { - maxVal = val; - } + // map values in [0, 2*size*size2-1] --> [1, 255] + val = 1 + (254 * (2*i)) / (2*size*size2 - 1); mat[y1 * size + x1] = val; + val = 1 + (254 * (2*i+1)) / (2*size*size2 - 1); if (y1 < size2) { mat[(y1 + size2) * size + x1 + size2] = val; } else { @@ -103,13 +203,155 @@ SplashScreen::SplashScreen(int sizeA) { gfree(dist); } -SplashScreen::SplashScreen(SplashScreen *screen) { - int n; +// Compute the distance between two points on a toroid. +int SplashScreen::distance(int x0, int y0, int x1, int y1) { + int dx0, dx1, dx, dy0, dy1, dy; + + dx0 = abs(x0 - x1); + dx1 = size - dx0; + dx = dx0 < dx1 ? dx0 : dx1; + dy0 = abs(y0 - y1); + dy1 = size - dy0; + dy = dy0 < dy1 ? dy0 : dy1; + return dx * dx + dy * dy; +} + +// Algorithm taken from: +// Victor Ostromoukhov and Roger D. Hersch, "Stochastic Clustered-Dot +// Dithering" in Color Imaging: Device-Independent Color, Color +// Hardcopy, and Graphic Arts IV, SPIE Vol. 3648, pp. 496-505, 1999. +void SplashScreen::buildSCDMatrix(int r) { + SplashScreenPoint *dots, *pts; + int dotsLen, dotsSize; + char *tmpl; + char *grid; + int *region, *dist; + int x, y, xx, yy, x0, x1, y0, y1, i, j, d, iMin, dMin, n; + + //~ this should probably happen somewhere else + srand(123); + + // generate the random space-filling curve + pts = (SplashScreenPoint *)gmallocn(size * size, sizeof(SplashScreenPoint)); + i = 0; + for (y = 0; y < size; ++y) { + for (x = 0; x < size; ++x) { + pts[i].x = x; + pts[i].y = y; + ++i; + } + } + for (i = 0; i < size * size; ++i) { + j = i + (int)((double)(size * size - i) * + (double)rand() / ((double)RAND_MAX + 1.0)); + x = pts[i].x; + y = pts[i].y; + pts[i].x = pts[j].x; + pts[i].y = pts[j].y; + pts[j].x = x; + pts[j].y = y; + } + + // construct the circle template + tmpl = (char *)gmallocn((r+1)*(r+1), sizeof(char)); + for (y = 0; y <= r; ++y) { + for (x = 0; x <= r; ++x) { + tmpl[y*(r+1) + x] = (x * y <= r * r) ? 1 : 0; + } + } + + // mark all grid cells as free + grid = (char *)gmallocn(size * size, sizeof(char)); + for (y = 0; y < size; ++y) { + for (x = 0; x < size; ++x) { + grid[y*size + x] = 0; + } + } + + // walk the space-filling curve, adding dots + dotsLen = 0; + dotsSize = 32; + dots = (SplashScreenPoint *)gmallocn(dotsSize, sizeof(SplashScreenPoint)); + for (i = 0; i < size * size; ++i) { + x = pts[i].x; + y = pts[i].y; + if (!grid[y*size + x]) { + if (dotsLen == dotsSize) { + dotsSize *= 2; + dots = (SplashScreenPoint *)greallocn(dots, dotsSize, + sizeof(SplashScreenPoint)); + } + dots[dotsLen++] = pts[i]; + for (yy = 0; yy <= r; ++yy) { + y0 = (y + yy) % size; + y1 = (y - yy + size) % size; + for (xx = 0; xx <= r; ++xx) { + if (tmpl[yy*(r+1) + xx]) { + x0 = (x + xx) % size; + x1 = (x - xx + size) % size; + grid[y0*size + x0] = 1; + grid[y0*size + x1] = 1; + grid[y1*size + x0] = 1; + grid[y1*size + x1] = 1; + } + } + } + } + } + + gfree(tmpl); + gfree(grid); + // assign each cell to a dot, compute distance to center of dot + region = (int *)gmallocn(size * size, sizeof(int)); + dist = (int *)gmallocn(size * size, sizeof(int)); + for (y = 0; y < size; ++y) { + for (x = 0; x < size; ++x) { + iMin = 0; + dMin = distance(dots[0].x, dots[0].y, x, y); + for (i = 1; i < dotsLen; ++i) { + d = distance(dots[i].x, dots[i].y, x, y); + if (d < dMin) { + iMin = i; + dMin = d; + } + } + region[y*size + x] = iMin; + dist[y*size + x] = dMin; + } + } + + // compute threshold values + for (i = 0; i < dotsLen; ++i) { + n = 0; + for (y = 0; y < size; ++y) { + for (x = 0; x < size; ++x) { + if (region[y*size + x] == i) { + pts[n].x = x; + pts[n].y = y; + pts[n].dist = distance(dots[i].x, dots[i].y, x, y); + ++n; + } + } + } + qsort(pts, n, sizeof(SplashScreenPoint), &cmpDistances); + for (j = 0; j < n; ++j) { + // map values in [0 .. n-1] --> [255 .. 1] + mat[pts[j].y * size + pts[j].x] = 255 - (254 * j) / (n - 1); + } + } + + gfree(pts); + gfree(region); + gfree(dist); + + gfree(dots); +} + +SplashScreen::SplashScreen(SplashScreen *screen) { size = screen->size; - n = size * size * sizeof(SplashCoord); - mat = (SplashCoord *)gmalloc(n); - memcpy(mat, screen->mat, n); + mat = (Guchar *)gmallocn(size * size, sizeof(Guchar)); + memcpy(mat, screen->mat, size * size * sizeof(Guchar)); minVal = screen->minVal; maxVal = screen->maxVal; } @@ -118,7 +360,7 @@ SplashScreen::~SplashScreen() { gfree(mat); } -int SplashScreen::test(int x, int y, SplashCoord value) { +int SplashScreen::test(int x, int y, Guchar value) { int xx, yy; if (value < minVal) { @@ -136,6 +378,6 @@ int SplashScreen::test(int x, int y, SplashCoord value) { return value < mat[yy * size + xx] ? 0 : 1; } -GBool SplashScreen::isStatic(SplashCoord value) { +GBool SplashScreen::isStatic(Guchar value) { return value < minVal || value >= maxVal; } diff --git a/splash/SplashScreen.h b/splash/SplashScreen.h index e0df8bbd..4174c80e 100644 --- a/splash/SplashScreen.h +++ b/splash/SplashScreen.h @@ -20,7 +20,7 @@ class SplashScreen { public: - SplashScreen(int sizeA); + SplashScreen(SplashScreenParams *params); SplashScreen(SplashScreen *screen); ~SplashScreen(); @@ -28,20 +28,26 @@ public: // Return the computed pixel value (0=black, 1=white) for the gray // level <value> at (<x>, <y>). - int test(int x, int y, SplashCoord value); + int test(int x, int y, Guchar value); // Returns true if value is above the white threshold or below the // black threshold, i.e., if the corresponding halftone will be // solid white or black. - GBool isStatic(SplashCoord value); + GBool isStatic(Guchar value); private: - SplashCoord *mat; // threshold matrix + void buildDispersedMatrix(int i, int j, int val, + int delta, int offset); + void buildClusteredMatrix(); + int distance(int x0, int y0, int x1, int y1); + void buildSCDMatrix(int r); + + Guchar *mat; // threshold matrix int size; // size of the threshold matrix - SplashCoord minVal; // any pixel value below minVal generates + Guchar minVal; // any pixel value below minVal generates // solid black - SplashCoord maxVal; // any pixel value above maxVal generates + Guchar maxVal; // any pixel value above maxVal generates // solid white }; diff --git a/splash/SplashState.cc b/splash/SplashState.cc index dd011c01..70a79acc 100644 --- a/splash/SplashState.cc +++ b/splash/SplashState.cc @@ -15,6 +15,7 @@ #include "SplashPattern.h" #include "SplashScreen.h" #include "SplashClip.h" +#include "SplashBitmap.h" #include "SplashState.h" //------------------------------------------------------------------------ @@ -23,16 +24,20 @@ // number of components in each color mode int splashColorModeNComps[] = { - 1, 1, 2, 3, 3, 4, 4 + 1, 1, 3, 3, 4 }; -SplashState::SplashState(int width, int height) { +SplashState::SplashState(int width, int height, GBool vectorAntialias, + SplashScreenParams *screenParams) { SplashColor color; + matrix[0] = 1; matrix[1] = 0; + matrix[2] = 0; matrix[3] = 1; + matrix[4] = 0; matrix[5] = 0; memset(&color, 0, sizeof(SplashColor)); strokePattern = new SplashSolidColor(color); fillPattern = new SplashSolidColor(color); - screen = new SplashScreen(10); + screen = new SplashScreen(screenParams); blendFunc = NULL; strokeAlpha = 1; fillAlpha = 1; @@ -44,11 +49,46 @@ SplashState::SplashState(int width, int height) { lineDash = NULL; lineDashLength = 0; lineDashPhase = 0; - clip = new SplashClip(0, 0, width - 1, height - 1); + strokeAdjust = gFalse; + clip = new SplashClip(0, 0, width - 0.001, height - 0.001, vectorAntialias); + softMask = NULL; + deleteSoftMask = gFalse; + inNonIsolatedGroup = gFalse; + next = NULL; +} + +SplashState::SplashState(int width, int height, GBool vectorAntialias, + SplashScreen *screenA) { + SplashColor color; + + matrix[0] = 1; matrix[1] = 0; + matrix[2] = 0; matrix[3] = 1; + matrix[4] = 0; matrix[5] = 0; + memset(&color, 0, sizeof(SplashColor)); + strokePattern = new SplashSolidColor(color); + fillPattern = new SplashSolidColor(color); + screen = screenA->copy(); + blendFunc = NULL; + strokeAlpha = 1; + fillAlpha = 1; + lineWidth = 0; + lineCap = splashLineCapButt; + lineJoin = splashLineJoinMiter; + miterLimit = 10; + flatness = 1; + lineDash = NULL; + lineDashLength = 0; + lineDashPhase = 0; + strokeAdjust = gFalse; + clip = new SplashClip(0, 0, width - 0.001, height - 0.001, vectorAntialias); + softMask = NULL; + deleteSoftMask = gFalse; + inNonIsolatedGroup = gFalse; next = NULL; } SplashState::SplashState(SplashState *state) { + memcpy(matrix, state->matrix, 6 * sizeof(SplashCoord)); strokePattern = state->strokePattern->copy(); fillPattern = state->fillPattern->copy(); screen = state->screen->copy(); @@ -69,7 +109,11 @@ SplashState::SplashState(SplashState *state) { lineDashLength = 0; } lineDashPhase = state->lineDashPhase; + strokeAdjust = state->strokeAdjust; clip = state->clip->copy(); + softMask = state->softMask; + deleteSoftMask = gFalse; + inNonIsolatedGroup = state->inNonIsolatedGroup; next = NULL; } @@ -79,6 +123,9 @@ SplashState::~SplashState() { delete screen; gfree(lineDash); delete clip; + if (deleteSoftMask && softMask) { + delete softMask; + } } void SplashState::setStrokePattern(SplashPattern *strokePatternA) { @@ -108,3 +155,11 @@ void SplashState::setLineDash(SplashCoord *lineDashA, int lineDashLengthA, } lineDashPhase = lineDashPhaseA; } + +void SplashState::setSoftMask(SplashBitmap *softMaskA) { + if (deleteSoftMask) { + delete softMask; + } + softMask = softMaskA; + deleteSoftMask = gTrue; +} diff --git a/splash/SplashState.h b/splash/SplashState.h index d00de08a..d0e05df3 100644 --- a/splash/SplashState.h +++ b/splash/SplashState.h @@ -16,6 +16,7 @@ class SplashPattern; class SplashScreen; class SplashClip; +class SplashBitmap; //------------------------------------------------------------------------ // line cap values @@ -41,7 +42,10 @@ class SplashState { public: // Create a new state object, initialized with default settings. - SplashState(int width, int height); + SplashState(int width, int height, GBool vectorAntialias, + SplashScreenParams *screenParams); + SplashState(int width, int height, GBool vectorAntialias, + SplashScreen *screenA); // Copy a state object. SplashState *copy() { return new SplashState(this); } @@ -61,10 +65,14 @@ public: void setLineDash(SplashCoord *lineDashA, int lineDashLengthA, SplashCoord lineDashPhaseA); + // Set the soft mask bitmap. + void setSoftMask(SplashBitmap *softMaskA); + private: SplashState(SplashState *state); + SplashCoord matrix[6]; SplashPattern *strokePattern; SplashPattern *fillPattern; SplashScreen *screen; @@ -79,7 +87,11 @@ private: SplashCoord *lineDash; int lineDashLength; SplashCoord lineDashPhase; + GBool strokeAdjust; SplashClip *clip; + SplashBitmap *softMask; + GBool deleteSoftMask; + GBool inNonIsolatedGroup; SplashState *next; // used by Splash class diff --git a/splash/SplashT1Font.cc b/splash/SplashT1Font.cc index 708cc261..b30c0fc7 100644 --- a/splash/SplashT1Font.cc +++ b/splash/SplashT1Font.cc @@ -63,8 +63,9 @@ static Guchar bitReverse[256] = { // SplashT1Font //------------------------------------------------------------------------ -SplashT1Font::SplashT1Font(SplashT1FontFile *fontFileA, SplashCoord *matA): - SplashFont(fontFileA, matA, ((SplashT1FontFile *)fontFileA)->engine->aa) +SplashT1Font::SplashT1Font(SplashT1FontFile *fontFileA, SplashCoord *matA, + SplashCoord *textMatA): + SplashFont(fontFileA, matA, textMatA, fontFileA->engine->aa) { T1_TMATRIX matrix; BBox bbox; @@ -72,6 +73,7 @@ SplashT1Font::SplashT1Font(SplashT1FontFile *fontFileA, SplashCoord *matA): int x, y; t1libID = T1_CopyFont(fontFileA->t1libID); + outlineID = -1; // compute font size size = (float)splashSqrt(mat[2]*mat[2] + mat[3]*mat[3]); @@ -168,6 +170,9 @@ SplashT1Font::SplashT1Font(SplashT1FontFile *fontFileA, SplashCoord *matA): SplashT1Font::~SplashT1Font() { T1_DeleteFont(t1libID); + if (outlineID >= 0) { + T1_DeleteFont(outlineID); + } } GBool SplashT1Font::getGlyph(int c, int xFrac, int yFrac, @@ -210,6 +215,7 @@ GBool SplashT1Font::makeGlyph(int c, int xFrac, int yFrac, } SplashPath *SplashT1Font::getGlyphPath(int c) { + T1_TMATRIX matrix; SplashPath *path; T1_OUTLINE *outline; T1_PATHSEGMENT *seg; @@ -217,47 +223,64 @@ SplashPath *SplashT1Font::getGlyphPath(int c) { SplashCoord x, y, x1, y1; GBool needClose; - path = new SplashPath(); - if (!(outline = T1_GetCharOutline(t1libID, c, size, NULL))) { - return path; + if (outlineID < 0) { + outlineID = T1_CopyFont(((SplashT1FontFile *)fontFile)->t1libID); + outlineSize = (float)splashSqrt(textMat[2]*textMat[2] + + textMat[3]*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); } - x = 0; - y = 0; - needClose = gFalse; - for (seg = outline; seg; seg = seg->link) { - switch (seg->type) { - case T1_PATHTYPE_MOVE: - if (needClose) { - path->close(); - needClose = gFalse; + + path = new SplashPath(); + if ((outline = T1_GetCharOutline(outlineID, c, outlineSize, NULL))) { + x = 0; + 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 * outlineMul; + y += seg->dest.y * outlineMul; + path->moveTo(x, -y); + break; + case T1_PATHTYPE_LINE: + x += seg->dest.x * outlineMul; + y += seg->dest.y * outlineMul; + path->lineTo(x, -y); + needClose = gTrue; + break; + case T1_PATHTYPE_BEZIER: + bez = (T1_BEZIERSEGMENT *)seg; + x1 = x + (SplashCoord)(bez->dest.x * outlineMul); + y1 = y + (SplashCoord)(bez->dest.y * outlineMul); + path->curveTo(x + (SplashCoord)(bez->B.x * outlineMul), + -(y + (SplashCoord)(bez->B.y * outlineMul)), + x + (SplashCoord)(bez->C.x * outlineMul), + -(y + (SplashCoord)(bez->C.y * outlineMul)), + x1, -y1); + x = x1; + y = y1; + needClose = gTrue; + break; } - x += seg->dest.x / 65536.0; - y += seg->dest.y / 65536.0; - path->moveTo(x, y); - break; - case T1_PATHTYPE_LINE: - x += seg->dest.x / 65536.0; - y += seg->dest.y / 65536.0; - path->lineTo(x, y); - needClose = gTrue; - break; - case T1_PATHTYPE_BEZIER: - bez = (T1_BEZIERSEGMENT *)seg; - x1 = x + bez->dest.x / 65536.0; - y1 = y + bez->dest.y / 65536.0; - path->curveTo(x + bez->B.x / 65536.0, y + bez->B.y / 65536.0, - x + bez->C.x / 65536.0, y + bez->C.y / 65536.0, - x1, y1); - x = x1; - y = y1; - needClose = gTrue; - break; } + if (needClose) { + path->close(); + } + T1_FreeOutline(outline); } - if (needClose) { - path->close(); - } - T1_FreeOutline(outline); + return path; } diff --git a/splash/SplashT1Font.h b/splash/SplashT1Font.h index 18cd6501..919dc032 100644 --- a/splash/SplashT1Font.h +++ b/splash/SplashT1Font.h @@ -24,7 +24,8 @@ class SplashT1FontFile; class SplashT1Font: public SplashFont { public: - SplashT1Font(SplashT1FontFile *fontFileA, SplashCoord *matA); + SplashT1Font(SplashT1FontFile *fontFileA, SplashCoord *matA, + SplashCoord *textMatA); virtual ~SplashT1Font(); @@ -43,7 +44,10 @@ public: 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 diff --git a/splash/SplashT1FontEngine.cc b/splash/SplashT1FontEngine.cc index d180a5a8..a19d1bce 100644 --- a/splash/SplashT1FontEngine.cc +++ b/splash/SplashT1FontEngine.cc @@ -84,7 +84,7 @@ SplashT1FontEngine::~SplashT1FontEngine() { SplashFontFile *SplashT1FontEngine::loadType1Font(SplashFontFileID *idA, SplashFontSrc *src, char **enc) { - return SplashT1FontFile::loadType1Font(this, idA, src, enc); + return SplashT1FontFile::loadType1Font(this, idA, fileName, deleteFile, enc); } SplashFontFile *SplashT1FontEngine::loadType1CFont(SplashFontFileID *idA, @@ -109,7 +109,7 @@ SplashFontFile *SplashT1FontEngine::loadType1CFont(SplashFontFileID *idA, delete ff; return NULL; } - ff->convertToType1(NULL, gTrue, &fileWrite, tmpFile); + ff->convertToType1(NULL, NULL, gTrue, &fileWrite, tmpFile); delete ff; fclose(tmpFile); newsrc = new SplashFontSrc; diff --git a/splash/SplashT1FontFile.cc b/splash/SplashT1FontFile.cc index c5e9f5fe..16a1473c 100644 --- a/splash/SplashT1FontFile.cc +++ b/splash/SplashT1FontFile.cc @@ -33,6 +33,7 @@ SplashFontFile *SplashT1FontFile::loadType1Font(SplashT1FontEngine *engineA, int encStrSize; char *encPtr; int i; + GString *fileNameA; SplashFontSrc *newsrc = NULL; SplashFontFile *ff; @@ -52,8 +53,7 @@ SplashFontFile *SplashT1FontFile::loadType1Font(SplashT1FontEngine *engineA, fileNameA = src->fileName; // load the font file if ((t1libIDA = T1_AddFont(fileNameA)) < 0) { - if (newsrc) - delete newsrc; + delete newsrc; return NULL; } T1_LoadFont(t1libIDA); @@ -105,10 +105,11 @@ SplashT1FontFile::~SplashT1FontFile() { T1_DeleteFont(t1libID); } -SplashFont *SplashT1FontFile::makeFont(SplashCoord *mat) { +SplashFont *SplashT1FontFile::makeFont(SplashCoord *mat, + SplashCoord *textMat) { SplashFont *font; - font = new SplashT1Font(this, mat); + font = new SplashT1Font(this, mat, textMat); font->initCache(); return font; } diff --git a/splash/SplashT1FontFile.h b/splash/SplashT1FontFile.h index 2ab72167..2282deae 100644 --- a/splash/SplashT1FontFile.h +++ b/splash/SplashT1FontFile.h @@ -26,20 +26,21 @@ public: static SplashFontFile *loadType1Font(SplashT1FontEngine *engineA, SplashFontFileID *idA, - SplashFontSrc *src, + char *fileNameA, char **encA); virtual ~SplashT1FontFile(); // Create a new SplashT1Font, i.e., a scaled instance of this font // file. - virtual SplashFont *makeFont(SplashCoord *mat); + virtual SplashFont *makeFont(SplashCoord *mat, + SplashCoord *textMat); private: SplashT1FontFile(SplashT1FontEngine *engineA, SplashFontFileID *idA, - SplashFontSrc *src, + char *fileNameA, int t1libIDA, char **encA, char *encStrA); SplashT1FontEngine *engine; diff --git a/splash/SplashTypes.h b/splash/SplashTypes.h index 82aeb8ed..f367be72 100644 --- a/splash/SplashTypes.h +++ b/splash/SplashTypes.h @@ -21,6 +21,12 @@ typedef double SplashCoord; #endif //------------------------------------------------------------------------ +// antialiasing +//------------------------------------------------------------------------ + +#define splashAASize 4 + +//------------------------------------------------------------------------ // colors //------------------------------------------------------------------------ @@ -28,24 +34,16 @@ enum SplashColorMode { splashModeMono1, // 1 bit per component, 8 pixels per byte, // MSbit is on the left splashModeMono8, // 1 byte per component, 1 byte per pixel - splashModeAMono8, // 1 byte per component, 2 bytes per pixel: - // AMAM... splashModeRGB8, // 1 byte per component, 3 bytes per pixel: // RGBRGB... splashModeBGR8, // 1 byte per component, 3 bytes per pixel: // BGRBGR... - splashModeARGB8, // 1 byte per component, 4 bytes per pixel: - // ARGBARGB... - splashModeRGB8Qt, // 1 byte per component, 4 bytes per pixel: - // Specially hacked to use in Qt frontends - splashModeBGRA8 // 1 byte per component, 4 bytes per pixel: - // BGRABGRA... + splashModeRGBX8, // 1 byte per component, 4 bytes per pixel: + // RGBXRGBX... #if SPLASH_CMYK , - splashModeCMYK8, // 1 byte per component, 4 bytes per pixel: + splashModeCMYK8 // 1 byte per component, 4 bytes per pixel: // CMYKCMYK... - splashModeACMYK8 // 1 byte per component, 5 bytes per pixel: - // ACMYKACMYK #endif }; @@ -55,18 +53,14 @@ extern int splashColorModeNComps[]; // max number of components in any SplashColor #if SPLASH_CMYK -# define splashMaxColorComps 5 -#else # define splashMaxColorComps 4 +#else +# define splashMaxColorComps 3 #endif typedef Guchar SplashColor[splashMaxColorComps]; typedef Guchar *SplashColorPtr; -// AMono8 -static inline Guchar splashAMono8A(SplashColorPtr am8) { return am8[0]; } -static inline Guchar splashAMono8M(SplashColorPtr am8) { return am8[1]; } - // RGB8 static inline Guchar splashRGB8R(SplashColorPtr rgb8) { return rgb8[0]; } static inline Guchar splashRGB8G(SplashColorPtr rgb8) { return rgb8[1]; } @@ -77,40 +71,20 @@ static inline Guchar splashBGR8R(SplashColorPtr bgr8) { return bgr8[2]; } static inline Guchar splashBGR8G(SplashColorPtr bgr8) { return bgr8[1]; } static inline Guchar splashBGR8B(SplashColorPtr bgr8) { return bgr8[0]; } -// ARGB8 -static inline Guchar splashARGB8A(SplashColorPtr argb8) { return argb8[0]; } -static inline Guchar splashARGB8R(SplashColorPtr argb8) { return argb8[1]; } -static inline Guchar splashARGB8G(SplashColorPtr argb8) { return argb8[2]; } -static inline Guchar splashARGB8B(SplashColorPtr argb8) { return argb8[3]; } - -// ARGB8 -static inline Guchar splashBGRA8A(SplashColorPtr bgra8) { return bgra8[3]; } -static inline Guchar splashBGRA8R(SplashColorPtr bgra8) { return bgra8[2]; } -static inline Guchar splashBGRA8G(SplashColorPtr bgra8) { return bgra8[1]; } -static inline Guchar splashBGRA8B(SplashColorPtr bgra8) { return bgra8[0]; } - #if SPLASH_CMYK // CMYK8 static inline Guchar splashCMYK8C(SplashColorPtr cmyk8) { return cmyk8[0]; } static inline Guchar splashCMYK8M(SplashColorPtr cmyk8) { return cmyk8[1]; } static inline Guchar splashCMYK8Y(SplashColorPtr cmyk8) { return cmyk8[2]; } static inline Guchar splashCMYK8K(SplashColorPtr cmyk8) { return cmyk8[3]; } - -// ACMYK8 -static inline Guchar splashACMYK8A(SplashColorPtr acmyk8) { return acmyk8[0]; } -static inline Guchar splashACMYK8C(SplashColorPtr acmyk8) { return acmyk8[1]; } -static inline Guchar splashACMYK8M(SplashColorPtr acmyk8) { return acmyk8[2]; } -static inline Guchar splashACMYK8Y(SplashColorPtr acmyk8) { return acmyk8[3]; } -static inline Guchar splashACMYK8K(SplashColorPtr acmyk8) { return acmyk8[4]; } #endif static inline void splashColorCopy(SplashColorPtr dest, SplashColorPtr src) { dest[0] = src[0]; dest[1] = src[1]; dest[2] = src[2]; - dest[3] = src[3]; #if SPLASH_CMYK - dest[4] = src[4]; + dest[3] = src[3]; #endif } @@ -118,9 +92,8 @@ static inline void splashColorXor(SplashColorPtr dest, SplashColorPtr src) { dest[0] ^= src[0]; dest[1] ^= src[1]; dest[2] ^= src[2]; - dest[3] ^= src[3]; #if SPLASH_CMYK - dest[4] ^= src[4]; + dest[3] ^= src[3]; #endif } @@ -132,6 +105,25 @@ typedef void (*SplashBlendFunc)(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm); //------------------------------------------------------------------------ +// screen parameters +//------------------------------------------------------------------------ + +enum SplashScreenType { + splashScreenDispersed, + splashScreenClustered, + splashScreenStochasticClustered +}; + +struct SplashScreenParams { + SplashScreenType type; + int size; + int dotRadius; + SplashCoord gamma; + SplashCoord blackThreshold; + SplashCoord whiteThreshold; +}; + +//------------------------------------------------------------------------ // error results //------------------------------------------------------------------------ diff --git a/splash/SplashXPath.cc b/splash/SplashXPath.cc index 7fa0865a..d7e114d5 100644 --- a/splash/SplashXPath.cc +++ b/splash/SplashXPath.cc @@ -19,7 +19,31 @@ //------------------------------------------------------------------------ -#define maxCurveSplits (1 << 10) +struct SplashXPathPoint { + SplashCoord x, y; +}; + +struct SplashXPathAdjust { + int firstPt, lastPt; // range of points + GBool vert; // vertical or horizontal hint + SplashCoord x0a, x0b, // hint boundaries + xma, xmb, + x1a, x1b; + SplashCoord x0, x1, xm; // adjusted coordinates +}; + +//------------------------------------------------------------------------ + +// Transform a point from user space to device space. +inline void SplashXPath::transform(SplashCoord *matrix, + SplashCoord xi, SplashCoord yi, + SplashCoord *xo, SplashCoord *yo) { + // [ m[0] m[1] 0 ] + // [xo yo 1] = [xi yi 1] * [ m[2] m[3] 0 ] + // [ m[4] m[5] 1 ] + *xo = xi * matrix[0] + yi * matrix[2] + matrix[4]; + *yo = xi * matrix[1] + yi * matrix[3] + matrix[5]; +} //------------------------------------------------------------------------ // SplashXPath @@ -30,32 +54,118 @@ SplashXPath::SplashXPath() { length = size = 0; } -SplashXPath::SplashXPath(SplashPath *path, SplashCoord flatness, - GBool closeSubpaths) { - SplashCoord xc, yc, dx, dy, r, x0, y0, x1, y1; - int quad0, quad1, quad; - int curSubpath, n, i, j; +SplashXPath::SplashXPath(SplashPath *path, SplashCoord *matrix, + SplashCoord flatness, GBool closeSubpaths) { + SplashPathHint *hint; + SplashXPathPoint *pts; + SplashXPathAdjust *adjusts, *adjust; + SplashCoord x0, y0, x1, y1, x2, y2, x3, y3, xsp, ysp; + SplashCoord adj0, adj1, w; + int ww; + int curSubpath, curSubpathX, i, j; + + // transform the points + pts = (SplashXPathPoint *)gmallocn(path->length, sizeof(SplashXPathPoint)); + for (i = 0; i < path->length; ++i) { + transform(matrix, path->pts[i].x, path->pts[i].y, &pts[i].x, &pts[i].y); + } + + // set up the stroke adjustment hints + if (path->hints) { + adjusts = (SplashXPathAdjust *)gmallocn(path->hintsLength, + sizeof(SplashXPathAdjust)); + for (i = 0; i < path->hintsLength; ++i) { + hint = &path->hints[i]; + if (hint->ctrl0 + 1 >= path->length || hint->ctrl1 + 1 >= path->length) { + gfree(adjusts); + adjusts = NULL; + break; + } + 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; + } + w = adj1 - adj0; + ww = splashRound(w); + if (ww == 0) { + ww = 1; + } + adjusts[i].x0a = adj0 - 0.01; + adjusts[i].x0b = adj0 + 0.01; + adjusts[i].xma = (SplashCoord)0.5 * (adj0 + adj1) - 0.01; + adjusts[i].xmb = (SplashCoord)0.5 * (adj0 + adj1) + 0.01; + adjusts[i].x1a = adj1 - 0.01; + adjusts[i].x1b = adj1 + 0.01; + adjusts[i].x0 = (SplashCoord)splashRound(adj0); + adjusts[i].x1 = adjusts[i].x0 + ww - 0.01; + 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); + } segs = NULL; length = size = 0; - i = 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) { // first point in subpath - skip it if (path->flags[i] & splashPathFirst) { + x0 = pts[i].x; + y0 = pts[i].y; + xsp = x0; + ysp = y0; curSubpath = i; + curSubpathX = length; ++i; } else { // curve segment if (path->flags[i] & splashPathCurve) { - addCurve(path->pts[i-1].x, path->pts[i-1].y, - path->pts[i ].x, path->pts[i ].y, - path->pts[i+1].x, path->pts[i+1].y, - path->pts[i+2].x, path->pts[i+2].y, + x1 = pts[i].x; + y1 = pts[i].y; + x2 = pts[i+1].x; + y2 = pts[i+1].y; + x3 = pts[i+2].x; + y3 = pts[i+2].y; + addCurve(x0, y0, x1, y1, x2, y2, x3, y3, flatness, (path->flags[i-1] & splashPathFirst), (path->flags[i+2] & splashPathLast), @@ -65,83 +175,15 @@ SplashXPath::SplashXPath(SplashPath *path, SplashCoord flatness, !closeSubpaths && (path->flags[i+2] & splashPathLast) && !(path->flags[i+2] & splashPathClosed)); + x0 = x3; + y0 = y3; i += 3; - // clockwise circular arc - } else if (path->flags[i] & splashPathArcCW) { - xc = path->pts[i].x; - yc = path->pts[i].y; - dx = path->pts[i+1].x - xc; - dy = path->pts[i+1].y - yc; - r = splashSqrt(dx * dx + dy * dy); - if (path->pts[i-1].x < xc && path->pts[i-1].y <= yc) { - quad0 = 0; - } else if (path->pts[i-1].x >= xc && path->pts[i-1].y < yc) { - quad0 = 1; - } else if (path->pts[i-1].x > xc && path->pts[i-1].y >= yc) { - quad0 = 2; - } else { - quad0 = 3; - } - if (path->pts[i+1].x <= xc && path->pts[i+1].y < yc) { - quad1 = 0; - } else if (path->pts[i+1].x > xc && path->pts[i+1].y <= yc) { - quad1 = 1; - } else if (path->pts[i+1].x >= xc && path->pts[i+1].y > yc) { - quad1 = 2; - } else { - quad1 = 3; - } - n = 0; // make gcc happy - if (quad0 == quad1) { - switch (quad0) { - case 0: - case 1: n = path->pts[i-1].x < path->pts[i+1].x ? 0 : 4; break; - case 2: - case 3: n = path->pts[i-1].x > path->pts[i+1].x ? 0 : 4; break; - } - } else { - n = (quad1 - quad0) & 3; - } - x0 = path->pts[i-1].x; - y0 = path->pts[i-1].y; - x1 = y1 = 0; // make gcc happy - quad = quad0; - for (j = 0; j < n; ++j) { - switch (quad) { - case 0: x1 = xc; y1 = yc - r; break; - case 1: x1 = xc + r; y1 = yc; break; - case 2: x1 = xc; y1 = yc + r; break; - case 3: x1 = xc - r; y1 = yc; break; - } - addArc(x0, y0, x1, y1, - xc, yc, r, quad, flatness, - quad == quad0 && (path->flags[i-1] & splashPathFirst), - gFalse, - quad == quad0 && !closeSubpaths && - (path->flags[i-1] & splashPathFirst) && - !(path->flags[i-1] & splashPathClosed), - gFalse); - x0 = x1; - y0 = y1; - quad = (quad + 1) & 3; - } - addArc(x0, y0, path->pts[i+1].x, path->pts[i+1].y, - xc, yc, r, quad, flatness, - quad == quad0 && (path->flags[i-1] & splashPathFirst), - (path->flags[i+1] & splashPathLast), - quad == quad0 && !closeSubpaths && - (path->flags[i-1] & splashPathFirst) && - !(path->flags[i-1] & splashPathClosed), - !closeSubpaths && - (path->flags[i+1] & splashPathLast) && - !(path->flags[i+1] & splashPathClosed)); - i += 2; - // line segment } else { - addSegment(path->pts[i-1].x, path->pts[i-1].y, - path->pts[i].x, path->pts[i].y, + x1 = pts[i].x; + y1 = pts[i].y; + addSegment(x0, y0, x1, y1, path->flags[i-1] & splashPathFirst, path->flags[i] & splashPathLast, !closeSubpaths && @@ -150,20 +192,49 @@ SplashXPath::SplashXPath(SplashPath *path, SplashCoord flatness, !closeSubpaths && (path->flags[i] & splashPathLast) && !(path->flags[i] & splashPathClosed)); + x0 = x1; + y0 = y1; ++i; } // close a subpath if (closeSubpaths && (path->flags[i-1] & splashPathLast) && - (path->pts[i-1].x != path->pts[curSubpath].x || - path->pts[i-1].y != path->pts[curSubpath]. y)) { - addSegment(path->pts[i-1].x, path->pts[i-1].y, - path->pts[curSubpath].x, path->pts[curSubpath].y, + (pts[i-1].x != pts[curSubpath].x || + pts[i-1].y != pts[curSubpath].y)) { + addSegment(x0, y0, xsp, ysp, gFalse, gTrue, gFalse, gFalse); } } } + + gfree(pts); +} + +// Apply the stroke adjust hints to point <pt>: (*<xp>, *<yp>). +void SplashXPath::strokeAdjust(SplashXPathAdjust *adjust, + SplashCoord *xp, SplashCoord *yp) { + SplashCoord x, y; + + 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; + } + } 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; + } + } } SplashXPath::SplashXPath(SplashXPath *xPath) { @@ -196,9 +267,9 @@ void SplashXPath::addCurve(SplashCoord x0, SplashCoord y0, SplashCoord x3, SplashCoord y3, SplashCoord flatness, GBool first, GBool last, GBool end0, GBool end1) { - SplashCoord cx[maxCurveSplits + 1][3]; - SplashCoord cy[maxCurveSplits + 1][3]; - int cNext[maxCurveSplits + 1]; + SplashCoord cx[splashMaxCurveSplits + 1][3]; + SplashCoord cy[splashMaxCurveSplits + 1][3]; + int cNext[splashMaxCurveSplits + 1]; SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh; SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh; SplashCoord dx, dy, mx, my, d1, d2, flatness2; @@ -208,14 +279,14 @@ void SplashXPath::addCurve(SplashCoord x0, SplashCoord y0, // initial segment p1 = 0; - p2 = maxCurveSplits; + p2 = splashMaxCurveSplits; cx[p1][0] = x0; cy[p1][0] = y0; cx[p1][1] = x1; cy[p1][1] = y1; cx[p1][2] = x2; cy[p1][2] = y2; cx[p2][0] = x3; cy[p2][0] = y3; cNext[p1] = p2; - while (p1 < maxCurveSplits) { + while (p1 < splashMaxCurveSplits) { // get the next segment xl0 = cx[p1][0]; yl0 = cy[p1][0]; @@ -242,9 +313,9 @@ void SplashXPath::addCurve(SplashCoord x0, SplashCoord y0, if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) { addSegment(xl0, yl0, xr3, yr3, p1 == 0 && first, - p2 == maxCurveSplits && last, + p2 == splashMaxCurveSplits && last, p1 == 0 && end0, - p2 == maxCurveSplits && end1); + p2 == splashMaxCurveSplits && end1); p1 = p2; // otherwise, subdivide the curve @@ -274,73 +345,6 @@ void SplashXPath::addCurve(SplashCoord x0, SplashCoord y0, } } -void SplashXPath::addArc(SplashCoord x0, SplashCoord y0, - SplashCoord x1, SplashCoord y1, - SplashCoord xc, SplashCoord yc, - SplashCoord r, int quad, - SplashCoord flatness, - GBool first, GBool last, GBool end0, GBool end1) { - SplashCoord px[maxCurveSplits + 1]; - SplashCoord py[maxCurveSplits + 1]; - int pNext[maxCurveSplits + 1]; - SplashCoord r2, flatness2; - SplashCoord xx0, yy0, xx1, yy1, xm, ym, t, dx, dy; - int p1, p2, p3; - - r2 = r * r; - flatness2 = flatness * flatness; - - // initial segment - p1 = 0; - p2 = maxCurveSplits; - px[p1] = x0; py[p1] = y0; - px[p2] = x1; py[p2] = y1; - pNext[p1] = p2; - - while (p1 < maxCurveSplits) { - - // get the next segment - xx0 = px[p1]; yy0 = py[p1]; - p2 = pNext[p1]; - xx1 = px[p2]; yy1 = py[p2]; - - // compute the arc midpoint - t = (xx0 - xc) * (xx1 - xc) - (yy0 - yc) * (yy1 - yc); - xm = splashSqrt((SplashCoord)0.5 * (r2 + t)); - ym = splashSqrt((SplashCoord)0.5 * (r2 - t)); - switch (quad) { - case 0: xm = xc - xm; ym = yc - ym; break; - case 1: xm = xc + xm; ym = yc - ym; break; - case 2: xm = xc + xm; ym = yc + ym; break; - case 3: xm = xc - xm; ym = yc + ym; break; - } - - // compute distance from midpoint of straight segment to midpoint - // of arc - dx = (SplashCoord)0.5 * (xx0 + xx1) - xm; - dy = (SplashCoord)0.5 * (yy0 + yy1) - ym; - - // if the arc is flat enough, or no more subdivisions are allowed, - // add the straight line segment - if (p2 - p1 == 1 || dx * dx + dy * dy <= flatness2) { - addSegment(xx0, yy0, xx1, yy1, - p1 == 0 && first, - p2 == maxCurveSplits && last, - p1 == 0 && end0, - p2 == maxCurveSplits && end1); - p1 = p2; - - // otherwise, subdivide the arc - } else { - p3 = (p1 + p2) / 2; - px[p3] = xm; - py[p3] = ym; - pNext[p1] = p3; - pNext[p3] = p2; - } - } -} - void SplashXPath::addSegment(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1, GBool first, GBool last, GBool end0, GBool end1) { @@ -372,8 +376,21 @@ void SplashXPath::addSegment(SplashCoord x0, SplashCoord y0, segs[length].dxdy = segs[length].dydx = 0; segs[length].flags |= splashXPathVert; } else { +#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; + } + } +#else segs[length].dxdy = (x1 - x0) / (y1 - y0); segs[length].dydx = (SplashCoord)1 / segs[length].dxdy; +#endif } if (y0 > y1) { segs[length].flags |= splashXPathFlip; @@ -409,6 +426,18 @@ static int cmpXPathSegs(const void *arg0, const void *arg1) { return 0; } +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; + } +} + void SplashXPath::sort() { qsort(segs, length, sizeof(SplashXPathSeg), &cmpXPathSegs); } diff --git a/splash/SplashXPath.h b/splash/SplashXPath.h index a72d5633..942104fb 100644 --- a/splash/SplashXPath.h +++ b/splash/SplashXPath.h @@ -14,6 +14,11 @@ #include "SplashTypes.h" class SplashPath; +struct SplashXPathAdjust; + +//------------------------------------------------------------------------ + +#define splashMaxCurveSplits (1 << 10) //------------------------------------------------------------------------ // SplashXPathSeg @@ -45,16 +50,21 @@ class SplashXPath { public: // Expands (converts to segments) and flattens (converts curves to - // lines) <path>. If <closeSubpaths> is true, closes all open + // lines) <path>. Transforms all points from user space to device + // space, via <matrix>. If <closeSubpaths> is true, closes all open // subpaths. - SplashXPath(SplashPath *path, SplashCoord flatness, - GBool closeSubpaths); + SplashXPath(SplashPath *path, SplashCoord *matrix, + SplashCoord flatness, GBool closeSubpaths); // Copy an expanded path. SplashXPath *copy() { return new SplashXPath(this); } ~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(); @@ -62,6 +72,10 @@ private: SplashXPath(); SplashXPath(SplashXPath *xPath); + void transform(SplashCoord *matrix, SplashCoord xi, SplashCoord yi, + SplashCoord *xo, SplashCoord *yo); + void strokeAdjust(SplashXPathAdjust *adjust, + SplashCoord *xp, SplashCoord *yp); void grow(int nSegs); void addCurve(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1, @@ -69,12 +83,6 @@ private: SplashCoord x3, SplashCoord y3, SplashCoord flatness, GBool first, GBool last, GBool end0, GBool end1); - void addArc(SplashCoord x0, SplashCoord y0, - SplashCoord x1, SplashCoord y1, - SplashCoord xc, SplashCoord yc, - SplashCoord r, int quad, - SplashCoord flatness, - GBool first, GBool last, GBool end0, GBool end1); void addSegment(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1, GBool first, GBool last, GBool end0, GBool end1); diff --git a/splash/SplashXPathScanner.cc b/splash/SplashXPathScanner.cc index 9b1c1c4f..99a0ce87 100644 --- a/splash/SplashXPathScanner.cc +++ b/splash/SplashXPathScanner.cc @@ -11,9 +11,11 @@ #endif #include <stdlib.h> +#include <string.h> #include "goo/gmem.h" #include "SplashMath.h" #include "SplashXPath.h" +#include "SplashBitmap.h" #include "SplashXPathScanner.h" //------------------------------------------------------------------------ @@ -97,6 +99,14 @@ SplashXPathScanner::~SplashXPathScanner() { gfree(inter); } +void SplashXPathScanner::getBBoxAA(int *xMinA, int *yMinA, + int *xMaxA, int *yMaxA) { + *xMinA = xMin / splashAASize; + *yMinA = yMin / splashAASize; + *xMaxA = xMax / splashAASize; + *yMaxA = yMax / splashAASize; +} + void SplashXPathScanner::getSpanBounds(int y, int *spanXMin, int *spanXMax) { if (interY != y) { computeIntersections(y); @@ -283,3 +293,136 @@ void SplashXPathScanner::computeIntersections(int y) { interIdx = 0; interCount = 0; } + +void SplashXPathScanner::renderAALine(SplashBitmap *aaBuf, + int *x0, int *x1, int y) { + int xx0, xx1, xx, xxMin, xxMax, yy; + Guchar mask; + SplashColorPtr p; + + memset(aaBuf->getDataPtr(), 0, aaBuf->getRowSize() * aaBuf->getHeight()); + xxMin = aaBuf->getWidth(); + xxMax = -1; + for (yy = 0; yy < splashAASize; ++yy) { + computeIntersections(splashAASize * y + yy); + while (interIdx < interLen) { + xx0 = inter[interIdx].x0; + xx1 = inter[interIdx].x1; + interCount += inter[interIdx].count; + ++interIdx; + while (interIdx < interLen && + (inter[interIdx].x0 <= xx1 || + (eo ? (interCount & 1) : (interCount != 0)))) { + if (inter[interIdx].x1 > xx1) { + xx1 = inter[interIdx].x1; + } + interCount += inter[interIdx].count; + ++interIdx; + } + if (xx0 < 0) { + xx0 = 0; + } + ++xx1; + if (xx1 > aaBuf->getWidth()) { + xx1 = aaBuf->getWidth(); + } + // 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; + } + } + } + *x0 = xxMin / splashAASize; + *x1 = (xxMax - 1) / splashAASize; +} + +void SplashXPathScanner::clipAALine(SplashBitmap *aaBuf, + int *x0, int *x1, int y) { + int xx0, xx1, xx, yy; + Guchar mask; + SplashColorPtr p; + + for (yy = 0; yy < splashAASize; ++yy) { + xx = *x0 * splashAASize; + computeIntersections(splashAASize * y + yy); + while (interIdx < interLen && xx < (*x1 + 1) * splashAASize) { + xx0 = inter[interIdx].x0; + xx1 = inter[interIdx].x1; + interCount += inter[interIdx].count; + ++interIdx; + while (interIdx < interLen && + (inter[interIdx].x0 <= xx1 || + (eo ? (interCount & 1) : (interCount != 0)))) { + if (inter[interIdx].x1 > xx1) { + xx1 = inter[interIdx].x1; + } + interCount += inter[interIdx].count; + ++interIdx; + } + if (xx0 > aaBuf->getWidth()) { + xx0 = aaBuf->getWidth(); + } + // set [xx, xx0) to 0 + if (xx < xx0) { + p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + (xx >> 3); + if (xx & 7) { + mask = (Guchar)(0xff00 >> (xx & 7)); + if ((xx & ~7) == (xx0 & ~7)) { + mask |= 0xff >> (xx0 & 7); + } + *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; + } + } + 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; + } + for (; xx + 7 <= xx0; xx += 8) { + *p++ = 0x00; + } + if (xx <= xx0) { + *p &= 0xff >> (xx0 & 7); + } + } + } +} diff --git a/splash/SplashXPathScanner.h b/splash/SplashXPathScanner.h index 169d4b0c..1cc4f3cd 100644 --- a/splash/SplashXPathScanner.h +++ b/splash/SplashXPathScanner.h @@ -14,6 +14,7 @@ #include "SplashTypes.h" class SplashXPath; +class SplashBitmap; struct SplashIntersect; //------------------------------------------------------------------------ @@ -32,6 +33,9 @@ public: 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); + // Return the min/max x values for the span at <y>. void getSpanBounds(int y, int *spanXMin, int *spanXMax); @@ -49,6 +53,15 @@ public: // 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); + private: void computeIntersections(int y); |