diff --git a/poppler/Gfx.cc b/poppler/Gfx.cc index 50870cc..c13bb14 100644 --- a/poppler/Gfx.cc +++ b/poppler/Gfx.cc @@ -487,7 +487,7 @@ GBool GfxResources::lookupGState(char *name, Object *obj) { if (!obj->isRef()) return gTrue; - + const Ref ref = obj->getRef(); if (!gStateCache.lookup(ref, obj)->isNull()) return gTrue; @@ -714,7 +714,7 @@ void Gfx::go(GBool topLevel) { data_p = new ProfileData(); hash->add (cmd_g, data_p); } - + data_p->addElement(timer.getElapsed ()); } } @@ -2169,7 +2169,7 @@ void Gfx::doShadingPatternFill(GfxShadingPattern *sPat, #if 1 //~tmp: turn off anti-aliasing temporarily GBool vaa = out->getVectorAntialias(); - if (vaa) { + if (vaa && !textHaveCSPattern ) { out->setVectorAntialias(gFalse); } #endif @@ -2379,14 +2379,14 @@ void Gfx::doFunctionShFill1(GfxFunctionShading *shading, colors2[2] = colorM0; colors2[3] = colorMM; doFunctionShFill1(shading, x0, y0, xM, yM, colors2, depth + 1); - + // lower-left sub-rectangle colors2[0] = color0M; colors2[1] = colors[1]; colors2[2] = colorMM; colors2[3] = colorM1; doFunctionShFill1(shading, x0, yM, xM, y1, colors2, depth + 1); - + // upper-right sub-rectangle colors2[0] = colorM0; colors2[1] = colorMM; @@ -2584,10 +2584,10 @@ void Gfx::doAxialShFill(GfxAxialShading *shading) { } shading->getColor(tt, &color1); if (isSameGfxColor(color1, color0, nComps, axialColorDelta)) { - // in these two if what we guarantee is that if we are skipping lots of + // in these two if what we guarantee is that if we are skipping lots of // positions because the colors are the same, we still create a region // with vertexs passing by bboxIntersections[1] and bboxIntersections[2] - // otherwise we can have empty regions that should really be painted + // otherwise we can have empty regions that should really be painted // like happened in bug 19896 // What we do to ensure that we pass a line through this points // is making sure use the exact bboxIntersections[] value as one of the used ta[] values @@ -3332,7 +3332,7 @@ void Gfx::opBeginText(Object args[], int numArgs) { out->updateTextMat(state); out->updateTextPos(state); fontChanged = gTrue; - if (out->supportTextCSPattern(state)) { + if (!(state->getRender() & 4) && out->supportTextCSPattern(state)) { textHaveCSPattern = gTrue; } } @@ -3387,9 +3387,22 @@ void Gfx::opSetTextLeading(Object args[], int numArgs) { } void Gfx::opSetTextRender(Object args[], int numArgs) { + int rm = state->getRender(); state->setRender(args[0].getInt()); - if (args[0].getInt() == 7) { + if ((args[0].getInt() & 4) && textHaveCSPattern && drawText) { + GBool needFill = out->deviceHasTextClip(state); + out->endTextObject(state); + if (needFill) { + doPatternFill(gTrue); + } + out->restoreState(state); + out->beginTextObject(state); + out->updateTextMat(state); + out->updateTextPos(state); textHaveCSPattern = gFalse; + } else if ((rm & 4) && !(args[0].getInt() & 4) && out->supportTextCSPattern(state) && drawText) { + out->beginTextObject(state); + textHaveCSPattern = gTrue; } out->updateRender(state); } @@ -4350,7 +4363,7 @@ void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox, // draw the form display(str, gFalse); - + if (stateBefore != state) { if (state->isParentState(stateBefore)) { error(-1, "There's a form with more q than Q, trying to fix"); @@ -4403,7 +4416,7 @@ void Gfx::opBeginImage(Object args[], int numArgs) { // display the image if (str) { doImage(NULL, str, gTrue); - + // skip 'EI' tag c1 = str->getUndecodedStream()->getChar(); c2 = str->getUndecodedStream()->getChar(); @@ -4525,7 +4538,7 @@ GBool Gfx::contentIsHidden() { void Gfx::opBeginMarkedContent(Object args[], int numArgs) { // push a new stack entry pushMarkedContent(); - + OCGs *contentConfig = catalog->getOptContentConfig(); char* name0 = args[0].getName(); if ( strncmp( name0, "OC", 2) == 0 && contentConfig) { diff --git a/poppler/SplashOutputDev.cc b/poppler/SplashOutputDev.cc index 666ee18..4fcda00 100644 --- a/poppler/SplashOutputDev.cc +++ b/poppler/SplashOutputDev.cc @@ -80,6 +80,86 @@ extern "C" int unlink(char *filename); #endif //------------------------------------------------------------------------ +// SplashSolidColor +//------------------------------------------------------------------------ + +SplashAxialPattern::SplashAxialPattern(SplashColorMode colorModeA, GfxState *stateA, GfxAxialShading *shadingA) { + Matrix ctm; + + shading = shadingA; + state = stateA; + colorMode = colorModeA; + state->getCTM (&ctm); + ctm.invertTo (&ictm); + + shading->getCoords(&x0, &y0, &x1, &y1); + dx = x1 - x0; + dy = y1 - y0; + mul = 1 / (dx * dx + dy * dy); + + // get the function domain + t0 = shading->getDomain0(); + t1 = shading->getDomain1(); +} + +SplashAxialPattern::~SplashAxialPattern() { +} + +void SplashAxialPattern::getColor(int x, int y, SplashColorPtr c) { + SplashColor color; + GfxColor gfxColor; + GfxGray gray; + GfxRGB rgb; +#if SPLASH_CMYK + GfxCMYK cmyk; +#endif + double tt; + + double xc, yc, xaxis; + ictm.transform(x, y, &xc, &yc); + xaxis = ((xc - x0) * dx + (yc - y0) * dy) * mul; + if (xaxis < 0) { // && !shading->getExtend0()) { + tt = t0; + } else if (xaxis > 1) { // && !shading->getExtend1()) { + tt = t1; + } else if (xaxis >= 0 && xaxis <= 1) { + tt = t0 + (t1 -t0) * xaxis; + } else + return; + shading->getColor(tt, &gfxColor); + state->setFillColor(&gfxColor); + state->getFillGray(&gray); + state->getFillRGB(&rgb); +#if SPLASH_CMYK + state->getFillCMYK(&cmyk); +#endif + switch (colorMode) { + case splashModeMono1: + case splashModeMono8: + color[0] = colToByte(gray); + break; + case splashModeXBGR8: + color[3] = 255; + case splashModeRGB8: + case splashModeBGR8: + color[0] = colToByte(rgb.r); + color[1] = colToByte(rgb.g); + color[2] = colToByte(rgb.b); + break; +#if SPLASH_CMYK + case splashModeCMYK8: + color[0] = colToByte(cmyk.c); + color[1] = colToByte(cmyk.m); + color[2] = colToByte(cmyk.y); + color[3] = colToByte(cmyk.k); + break; +#endif + } + + splashColorCopy(c, color); +} + +//------------------------------------------------------------------------ // Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result. static inline Guchar div255(int x) { @@ -1225,15 +1305,6 @@ void SplashOutputDev::updateFont(GfxState * /*state*/) { needFontUpdate = gTrue; } -void SplashOutputDev::updateRender(GfxState *state) { - int rm; - rm = state->getRender(); - if (rm == 7 && haveCSPattern) { - haveCSPattern = gFalse; - restoreState(state); - } -} - void SplashOutputDev::doUpdateFont(GfxState *state) { GfxFont *gfxFont; GfxFontType fontType; @@ -1621,7 +1692,7 @@ void SplashOutputDev::drawChar(GfxState *state, double x, double y, // fill if (!(render & 1)) { - if (!state->getFillColorSpace()->isNonMarking()) { + if (!haveCSPattern && !state->getFillColorSpace()->isNonMarking()) { splash->fillChar((SplashCoord)x, (SplashCoord)y, code, font); } } @@ -1638,7 +1709,7 @@ void SplashOutputDev::drawChar(GfxState *state, double x, double y, } // clip - if (render & 4) { + if (haveCSPattern || (render & 4)) { if ((path = font->getGlyphPath(code))) { path->offset((SplashCoord)x, (SplashCoord)y); if (textClipPath) { @@ -1927,17 +1998,14 @@ void SplashOutputDev::drawType3Glyph(T3FontCache *t3Font, } void SplashOutputDev::beginTextObject(GfxState *state) { - if (state->getFillColorSpace()->getMode() == csPattern) { + if (!(state->getRender() & 4) && state->getFillColorSpace()->getMode() == csPattern) { haveCSPattern = gTrue; saveState(state); - savedRender = state->getRender(); - state->setRender(7); } } void SplashOutputDev::endTextObject(GfxState *state) { if (haveCSPattern) { - state->setRender(savedRender); haveCSPattern = gFalse; if (state->getFillColorSpace()->getMode() != csPattern) { if (textClipPath) { @@ -3198,3 +3266,529 @@ void SplashOutputDev::setFreeTypeHinting(GBool enable) { enableFreeTypeHinting = enable; } + +/* implement shading routines */ +GBool SplashOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) { + double xMin, yMin, xMax, yMax; + SplashPath *path; + + // 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()); + + SplashPattern *pattern = new SplashAxialPattern(colorMode, state, shading); + splash->shadedFill(path, gFalse, pattern); + state->clearPath(); + delete path; + + return gFalse; + +} + +// copied from Gfx.cc +// TODO: implement SplashFunctionPattern + +// Max recursive depth for a function shading fill. +#define functionMaxDepth 6 + +// Max delta allowed in any color component for a function shading fill. +#define functionColorDelta (dblToCol(1 / 256.0)) + +GBool SplashOutputDev::functionShadedFill(GfxState *state, + GfxFunctionShading *shading) { + double x0, y0, x1, y1; + GfxColor colors[4]; + + shading->getDomain(&x0, &y0, &x1, &y1); + shading->getColor(x0, y0, &colors[0]); + shading->getColor(x0, y1, &colors[1]); + shading->getColor(x1, y0, &colors[2]); + shading->getColor(x1, y1, &colors[3]); + doFunctionShFill1(shading, state, x0, y0, x1, y1, colors, 0); + return gTrue; +} + +void SplashOutputDev::doFunctionShFill1(GfxFunctionShading *shading, + GfxState *state, + double x0, double y0, + double x1, double y1, + GfxColor *colors, int depth) { + GfxColor fillColor; + GfxColor color0M, color1M, colorM0, colorM1, colorMM; + GfxColor colors2[4]; + double *matrix; + double xM, yM; + int nComps, i, j; + + nComps = shading->getColorSpace()->getNComps(); + matrix = shading->getMatrix(); + + // compare the four corner colors + for (i = 0; i < 4; ++i) { + for (j = 0; j < nComps; ++j) { + if (abs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) { + break; + } + } + if (j < nComps) { + break; + } + } + + // center of the rectangle + xM = 0.5 * (x0 + x1); + yM = 0.5 * (y0 + y1); + + // the four corner colors are close (or we hit the recursive limit) + // -- fill the rectangle; but require at least one subdivision + // (depth==0) to avoid problems when the four outer corners of the + // shaded region are the same color + if ((i == 4 && depth > 0) || depth == functionMaxDepth) { + + // use the center color + shading->getColor(xM, yM, &fillColor); + state->setFillColor(&fillColor); + updateFillColor(state); + + // fill the rectangle + state->moveTo(x0 * matrix[0] + y0 * matrix[2] + matrix[4], + x0 * matrix[1] + y0 * matrix[3] + matrix[5]); + state->lineTo(x1 * matrix[0] + y0 * matrix[2] + matrix[4], + x1 * matrix[1] + y0 * matrix[3] + matrix[5]); + state->lineTo(x1 * matrix[0] + y1 * matrix[2] + matrix[4], + x1 * matrix[1] + y1 * matrix[3] + matrix[5]); + state->lineTo(x0 * matrix[0] + y1 * matrix[2] + matrix[4], + x0 * matrix[1] + y1 * matrix[3] + matrix[5]); + state->closePath(); + fill(state); + state->clearPath(); + + // the four corner colors are not close enough -- subdivide the + // rectangle + } else { + + // colors[0] colorM0 colors[2] + // (x0,y0) (xM,y0) (x1,y0) + // +----------+----------+ + // | | | + // | UL | UR | + // color0M | colorMM | color1M + // (x0,yM) +----------+----------+ (x1,yM) + // | (xM,yM) | + // | LL | LR | + // | | | + // +----------+----------+ + // colors[1] colorM1 colors[3] + // (x0,y1) (xM,y1) (x1,y1) + + shading->getColor(x0, yM, &color0M); + shading->getColor(x1, yM, &color1M); + shading->getColor(xM, y0, &colorM0); + shading->getColor(xM, y1, &colorM1); + shading->getColor(xM, yM, &colorMM); + + // upper-left sub-rectangle + colors2[0] = colors[0]; + colors2[1] = color0M; + colors2[2] = colorM0; + colors2[3] = colorMM; + doFunctionShFill1(shading, state, x0, y0, xM, yM, colors2, depth + 1); + + // lower-left sub-rectangle + colors2[0] = color0M; + colors2[1] = colors[1]; + colors2[2] = colorMM; + colors2[3] = colorM1; + doFunctionShFill1(shading, state, x0, yM, xM, y1, colors2, depth + 1); + + // upper-right sub-rectangle + colors2[0] = colorM0; + colors2[1] = colorMM; + colors2[2] = colors[2]; + colors2[3] = color1M; + doFunctionShFill1(shading, state, xM, y0, x1, yM, colors2, depth + 1); + + // lower-right sub-rectangle + colors2[0] = colorMM; + colors2[1] = colorM1; + colors2[2] = color1M; + colors2[3] = colors[3]; + doFunctionShFill1(shading, state, xM, yM, x1, y1, colors2, depth + 1); + } +} + +// copied from Gfx.cc +// TODO: implement SplashRadialPattern + +// the MSVC math.h doesn't define this +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +// 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 / 256.0)) + +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 SplashOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading, double /*sMin*/, double /*sMax*/) { + double xMin, yMin, xMax, yMax; + double x0, y0, r0, x1, y1, r1, t0, t1; + int nComps; + GfxColor colorA, colorB; + double xa, ya, xb, yb, ra, rb; + double ta, tb, sa, sb; + double sz, xz, yz, sMin, sMax; + GBool enclosed; + int ia, ib, k, n; + double *ctm; + double theta, alpha, angle, t; + GBool needExtend = gTrue; + + // get the shading info + shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1); + t0 = shading->getDomain0(); + t1 = shading->getDomain1(); + nComps = shading->getColorSpace()->getNComps(); + + // Compute the point at which r(s) = 0; check for the enclosed + // circles case; and compute the angles for the tangent lines. + if (x0 == x1 && y0 == y1) { + enclosed = gTrue; + theta = 0; // make gcc happy + sz = 0; // make gcc happy + } else if (r0 == r1) { + enclosed = gFalse; + theta = 0; + sz = 0; // make gcc happy + } else { + sz = -r0 / (r1 - r0); + xz = x0 + sz * (x1 - x0); + yz = y0 + sz * (y1 - y0); + enclosed = (xz - x0) * (xz - x0) + (yz - y0) * (yz - y0) <= r0 * r0; + theta = asin(r0 / sqrt((x0 - xz) * (x0 - xz) + (y0 - yz) * (y0 - yz))); + if (r0 > r1) { + theta = -theta; + } + } + if (enclosed) { + alpha = 0; + } else { + alpha = atan2(y1 - y0, x1 - x0); + } + + // compute the (possibly extended) s range + state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); + if (enclosed) { + sMin = 0; + sMax = 1; + } else { + sMin = 1; + sMax = 0; + // solve for x(s) + r(s) = xMin + if ((x1 + r1) - (x0 + r0) != 0) { + sa = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0)); + if (sa < sMin) { + sMin = sa; + } else if (sa > sMax) { + sMax = sa; + } + } + // solve for x(s) - r(s) = xMax + if ((x1 - r1) - (x0 - r0) != 0) { + sa = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0)); + if (sa < sMin) { + sMin = sa; + } else if (sa > sMax) { + sMax = sa; + } + } + // solve for y(s) + r(s) = yMin + if ((y1 + r1) - (y0 + r0) != 0) { + sa = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0)); + if (sa < sMin) { + sMin = sa; + } else if (sa > sMax) { + sMax = sa; + } + } + // solve for y(s) - r(s) = yMax + if ((y1 - r1) - (y0 - r0) != 0) { + sa = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0)); + if (sa < sMin) { + sMin = sa; + } else if (sa > sMax) { + sMax = sa; + } + } + // check against sz + if (r0 < r1) { + if (sMin < sz) { + sMin = sz; + } + } else if (r0 > r1) { + if (sMax > sz) { + sMax = sz; + } + } + // check the 'extend' flags + if (!shading->getExtend0() && sMin < 0) { + sMin = 0; + } + if (!shading->getExtend1() && sMax > 1) { + sMax = 1; + } + } + + // compute the number of steps into which circles must be divided to + // achieve a curve flatness of 0.1 pixel in device space for the + // largest circle (note that "device space" is 72 dpi when generating + // PostScript, hence the relatively small 0.1 pixel accuracy) + ctm = state->getCTM(); + t = fabs(ctm[0]); + if (fabs(ctm[1]) > t) { + t = fabs(ctm[1]); + } + if (fabs(ctm[2]) > t) { + t = fabs(ctm[2]); + } + if (fabs(ctm[3]) > t) { + t = fabs(ctm[3]); + } + if (r0 > r1) { + t *= r0; + } else { + t *= r1; + } + if (t < 1) { + n = 3; + } else { + n = (int)(M_PI / acos(1 - 0.1 / t)); + if (n < 3) { + n = 3; + } else if (n > 200) { + n = 200; + } + } + + // setup for the start circle + ia = 0; + sa = sMin; + ta = t0 + sa * (t1 - t0); + xa = x0 + sa * (x1 - x0); + ya = y0 + sa * (y1 - y0); + ra = r0 + sa * (r1 - r0); + if (ta < t0) { + shading->getColor(t0, &colorA); + } else if (ta > t1) { + shading->getColor(t1, &colorA); + } else { + shading->getColor(ta, &colorA); + } + + // fill the circles + while (ia < radialMaxSplits) { + + // go as far along the t axis (toward t1) as we can, such that the + // color difference is within the tolerance (radialColorDelta) -- + // this uses bisection (between the current value, t, and t1), + // limited to radialMaxSplits points along the t axis; require at + // least one split to avoid problems when the innermost and + // outermost colors are the same + ib = radialMaxSplits; + sb = sMax; + tb = t0 + sb * (t1 - t0); + if (tb < t0) { + shading->getColor(t0, &colorB); + } else if (tb > t1) { + shading->getColor(t1, &colorB); + } else { + shading->getColor(tb, &colorB); + } + while (ib - ia > 1) { + if (isSameGfxColor(colorB, colorA, nComps, radialColorDelta) && ib < radialMaxSplits) { + // 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 + // Do another bisection to be a bit more sure we are not doing something wrong + GfxColor colorC; + int ic = (ia + ib) / 2; + double sc = sMin + ((double)ic / (double)radialMaxSplits) * (sMax - sMin); + double tc = t0 + sc * (t1 - t0); + if (tc < t0) { + shading->getColor(t0, &colorC); + } else if (tc > t1) { + shading->getColor(t1, &colorC); + } else { + shading->getColor(tc, &colorC); + } + if (isSameGfxColor(colorC, colorA, nComps, radialColorDelta)) + break; + } + ib = (ia + ib) / 2; + sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin); + tb = t0 + sb * (t1 - t0); + if (tb < t0) { + shading->getColor(t0, &colorB); + } else if (tb > t1) { + shading->getColor(t1, &colorB); + } else { + shading->getColor(tb, &colorB); + } + } + + // compute center and radius of the circle + xb = x0 + sb * (x1 - x0); + yb = y0 + sb * (y1 - y0); + rb = r0 + sb * (r1 - r0); + + // use the average of the colors at the two circles + for (k = 0; k < nComps; ++k) { + colorA.c[k] = (colorA.c[k] + colorB.c[k]) / 2; + } + state->setFillColor(&colorA); + updateFillColor(state); + + if (needExtend) { + if (enclosed) { + // construct path for first circle (counterclockwise) + state->moveTo(xa + ra, ya); + for (k = 1; k < n; ++k) { + angle = ((double)k / (double)n) * 2 * M_PI; + state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle)); + } + state->closePath(); + + // construct and append path for second circle (clockwise) + state->moveTo(xb + rb, yb); + for (k = 1; k < n; ++k) { + angle = -((double)k / (double)n) * 2 * M_PI; + state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle)); + } + state->closePath(); + } else { + // construct the first subpath (clockwise) + state->moveTo(xa + ra * cos(alpha + theta + 0.5 * M_PI), + ya + ra * sin(alpha + theta + 0.5 * M_PI)); + for (k = 0; k < n; ++k) { + angle = alpha + theta + 0.5 * M_PI + - ((double)k / (double)n) * (2 * theta + M_PI); + state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle)); + } + for (k = 0; k < n; ++k) { + angle = alpha - theta - 0.5 * M_PI + + ((double)k / (double)n) * (2 * theta - M_PI); + state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle)); + } + state->closePath(); + + // construct the second subpath (counterclockwise) + state->moveTo(xa + ra * cos(alpha + theta + 0.5 * M_PI), + ya + ra * sin(alpha + theta + 0.5 * M_PI)); + for (k = 0; k < n; ++k) { + angle = alpha + theta + 0.5 * M_PI + + ((double)k / (double)n) * (-2 * theta + M_PI); + state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle)); + } + for (k = 0; k < n; ++k) { + angle = alpha - theta - 0.5 * M_PI + + ((double)k / (double)n) * (2 * theta + M_PI); + state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle)); + } + state->closePath(); + } + } + + fill(state); + state->clearPath(); + + // step to the next value of t + ia = ib; + sa = sb; + ta = tb; + xa = xb; + ya = yb; + ra = rb; + colorA = colorB; + } + + if (!needExtend) + return gTrue; + + if (enclosed) { + // extend the smaller circle + 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); + state->setFillColor(&colorA); + updateFillColor(state); + state->moveTo(xa + ra, ya); + for (k = 1; k < n; ++k) { + angle = ((double)k / (double)n) * 2 * M_PI; + state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle)); + } + state->closePath(); + fill(state); + state->clearPath(); + } + + // extend the larger circle + 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); + state->setFillColor(&colorA); + updateFillColor(state); + state->moveTo(xMin, yMin); + state->lineTo(xMin, yMax); + state->lineTo(xMax, yMax); + state->lineTo(xMax, yMin); + state->closePath(); + state->moveTo(xa + ra, ya); + for (k = 1; k < n; ++k) { + angle = ((double)k / (double)n) * 2 * M_PI; + state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle)); + } + state->closePath(); + fill(state); + state->clearPath(); + } + } + return gTrue; +} diff --git a/poppler/SplashOutputDev.h b/poppler/SplashOutputDev.h index 37b771a..b77170b 100644 --- a/poppler/SplashOutputDev.h +++ b/poppler/SplashOutputDev.h @@ -31,6 +31,7 @@ #include "goo/gtypes.h" #include "splash/SplashTypes.h" +#include "splash/SplashPattern.h" #include "poppler-config.h" #include "OutputDev.h" #include "GfxState.h" @@ -39,7 +40,6 @@ class Gfx8BitFont; class SplashBitmap; class Splash; class SplashPath; -class SplashPattern; class SplashFontEngine; class SplashFont; class T3FontCache; @@ -53,6 +53,35 @@ struct SplashTransparencyGroup; #define splashOutT3FontCacheSize 8 //------------------------------------------------------------------------ +// SplashShadingColor +//------------------------------------------------------------------------ + +class SplashAxialPattern: public SplashPattern { +public: + + SplashAxialPattern(SplashColorMode colorMode, GfxState *state, GfxAxialShading *shading); + + virtual SplashPattern *copy() { return new SplashAxialPattern(colorMode, state, shading); } + + virtual ~SplashAxialPattern(); + + virtual void getColor(int x, int y, SplashColorPtr c); + + virtual GBool isStatic() { return gFalse; } + +private: + + Matrix ictm; + double x0, y0, x1, y1; + double dx, dy, mul; + double t0, t1; + GfxAxialShading *shading; + GfxState *state; + SplashColorMode colorMode; + double *bbox; +}; + +//------------------------------------------------------------------------ // SplashOutputDev //------------------------------------------------------------------------ @@ -70,6 +99,11 @@ public: //----- get info about output device + // Does this device use functionShadedFill(), axialShadedFill(), and + // radialShadedFill()? If this returns false, these shaded fills + // will be reduced to a series of other drawing operations. + virtual GBool useShadedFills() { return gTrue; } + // Does this device use upside-down coordinates? // (Upside-down means (0,0) is the top left corner of the page.) virtual GBool upsideDown() { return gTrue; } @@ -116,12 +150,19 @@ public: //----- update text state virtual void updateFont(GfxState *state); - virtual void updateRender(GfxState *state); //----- path painting virtual void stroke(GfxState *state); virtual void fill(GfxState *state); virtual void eoFill(GfxState *state); + virtual GBool axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax); + virtual GBool functionShadedFill(GfxState *state, + GfxFunctionShading *shading); + virtual GBool radialShadedFill(GfxState *state, GfxRadialShading *shading, double /*sMin*/, double /*sMax*/); + virtual void doFunctionShFill1(GfxFunctionShading *shading, GfxState *state, + double x0, double y0, + double x1, double y1, + GfxColor *colors, int depth); //----- path clipping virtual void clip(GfxState *state); @@ -246,8 +287,7 @@ private: Guchar *alphaLine); GBool haveCSPattern; // set if text has been drawn with a - // clipping render mode because of pattern colorspace - int savedRender; // use if pattern colorspace + // clipping render mode because of pattern colorspace GBool keepAlphaChannel; // don't fill with paper color, keep alpha channel SplashColorMode colorMode; diff --git a/splash/Splash.cc b/splash/Splash.cc index 5e27d42..f117477 100644 --- a/splash/Splash.cc +++ b/splash/Splash.cc @@ -3592,3 +3592,59 @@ void Splash::dumpXPath(SplashXPath *path) { (path->segs[i].flags & splashXPathFlip) ? "P" : " "); } } + +SplashError Splash::shadedFill(SplashPath *path, GBool eo, + SplashPattern *pattern) { + SplashPipe pipe; + SplashXPath *xPath; + SplashXPathScanner *scanner; + int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y; + SplashClipResult clipRes; + + if (path->length == 0) { + return splashErrEmptyPath; + } + xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue); + xPath->aaScale(); + xPath->sort(); + scanner = new SplashXPathScanner(xPath, eo); + + // get the min and max x and y values + scanner->getBBoxAA(&xMinI, &yMinI, &xMaxI, &yMaxI); + + // check clipping + if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI)) + != splashClipAllOutside) { + + // limit the y range + if (yMinI < state->clip->getYMinI()) { + yMinI = state->clip->getYMinI(); + } + if (yMaxI > state->clip->getYMaxI()) { + yMaxI = state->clip->getYMaxI(); + } + + pipeInit(&pipe, 0, yMinI, pattern, NULL, state->fillAlpha, vectorAntialias, gFalse); + drawAAPixelInit(); + + // 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); +/* + for (x = x0; x <= x1; ++x) { + pipeSetXY(&pipe, x, y); + drawAAPixel(&pipe, x, y); + } +*/ + } + } + opClipRes = clipRes; + + delete scanner; + delete xPath; + return splashOk; +} diff --git a/splash/Splash.h b/splash/Splash.h index c9d57cc..3dd93e1 100644 --- a/splash/Splash.h +++ b/splash/Splash.h @@ -248,6 +248,8 @@ public: GBool getVectorAntialias() { return vectorAntialias; } void setVectorAntialias(GBool vaa) { vectorAntialias = vaa; } #endif + SplashError shadedFill(SplashPath *path, GBool eo, + SplashPattern *pattern); private: @@ -301,6 +303,7 @@ private: SplashClipResult opClipRes; GBool vectorAntialias; GBool debugMode; + }; #endif