diff --git a/poppler/SplashOutputDev.cc b/poppler/SplashOutputDev.cc index 0b3722a..cc0af1a 100644 --- a/poppler/SplashOutputDev.cc +++ b/poppler/SplashOutputDev.cc @@ -153,6 +153,476 @@ 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 32768 + +// 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, sa; + 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 (splashFloor(xMax) - splashFloor(xMin) > width) + width = splashFloor(xMax) - splashFloor(xMin); + if (splashFloor(yMax) - splashFloor(yMin) > height) + height = splashFloor(yMax) - splashFloor(yMin); + if (radialMaxSize > 0 && width > 0 && height > 0) { + if ( width > height) + scale = radialMaxSize / height; + else + scale = radialMaxSize / width; + } else + scale = 1; + if (scale < .5 && radialMaxSize > 0 && (r1 > 0 || r0 > 0)) { + if ( r1 > r0) + scale = radialMaxSize / r1; + else + scale = radialMaxSize / r0; + } else if (scale < .5) + scale = 1; + + // calculate sMax for larger extend circle + sMaxExtend = sMax; + sMinExtend = sMin; + maxSplitsExtend = radialMinSplits; + if ((shading->getExtend0() && r0 > r1) || + (shading->getExtend1() && r1 >= r0)) { + // solve for x(s) + r(s) = xMin or x(s) + r(s) = xMax + if ((x1 + r1) - (x0 + r0) != 0) { + sa = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0)); + if (sa < sMinExtend) { + sMinExtend = sa; + } else if (sa > sMaxExtend) { + sMaxExtend = sa; + } + sa = (xMax - (x0 + r0)) / ((x1 + r1) - (x0 + r0)); + if (sa < sMinExtend) { + sMinExtend = sa; + } else if (sa > sMaxExtend) { + sMaxExtend = sa; + } + } + // solve for x(s) - r(s) = xMax and x(s) - r(s) = xMin + if ((x1 - r1) - (x0 - r0) != 0) { + sa = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0)); + if (sa < sMinExtend) { + sMinExtend = sa; + } else if (sa > sMaxExtend) { + sMaxExtend = sa; + } + sa = (xMin - (x0 - r0)) / ((x1 - r1) - (x0 - r0)); + if (sa < sMinExtend) { + sMinExtend = sa; + } else if (sa > sMaxExtend) { + sMaxExtend = sa; + } + } + // solve for y(s) + r(s) = yMin or y(s) + r(s) = yMax + if ((y1 + r1) - (y0 + r0) != 0) { + sa = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0)); + if (sa < sMinExtend) { + sMinExtend = sa; + } else if (sa > sMaxExtend) { + sMaxExtend = sa; + } + sa = (yMax - (y0 + r0)) / ((y1 + r1) - (y0 + r0)); + if (sa < sMinExtend) { + sMinExtend = sa; + } else if (sa > sMaxExtend) { + sMaxExtend = sa; + } + } + // solve for y(s) - r(s) = yMax or y(s) - r(s) = yMin + if ((y1 - r1) - (y0 - r0) != 0) { + sa = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0)); + if (sa < sMinExtend) { + sMinExtend = sa; + } else if (sa > sMaxExtend) { + sMaxExtend = sa; + } + sa = (yMin - (y0 - r0)) / ((y1 - r1) - (y0 - r0)); + if (sa < sMinExtend) { + sMinExtend = sa; + } else if (sa > sMaxExtend) { + sMaxExtend = sa; + } + } + if (abs(sMinExtend) > abs(sMaxExtend)) + sMaxExtend = (sMinExtend < 0) ? -sMinExtend : sMinExtend; + else + sMinExtend = (sMaxExtend > 0) ? -sMaxExtend : sMaxExtend; + + sMaxExtend *= 4; + sMinExtend *= 4; + // limit sMin/MaxExtend by memory (need for Bresenham pts) + if (r1 != r0) { + sa = 15000000 / scale - r0; + if (r1 > r0) + sa = sa / (r1 - r0); + else + sa = sa / (r0 - r1); + if (sMinExtend < -sa) { + sMinExtend = -sa; + maxSplitsExtend = 16; + } + if (sMaxExtend > sa) { + sMaxExtend = sa; + maxSplitsExtend = 16; + } + } + } + + xMin *= scale; + xMax *= scale; + yMin *= scale; + yMax *= scale; + width *= scale; + height *= scale; + if (width > 0 && height > 0) { + bitmap = new SplashBitmap(splashRound(width), splashRound(height), 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, xMin, yMin, xMax, yMax; + Guchar *bitmapAlpha; + Gulong superCell[splashMaxColorComps]; + SplashColor cell; + int cp = 0, i, ca = 0; + + for (i = 0; i < splashMaxColorComps; i++) + superCell[i] = 0; + bitmapAlpha = bitmap->getAlphaPtr(); + ictm.transform(x, y, &xc, &yc); + xMin = splashFloor(xc); + yMin = splashFloor(yc); + if (xMin < 0) xMin = 0; + if (xMin >= bitmap->getWidth()) xMin = bitmap->getWidth() - 1; + if (yMin < 0) yMin = 0; + if (yMin >= bitmap->getHeight()) yMin = bitmap->getHeight() - 1; + ictm.transform(x + 1, y + 1, &xc, &yc); + xMax = splashFloor(xc); + yMax = splashFloor(yc); + if (xMax < 0) xMax = 0; + if (xMax >= bitmap->getWidth()) xMax = bitmap->getWidth() - 1; + if (yMax < 0) yMax = 0; + if (yMax >= bitmap->getHeight()) yMax = bitmap->getHeight() - 1; + if (xMax < xMin) { i = xMax; xMax = xMin; xMin = i; } + if (yMax < yMin) { i = yMax; yMax = yMin; yMin = i; } + for (ys = yMin; ys <= yMax; ys++) { + for (xs = xMin; xs <= xMax; xs++) { + // 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: + cp++; + if (bitmapAlpha[ys * bitmap->getWidth() + xs]) { + bitmap->getPixel(xs, ys, cell); + for (i = 0; i < splashMaxColorComps; i++) + superCell[i] += cell[i]; + } else { + ca++; + } + } + } + if (ca < cp) { + for (i = 0; i < splashMaxColorComps; i++) + c[i] = (Guchar) (superCell[i] / (cp - ca)); + return gTrue; + } + return gFalse; +} + +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 = splashFloor(ra * scale); + *xsc = splashFloor(xa * scale - xMin); *ysc = splashFloor(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 = sMax; + 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 = splashFloor(ra * scale); + *xsc = splashFloor(xa * scale - xMin); *ysc = splashFloor(ya * scale - yMin); + convertGfxColor(c, colorMode, srcColorSpace, &colorB); + ia = (maxSplits > radialMinSplits && (*xsc < -100 || *ysc < -100) && ib + 32 < maxSplits) ? ib + 32 : 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 = splashFloor(ra * scale); + *xsc = splashFloor(xa * scale - xMin); *ysc = splashFloor(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 >= maxSplitsExtend) + return gFalse; + if (x0 == x1 && y0 == y1) + ia = maxSplitsExtend; + sStart = (r0 > r1) ? sMin : sMax; + // buest guess: + sEnd = (fExtend) ? sMaxExtend : -sMaxExtend; + factor = (double)ia / (double)maxSplitsExtend; + 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; + *radius = splashFloor(ra); + *xsc = splashFloor(xa); *ysc = splashFloor(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 = splashFloor(ra * scale); + *xsc = splashFloor(xa * scale - xMin); *ysc = splashFloor(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 = splashFloor(ra * scale); + *xsc = splashFloor(xa * scale - xMin); *ysc = splashFloor(ya * scale - yMin); + return gTrue; +} + +SplashClipResult SplashRadialPattern::testClip(SplashCoord x, SplashCoord y, SplashCoord r) { + if (x + r < 0 || y + r < 0 || x - r >= bitmap->getWidth() || y - r >= bitmap->getHeight()) + return splashClipAllOutside; + double rs = r * r; + double xls = x * x; + double xrs = (x - bitmap->getWidth()) * (x - bitmap->getWidth()); + double yls = y * y; + double yus = (y - bitmap->getHeight()) * (y - bitmap->getHeight()); + if (xls + yus < rs && xrs + yus < rs && + xls + yls < rs && xrs + yls < rs) + return splashClipAllInside; + return splashClipPartial; +} + +GBool SplashRadialPattern::encloseCircle(SplashCoord x1, SplashCoord y1, SplashCoord r1, + SplashCoord x2, SplashCoord y2, SplashCoord r2) { + if (x1 == x2 && y1 == y2) + return r2 < r1; + double dist = sqrt((x1 -x2) * (x1 -x2) + (y1 - y2) * (y1 - y2)); + return dist + r2 < r1; +} + +//------------------------------------------------------------------------ // SplashAxialPattern //------------------------------------------------------------------------ @@ -3011,14 +3481,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 +3849,37 @@ 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 + if (shading->getHasBBox()) + shading->getBBox(&xMin, &yMin, &xMax, &yMax); + else + 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..5be95b5 100644 --- a/poppler/SplashOutputDev.h +++ b/poppler/SplashOutputDev.h @@ -105,6 +105,52 @@ 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; } + virtual SplashClipResult testClip(SplashCoord x, SplashCoord y, SplashCoord r); + virtual GBool encloseCircle(SplashCoord x0, SplashCoord y0, SplashCoord r0, + SplashCoord x1, SplashCoord y1, SplashCoord r1); +private: + GfxRadialShading *shading; + GfxState *state; + Matrix ictm; + double sMin, sMax, sMaxExtend, sMinExtend; + double xMin, xMax, yMin, yMax; + double scale; + double xa, ya, ra, ta, sa; + int ia; + int maxSplits, maxSplitsExtend; + double x0, y0, r0, x1, y1, r1, t0, t1; + Splash *splash; + SplashBitmap *bitmap; + SplashColorMode colorMode; +}; + //------------------------------------------------------------------------ // number of Type 3 fonts to cache @@ -132,7 +178,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 +232,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..0d9f5a8 100644 --- a/splash/Splash.cc +++ b/splash/Splash.cc @@ -253,30 +253,8 @@ inline void Splash::pipeRun(SplashPipe *pipe) { // dynamic pattern if (pipe->pattern) { if (!pipe->pattern->getColor(pipe->x, pipe->y, pipe->cSrcVal)) { - switch (bitmap->mode) { - case splashModeMono1: - if (!(pipe->destColorMask >>= 1)) - ++pipe->destColorPtr; - break; - case splashModeMono8: - ++pipe->destColorPtr; - break; - case splashModeBGR8: - case splashModeRGB8: - pipe->destColorPtr += 3; - break; - case splashModeXBGR8: -#if SPLASH_CMYK - case splashModeCMYK8: -#endif - pipe->destColorPtr += 4; - break; - } - if (pipe->destAlphaPtr) { - ++pipe->destAlphaPtr; - } - ++pipe->x; - return; + pipeIncX(pipe); + return; } } @@ -3531,6 +3509,697 @@ 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 - 1); + if (xiMax != xMax) + drawPartCircleLine(pipe, y, xiMax+ 1, 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); + } + } +// fprintf(stderr, "Not drawn: %d\n", notdrawn); + 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; + int tl, tr, tu, td, enclosure; + SplashCoord ydl, yul, xll, xrl; + 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; + if (oradius == 0) + drawPartCircleLine(&pipe, yo, xo, xo); + 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; + tl = tr = tu = td = enclosure = 0; + ydl = xll = yul = xrl = -1; + // draw upper half + int startPoint = 0; + if (yo + oradius >= bitmap->getHeight()) { + startPoint = splashRound(yo + oradius - 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; + bresOuterPoints->getPoint(i, &curX, &curY, &dummy); + curY = yo - curY; + xMax = xo + curX; + xMin = xo - curX; + drawPartCircleLine(&pipe, curY, xMin, xMax); + } + // draw lower half + endPoint = splashRound(bitmap->getHeight() - (yo - oradius)); + 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; + bresOuterPoints->getPoint(i, &curX, &curY, &dummy); + curY += yo; + xMax = xo + curX; + xMin = xo - curX; + 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; + // test for enclosure + if (shading->encloseCircle(xi, yi, iradius, xo, yo, oradius)) + enclosure++; + else + enclosure = 0; + // test for tangents + if (yo == yi && abs(xi - xo + oradius - iradius) < 2) { + if (xll == -1) + xll = xi - iradius; + if (abs(xll - xo + oradius) < 2) + tl++; + else { + tl = 0; + xll = xi - iradius; + } + } + if (yo == yi && abs(xi - xo + iradius - oradius) < 2) { + if (xrl == -1) + xrl = xi + iradius; + if (abs(xrl - xo - oradius) < 2) + tr++; + else { + tr = 0; + xrl = xi + iradius; + } + } + if (xo == xi && abs(yi - yo + oradius - iradius) < 2) { + if (ydl == -1) + ydl = yi - iradius; + if (abs(ydl - yo + oradius) < 2) + td++; + else { + td = 0; + ydl = yi - iradius; + } + } + if (xo == xi && abs(yi - yo + iradius - oradius) < 2) { + if (yul == -1) + yul = yi + iradius; + if (abs(yul - yo - oradius) < 2) + tu++; + else { + tu = 0; + yul = yi + iradius; + } + } + // do enclosure + if (enclosure == 6) { + SplashCoord curX, curY; + SplashCoord xMin, xMax; + Guchar dummy; + xll = (tl >= 6) ? xi - iradius : 0; + xrl = (tr >= 6) ? xi + iradius : bitmap->getWidth() - 1; + ydl = (td >= 6) ? yi - iradius : 0; + yul = (tu >= 6) ? yi + iradius : bitmap->getHeight() - 1; + bresOuterPoints->getPoint(0, &curX, &curY, &dummy); + curY += yo; + for (int i = splashRound(curY) + 1; i < bitmap->getHeight(); i++) + if (i >= ydl && i <= yul) + drawPartCircleLine(&pipe, i, xll, xrl); + for (int i = 0; i < bresOuterPoints->getLength(); i++) { + bresOuterPoints->getPoint(i, &curX, &curY, &dummy); + curY += yo; + xMax = xo + curX; + xMin = xo - curX; + if (curY >= ydl && curY <= yul) { + if (xMin > 0) + drawPartCircleLine(&pipe, curY, xll, xMin); + if (xMax < bitmap->getWidth()) + drawPartCircleLine(&pipe, curY, xMax, xrl); + } + curY = 2 * yo - curY; + if (curY >= ydl && curY <= yul) { + if (xMin > 0) + drawPartCircleLine(&pipe, curY, xll, xMin); + if (xMax < bitmap->getWidth()) + drawPartCircleLine(&pipe, curY, xMax, xrl); + } + } + bresOuterPoints->getPoint(0, &curX, &curY, &dummy); + curY = yo - curY; + for (int i = 0; i < curY; i++) + if (i >= ydl && i <= yul) + drawPartCircleLine(&pipe, i, xll, xrl); + delete bresOuterPoints; + bresOuterPoints = bresInnerPoints; + break; + } + // do tangents if other side is completely outside + if (tl >= 6) { + if (shading->encloseCircle(xi, yi, iradius, bitmap->getWidth() - 1, bitmap->getHeight() - 1, 0) && + shading->encloseCircle(xi, yi, iradius, bitmap->getWidth() - 1, 0, 0)) { + for (int i = 0; i < bitmap->getHeight(); i++) + drawPartCircleLine(&pipe, i, xi - iradius, xi - iradius); + delete bresOuterPoints; + bresOuterPoints = bresInnerPoints; + break; + } + } + if (tr >= 6) { + if (shading->encloseCircle(xi, yi, iradius, 0, bitmap->getHeight() - 1, 0) && + shading->encloseCircle(xi, yi, iradius, 0, 0, 0)) { + for (int i = 0; i < bitmap->getHeight(); i++) + drawPartCircleLine(&pipe, i, xi + iradius, xi + iradius); + delete bresOuterPoints; + bresOuterPoints = bresInnerPoints; + break; + } + } + if (tu >= 6) { + if (shading->encloseCircle(xi, yi, iradius, bitmap->getWidth() - 1, 0, 0) && + shading->encloseCircle(xi, yi, iradius, 0, 0, 0)) { + drawPartCircleLine(&pipe, yi + iradius, 0, bitmap->getWidth() - 1); + delete bresOuterPoints; + bresOuterPoints = bresInnerPoints; + break; + } + } + if (td >= 6) { + if (shading->encloseCircle(xi, yi, iradius, bitmap->getWidth() - 1, bitmap->getHeight() - 1, 0) && + shading->encloseCircle(xi, yi, iradius, 0, bitmap->getHeight() - 1, 0)) { + drawPartCircleLine(&pipe, yi - iradius, 0, bitmap->getWidth() - 1); + delete bresOuterPoints; + bresOuterPoints = bresInnerPoints; + break; + } + } + delete bresOuterPoints; + bresOuterPoints = bresInnerPoints; + yo = yi; xo = xi; oradius = iradius; + } + delete bresOuterPoints; + } + + shading->getStartCircle(&xo, &yo, &oradius, cur); + if (oradius == 0) + drawPartCircleLine(&pipe, yo, xo, xo); + SplashPath *bresOuterPoints = getBresenhamPoints(xo, yo, oradius); + if (bresOuterPoints == NULL) + return splashErrEmptyPath; + tl = tr = tu = td = 0; + ydl = xll = yul = xrl = -1; + while (shading->getNextCircle(&xi, &yi, &iradius, curNext)) { + if (xi == xo && yi == yo && iradius == oradius) { + splashColorCopy(cur, curNext); + continue; + } + // test for tangents + if (yo == yi && abs(xi - xo + oradius - iradius) < 2) { + if (xll == -1) + xll = xi - iradius; + if (abs(xll - xo + oradius) < 2) + tl++; + else { + tl = 0; + xll = xi - iradius; + } + } + if (yo == yi && abs(xi - xo + iradius - oradius) < 2) { + if (xrl == -1) + xrl = xi + iradius; + if (abs(xrl - xo - oradius) < 2) + tr++; + else { + tr = 0; + xrl = xi + iradius; + } + } + if (xo == xi && abs(yi - yo + oradius - iradius) < 2) { + if (ydl == -1) + ydl = yi - iradius; + if (abs(ydl - yo + oradius) < 2) + td++; + else { + td = 0; + ydl = yi - iradius; + } + } + if (xo == xi && abs(yi - yo + iradius - oradius) < 2) { + if (yul == -1) + yul = yi + iradius; + if (abs(yul - yo - oradius) < 2) + tu++; + else { + tu = 0; + yul = yi + iradius; + } + } + if (shading->testClip(xi, yi, iradius) != splashClipAllOutside) { + SplashPath *bresInnerPoints = drawCircle(xo, yo, oradius, cur, bresOuterPoints, + xi, yi, iradius, curNext, fExtend, &pipe); + if (bresInnerPoints == NULL) + break; + delete bresOuterPoints; + bresOuterPoints = bresInnerPoints; + } + if (shading->testClip(xi, yi, iradius) == splashClipAllInside && iradius >= oradius) + break; + // do tangents if other side is completely outside + if (tl >= 256 && fExtend) { + if (shading->encloseCircle(xi, yi, iradius, bitmap->getWidth() - 1, bitmap->getHeight() - 1, 0) && + shading->encloseCircle(xi, yi, iradius, bitmap->getWidth() - 1, 0, 0)) { + break; + } + } + if (tr >= 256 && fExtend) { + if (shading->encloseCircle(xi, yi, iradius, 0, bitmap->getHeight() - 1, 0) && + shading->encloseCircle(xi, yi, iradius, 0, 0, 0)) { + break; + } + } + if (tu >= 256 && fExtend) { + if (shading->encloseCircle(xi, yi, iradius, bitmap->getWidth() - 1, 0, 0) && + shading->encloseCircle(xi, yi, iradius, 0, 0, 0)) { + break; + } + } + if (td >= 256 && fExtend) { + if (shading->encloseCircle(xi, yi, iradius, bitmap->getWidth() - 1, bitmap->getHeight() - 1, 0) && + shading->encloseCircle(xi, yi, iradius, 0, bitmap->getHeight() - 1, 0)) { + break; + } + } + 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; + } + if (shading->testClip(xo, yo, oradius) == splashClipAllOutside) + break; + 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)) { + tl = tr = tu = td = enclosure = 0; + ydl = xll = yul = xrl = -1; + 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; + } + if (shading->testClip(xo, yo, oradius) == splashClipAllInside) + break; + SplashPath *bresInnerPoints = drawCircle(xo, yo, oradius, cur, bresOuterPoints, + xi, yi, iradius, cur, gTrue, &pipe); + if (bresInnerPoints == NULL) + break; + // test for enclosure + if (shading->encloseCircle(xi, yi, iradius, xo, yo, oradius)) + enclosure++; + else + enclosure = 0; + // test for tangents + if (yo == yi && abs(xi - xo + oradius - iradius) < 2) { + if (xll == -1) + xll = xi - iradius; + if (abs(xll - xo + oradius) < 2) + tl++; + else { + tl = 0; + xll = xi - iradius; + } + } + if (yo == yi && abs(xi - xo + iradius - oradius) < 2) { + if (xrl == -1) + xrl = xi + iradius; + if (abs(xrl - xo - oradius) < 2) + tr++; + else { + tr = 0; + xrl = xi + iradius; + } + } + if (xo == xi && abs(yi - yo + oradius - iradius) < 2) { + if (ydl == -1) + ydl = yi - iradius; + if (abs(ydl - yo + oradius) < 2) + td++; + else { + td = 0; + ydl = yi - iradius; + } + } + if (xo == xi && abs(yi - yo + iradius - oradius) < 2) { + if (yul == -1) + yul = yi + iradius; + if (abs(yul - yo - oradius) < 2) + tu++; + else { + tu = 0; + yul = yi + iradius; + } + } + // do enclosure + if (enclosure == 6) { + SplashCoord curX, curY; + SplashCoord xMin, xMax; + Guchar dummy; + xll = (tl >= 6) ? xi - iradius : 0; + xrl = (tr >= 6) ? xi + iradius : bitmap->getWidth() - 1; + ydl = (td >= 6) ? yi - iradius : 0; + yul = (tu >= 6) ? yi + iradius : bitmap->getHeight() - 1; + bresOuterPoints->getPoint(0, &curX, &curY, &dummy); + curY += yo; + for (int i = splashRound(curY) + 1; i < bitmap->getHeight(); i++) + if (i >= ydl && i <= yul) + drawPartCircleLine(&pipe, i, xll, xrl); + for (int i = 0; i < bresOuterPoints->getLength(); i++) { + bresOuterPoints->getPoint(i, &curX, &curY, &dummy); + curY += yo; + xMax = xo + curX; + xMin = xo - curX; + if (curY >= ydl && curY <= yul) { + if (xMin > 0) + drawPartCircleLine(&pipe, curY, xll, xMin); + if (xMax < bitmap->getWidth()) + drawPartCircleLine(&pipe, curY, xMax, xrl); + } + curY = 2 * yo - curY; + if (curY >= ydl && curY <= yul) { + if (xMin > 0) + drawPartCircleLine(&pipe, curY, xll, xMin); + if (xMax < bitmap->getWidth()) + drawPartCircleLine(&pipe, curY, xMax, xrl); + } + } + bresOuterPoints->getPoint(0, &curX, &curY, &dummy); + curY = yo - curY; + for (int i = 0; i < curY; i++) + if (i >= ydl && i <= yul) + drawPartCircleLine(&pipe, i, xll, xrl); + delete bresOuterPoints; + bresOuterPoints = bresInnerPoints; + break; + } + // do tangents if other side is completely outside + if (tl >= 6) { + if (shading->encloseCircle(xi, yi, iradius, bitmap->getWidth() - 1, bitmap->getHeight() - 1, 0) && + shading->encloseCircle(xi, yi, iradius, bitmap->getWidth() - 1, 0, 0)) { + for (int i = 0; i < bitmap->getHeight(); i++) + drawPartCircleLine(&pipe, i, xi - iradius, xi - iradius); + delete bresOuterPoints; + bresOuterPoints = bresInnerPoints; + break; + } + } + if (tr >= 6) { + if (shading->encloseCircle(xi, yi, iradius, 0, bitmap->getHeight() - 1, 0) && + shading->encloseCircle(xi, yi, iradius, 0, 0, 0)) { + for (int i = 0; i < bitmap->getHeight(); i++) + drawPartCircleLine(&pipe, i, xi + iradius, xi + iradius); + delete bresOuterPoints; + bresOuterPoints = bresInnerPoints; + break; + } + } + if (tu >= 6) { + if (shading->encloseCircle(xi, yi, iradius, bitmap->getWidth() - 1, 0, 0) && + shading->encloseCircle(xi, yi, iradius, 0, 0, 0)) { + drawPartCircleLine(&pipe, yi + iradius, 0, bitmap->getWidth() - 1); + delete bresOuterPoints; + bresOuterPoints = bresInnerPoints; + break; + } + } + if (td >= 6) { + if (shading->encloseCircle(xi, yi, iradius, bitmap->getWidth() - 1, bitmap->getHeight() - 1, 0) && + shading->encloseCircle(xi, yi, iradius, 0, bitmap->getHeight() - 1, 0)) { + drawPartCircleLine(&pipe, yi - iradius, 0, bitmap->getWidth() - 1); + delete bresOuterPoints; + bresOuterPoints = bresInnerPoints; + 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 +4619,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 +4652,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..f0b5f36 100644 --- a/splash/SplashPattern.h +++ b/splash/SplashPattern.h @@ -26,8 +26,11 @@ #endif #include "SplashTypes.h" +#include "SplashClip.h" class SplashScreen; +class Splash; +class SplashBitmap; //------------------------------------------------------------------------ // SplashPattern @@ -92,4 +95,25 @@ 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 SplashClipResult testClip(SplashCoord x, SplashCoord y, SplashCoord r) = 0; + virtual GBool encloseCircle(SplashCoord x0, SplashCoord y0, SplashCoord r0, + SplashCoord x1, SplashCoord y1, SplashCoord r1) = 0; + virtual GBool enlargeCircles() = 0; + virtual GBool isOk() = 0; + virtual Splash *getSplash() = 0; + virtual SplashBitmap *getBitmap() = 0; +}; + #endif