diff --git a/poppler/SplashOutputDev.cc b/poppler/SplashOutputDev.cc index 0b3722a..b2fd5ee 100644 --- a/poppler/SplashOutputDev.cc +++ b/poppler/SplashOutputDev.cc @@ -153,6 +153,270 @@ void SplashGouraudPattern::getParameterizedColor(double colorinterp, SplashColor } //------------------------------------------------------------------------ +// SplashRadialPattern +//------------------------------------------------------------------------ +// Max number of splits along the t axis for a radial shading fill. +#define radialMaxSplits 256 + +// 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; + sMin = sMinA; + sMax = sMaxA; + colorMode = colorModeA; + state->getCTM(&ctm); + ctm.invertTo(&ictm); + ia = 0; + 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; + /* + xMin = splashRound(xMin); + xMax = splashRound(xMax); + yMin = splashRound(yMin); + yMax = splashRound(yMax); + */ + 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) + 1, splashRound(height) + 1, 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; +} + +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) + 1; + ys = splashRound(yc); + //if (xs == -1) xs = 0; + if (xs < 0 || xs >= bitmap->getWidth()) + return gFalse; + if (ys < 0 || ys >= bitmap->getHeight()) + return gFalse; + 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(); + sa = (r0 > r1) ? sMin : sMax; + 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 >= radialMaxSplits) + return gFalse; + getShadingColorRadialHelper(t0, t1, ta, shading, &colorA); + ib = radialMaxSplits; + sb = (r0 > r1) ? 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 = (r0 > r1) ? (double)ic / (double)radialMaxSplits : 1 - (double)ic / (double)radialMaxSplits; + 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 = (r0 > r1) ? (double)ib / (double)radialMaxSplits : 1 - (double)ib / (double)radialMaxSplits; + sb = sMin + factor * (sMax - sMin); + tb = t0 + sb * (t1 - t0); + getShadingColorRadialHelper(t0, t1, tb, shading, &colorB); + break; + } + ib = (ia + ib) / 2; + factor = (r0 > r1) ? (double)ib / (double)radialMaxSplits : 1 - (double)ib / (double)radialMaxSplits; + 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); + return gTrue; + } + return gFalse; +} + +GBool SplashRadialPattern::getSmallerExtendCircle(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); + return gTrue; + } + return gFalse; +} +//------------------------------------------------------------------------ // SplashAxialPattern //------------------------------------------------------------------------ @@ -3011,14 +3275,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 +3643,35 @@ 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..1e243c2 100644 --- a/poppler/SplashOutputDev.h +++ b/poppler/SplashOutputDev.h @@ -105,6 +105,45 @@ 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 getSmallerExtendCircle(SplashCoord *x0, SplashCoord *y0, SplashCoord *radius, SplashColorPtr c); +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; + double x0, y0, r0, x1, y1, r1, t0, t1; + Splash *splash; + SplashBitmap *bitmap; + SplashColorMode colorMode; +}; + //------------------------------------------------------------------------ // number of Type 3 fonts to cache @@ -132,7 +171,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 +225,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..ef48921 100644 --- a/splash/Splash.cc +++ b/splash/Splash.cc @@ -3531,6 +3531,257 @@ 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) + { + // ddF_x == 2 * x + 1; + // ddF_y == -2 * y; + // f == x*x + y*y - radius*radius + 2*x - y + 1; + 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 getBEllipseLine(SplashPath *bresPoints, SplashCoord y, SplashCoord y0, SplashCoord r, SplashCoord *x) { + GBool found = gFalse; + if (y == y0) + *x = r; + else if (y > y0) { + SplashCoord curX, curY; + Guchar dummy; + for (int i = 0; i < bresPoints->getLength(); i++) { + bresPoints->getPoint(i, &curX, &curY, &dummy); + if (curY + y0 == y) { + *x = curX; + return; + } + } + } else { + SplashCoord curX, curY; + Guchar dummy; + for (int i = bresPoints->getLength() - 1; i >= 0; i--) { + bresPoints->getPoint(i, &curX, &curY, &dummy); + if (y0 - curY == y) { + *x = curX; + return; + } + } + } +} + +void Splash::drawPartEllipseLine(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) { + if (xminI < 0) + xminI = 0; + if (xminI < width) { + if (xmaxI > width - 1) + xmaxI = width - 1; + drawSpan(pipe, xminI, xmaxI, yI, gTrue); + } + } +} + +void Splash::drawBEllipseLine(SplashPipe *pipe, SplashPath *bresInnerPoints, + SplashCoord y, SplashCoord yiMin, SplashCoord yiMax, + SplashCoord yi, SplashCoord xi, SplashCoord iradius, + SplashCoord xMin, SplashCoord xMax) { + if (y > yiMax || y < yiMin) + drawPartEllipseLine(pipe, y, xMin, xMax); + else { + SplashCoord xiMin, xiMax; + getBEllipseLine(bresInnerPoints, y, yi, iradius, &xiMin); + xiMax = xi + xiMin; + xiMin = xi - xiMin; + if (xiMin >= xMin || xiMax <= xMax) { + if (xiMax < xMin || xiMin > xMax) + drawPartEllipseLine(pipe, y, xMin - 1, xMax + 1); + else if (xiMin < xMin && xiMax < xMax) + drawPartEllipseLine(pipe, y, xiMax - 1, xMax + 1); + else if (xiMin > xMin && xiMax < xMax) { + drawPartEllipseLine(pipe, y, xMin - 1, xiMin + 1); + drawPartEllipseLine(pipe, y, xiMax - 1, xMax + 1); + } else + drawPartEllipseLine(pipe, y, xMin - 1, xiMin + 1); + } + } +} + +SplashError Splash::radialShadedFill(SplashRadialColor *shading) { + SplashPipe pipe; + SplashColor cSrcVal, cNextVal; + SplashColorPtr cur = cSrcVal, curNext = cNextVal; + + pipeInit(&pipe, 0, 0, NULL, cSrcVal, state->strokeAlpha, gFalse, gFalse); + + SplashCoord xo, yo, oradius; + SplashCoord xi, yi, iradius; + shading->getStartCircle(&xo, &yo, &oradius, cur); + SplashPath *bresOuterPoints = getBresenhamPoints(xo, yo, oradius); + if (bresOuterPoints == NULL) + return splashErrEmptyPath; + pipe.cSrc = cur; + while (shading->getNextCircle(&xi, &yi, &iradius, curNext)) { + if (xi == xo && yi == yo && iradius == oradius) { + splashColorCopy(cur, curNext); + continue; + } + SplashPath *bresInnerPoints = getBresenhamPoints(xi, yi, iradius); + if (bresInnerPoints == NULL) + break; + SplashCoord yiMax = yi + iradius, yiMin = yi - iradius; + Guchar dummy; + + for (int i = 0; i < bresOuterPoints->getLength(); i++) { + SplashCoord curX, curY; + SplashCoord xMin, xMax; + bresOuterPoints->getPoint(i, &curX, &curY, &dummy); + xMax = xo + curX; + xMin = xo - curX; + curY += yo; + if (curY >= 0 && curY < bitmap->getHeight()) + drawBEllipseLine(&pipe, bresInnerPoints, + curY, yiMin, yiMax, yi, xi, iradius, xMin, xMax); + curY = 2 * yo - curY; + if (curY >= 0 && curY < bitmap->getHeight()) + drawBEllipseLine(&pipe, bresInnerPoints, + curY, yiMin, yiMax, yi, xi, iradius, xMin, xMax); + } + delete bresOuterPoints; + bresOuterPoints = bresInnerPoints; + yo = yi; xo = xi; oradius = iradius; + splashColorCopy(cur, curNext); + } + delete bresOuterPoints; + if (shading->getSmallerExtendCircle(&xo, &yo, &oradius, cur)) { + SplashPath *bresPoints = getBresenhamPoints(xo, yo, oradius); + if (bresPoints == NULL) + goto DrawLarge; + SplashCoord xMin, xMax; + Guchar dummy; + for (int i = 0; i < bresPoints->getLength(); i++) { + SplashCoord curX, curY; + bresPoints->getPoint(i, &curX, &curY, &dummy); + curY += yo; + xMax = xo + curX; + xMin = xo - curX; + if (curY >= 0 && curY < bitmap->getHeight()) + drawPartEllipseLine(&pipe, curY, xMin, xMax); + curY = 2 * yo - curY; + if (curY >= 0 && curY < bitmap->getHeight()) + drawPartEllipseLine(&pipe, 2* yo - curY, xMin, xMax); + } + delete bresPoints; + } +DrawLarge: + if (shading->getLargerExtendCircle(&xo, &yo, &oradius, cur)) { + SplashPath *bresPoints = getBresenhamPoints(xo, yo, oradius); + if (bresPoints == NULL) + goto RadialEnd; + SplashCoord xMin, xMax, y; + SplashCoord yMin, yMax; + yMax = yo + oradius; + yMin = yo - oradius; + Guchar dummy; + int i = 0; + if (yMax >= bitmap->getHeight()) { + i = splashRound(yMax - bitmap->getHeight() + 1); + } + if (yo >= bitmap->getHeight()) { + i = bresPoints->getLength() - 1 - splashRound(yo - bitmap->getHeight() + 1); + } + for (y = bitmap->getHeight() - 1; y >= 0; y--) { + if (y > yMax || y < yMin) + drawPartEllipseLine(&pipe, y, 0, bitmap->getWidth() - 1); + else if (y > yo) { + SplashCoord curX, curY; + bresPoints->getPoint(i++, &curX, &curY, &dummy); + xMin = xo - curX; + xMax = xo + curX; + if (xMin - 1 > 0) + drawPartEllipseLine(&pipe, y, 0, xMin + 1); + if (xMax + 1 < 0) + drawPartEllipseLine(&pipe, y, 0, bitmap->getWidth() - 1); + else if (xMax + 1 < bitmap->getWidth()) + drawPartEllipseLine(&pipe, y, xMax - 1, bitmap->getWidth() - 1); + } else if (y == yo) { + i = bresPoints->getLength() - 1; + xMin = xo - oradius; + xMax = xo + oradius; + if (xMin - 1 > 0) + drawPartEllipseLine(&pipe, y, 0, xMin + 1); + if (xMax + 1 < 0) + drawPartEllipseLine(&pipe, y, 0, bitmap->getWidth() - 1); + else if (xMax + 1 < bitmap->getWidth()) + drawPartEllipseLine(&pipe, y, xMax - 1, bitmap->getWidth() - 1); + } else { + SplashCoord curX, curY; + bresPoints->getPoint(--i, &curX, &curY, &dummy); + xMin = xo - curX; + xMax = xo + curX; + if (xMin - 1 > 0) + drawPartEllipseLine(&pipe, y, 0, xMin + 1); + if (xMax + 1 < 0) + drawPartEllipseLine(&pipe, y, 0, bitmap->getWidth() - 1); + else if (xMax + 1 < bitmap->getWidth()) + drawPartEllipseLine(&pipe, y, xMax - 1, bitmap->getWidth() - 1); + } + } + delete bresPoints; + } +RadialEnd: + return splashOk; +} + SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc, int xDest, int yDest, int w, int h) { SplashColor pixel; @@ -3950,19 +4201,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 +4234,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..37e63f9 100644 --- a/splash/Splash.h +++ b/splash/Splash.h @@ -255,6 +255,14 @@ public: SplashPattern *pattern); // Draw a gouraud triangle shading. GBool gouraudTriangleShadedFill(SplashGouraudColor *shading); + // Draw a radial shading. + void drawPartEllipseLine(SplashPipe *pipe, SplashCoord y, SplashCoord xMin, SplashCoord xMax); + void drawBEllipseLine(SplashPipe *pipe, SplashPath *bresInnerPoints, + SplashCoord y, SplashCoord yiMin, SplashCoord yiMax, + SplashCoord yi, SplashCoord xi, SplashCoord iradius, + SplashCoord xMin, SplashCoord xMax); + SplashPath *getBresenhamPoints(SplashCoord x0, SplashCoord y0, SplashCoord radius); + SplashError radialShadedFill(SplashRadialColor *shading); private: diff --git a/splash/SplashPattern.h b/splash/SplashPattern.h index 09e9b1a..fb82df2 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,19 @@ 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) = 0; + virtual GBool getLargerExtendCircle(SplashCoord *x0, SplashCoord *y0, SplashCoord *radius, SplashColorPtr c) = 0; + virtual GBool isOk() = 0; + virtual Splash *getSplash() = 0; + virtual SplashBitmap *getBitmap() = 0; +}; + #endif