diff --git a/poppler/SplashOutputDev.cc b/poppler/SplashOutputDev.cc index 0b3722a..1dc4c8e 100644 --- a/poppler/SplashOutputDev.cc +++ b/poppler/SplashOutputDev.cc @@ -153,6 +153,328 @@ void SplashGouraudPattern::getParameterizedColor(double colorinterp, SplashColor } //------------------------------------------------------------------------ +// SplashRadialPattern +//------------------------------------------------------------------------ +// Min number of splits along the t axis for a radial shading fill. +#define radialMinSplits 256 + +// Max number of splits along the t axis for a radial shading fill. +#define radialMaxSplits 2048 + +// Max delta allowed in any color component for a radial shading fill. +#define radialColorDelta (dblToCol(1.0 / 256.0)) + +// Max size of pattern bitmap of min (width, height) +#define radialMaxSize 256 + +SplashRadialPattern::SplashRadialPattern(GfxState *stateA, GfxRadialShading *shadingA, + double sMinA, double sMaxA, SplashColorMode colorModeA) { + double width, height; + Matrix ctm; + + state = stateA; + shading = shadingA; + // ignore sMin ans sMax from Gfx (they include already extends!): + sMax = 1; + sMin = 0; + colorMode = colorModeA; + state->getCTM(&ctm); + ctm.invertTo(&ictm); + splash = NULL; + bitmap = NULL; + // get the shading info + shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1); + t0 = shading->getDomain0(); + t1 = shading->getDomain1(); + if (shading->getHasBBox()) + shading->getBBox(&xMin, &yMin, &xMax, &yMax); + else + state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); + width = xMax - xMin; + height = yMax - yMin; + if (splashRound(xMax) - splashRound(xMin) > width) + width = splashRound(xMax) - splashRound(xMin); + if (splashRound(yMax) - splashRound(yMin) > height) + height = splashRound(yMax) - splashRound(yMin); + if (radialMaxSize > 0 && width > 0 && height > 0) { + if ( width > height) + scale = radialMaxSize / height; + else + scale = radialMaxSize / width; + } else + scale = 1; + xMin *= scale; + xMax *= scale; + yMin *= scale; + yMax *= scale; + width *= scale; + height *= scale; + if (width > 0 && height > 0) { + bitmap = new SplashBitmap(splashRound(width) + 2, splashRound(height) + 2, colorMode != splashModeMono1, colorMode, gTrue); + splash = new Splash(bitmap, gFalse /* AntiAlias in shading Dict! */); + + // show only what is painted, delete alpha channel + Guchar *bitmapAlpha = bitmap->getAlphaPtr(); + int size = bitmap->getWidth() * bitmap->getHeight(); + for (int i = 0; i < size; ++i) + bitmapAlpha[i] = 0; + } + + // change transfer matrix to fit to scaled bitmap + ictm.m[0] *= scale; + ictm.m[1] *= scale; + ictm.m[2] *= scale; + ictm.m[3] *= scale; + ictm.m[4] *= scale; + ictm.m[5] *= scale; + ictm.m[4] -= xMin; + ictm.m[5] -= yMin; + maxSplits = splashRound((r0 - r1) * scale / 5); + if (maxSplits < 0) maxSplits = -maxSplits; + if (maxSplits < radialMinSplits) + maxSplits = radialMinSplits; + if (maxSplits > radialMaxSplits) + maxSplits = radialMaxSplits; +} + +SplashRadialPattern::~SplashRadialPattern() { + if (splash) { + delete splash; + } + if (bitmap) { + delete bitmap; + } +} + +GBool SplashRadialPattern::getColor(int x, int y, SplashColorPtr c) { + double xc, yc; + int xs, ys; + Guchar *bitmapAlpha; + + bitmapAlpha = bitmap->getAlphaPtr(); + ictm.transform(x, y, &xc, &yc); + xs = splashRound(xc); + ys = splashRound(yc); + // Because of rounding problems, coordinates could be + // outside the bitmap. Reset them on the outer bound now + // and let it up to the alpha channel if they are shown: + if (xs < 0) xs = 0; + if (xs >= bitmap->getWidth()) xs = bitmap->getWidth() - 1; + if (ys < 0) ys = 0; + if (ys >= bitmap->getHeight()) ys = bitmap->getHeight() - 1; + if (bitmapAlpha[ys * bitmap->getWidth() + xs]) + bitmap->getPixel(xs, ys, c); + else + return gFalse; + return gTrue; +} + +static inline void getShadingColorRadialHelper(double t0, double t1, double t, GfxRadialShading *shading, GfxColor *color) +{ + if (t0 < t1) { + if (t < t0) { + shading->getColor(t0, color); + } else if (t > t1) { + shading->getColor(t1, color); + } else { + shading->getColor(t, color); + } + } else { + if (t > t0) { + shading->getColor(t0, color); + } else if (t < t1) { + shading->getColor(t1, color); + } else { + shading->getColor(t, color); + } + } +} + +void SplashRadialPattern::getStartCircle(SplashCoord *xsc, SplashCoord *ysc, + SplashCoord *radius, + SplashColorPtr c) { + GfxColor colorA; + GfxColorSpace* srcColorSpace = shading->getColorSpace(); + ia = 0; + sa = sMin; + ta = t0 + sa * (t1 - t0); + xa = x0 + sa * (x1 - x0); + ya = y0 + sa * (y1 - y0); + ra = r0 + sa * (r1 - r0); + getShadingColorRadialHelper(t0, t1, ta, shading, &colorA); + *radius = splashRound(ra * scale); + *xsc = splashRound(xa * scale - xMin); *ysc = splashRound(ya * scale - yMin); + convertGfxColor(c, colorMode, srcColorSpace, &colorA); +} + +static inline GBool isSameGfxColor(const GfxColor &colorA, const GfxColor &colorB, Guint nComps, double delta) { + for (Guint k = 0; k < nComps; ++k) { + if (abs(colorA.c[k] - colorB.c[k]) > delta) { + return false; + } + } + return true; +} + +GBool SplashRadialPattern::getNextCircle(SplashCoord *xsc, SplashCoord *ysc, + SplashCoord *radius, + SplashColorPtr c) { + GfxColor colorA, colorB; + GfxColorSpace* srcColorSpace = shading->getColorSpace(); + double sb, tb, factor; + int ib, nComps; + nComps = shading->getColorSpace()->getNComps(); + if (ia >= maxSplits) + return gFalse; + getShadingColorRadialHelper(t0, t1, ta, shading, &colorA); + ib = maxSplits; + sb = (r1 > r0) ? sMax : sMin; + tb = t0 + sb * (t1 - t0); + getShadingColorRadialHelper(t0, t1, tb, shading, &colorB); + while (ib - ia > 1) { + if (isSameGfxColor(colorB, colorA, nComps, radialColorDelta)) { + // The shading is not necessarily lineal so having two points with the + // same color does not mean all the areas in between have the same color too + int ic = ia + 1; + for (; ic <= ib; ic++) { + GfxColor colorC; + factor = (double)ic / (double)maxSplits; + double sc = sMin + factor * (sMax - sMin); + double tc = t0 + sc * (t1 - t0); + getShadingColorRadialHelper(t0, t1, tc, shading, &colorC); + if (!isSameGfxColor(colorC, colorA, nComps, radialColorDelta)) { + break; + } + } + ib = (ic > ia + 1)? ic - 1 : ia + 1; + factor = (double)ib / (double)maxSplits; + sb = sMin + factor * (sMax - sMin); + tb = t0 + sb * (t1 - t0); + getShadingColorRadialHelper(t0, t1, tb, shading, &colorB); + break; + } + ib = (ia + ib) / 2; + factor = (double)ib / (double)maxSplits; + sb = sMin + factor * (sMax - sMin); + tb = t0 + sb * (t1 - t0); + getShadingColorRadialHelper(t0, t1, tb, shading, &colorB); + } + // compute center and radius of the circle + xa = x0 + sb * (x1 - x0); + ya = y0 + sb * (y1 - y0); + ra = r0 + sb * (r1 - r0); + *radius = splashRound(ra * scale); + *xsc = splashRound(xa * scale - xMin); *ysc = splashRound(ya * scale - yMin); + convertGfxColor(c, colorMode, srcColorSpace, &colorB); + ia = ib; + ta = tb; + return gTrue; +} + +GBool SplashRadialPattern::getLargerExtendCircle(SplashCoord *xsc, SplashCoord *ysc, + SplashCoord *radius, SplashColorPtr c) { + GfxColor colorA; + GfxColorSpace* srcColorSpace = shading->getColorSpace(); + if ((shading->getExtend0() && r0 > r1) || + (shading->getExtend1() && r1 >= r0)) { + if (r0 >= r1) { + ta = t0; + ra = r0; + xa = x0; + ya = y0; + } else { + ta = t1; + ra = r1; + xa = x1; + ya = y1; + } + shading->getColor(ta, &colorA); + *radius = splashRound(ra * scale); + *xsc = splashRound(xa * scale - xMin); *ysc = splashRound(ya * scale - yMin); + convertGfxColor(c, colorMode, srcColorSpace, &colorA); + ia = 0; + return gTrue; + } + return gFalse; +} + +GBool SplashRadialPattern::getNextLargerExtendCircle(SplashCoord *xsc, SplashCoord *ysc, + SplashCoord *radius, GBool fExtend) { + double sb, factor; + double sStart, sEnd; + if (ia++ + 1 >= radialMinSplits) + return gFalse; + sStart = (r0 > r1) ? sMin : sMax; + // buest guess: + sEnd = (fExtend) ? sMax * 16 : -sMax * 16; + factor = (double)ia / (double)radialMinSplits; + sb = sStart + factor * (sEnd - sStart); + // compute center and radius of the circle + xa = x0 + sb * (x1 - x0); + ya = y0 + sb * (y1 - y0); + ra = r0 + sb * (r1 - r0); + ra = ra * scale; + xa = xa * scale - xMin; + ya = ya * scale - yMin; + if (ya - ra < yMin && ya + ra > yMax && xa - ra < xMin && xa + ra > xMax) + ia = radialMinSplits; + *radius = splashRound(ra); + *xsc = splashRound(xa); *ysc = splashRound(ya); + return gTrue; +} + +GBool SplashRadialPattern::getSmallerExtendCircle(SplashCoord *xsc, SplashCoord *ysc, + SplashCoord *radius, SplashColorPtr c, GBool fExtend) { + GfxColor colorA; + GfxColorSpace* srcColorSpace = shading->getColorSpace(); + if ((shading->getExtend0() && r0 <= r1 && r0 > 0) || + (shading->getExtend1() && r1 < r0 && r1 > 0)) { + double sStart, sEnd; + sEnd = (r1 > r0) ? sMin : sMax; + sStart = (r1!= r0) ? -r0 / (r1 - r0) : 0; + double sb = (fExtend) ? sStart : sEnd; + xa = x0 + sb * (x1 - x0); + ya = y0 + sb * (y1 - y0); + ra = r0 + sb * (r1 - r0); + if (r0 < r1) { + ta = t0; + } else { + ta = t1; + } + shading->getColor(ta, &colorA); + *radius = splashRound(ra * scale); + *xsc = splashRound(xa * scale - xMin); *ysc = splashRound(ya * scale - yMin); + convertGfxColor(c, colorMode, srcColorSpace, &colorA); + ia = 0; + return gTrue; + } + return gFalse; +} + +GBool SplashRadialPattern::getNextSmallerExtendCircle(SplashCoord *xsc, SplashCoord *ysc, + SplashCoord *radius, GBool fExtend) { + double sb, factor; + double sStart, sEnd; + if (ia++ + 1 >= maxSplits) + return gFalse; + sEnd = (r1 > r0) ? sMax : sMin; + sStart = (r1!= r0) ? -r0 / (r1 - r0) : sMax * 16; + if (!fExtend) { + sEnd = sStart; + sStart = (r1 > r0) ? sMin : sMax; + } + factor = (double)ia / (double)maxSplits; + sb = sStart + factor * (sEnd - sStart); + // compute center and radius of the circle + xa = x0 + sb * (x1 - x0); + ya = y0 + sb * (y1 - y0); + ra = r0 + sb * (r1 - r0); + *radius = splashRound(ra * scale); + *xsc = splashRound(xa * scale - xMin); *ysc = splashRound(ya * scale - yMin); + return gTrue; +} + +//------------------------------------------------------------------------ // SplashAxialPattern //------------------------------------------------------------------------ @@ -3011,14 +3333,14 @@ void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox, tx = (int)floor(xMin); if (tx < 0) { tx = 0; - } else if (tx > bitmap->getWidth()) { - tx = bitmap->getWidth(); + } else if (tx > bitmap->getWidth() - 1) { + tx = bitmap->getWidth() - 1; } ty = (int)floor(yMin); if (ty < 0) { ty = 0; - } else if (ty > bitmap->getHeight()) { - ty = bitmap->getHeight(); + } else if (ty > bitmap->getHeight() - 1) { + ty = bitmap->getHeight() - 1; } w = (int)ceil(xMax) - tx + 1; if (tx + w > bitmap->getWidth()) { @@ -3379,3 +3701,34 @@ GBool SplashOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading return retVal; } + +GBool SplashOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading, double sMin, double sMax) { + double xMin, yMin, xMax, yMax; + SplashPath *path; + + GBool retVal = gFalse; + // get the clip region bbox + state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); + // fill the region + state->moveTo(xMin, yMin); + state->lineTo(xMax, yMin); + state->lineTo(xMax, yMax); + state->lineTo(xMin, yMax); + state->closePath(); + path = convertPath(state, state->getPath()); + + SplashRadialColor *pattern = new SplashRadialPattern(state, shading, sMin, sMax, colorMode); + if (pattern->isOk()) { + // first draw the radial shading in its own bitmap + retVal = (pattern->getSplash()->radialShadedFill(pattern) == splashOk); + // now use this bitmap as dynamic pattern: + if (retVal) { + retVal = (splash->shadedFill(path, shading->getHasBBox(), pattern) == splashOk); + } + } + state->clearPath(); + delete pattern; + delete path; + + return retVal; +} diff --git a/poppler/SplashOutputDev.h b/poppler/SplashOutputDev.h index 570d036..85fc130 100644 --- a/poppler/SplashOutputDev.h +++ b/poppler/SplashOutputDev.h @@ -105,6 +105,49 @@ private: GBool bDirectColorTranslation; }; +// see GfxState.h, GfxRadialShading +class SplashRadialPattern: public SplashRadialColor { +public: + + SplashRadialPattern(GfxState *state, GfxRadialShading *shading, double sMin, double sMax, SplashColorMode colorMode); + + GBool isOk() { return (bitmap != NULL); } + + Splash *getSplash() { return splash; } + + SplashBitmap *getBitmap() { return bitmap; } + + virtual SplashPattern *copy() { return new SplashRadialPattern(state, shading, sMin, sMax, colorMode); } + + virtual ~SplashRadialPattern(); + + virtual GBool getColor(int x, int y, SplashColorPtr c); + + virtual GBool isStatic() { return gFalse; } + + virtual void getStartCircle(SplashCoord *x0, SplashCoord *y0, SplashCoord *radius, SplashColorPtr c); + virtual GBool getNextCircle(SplashCoord *x0, SplashCoord *y0, SplashCoord *radius, SplashColorPtr c); + virtual GBool getLargerExtendCircle(SplashCoord *x0, SplashCoord *y0, SplashCoord *radius, SplashColorPtr c); + virtual GBool getNextLargerExtendCircle(SplashCoord *x0, SplashCoord *y0, SplashCoord *radius, GBool fExtend); + virtual GBool getSmallerExtendCircle(SplashCoord *x0, SplashCoord *y0, SplashCoord *radius, SplashColorPtr c, GBool fExtend); + virtual GBool getNextSmallerExtendCircle(SplashCoord *x0, SplashCoord *y0, SplashCoord *radius, GBool fExtend); + virtual GBool enlargeCircles() { return r1 > r0; } +private: + GfxRadialShading *shading; + GfxState *state; + Matrix ictm; + double sMin, sMax; + double xMin, xMax, yMin, yMax; + double scale; + double xa, ya, ra, ta, sa; + int ia; + int maxSplits; + double x0, y0, r0, x1, y1, r1, t0, t1; + Splash *splash; + SplashBitmap *bitmap; + SplashColorMode colorMode; +}; + //------------------------------------------------------------------------ // number of Type 3 fonts to cache @@ -132,7 +175,7 @@ public: // radialShadedFill()? If this returns false, these shaded fills // will be reduced to a series of other drawing operations. virtual GBool useShadedFills(int type) - { return (type == 2 || type == 4 || type == 5 ) ? gTrue : gFalse; } + { return (type >= 2 && type <= 5) ? gTrue : gFalse; } // Does this device use upside-down coordinates? // (Upside-down means (0,0) is the top left corner of the page.) @@ -186,6 +229,7 @@ public: virtual void fill(GfxState *state); virtual void eoFill(GfxState *state); virtual GBool axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax); + virtual GBool radialShadedFill(GfxState *state, GfxRadialShading *shading, double sMin, double sMax); virtual GBool gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading); //----- path clipping diff --git a/splash/Splash.cc b/splash/Splash.cc index bc317a6..f5c2f7b 100644 --- a/splash/Splash.cc +++ b/splash/Splash.cc @@ -272,10 +272,16 @@ inline void Splash::pipeRun(SplashPipe *pipe) { pipe->destColorPtr += 4; break; } + if (state->softMask) { + pipe->softMaskPtr++; + } if (pipe->destAlphaPtr) { ++pipe->destAlphaPtr; } - ++pipe->x; + if (pipe->alpha0Ptr) { + pipe->alpha0Ptr++; + } + ++pipe->x; return; } } @@ -3531,6 +3537,336 @@ GBool Splash::gouraudTriangleShadedFill(SplashGouraudColor *shading) return gTrue; } +SplashPath *Splash::getBresenhamPoints(SplashCoord xc, SplashCoord yc, SplashCoord rc) { + int radius = splashRound(rc); + int x0 = splashRound(xc); + int y0 = splashRound(yc); + int f = 1 - radius; + int ddF_x = 1; + int ddF_y = -2 * radius; + int x = 0, lastX = x; + int y = radius, lastY = y; + Guchar dummy; + SplashPath *splashPath = new SplashPath(); + + if (radius == 0) { + splashPath->moveTo(lastX, lastY); + return splashPath; + } + + while(x < y) + { + if(f >= 0) + { + y--; + ddF_y += 2; + f += ddF_y; + } + x++; + ddF_x += 2; + f += ddF_x; + if (lastY != y) { + if (splashPath->getLength()) + splashPath->lineTo(lastX, lastY); + else + splashPath->moveTo(lastX, lastY); + lastY = y; + } + lastX = x; + } + for (int i = splashPath->getLength() -1; i >= 0; i--) { + SplashCoord curX, curY; + splashPath->getPoint(i, &curY, &curX, &dummy); + for (SplashCoord j = lastY; j >= curY; j--) + splashPath->lineTo(curX, j); + lastY = splashRound(curY - 1); + } + while (lastY >= 0) + splashPath->lineTo(rc, lastY--); + return splashPath; +} + +static inline void getBCircleLine(SplashPath *bresPoints, SplashCoord y, SplashCoord y0, SplashCoord r, SplashCoord *x) { + SplashCoord curX, curY; + Guchar dummy; + int i = (y > y0) ? splashRound(r - (y - y0)) : splashRound(r - (y0 - y)); + if (i < bresPoints->getLength()) { + bresPoints->getPoint(i, &curX, &curY, &dummy); + *x = curX; + } +} + +void Splash::drawPartCircleLine(SplashPipe *pipe, + SplashCoord y, + SplashCoord xMin, SplashCoord xMax) { + int yI, xmaxI, xminI; + int height = bitmap->getHeight(); + int width = bitmap->getWidth(); + Guchar *bitmapAlpha = bitmap->getAlphaPtr(); + yI = splashRound(y); + xmaxI = splashRound(xMax); + xminI = splashRound(xMin); + if (yI >= 0 && yI < height && xMax >= 0) { + if (xminI < 0) + xminI = 0; + if (xminI < width) { + if (xmaxI > width - 1) + xmaxI = width - 1; + drawSpan(pipe, xminI, xmaxI, yI, gTrue); + } + } +} + +void Splash::drawBCircleLine(SplashPipe *pipe, SplashPath *bresInnerPoints, + SplashCoord y, SplashCoord yiMin, SplashCoord yiMax, + SplashCoord yi, SplashCoord xi, SplashCoord iradius, + SplashCoord xMin, SplashCoord xMax, int lineWidth) { + if (y > yiMax || y < yiMin) + drawPartCircleLine(pipe, y, xMin, xMax); + else { + SplashCoord xiMin, xiMax; + getBCircleLine(bresInnerPoints, y, yi, iradius, &xiMin); + xiMax = xi + xiMin; + xiMin = xi - xiMin; + if (xiMin >= xMin || xiMax <= xMax) { + if (xiMax < xMin || xiMin > xMax) + drawPartCircleLine(pipe, y, xMin, xMax); + else if (xiMin <= xMin && xiMax < xMax) { + if (xiMin != xMin) + drawPartCircleLine(pipe, y, xMin, xMin + lineWidth); + drawPartCircleLine(pipe, y, (xMax - xiMax > lineWidth) ? xiMax : xMax - lineWidth, xMax); + } else if (xiMin >= xMin && xiMax <= xMax) { + if (xiMin != xMin) + drawPartCircleLine(pipe, y, xMin, (xiMin - xMin > lineWidth) ? xiMin : xMin + lineWidth); + drawPartCircleLine(pipe, y, (xMax - xiMax > lineWidth) ? xiMax : xMax - lineWidth, xMax); + } else { + if (xiMin != xMin) + drawPartCircleLine(pipe, y, xMin, (xiMin - xMin > lineWidth) ? xiMin : xMin + lineWidth); + drawPartCircleLine(pipe, y, xMax - lineWidth, xMax); + } + } + } +} + +SplashPath *Splash::drawCircle(SplashCoord xo, SplashCoord yo, SplashCoord oradius, SplashColorPtr cur, SplashPath *bresOuterPoints, + SplashCoord xi, SplashCoord yi, SplashCoord iradius, SplashColorPtr curNext, GBool fExtend, + SplashPipe *pipe) { + SplashPath *bresInnerPoints = getBresenhamPoints(xi, yi, iradius); + if (bresInnerPoints == NULL) + return bresInnerPoints; + + if (!fExtend) { + SplashCoord yiMax = yi + iradius, yiMin = yi - iradius; + SplashCoord yoMax = yo + oradius, yoMin = yo - oradius; + Guchar dummy; + int lineWidth = splashRound(oradius - iradius); + if (lineWidth > 3) lineWidth = 3; + // draw upper half + int startPoint = 0; + if (yoMax >= bitmap->getHeight()) { + startPoint = splashRound(yoMax - bitmap->getHeight() + 1); + } + int endPoint = startPoint + bitmap->getHeight(); + if (endPoint > bresOuterPoints->getLength()) + endPoint = bresOuterPoints->getLength(); + for (int i = startPoint; i < endPoint; i++) { + SplashCoord curX, curY; + SplashCoord xMin, xMax; + bresOuterPoints->getPoint(i, &curX, &curY, &dummy); + xMax = xo + curX; + xMin = xo - curX; + curY = yo + curY; + drawBCircleLine(pipe, bresInnerPoints, + curY, yiMin, yiMax, yi, xi, iradius, xMin, xMax, lineWidth); + } + // draw lower half + endPoint = splashRound(bitmap->getHeight() - yoMin); + startPoint = endPoint - bitmap->getHeight(); + if (endPoint > bresOuterPoints->getLength()) + endPoint = bresOuterPoints->getLength(); + if (startPoint < 0) + startPoint = 0; + for (int i = startPoint; i < endPoint; i++) { + SplashCoord curX, curY; + SplashCoord xMin, xMax; + bresOuterPoints->getPoint(i, &curX, &curY, &dummy); + xMax = xo + curX; + xMin = xo - curX; + curY = yo - curY; + drawBCircleLine(pipe, bresInnerPoints, + curY, yiMin, yiMax, yi, xi, iradius, xMin, xMax, lineWidth); + } + splashColorCopy(cur, curNext); + } else { + SplashCoord yoMax = yo + oradius, yoMin = yo - oradius; + SplashCoord yiMax = yi + iradius, yiMin = yi - iradius; + Guchar dummy; + int lineWidth = splashRound(iradius - oradius); + if (lineWidth > 3) lineWidth = 3; + splashColorCopy(cur, curNext); + // draw upper half + int startPoint = 0; + if (yiMax >= bitmap->getHeight()) { + startPoint = splashRound(yiMax - bitmap->getHeight() + 1); + } + int endPoint = startPoint + bitmap->getHeight(); + if (endPoint > bresInnerPoints->getLength()) + endPoint = bresInnerPoints->getLength(); + for (int i = startPoint; i < endPoint; i++) { + SplashCoord curX, curY; + SplashCoord xMin, xMax; + bresInnerPoints->getPoint(i, &curX, &curY, &dummy); + xMax = xi + curX; + xMin = xi - curX; + curY = yi + curY; + drawBCircleLine(pipe, bresOuterPoints, + curY, yoMin, yoMax, yo, xo, oradius, xMin, xMax, lineWidth); + } + // draw lower half + endPoint = splashRound(bitmap->getHeight() - yiMin); + startPoint = endPoint - bitmap->getHeight(); + if (endPoint > bresInnerPoints->getLength()) + endPoint = bresInnerPoints->getLength(); + if (startPoint < 0) + startPoint = 0; + for (int i = startPoint; i < endPoint; i++) { + SplashCoord curX, curY; + SplashCoord xMin, xMax; + bresInnerPoints->getPoint(i, &curX, &curY, &dummy); + xMax = xi + curX; + xMin = xi - curX; + curY = yi - curY; + drawBCircleLine(pipe, bresOuterPoints, + curY, yoMin, yoMax, yo, xo, oradius, xMin, xMax, lineWidth); + } + } + return bresInnerPoints; +} + +SplashError Splash::radialShadedFill(SplashRadialColor *shading) { + SplashPipe pipe; + SplashColor cSrcVal, cNextVal; + SplashColorPtr cur = cSrcVal, curNext = cNextVal; + SplashCoord xo, yo, oradius; + SplashCoord xi, yi, iradius; + GBool fExtend = shading->enlargeCircles(); + + pipeInit(&pipe, 0, 0, NULL, cSrcVal, state->strokeAlpha, gFalse, gFalse); + pipe.cSrc = cur; + + // extend starting circle + if (fExtend && shading->getSmallerExtendCircle(&xo, &yo, &oradius, cur, fExtend)) { + SplashPath *bresOuterPoints = getBresenhamPoints(xo, yo, oradius); + if (bresOuterPoints == NULL) + return splashErrEmptyPath; + while (shading->getNextSmallerExtendCircle(&xi, &yi, &iradius, fExtend)) { + if (xi == xo && yi == yo && iradius == oradius) { + continue; + } + SplashPath *bresInnerPoints = drawCircle(xo, yo, oradius, cur, bresOuterPoints, + xi, yi, iradius, cur, fExtend, &pipe); + if (bresInnerPoints == NULL) + break; + delete bresOuterPoints; + bresOuterPoints = bresInnerPoints; + yo = yi; xo = xi; oradius = iradius; + } + delete bresOuterPoints; + } + if (!fExtend && shading->getLargerExtendCircle(&xo, &yo, &oradius, cur)) { + SplashPath *bresOuterPoints = getBresenhamPoints(xo, yo, oradius); + if (bresOuterPoints == NULL) + return splashErrEmptyPath; + // start with filling the circle + SplashCoord xMin, xMax; + Guchar dummy; + for (int i = 0; i < bresOuterPoints->getLength(); i++) { + SplashCoord curX, curY; + bresOuterPoints->getPoint(i, &curX, &curY, &dummy); + curY += yo; + xMax = xo + curX; + xMin = xo - curX; + if (curY >= 0 && curY < bitmap->getHeight()) + drawPartCircleLine(&pipe, curY, xMin, xMax); + curY = 2 * yo - curY; + if (curY >= 0 && curY < bitmap->getHeight()) + drawPartCircleLine(&pipe, curY, xMin, xMax); + } + while (shading->getNextLargerExtendCircle(&xi, &yi, &iradius, fExtend)) { + if (xi == xo && yi == yo && iradius == oradius) { + continue; + } + SplashPath *bresInnerPoints = drawCircle(xo, yo, oradius, cur, bresOuterPoints, + xi, yi, iradius, cur, gTrue, &pipe); + if (bresInnerPoints == NULL) + break; + delete bresOuterPoints; + bresOuterPoints = bresInnerPoints; + yo = yi; xo = xi; oradius = iradius; + } + delete bresOuterPoints; + } + + shading->getStartCircle(&xo, &yo, &oradius, cur); + SplashPath *bresOuterPoints = getBresenhamPoints(xo, yo, oradius); + if (bresOuterPoints == NULL) + return splashErrEmptyPath; + while (shading->getNextCircle(&xi, &yi, &iradius, curNext)) { + if (xi == xo && yi == yo && iradius == oradius) { + splashColorCopy(cur, curNext); + continue; + } + SplashPath *bresInnerPoints = drawCircle(xo, yo, oradius, cur, bresOuterPoints, + xi, yi, iradius, curNext, fExtend, &pipe); + if (bresInnerPoints == NULL) + break; + delete bresOuterPoints; + bresOuterPoints = bresInnerPoints; + yo = yi; xo = xi; oradius = iradius; + } + delete bresOuterPoints; + + // extend ending circle + if (!fExtend && shading->getSmallerExtendCircle(&xo, &yo, &oradius, cur, fExtend)) { + SplashPath *bresOuterPoints = getBresenhamPoints(xo, yo, oradius); + if (bresOuterPoints == NULL) + return splashErrEmptyPath; + while (shading->getNextSmallerExtendCircle(&xi, &yi, &iradius, fExtend)) { + if (xi == xo && yi == yo && iradius == oradius) { + continue; + } + SplashPath *bresInnerPoints = drawCircle(xo, yo, oradius, cur, bresOuterPoints, + xi, yi, iradius, cur, fExtend, &pipe); + if (bresInnerPoints == NULL) + break; + delete bresOuterPoints; + bresOuterPoints = bresInnerPoints; + yo = yi; xo = xi; oradius = iradius; + } + delete bresOuterPoints; + } + if (fExtend && shading->getLargerExtendCircle(&xo, &yo, &oradius, cur)) { + SplashPath *bresOuterPoints = getBresenhamPoints(xo, yo, oradius); + if (bresOuterPoints == NULL) + return splashErrEmptyPath; + // don't fill the circle here, just extend: + while (shading->getNextLargerExtendCircle(&xi, &yi, &iradius, gTrue)) { + if (xi == xo && yi == yo && iradius == oradius) { + continue; + } + SplashPath *bresInnerPoints = drawCircle(xo, yo, oradius, cur, bresOuterPoints, + xi, yi, iradius, cur, gTrue, &pipe); + if (bresInnerPoints == NULL) + break; + delete bresOuterPoints; + bresOuterPoints = bresInnerPoints; + yo = yi; xo = xi; oradius = iradius; + } + delete bresOuterPoints; + } + return splashOk; +} + SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc, int xDest, int yDest, int w, int h) { SplashColor pixel; @@ -3950,19 +4286,25 @@ SplashError Splash::shadedFill(SplashPath *path, GBool hasBBox, int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y; SplashClipResult clipRes; - if (aaBuf == NULL) { // should not happen, but to be secure + if (vectorAntialias && aaBuf == NULL) { // should not happen, but to be secure return splashErrGeneric; } if (path->length == 0) { return splashErrEmptyPath; } xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue); - xPath->aaScale(); + if (vectorAntialias) { + xPath->aaScale(); + } xPath->sort(); scanner = new SplashXPathScanner(xPath, gFalse); - // get the min and max x and y values - scanner->getBBoxAA(&xMinI, &yMinI, &xMaxI, &yMaxI); + // get the min and max x and y values + if (vectorAntialias) { + scanner->getBBoxAA(&xMinI, &yMinI, &xMaxI, &yMaxI); + } else { + scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI); + } // check clipping if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI)) != splashClipAllOutside) { @@ -3977,13 +4319,34 @@ SplashError Splash::shadedFill(SplashPath *path, GBool hasBBox, pipeInit(&pipe, 0, yMinI, pattern, NULL, state->fillAlpha, vectorAntialias && !hasBBox, gFalse); // draw the spans - 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); - } + 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 { + SplashClipResult clipRes2; + for (y = yMinI; y <= yMaxI; ++y) { + while (scanner->getNextSpan(y, &x0, &x1)) { + if (clipRes == splashClipAllInside) { + drawSpan(&pipe, x0, x1, y, gTrue); + } else { + // limit the x range + if (x0 < state->clip->getXMinI()) { + x0 = state->clip->getXMinI(); + } + if (x1 > state->clip->getXMaxI()) { + x1 = state->clip->getXMaxI(); + } + clipRes2 = state->clip->testSpan(x0, x1, y); + drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside); + } + } + } + } } opClipRes = clipRes; diff --git a/splash/Splash.h b/splash/Splash.h index a52dc13..a06f694 100644 --- a/splash/Splash.h +++ b/splash/Splash.h @@ -255,6 +255,17 @@ public: SplashPattern *pattern); // Draw a gouraud triangle shading. GBool gouraudTriangleShadedFill(SplashGouraudColor *shading); + // Draw a radial shading. + void drawPartCircleLine(SplashPipe *pipe, SplashCoord y, SplashCoord xMin, SplashCoord xMax); + void drawBCircleLine(SplashPipe *pipe, SplashPath *bresInnerPoints, + SplashCoord y, SplashCoord yiMin, SplashCoord yiMax, + SplashCoord yi, SplashCoord xi, SplashCoord iradius, + SplashCoord xMin, SplashCoord xMax, int lineWidth); + SplashPath *getBresenhamPoints(SplashCoord x0, SplashCoord y0, SplashCoord radius); + SplashPath *drawCircle(SplashCoord xo, SplashCoord yo, SplashCoord oradius, SplashColorPtr cur, + SplashPath *bresOuterPoints, SplashCoord xi, SplashCoord yi, SplashCoord iradius, + SplashColorPtr curNext, GBool fExtend, SplashPipe *pipe); + SplashError radialShadedFill(SplashRadialColor *shading); private: diff --git a/splash/SplashPattern.h b/splash/SplashPattern.h index 09e9b1a..02cc18d 100644 --- a/splash/SplashPattern.h +++ b/splash/SplashPattern.h @@ -28,6 +28,8 @@ #include "SplashTypes.h" class SplashScreen; +class Splash; +class SplashBitmap; //------------------------------------------------------------------------ // SplashPattern @@ -92,4 +94,22 @@ public: virtual void getParameterizedColor(double t, SplashColorMode mode, SplashColorPtr c) = 0; }; +//------------------------------------------------------------------------ +// SplashRadialColor (needed for radialShadedFill) +//------------------------------------------------------------------------ + +class SplashRadialColor: public SplashPattern { +public: + virtual void getStartCircle(SplashCoord *x0, SplashCoord *y0, SplashCoord *radius, SplashColorPtr c) = 0; + virtual GBool getNextCircle(SplashCoord *x0, SplashCoord *y0, SplashCoord *radius, SplashColorPtr c) = 0; + virtual GBool getSmallerExtendCircle(SplashCoord *x0, SplashCoord *y0, SplashCoord *radius, SplashColorPtr c, GBool fExtend) = 0; + virtual GBool getNextSmallerExtendCircle(SplashCoord *x0, SplashCoord *y0, SplashCoord *radius, GBool fExtend) = 0; + virtual GBool getLargerExtendCircle(SplashCoord *x0, SplashCoord *y0, SplashCoord *radius, SplashColorPtr c) = 0; + virtual GBool getNextLargerExtendCircle(SplashCoord *x0, SplashCoord *y0, SplashCoord *radius, GBool fExtend) = 0; + virtual GBool enlargeCircles() = 0; + virtual GBool isOk() = 0; + virtual Splash *getSplash() = 0; + virtual SplashBitmap *getBitmap() = 0; +}; + #endif