diff --git a/poppler/SplashOutputDev.cc b/poppler/SplashOutputDev.cc index 0b3722a..479590d 100644 --- a/poppler/SplashOutputDev.cc +++ b/poppler/SplashOutputDev.cc @@ -153,6 +153,1044 @@ 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 + +// the value, so that the smallest circle whose +// distance to the desired (degenerate) circle within the +// bounding box does not exceed tolerance. +#ifndef FLT_EPSILON +#define FLT_EPSILON 1.192092896e-07F +#endif + +#define radialTolerance FLT_EPSILON //0.5 + +#undef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +#undef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +// test if radial pattern is considered degenerate +// adapted from cairo _radial_pattern_is_degenerate +static GBool +radialPatternIsDegenerate (GfxRadialShading *shading) +{ + double x0, y0, r0, x1, y1, r1; + shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1); + + /* A radial pattern is considered degenerate if it can be + * represented as a solid or clear pattern. This corresponds to + * one of the two cases: + * + * 1) The radii are both very small: + * |dr| < FLT_EPSILON && min (r0, r1) < FLT_EPSILON + * + * 2) The two circles have about the same radius and are very + * close to each other (approximately a cylinder gradient that + * doesn't move with the parameter): + * |dr| < FLT_EPSILON && max (|dx|, |dy|) < 2 * FLT_EPSILON + * + * These checks are consistent with the assumptions used in + * _cairo_radial_pattern_box_to_parameter (). + */ + + return fabs (r0 - r1) < FLT_EPSILON && + (MIN (r0, r1) < FLT_EPSILON || + MAX (fabs (x0 - x1), + fabs (y0 - y1)) < 2 * FLT_EPSILON); +} + +// extend range, adapted from cairo, radialExtendRange +static GBool +radialExtendRange (double range[2], double value, GBool valid) +{ + if (!valid) + range[0] = range[1] = value; + else if (value < range[0]) + range[0] = value; + else if (value > range[1]) + range[1] = value; + + return gTrue; +} + +// calculate extended range, adapted from cairo, _cairo_radial_pattern_box_to_parameter +static void +radialGetMaxExtends (GfxRadialShading *shading, + double xMin, double yMin, + double xMax, double yMax, + double tolerance, + double range[2]) +{ + double cx, cy, cr, dx, dy, dr; + double a, x_focus, y_focus; + double mindr, minx, miny, maxx, maxy; + GBool valid; + double x0, y0, r0, x1, y1, r1; + + shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1); + if (radialPatternIsDegenerate(shading)) + return; + if (tolerance <= 0) + return; + if (xMin >= xMax) + return; + if (yMin >= yMax) + return; + + range[0] = range[1] = 0; + valid = FALSE; + + x_focus = y_focus = 0; /* silence gcc */ + + cx = x0; + cy = y0; + cr = r0; + dx = x1 - cx; + dy = y1 - cy; + dr = r1 - cr; + + /* translate by -(cx, cy) to simplify computations */ + xMin -= cx; + yMin -= cy; + xMax -= cx; + yMax -= cy; + + /* enlarge boundaries slightly to avoid rounding problems in the + * parameter range computation */ + xMin -= FLT_EPSILON; + yMin -= FLT_EPSILON; + xMax += FLT_EPSILON; + yMax += FLT_EPSILON; + + /* enlarge boundaries even more to avoid rounding problems when + * testing if a point belongs to the box */ + minx = xMin - FLT_EPSILON; + miny = yMin - FLT_EPSILON; + maxx = xMax + FLT_EPSILON; + maxy = yMax + FLT_EPSILON; + + /* we dont' allow negative radiuses, so we will be checking that + * t*dr >= mindr to consider t valid */ + mindr = -(cr + FLT_EPSILON); + + /* + * After the previous transformations, the start circle is + * centered in the origin and has radius cr. A 1-unit change in + * the t parameter corresponds to dx,dy,dr changes in the x,y,r of + * the circle (center coordinates, radius). + * + * To compute the minimum range needed to correctly draw the + * pattern, we start with an empty range and extend it to include + * the circles touching the bounding box or within it. + */ + + /* + * Focus, the point where the circle has radius == 0. + * + * r = cr + t * dr = 0 + * t = -cr / dr + * + * If the radius is constant (dr == 0) there is no focus (the + * gradient represents a cylinder instead of a cone). + */ + if (fabs (dr) >= FLT_EPSILON) { + double t_focus; + + t_focus = -cr / dr; + x_focus = t_focus * dx; + y_focus = t_focus * dy; + if (minx <= x_focus && x_focus <= maxx && + miny <= y_focus && y_focus <= maxy) + { + valid = radialExtendRange (range, t_focus, valid); + } + } + + /* + * Circles externally tangent to box edges. + * + * All circles have center in (dx, dy) * t + * + * If the circle is tangent to the line defined by the edge of the + * box, then at least one of the following holds true: + * + * (dx*t) + (cr + dr*t) == x0 (left edge) + * (dx*t) - (cr + dr*t) == x1 (right edge) + * (dy*t) + (cr + dr*t) == y0 (top edge) + * (dy*t) - (cr + dr*t) == y1 (bottom edge) + * + * The solution is only valid if the tangent point is actually on + * the edge, i.e. if its y coordinate is in [y0,y1] for left/right + * edges and if its x coordinate is in [x0,x1] for top/bottom + * edges. + * + * For the first equation: + * + * (dx + dr) * t = x0 - cr + * t = (x0 - cr) / (dx + dr) + * y = dy * t + * + * in the code this becomes: + * + * t_edge = (num) / (den) + * v = (delta) * t_edge + * + * If the denominator in t is 0, the pattern is tangent to a line + * parallel to the edge under examination. The corner-case where + * the boundary line is the same as the edge is handled by the + * focus point case and/or by the a==0 case. + */ +#define T_EDGE(num,den,delta,lower,upper) \ + if (fabs (den) >= FLT_EPSILON) { \ + double t_edge, v; \ + \ + t_edge = (num) / (den); \ + v = t_edge * (delta); \ + if (t_edge * dr >= mindr && (lower) <= v && v <= (upper)) \ + valid = radialExtendRange (range, t_edge, valid); \ + } + + /* circles tangent (externally) to left/right/top/bottom edge */ + T_EDGE (xMin - cr, dx + dr, dy, miny, maxy); + T_EDGE (xMax + cr, dx - dr, dy, miny, maxy); + T_EDGE (yMin - cr, dy + dr, dx, minx, maxx); + T_EDGE (yMax + cr, dy - dr, dx, minx, maxx); + +#undef T_EDGE + + /* + * Circles passing through a corner. + * + * A circle passing through the point (x,y) satisfies: + * + * (x-t*dx)^2 + (y-t*dy)^2 == (cr + t*dr)^2 + * + * If we set: + * a = dx^2 + dy^2 - dr^2 + * b = x*dx + y*dy + cr*dr + * c = x^2 + y^2 - cr^2 + * we have: + * a*t^2 - 2*b*t + c == 0 + */ + a = dx * dx + dy * dy - dr * dr; + if (fabs (a) < FLT_EPSILON * FLT_EPSILON) { + double b, maxd2; + + /* Ensure that gradients with both a and dr small are + * considered degenerate. + * The floating point version of the degeneracy test implemented + * in _radial_pattern_is_degenerate() is: + * + * 1) The circles are practically the same size: + * |dr| < FLT_EPSILON + * AND + * 2a) The circles are both very small: + * min (r0, r1) < FLT_EPSILON + * OR + * 2b) The circles are very close to each other: + * max (|dx|, |dy|) < 2 * FLT_EPSILON + * + * Assuming that the gradient is not degenerate, we want to + * show that |a| < FLT_EPSILON^2 implies |dr| >= FLT_EPSILON. + * + * If the gradient is not degenerate yet it has |dr| < + * FLT_EPSILON, (2b) is false, thus: + * + * max (|dx|, |dy|) >= 2*FLT_EPSILON + * which implies: + * 4*FLT_EPSILON^2 <= max (|dx|, |dy|)^2 <= dx^2 + dy^2 + * + * From the definition of a, we get: + * a = dx^2 + dy^2 - dr^2 < FLT_EPSILON^2 + * dx^2 + dy^2 - FLT_EPSILON^2 < dr^2 + * 3*FLT_EPSILON^2 < dr^2 + * + * which is inconsistent with the hypotheses, thus |dr| < + * FLT_EPSILON is false or the gradient is degenerate. + */ + assert (fabs (dr) >= FLT_EPSILON); + + /* + * If a == 0, all the circles are tangent to a line in the + * focus point. If this line is within the box extents, we + * should add the circle with infinite radius, but this would + * make the range unbounded, so we add the smallest circle whose + * distance to the desired (degenerate) circle within the + * bounding box does not exceed tolerance. + * + * The equation of the line is b==0, i.e.: + * x*dx + y*dy + cr*dr == 0 + * + * We compute the intersection of the line with the box and + * keep the intersection with maximum square distance (maxd2) + * from the focus point. + * + * In the code the intersection is represented in another + * coordinate system, whose origin is the focus point and + * which has a u,v axes, which are respectively orthogonal and + * parallel to the edge being intersected. + * + * The intersection is valid only if it belongs to the box, + * otherwise it is ignored. + * + * For example: + * + * y = y0 + * x*dx + y0*dy + cr*dr == 0 + * x = -(y0*dy + cr*dr) / dx + * + * which in (u,v) is: + * u = y0 - y_focus + * v = -(y0*dy + cr*dr) / dx - x_focus + * + * In the code: + * u = (edge) - (u_origin) + * v = -((edge) * (delta) + cr*dr) / (den) - v_focus + */ +#define T_EDGE(edge,delta,den,lower,upper,u_origin,v_origin) \ + if (fabs (den) >= FLT_EPSILON) { \ + double v; \ + \ + v = -((edge) * (delta) + cr * dr) / (den); \ + if ((lower) <= v && v <= (upper)) { \ + double u, d2; \ + \ + u = (edge) - (u_origin); \ + v -= (v_origin); \ + d2 = u*u + v*v; \ + if (maxd2 < d2) \ + maxd2 = d2; \ + } \ + } + + maxd2 = 0; + + /* degenerate circles (lines) passing through each edge */ + T_EDGE (yMin, dy, dx, minx, maxx, y_focus, x_focus); + T_EDGE (yMax, dy, dx, minx, maxx, y_focus, x_focus); + T_EDGE (xMin, dx, dy, miny, maxy, x_focus, y_focus); + T_EDGE (xMax, dx, dy, miny, maxy, x_focus, y_focus); + +#undef T_EDGE + + /* + * The limit circle can be transformed rigidly to the y=0 line + * and the circles tangent to it in (0,0) are: + * + * x^2 + (y-r)^2 = r^2 <=> x^2 + y^2 - 2*y*r = 0 + * + * y is the distance from the line, in our case tolerance; + * x is the distance along the line, i.e. sqrt(maxd2), + * so: + * + * r = cr + dr * t = (maxd2 + tolerance^2) / (2*tolerance) + * t = (r - cr) / dr = + * (maxd2 + tolerance^2 - 2*tolerance*cr) / (2*tolerance*dr) + */ + if (maxd2 > 0) { + double t_limit = maxd2 + tolerance*tolerance - 2*tolerance*cr; + t_limit /= 2 * tolerance * dr; + valid = radialExtendRange (range, t_limit, valid); + } + + /* + * Nondegenerate, nonlimit circles passing through the corners. + * + * a == 0 && a*t^2 - 2*b*t + c == 0 + * + * t = c / (2*b) + * + * The b == 0 case has just been handled, so we only have to + * compute this if b != 0. + */ +#define T_CORNER(x,y) \ + b = (x) * dx + (y) * dy + cr * dr; \ + if (fabs (b) >= FLT_EPSILON) { \ + double t_corner; \ + double x2 = (x) * (x); \ + double y2 = (y) * (y); \ + double cr2 = (cr) * (cr); \ + double c = x2 + y2 - cr2; \ + \ + t_corner = 0.5 * c / b; \ + if (t_corner * dr >= mindr) \ + valid = radialExtendRange (range, t_corner, valid); \ + } + + /* circles touching each corner */ + T_CORNER (xMin, yMin); + T_CORNER (xMin, yMax); + T_CORNER (xMax, yMin); + T_CORNER (xMax, yMax); + +#undef T_CORNER + } else { + double inva, b, c, d; + + inva = 1 / a; + + /* + * Nondegenerate, nonlimit circles passing through the corners. + * + * a != 0 && a*t^2 - 2*b*t + c == 0 + * + * t = (b +- sqrt (b*b - a*c)) / a + * + * If the argument of sqrt() is negative, then no circle + * passes through the corner. + */ +#define T_CORNER(x,y) \ + b = (x) * dx + (y) * dy + cr * dr; \ + c = (x) * (x) + (y) * (y) - cr * cr; \ + d = b * b - a * c; \ + if (d >= 0) { \ + double t_corner; \ + \ + d = sqrt (d); \ + t_corner = (b + d) * inva; \ + if (t_corner * dr >= mindr) \ + valid = radialExtendRange (range, t_corner, valid); \ + t_corner = (b - d) * inva; \ + if (t_corner * dr >= mindr) \ + valid = radialExtendRange (range, t_corner, valid); \ + } + + /* circles touching each corner */ + T_CORNER (xMin, yMin); + T_CORNER (xMin, yMax); + T_CORNER (xMax, yMin); + T_CORNER (xMax, yMax); + +#undef T_CORNER + } +} + + +SplashRadialPattern::SplashRadialPattern(GfxState *stateA, GfxRadialShading *shadingA, + double sMinA, double sMaxA, SplashColorMode colorModeA) { + Matrix ctm; + + state = stateA; + shading = shadingA; + // ignore sMin ans sMax from Gfx (they include already extends!): + range[1] = 1; + range[0] = 0; + colorMode = colorModeA; + state->getCTM(&ctm); + ctm.invertTo(&ictm); + // get the shading info + shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1); + t0 = shading->getDomain0(); + t1 = shading->getDomain1(); + // calculate sMax for larger extend circle + extendedRange[1] = range[1]; + extendedRange[0] = range[0]; + if (shading->getHasBBox()) + shading->getBBox(&xMin, &yMin, &xMax, &yMax); + else + state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); + if (shading->getExtend0() || shading->getExtend1()) { + if (xMin >= xMax) + return; + if (yMin >= yMax) + return; + radialGetMaxExtends(shading, xMin, yMin, xMax, yMax, radialTolerance, extendedRange); + } + + if (!shading->getHasBBox()) { + double xMinS, yMinS, xMaxS, yMaxS; + double x, y, r; + // shrink clip box to max area of shading, given by max extends + // first calculate shading bbox: + x = x0 + extendedRange[0] * (x1 - x0); + y = y0 + extendedRange[0] * (y1 - y0); + r = r0 + extendedRange[0] * (r1 - r0); + xMinS = x - r; xMaxS = x + r; yMinS = y - r; yMaxS = y + r; + x = x0 + extendedRange[1] * (x1 - x0); + y = y0 + extendedRange[1] * (y1 - y0); + r = r0 + extendedRange[1] * (r1 - r0); + if (x - r < xMinS) xMinS = x - r; + if (x + r > xMaxS) xMaxS = x + r; + if (y - r < yMinS) yMinS = y - r; + if (y + r > yMaxS) yMaxS = y + r; + // now intersect it with user clip bbox: + if (xMin < xMinS) xMin = xMinS; + if (xMax > xMaxS) xMax = xMaxS; + if (yMin < yMinS) yMin = yMinS; + if (yMax > yMaxS) yMax = yMaxS; + } + width = xMax - xMin; + height = yMax - yMin; +#ifdef SPLASH_RADIAL_USEBITMAP + 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 +#endif + scale = 1; + xMinScaled = scale * xMin; + yMinScaled = scale * yMin; + width *= scale; + height *= scale; +#ifdef SPLASH_RADIAL_USEBITMAP + maxSplitsExtend = radialMinSplits; + splash = NULL; + bitmap = NULL; + 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; + } +#endif + xd = x1 - x0; + yd = y1 - y0; + rd = r1 - r0; + td = t1 - t0; + radialConstant = xd * xd + yd * yd - rd * rd; + radiusConstant = rd * r0; + + // 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] -= xMinScaled; + ictm.m[5] -= yMinScaled; +#ifdef SPLASH_RADIAL_USEBITMAP + if (extendedRange[0] > 0) + range[0] = extendedRange[0]; + if (extendedRange[1] < 1) + range[1] = extendedRange[1]; + double rMin, rMax; + rMin = r0 + extendedRange[0] * (r1 - r0); + rMax = r0 + extendedRange[1] * (r1 - r0); + rMin *= scale; + rMax *= scale; + if (rMin > 65536 && r1 != r0) { + rMin = 65536; + extendedRange[0] = ((double) rMin / scale - r0) / (r1 - r0); + } + if (rMax > 65536 && r1 != r0) { + rMax = 65536; + extendedRange[1] = ((double) rMax / scale - r0) / (r1 - r0); + } + maxSplits = splashRound((rMax - rMin)/ 5); + if (maxSplits < 0) maxSplits = -maxSplits; + if (maxSplits < radialMinSplits) + maxSplits = radialMinSplits; + if (maxSplits > radialMaxSplits) + maxSplits = radialMaxSplits; +#endif +} + +SplashRadialPattern::~SplashRadialPattern() { +#ifdef SPLASH_RADIAL_USEBITMAP + if (splash) { + delete splash; + } + if (bitmap) { + delete bitmap; + } +#endif +} + +GBool SplashRadialPattern::getRadialColor(SplashCoord xs, SplashCoord ys, SplashColorPtr cell) { + double x, y, m, n, dx, dy, s, t, rh, tMin, tMax; + GfxColor color; + + // first we have to transform the bitmap poition (xs,ys) to radial + // coordinates (x,y) + x = ((double) xs + xMinScaled) / scale; + y = ((double) ys + yMinScaled) / scale; + tMin = (t0 < t1) ? t0 : t1; + tMax = (t0 < t1) ? t1 : t0; + + // now ew can calculate s for by solving following system of equations; + // + // 1. (x - xc(s))^2 + (y -yc(s))^2 = rc(s)^2 + // 2. xc(s) = x0 + s * (x1 - xo) + // 3. yc(s) = y0 + s * (y1 - yo) + // 4. rc(s) = r0 + s * (r1 - ro) + // + // this leads to: + // + // s^2 * radialConstant + 2 * s * m + n = 0 + // where + // radialConstant = (x1 - x0)^2 + (y1 - y0)^2 - (r1 - r0)^2 + // m = (x0 - x) * (x0 - x) + (y0 - y) * (y0 - y) - (r1 - r0) * r0 + // n = (x0 - x) * (x0 - x) + (y0 - y) * (y0 - y) - r0 * r0 + dx = x0 - x; + dy = y0 - y; + m = dx * xd + dy * yd - radiusConstant; + n = dx * dx + dy * dy - r0 * r0; + + if (fabs(radialConstant) < FLT_EPSILON) { + // if radialConstant is 0, we have a linear equation + // 2 * s * m + n = 0 + // if m is also 0, we have no solution: + if (fabs(m) <= FLT_EPSILON) + return gFalse; + // else + s = -n / (2 * m); + rh = r0 + s * rd; + // if radius < 0, value is always out of range + if (rh + FLT_EPSILON < 0) + return gFalse; + t = t0 + s * td; + // else look if t is in range or extension is allowed: + if (fabs(t - tMin) <= FLT_EPSILON) + t = tMin; + else if (t < tMin) { + if (!shading->getExtend0()) + return gFalse; + t = tMin; + } else if (fabs(t - tMax) <= FLT_EPSILON) + t = tMax; + else if (t > tMax) { + if (!shading->getExtend1()) + return gFalse; + t = tMax; + } + shading->getColor(t, &color); + convertGfxColor(cell, colorMode, shading->getColorSpace(), &color); + return gTrue; + } else { + // else we need to solve the quadratic equation + // s ^ 2 + 2 * p * s + q = 0; + double s1, s2, p, q, rt, rh1, rh2, th1, th2, t; + + p = m / radialConstant; + q = n / radialConstant; + rt = p * p - q; + if ( rt < 0 ) + return gFalse; + s1 = -p + sqrt(rt); + s2 = -p - sqrt(rt); + + // now make a complex decision which of the two possible + // values should be fit; + // 1) if both resulting radii are negativ, take none + // 2) if only one of the resulting radii is positiv, take that value + // 3) if both resulting radii are positiv, decide it on starting and + // ending value r0, r1 + // a) if r0 >= r1 and r(s1) > r(s2) and t(s2) is in range or extension is + // on, take s2 + // b) if r0 >= r1 and r(s1) > r(s2) and t(s2) is neither in range nor extension is + // on, take s1 + // c) if r0 >= r1 and r(s1) <= r(s2) and t(s1) is in range or extension is + // on, take s1 + // d) if r0 >= r1 and r(s1) <= r(s2) and t(s1) is neither in range nor extension is + // on, take s2 + // e) if r0 < r1 and r(s1) < r(s2) and t(s2) is in range or extension is + // on, take s2 + // f) if r0 < r1 and r(s1) < r(s2) and t(s2) is neither in range nor extension is + // on, take s1 + // g) if r0 < r1 and r(s1) >= r(s2) and t(s1) is in range or extension is + // on, take s1 + // h) if r0 < r1 and r(s1) >= r(s2) and t(s1) is neither in range nor extension is + // on, take s2 + rh1 = r0 + s1 * rd; + th1 = t0 + s1 * td; + if (fabs(th1 - tMin) <= FLT_EPSILON) + th1 = tMin; + if (fabs(th1 - tMax) <= FLT_EPSILON) + th1 = tMax; + rh2 = r0 + s2 * rd; + th2 = t0 + s2 * td; + if (fabs(th2 - tMin) <= FLT_EPSILON) + th2 = tMin; + if (fabs(th2 - tMax) <= FLT_EPSILON) + th2 = tMax; + if (rh1 + FLT_EPSILON >= 0) { + if (rh2 + FLT_EPSILON >= 0) { + if (r0 >= r1) { + if (rh1 > rh2) { + if ((th2 >= tMin && th2 <= tMax) || + (th2 < tMin && shading->getExtend0()) || + (th2 > tMax && shading->getExtend1())) + t = th2; + else + t = th1; + } else { + if ((th1 >= tMin && th1 <= tMax) || + (th1 < tMin && shading->getExtend0()) || + (th1 > tMin && shading->getExtend1())) + t = th1; + else + t = th2; + } + } else { + if (rh1 < rh2) { + if ((th2 >= tMin && th2 <= tMax) || + (th2 < tMin && shading->getExtend0()) || + (th2 > tMax && shading->getExtend1())) + t = th2; + else + t = th1; + } else { + if ((th1 >= tMin && th1 <= tMax) || + (th1 < tMin && shading->getExtend0()) || + (th1 > tMax && shading->getExtend1())) + t = th1; + else + t = th2; + } + } + } else + t = th1; + } else if (rh2 + FLT_EPSILON >= 0) { + t = th2; + } else + return gFalse; + if (t >= tMin && t <= tMax) { + shading->getColor(t, &color); + convertGfxColor(cell, colorMode, shading->getColorSpace(), &color); + return gTrue; + } + if (t < tMin && shading->getExtend0()) { + shading->getColor(tMin, &color); + convertGfxColor(cell, colorMode, shading->getColorSpace(), &color); + return gTrue; + } + if (t > tMax && shading->getExtend1()) { + shading->getColor(tMax, &color); + convertGfxColor(cell, colorMode, shading->getColorSpace(), &color); + return gTrue; + } + return gFalse; + } +} + +#ifdef SPLASH_RADIAL_USEBITMAP +GBool SplashRadialPattern::getBitmapColor(int xs, int ys, SplashColorPtr cell) { + if ((bitmap->getAlphaPtr())[ys * bitmap->getWidth() + xs]) { + bitmap->getPixel(xs, ys, cell); + return gTrue; + } + return gFalse; +} +#else +GBool SplashRadialPattern::isOk() { + if (!radialPatternIsDegenerate(shading) && xMin < xMax && yMin < yMax) + return gTrue; + return gFalse; +} +#endif + +GBool SplashRadialPattern::getColor(int x, int y, SplashColorPtr c) { +#ifdef SPLASH_RADIAL_USEBITMAP + double xc, yc; + int xs, ys, xMin, yMin, xMax, yMax; + Gulong superCell[splashMaxColorComps]; + SplashColor cell; + int cp = 0, i, ca = 0; + + for (i = 0; i < splashMaxColorComps; i++) + superCell[i] = 0; + ictm.transform(x, y, &xc, &yc); + xMin = splashFloor(xc); + yMin = splashFloor(yc); + ictm.transform(x + 1, y + 1, &xc, &yc); + xMax = splashFloor(xc); + yMax = splashFloor(yc); + if (xMax < 0) xMax = 0; + if (xMax >= splashRound(width)) xMax = splashRound(width) - 1; + if (yMax < 0) yMax = 0; + if (yMax >= splashRound(height)) yMax = splashRound(height) - 1; + if (xMin < 0) xMin = 0; + if (xMin >= splashRound(width)) xMin = splashRound(width) - 1; + if (yMin < 0) yMin = 0; + if (yMin >= splashRound(height)) yMin = splashRound(height) - 1; + if (xMin == xMax && yMin == yMax) + return getBitmapColor(xMin, yMin, c); + 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 (getBitmapColor(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; +#else + double xc, yc; + ictm.transform(x, y, &xc, &yc); + return getRadialColor(xc, yc, c); +#endif +} + +#ifdef SPLASH_RADIAL_USEBITMAP +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 = range[0]; + 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 - xMinScaled); *ysc = splashFloor(ya * scale - yMinScaled); + 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 = range[1]; + 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 = range[0] + factor * (range[1] - range[0]); + 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 = range[0] + factor * (range[1] - range[0]); + tb = t0 + sb * (t1 - t0); + getShadingColorRadialHelper(t0, t1, tb, shading, &colorB); + break; + } + ib = (ia + ib) / 2; + factor = (double)ib / (double)maxSplits; + sb = range[0] + factor * (range[1] - range[0]); + 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 - xMinScaled); *ysc = splashFloor(ya * scale - yMinScaled); + convertGfxColor(c, colorMode, srcColorSpace, &colorB); + ia = ib; + if (testClip(*xsc, *ysc, *radius) != splashClipPartial && + maxSplits > 8192 && ib + 32 < maxSplits) + ia = ib + 32; + 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 - xMinScaled); *ysc = splashFloor(ya * scale - yMinScaled); + 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) ? range[0] : range[1]; + // buest guess: + sEnd = (fExtend) ? extendedRange[1] : extendedRange[0]; + 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 - xMinScaled; + ya = ya * scale - yMinScaled; + *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) ? range[0] : range[1]; + 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 - xMinScaled); *ysc = splashFloor(ya * scale - yMinScaled); + 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) ? range[1] : range[0]; + sStart = (r1!= r0) ? -r0 / (r1 - r0) : range[1] * 16; + if (!fExtend) { + sEnd = sStart; + sStart = (r1 > r0) ? range[0] : range[1]; + } + 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 - xMinScaled); *ysc = splashFloor(ya * scale - yMinScaled); + 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; +} +#endif + +//------------------------------------------------------------------------ // SplashAxialPattern //------------------------------------------------------------------------ @@ -3011,14 +4049,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 +4417,36 @@ 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; + SplashRadialColor *pattern = new SplashRadialPattern(state, shading, sMin, sMax, colorMode); + if (pattern->isOk()) { + // get the clip region bbox + pattern->getClipBBox(&xMin, &yMin, &xMax, &yMax); + // clip this region + state->moveTo(xMin, yMin); + state->lineTo(xMax, yMin); + state->lineTo(xMax, yMax); + state->lineTo(xMin, yMax); + state->closePath(); + path = convertPath(state, state->getPath()); +#ifdef SPLASH_RADIAL_USEBITMAP + // 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); + } +#else + retVal = (splash->shadedFill(path, shading->getHasBBox(), pattern) == splashOk); +#endif + state->clearPath(); + delete path; + } + delete pattern; + return retVal; +} diff --git a/poppler/SplashOutputDev.h b/poppler/SplashOutputDev.h index 570d036..75ec4ae 100644 --- a/poppler/SplashOutputDev.h +++ b/poppler/SplashOutputDev.h @@ -105,6 +105,63 @@ private: GBool bDirectColorTranslation; }; +// see GfxState.h, GfxRadialShading +class SplashRadialPattern: public SplashRadialColor { +public: + + SplashRadialPattern(GfxState *state, GfxRadialShading *shading, double sMin, double sMax, SplashColorMode colorMode); + + virtual SplashPattern *copy() { return new SplashRadialPattern(state, shading, range[0], range[1], colorMode); } + + virtual ~SplashRadialPattern(); + + virtual GBool getColor(int x, int y, SplashColorPtr c); + virtual GBool isStatic() { return gFalse; } + +#ifdef SPLASH_RADIAL_USEBITMAP + Splash *getSplash() { return splash; } + + SplashBitmap *getBitmap() { return bitmap; } + + 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); + GBool isOk() { return (bitmap != NULL); } +#else + virtual GBool isOk(); +#endif + void getClipBBox(double *clipXMin, double *clipYMin, double *clipXMax, double *clipYMax) + { *clipXMin = xMin; *clipYMin = yMin; *clipXMax = xMax; *clipYMax = yMax; } +private: +#ifdef SPLASH_RADIAL_USEBITMAP + GBool getBitmapColor(int x, int y, SplashColorPtr c); + int ia; + int maxSplits, maxSplitsExtend; + Splash *splash; + SplashBitmap *bitmap; + double xa, ya, ra, ta, sa; +#endif + GBool getRadialColor(SplashCoord x, SplashCoord y, SplashColorPtr c); + double radialConstant, radiusConstant, yd, xd, rd, td; + double x0, y0, r0, x1, y1, r1, t0, t1; + SplashColorMode colorMode; + GfxRadialShading *shading; + GfxState *state; + Matrix ictm; + double range[2], extendedRange[2]; + double yMinScaled, xMinScaled; + double xMin, xMax, yMin, yMax; + double width, height; + double scale; +}; + //------------------------------------------------------------------------ // number of Type 3 fonts to cache @@ -132,7 +189,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 +243,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..666f60e 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; } } @@ -3530,6 +3508,698 @@ GBool Splash::gouraudTriangleShadedFill(SplashGouraudColor *shading) return gTrue; } +#ifdef SPLASH_RADIAL_USEBITMAP +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 == 256) { + 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 >= 256) { + 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 >= 256) { + 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 >= 256) { + 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 >= 256) { + 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 == 256) { + 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 >= 256) { + 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 >= 256) { + 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 >= 256) { + 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 >= 256) { + 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; +} +#endif SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc, int xDest, int yDest, int w, int h) { @@ -3950,19 +4620,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 +4653,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..e3b3b01 100644 --- a/splash/Splash.h +++ b/splash/Splash.h @@ -255,6 +255,19 @@ public: SplashPattern *pattern); // Draw a gouraud triangle shading. GBool gouraudTriangleShadedFill(SplashGouraudColor *shading); +#ifdef SPLASH_RADIAL_USEBITMAP + // 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); +#endif private: diff --git a/splash/SplashPattern.h b/splash/SplashPattern.h index 09e9b1a..450a76b 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,27 @@ public: virtual void getParameterizedColor(double t, SplashColorMode mode, SplashColorPtr c) = 0; }; +//------------------------------------------------------------------------ +// SplashRadialColor (needed for radialShadedFill) +//------------------------------------------------------------------------ + +class SplashRadialColor: public SplashPattern { +public: + virtual GBool isOk() = 0; + virtual void getClipBBox(double *clipXMin, double *clipYMin, double *clipXMax, double *clipYMax) = 0; +#ifdef SPLASH_RADIAL_USEBITMAP + 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 Splash *getSplash() = 0; + virtual SplashBitmap *getBitmap() = 0; +#endif +}; #endif