diff --git a/poppler/Function.cc b/poppler/Function.cc index 0a72278..6365ff5 100644 --- a/poppler/Function.cc +++ b/poppler/Function.cc @@ -39,7 +39,6 @@ #include "Stream.h" #include "Error.h" #include "Function.h" -#include "PopplerCache.h" #ifndef M_PI #define M_PI 3.14159265358979323846 @@ -678,6 +677,7 @@ StitchingFunction::StitchingFunction(Object *funcObj, Dict *dict, std::set } } + n = funcs[0]->getOutputSize(); ok = gTrue; return; @@ -1048,84 +1048,6 @@ void PSStack::roll(int n, int j) { } } -class PostScriptFunctionKey : public PopplerCacheKey -{ - public: - PostScriptFunctionKey(int sizeA, double *inA, bool copyA) - { - init(sizeA, inA, copyA); - } - - PostScriptFunctionKey(const PostScriptFunctionKey &key) - { - init(key.size, key.in, key.copied); - } - - void init(int sizeA, double *inA, bool copyA) - { - copied = copyA; - size = sizeA; - if (copied) { - in = new double[size]; - for (int i = 0; i < size; ++i) in[i] = inA[i]; - } else { - in = inA; - } - } - - ~PostScriptFunctionKey() - { - if (copied) delete[] in; - } - - bool operator==(const PopplerCacheKey &key) const - { - const PostScriptFunctionKey *k = static_cast(&key); - if (size == k->size) { - bool equal = true; - for (int i = 0; equal && i < size; ++i) { - equal = in[i] == k->in[i]; - } - return equal; - } else { - return false; - } - } - - bool copied; - int size; - double *in; -}; - -class PostScriptFunctionItem : public PopplerCacheItem -{ - public: - PostScriptFunctionItem(int sizeA, double *outA) - { - init(sizeA, outA); - } - - PostScriptFunctionItem(const PostScriptFunctionItem &item) - { - init(item.size, item.out); - } - - void init(int sizeA, double *outA) - { - size = sizeA; - out = new double[size]; - for (int i = 0; i < size; ++i) out[i] = outA[i]; - } - - ~PostScriptFunctionItem() - { - delete[] out; - } - - int size; - double *out; -}; - PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) { Stream *str; int codePtr; @@ -1134,9 +1056,7 @@ PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) { code = NULL; codeString = NULL; codeSize = 0; - stack = NULL; ok = gFalse; - cache = new PopplerCache(5); //----- initialize the generic stuff if (!init(dict)) { @@ -1173,8 +1093,6 @@ PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) { ok = gTrue; - stack = new PSStack(); - err2: str->close(); err1: @@ -1186,58 +1104,33 @@ PostScriptFunction::PostScriptFunction(PostScriptFunction *func) { code = (PSObject *)gmallocn(codeSize, sizeof(PSObject)); memcpy(code, func->code, codeSize * sizeof(PSObject)); codeString = func->codeString->copy(); - stack = new PSStack(); - memcpy(stack, func->stack, sizeof(PSStack)); - - cache = new PopplerCache(func->cache->size()); - for (int i = 0; i < func->cache->numberOfItems(); ++i) - { - PostScriptFunctionKey *key = new PostScriptFunctionKey(*(PostScriptFunctionKey*)func->cache->key(i)); - PostScriptFunctionItem *item = new PostScriptFunctionItem(*(PostScriptFunctionItem*)func->cache->item(i)); - cache->put(key, item); - } } PostScriptFunction::~PostScriptFunction() { gfree(code); delete codeString; - delete stack; - delete cache; } void PostScriptFunction::transform(double *in, double *out) { + PSStack stack; int i; - PostScriptFunctionKey key(m, in, false); - PopplerCacheItem *item = cache->lookup(key); - if (item) { - PostScriptFunctionItem *it = static_cast(item); - for (int i = 0; i < n; ++i) { - out[i] = it->out[i]; - } - return; - } - - stack->clear(); for (i = 0; i < m; ++i) { //~ may need to check for integers here - stack->pushReal(in[i]); + stack.pushReal(in[i]); } - exec(stack, 0); + exec(&stack, 0); for (i = n - 1; i >= 0; --i) { - out[i] = stack->popNum(); + out[i] = stack.popNum(); if (out[i] < range[i][0]) { out[i] = range[i][0]; } else if (out[i] > range[i][1]) { out[i] = range[i][1]; } } + stack.clear(); - PostScriptFunctionKey *newKey = new PostScriptFunctionKey(m, in, true); - PostScriptFunctionItem *newItem = new PostScriptFunctionItem(n, out); - cache->put(newKey, newItem); - - // if (!stack->empty()) { + // if (!stack.empty()) { // error(-1, "Extra values on stack at end of PostScript function"); // } } diff --git a/poppler/Function.h b/poppler/Function.h index 3541775..6cbc742 100644 --- a/poppler/Function.h +++ b/poppler/Function.h @@ -239,10 +239,8 @@ private: GooString *codeString; PSObject *code; - PSStack *stack; int codeSize; GBool ok; - PopplerCache *cache; }; #endif diff --git a/poppler/GfxState.cc b/poppler/GfxState.cc index 78ac5a8..757cd14 100644 --- a/poppler/GfxState.cc +++ b/poppler/GfxState.cc @@ -34,6 +34,7 @@ #pragma implementation #endif +#include #include #include #include @@ -51,11 +52,11 @@ //------------------------------------------------------------------------ -GBool Matrix::invertTo(Matrix *other) +GBool Matrix::invertTo(Matrix *other) const { double det; - det = 1 / (m[0] * m[3] - m[1] * m[2]); + det = 1 / determinant(); other->m[0] = m[3] * det; other->m[1] = -m[1] * det; other->m[2] = -m[2] * det; @@ -66,7 +67,7 @@ GBool Matrix::invertTo(Matrix *other) return gTrue; } -void Matrix::transform(double x, double y, double *tx, double *ty) +void Matrix::transform(double x, double y, double *tx, double *ty) const { double temp_x, temp_y; @@ -77,6 +78,21 @@ void Matrix::transform(double x, double y, double *tx, double *ty) *ty = temp_y; } +// Matrix norm, taken from _cairo_matrix_transformed_circle_major_axis +double Matrix::norm() const +{ + double f, g, h, i, j; + + i = m[0]*m[0] + m[1]*m[1]; + j = m[2]*m[2] + m[3]*m[3]; + + f = 0.5 * (i + j); + g = 0.5 * (i - j); + h = m[0]*m[2] + m[1]*m[3]; + + return sqrt (f + hypot (g, h)); +} + //------------------------------------------------------------------------ struct GfxBlendModeInfo { @@ -2708,22 +2724,17 @@ void GfxFunctionShading::getColor(double x, double y, GfxColor *color) { } //------------------------------------------------------------------------ -// GfxAxialShading +// GfxUnivariateShading //------------------------------------------------------------------------ -GfxAxialShading::GfxAxialShading(double x0A, double y0A, - double x1A, double y1A, - double t0A, double t1A, - Function **funcsA, int nFuncsA, - GBool extend0A, GBool extend1A): - GfxShading(2) +GfxUnivariateShading::GfxUnivariateShading(int typeA, + double t0A, double t1A, + Function **funcsA, int nFuncsA, + GBool extend0A, GBool extend1A): + GfxShading(typeA) { int i; - x0 = x0A; - y0 = y0A; - x1 = x1A; - y1 = y1A; t0 = t0A; t1 = t1A; nFuncs = nFuncsA; @@ -2732,17 +2743,19 @@ GfxAxialShading::GfxAxialShading(double x0A, double y0A, } extend0 = extend0A; extend1 = extend1A; + + cacheSize = 0; + lastMatch = 0; + cacheBounds = NULL; + cacheCoeff = NULL; + cacheValues = NULL; } -GfxAxialShading::GfxAxialShading(GfxAxialShading *shading): +GfxUnivariateShading::GfxUnivariateShading(GfxUnivariateShading *shading): GfxShading(shading) { int i; - x0 = shading->x0; - y0 = shading->y0; - x1 = shading->x1; - y1 = shading->y1; t0 = shading->t0; t1 = shading->t1; nFuncs = shading->nFuncs; @@ -2751,14 +2764,173 @@ GfxAxialShading::GfxAxialShading(GfxAxialShading *shading): } extend0 = shading->extend0; extend1 = shading->extend1; + + cacheSize = 0; + lastMatch = 0; + cacheBounds = NULL; + cacheCoeff = NULL; + cacheValues = NULL; } -GfxAxialShading::~GfxAxialShading() { +GfxUnivariateShading::~GfxUnivariateShading() { int i; for (i = 0; i < nFuncs; ++i) { delete funcs[i]; } + + gfree (cacheBounds); +} + +void GfxUnivariateShading::getColor(double t, GfxColor *color) { + double out[gfxColorMaxComps]; + int i, nComps; + + // NB: there can be one function with n outputs or n functions with + // one output each (where n = number of color components) + nComps = nFuncs * funcs[0]->getOutputSize(); + + if (cacheSize > 0) { + double x, ix, *l, *u, *upper; + + if (cacheBounds[lastMatch - 1] >= t) { + upper = std::lower_bound (cacheBounds, cacheBounds + lastMatch - 1, t); + lastMatch = upper - cacheBounds; + lastMatch = std::min(std::max(1, lastMatch), cacheSize - 1); + } else if (cacheBounds[lastMatch] < t) { + upper = std::lower_bound (cacheBounds + lastMatch + 1, cacheBounds + cacheSize, t); + lastMatch = upper - cacheBounds; + lastMatch = std::min(std::max(1, lastMatch), cacheSize - 1); + } + + x = (t - cacheBounds[lastMatch-1]) * cacheCoeff[lastMatch]; + ix = 1.0 - x; + u = cacheValues + lastMatch * nComps; + l = u - nComps; + + for (i = 0; i < nComps; ++i) { + out[i] = ix * l[i] + x * u[i]; + } + } else { + for (i = 0; i < nComps; ++i) { + out[i] = 0; + } + for (i = 0; i < nFuncs; ++i) { + funcs[i]->transform(&t, &out[i]); + } + } + + for (i = 0; i < nComps; ++i) { + color->c[i] = dblToCol(out[i]); + } +} + +void GfxUnivariateShading::setupCache(const Matrix *ctm, + double xMin, double yMin, + double xMax, double yMax) { + double sMin, sMax, tMin, tMax, upperBound; + int i, j, nComps, maxSize; + + gfree (cacheBounds); + cacheSize = 0; + + // NB: there can be one function with n outputs or n functions with + // one output each (where n = number of color components) + nComps = nFuncs * funcs[0]->getOutputSize(); + + getParameterRange(&sMin, &sMax, xMin, yMin, xMax, yMax); + upperBound = ctm->norm() * getDistance(sMin, sMax); + maxSize = ceil(upperBound); + maxSize = std::max(maxSize, 2); + + { + double x[4], y[4]; + + ctm->transform(xMin, yMin, &x[0], &y[0]); + ctm->transform(xMax, yMin, &x[1], &y[1]); + ctm->transform(xMin, yMax, &x[2], &y[2]); + ctm->transform(xMax, yMax, &x[3], &y[3]); + + xMin = xMax = x[0]; + yMin = yMax = y[0]; + for (i = 1; i < 4; i++) { + xMin = std::min(xMin, x[i]); + yMin = std::min(yMin, y[i]); + xMax = std::max(xMax, x[i]); + yMax = std::max(yMax, y[i]); + } + } + + if (maxSize > (xMax-xMin) * (yMax-yMin)) { + return; + } + + if (t0 < t1) { + tMin = t0 + sMin * (t1 - t0); + tMax = t0 + sMax * (t1 - t0); + } else { + tMin = t0 + sMax * (t1 - t0); + tMax = t0 + sMin * (t1 - t0); + } + + cacheBounds = (double *)gmallocn(maxSize, sizeof(double) * (nComps + 2)); + cacheCoeff = cacheBounds + maxSize; + cacheValues = cacheCoeff + maxSize; + + if (cacheSize != 0) { + for (j = 0; j < cacheSize; ++j) { + cacheCoeff[j] = 1 / (cacheBounds[j+1] - cacheBounds[j]); + } + } else { + double step = (tMax - tMin) / (maxSize - 1); + double coeff = (maxSize - 1) / (tMax - tMin); + + cacheSize = maxSize; + + for (j = 0; j < cacheSize; ++j) { + cacheBounds[j] = tMin + j * step; + cacheCoeff[j] = coeff; + + for (i = 0; i < nComps; ++i) { + cacheValues[j*nComps + i] = 0; + } + for (i = 0; i < nFuncs; ++i) { + funcs[i]->transform(&cacheBounds[j], &cacheValues[j*nComps + i]); + } + } + } + + lastMatch = 1; +} + + +//------------------------------------------------------------------------ +// GfxAxialShading +//------------------------------------------------------------------------ + +GfxAxialShading::GfxAxialShading(double x0A, double y0A, + double x1A, double y1A, + double t0A, double t1A, + Function **funcsA, int nFuncsA, + GBool extend0A, GBool extend1A): + GfxUnivariateShading(2, t0A, t1A, funcsA, nFuncsA, extend0A, extend1A) +{ + x0 = x0A; + y0 = y0A; + x1 = x1A; + y1 = y1A; +} + +GfxAxialShading::GfxAxialShading(GfxAxialShading *shading): + GfxUnivariateShading(shading) +{ + x0 = shading->x0; + y0 = shading->y0; + x1 = shading->x1; + y1 = shading->y1; +} + +GfxAxialShading::~GfxAxialShading() { } GfxAxialShading *GfxAxialShading::parse(Dict *dict, Gfx *gfx) { @@ -2862,79 +3034,101 @@ GfxShading *GfxAxialShading::copy() { return new GfxAxialShading(this); } -void GfxAxialShading::getColor(double t, GfxColor *color) { - double out[gfxColorMaxComps]; - int i; +double GfxAxialShading::getDistance(double sMin, double sMax) { + double xMin, yMin, xMax, yMax; - // NB: there can be one function with n outputs or n functions with - // one output each (where n = number of color components) - for (i = 0; i < gfxColorMaxComps; ++i) { - out[i] = 0; - } - for (i = 0; i < nFuncs; ++i) { - funcs[i]->transform(&t, &out[i]); - } - for (i = 0; i < gfxColorMaxComps; ++i) { - color->c[i] = dblToCol(out[i]); - } + xMin = x0 + sMin * (x1 - x0); + yMin = y0 + sMin * (y1 - y0); + xMax = x0 + sMax * (x1 - x0); + yMax = y0 + sMax * (y1 - y0); + + return hypot(xMax-xMin, yMax-yMin); } +void GfxAxialShading::getParameterRange(double *lower, double *upper, + double xMin, double yMin, + double xMax, double yMax) { + double pdx, pdy, invsqnorm, tdx, tdy, t, range[2]; + + // Linear gradients are orthogonal to the line passing through their + // extremes. Because of convexity, the parameter range can be + // computed as the convex hull (one the real line) of the parameter + // values of the 4 corners of the box. + // + // The parameter value t for a point (x,y) can be computed as: + // + // t = (p2 - p1) . (x,y) / |p2 - p1|^2 + // + // t0 is the t value for the top left corner + // tdx is the difference between left and right corners + // tdy is the difference between top and bottom corners + + pdx = x1 - x0; + pdy = y1 - y0; + invsqnorm = 1.0 / (pdx * pdx + pdy * pdy); + pdx *= invsqnorm; + pdy *= invsqnorm; + + t = (xMin - x0) * pdx + (yMin - y0) * pdy; + tdx = (xMax - xMin) * pdx; + tdy = (yMax - yMin) * pdy; + + // Because of the linearity of the t value, tdx can simply be added + // the t0 to move along the top edge. After this, *lower and *upper + // represent the parameter range for the top edge, so extending it + // to include the whole box simply requires adding tdy to the + // correct extreme. + + range[0] = range[1] = t; + if (tdx < 0) + range[0] += tdx; + else + range[1] += tdx; + + if (tdy < 0) + range[0] += tdy; + else + range[1] += tdy; + + *lower = std::max(0., std::min(1., range[0])); + *upper = std::max(0., std::min(1., range[1])); +} + //------------------------------------------------------------------------ // GfxRadialShading //------------------------------------------------------------------------ +#ifndef RADIAL_EPSILON +#define RADIAL_EPSILON (1. / 1024 / 1024) +#endif + GfxRadialShading::GfxRadialShading(double x0A, double y0A, double r0A, double x1A, double y1A, double r1A, double t0A, double t1A, Function **funcsA, int nFuncsA, GBool extend0A, GBool extend1A): - GfxShading(3) + GfxUnivariateShading(3, t0A, t1A, funcsA, nFuncsA, extend0A, extend1A) { - int i; - x0 = x0A; y0 = y0A; r0 = r0A; x1 = x1A; y1 = y1A; r1 = r1A; - t0 = t0A; - t1 = t1A; - nFuncs = nFuncsA; - for (i = 0; i < nFuncs; ++i) { - funcs[i] = funcsA[i]; - } - extend0 = extend0A; - extend1 = extend1A; } GfxRadialShading::GfxRadialShading(GfxRadialShading *shading): - GfxShading(shading) + GfxUnivariateShading(shading) { - int i; - x0 = shading->x0; y0 = shading->y0; r0 = shading->r0; x1 = shading->x1; y1 = shading->y1; r1 = shading->r1; - t0 = shading->t0; - t1 = shading->t1; - nFuncs = shading->nFuncs; - for (i = 0; i < nFuncs; ++i) { - funcs[i] = shading->funcs[i]->copy(); - } - extend0 = shading->extend0; - extend1 = shading->extend1; } GfxRadialShading::~GfxRadialShading() { - int i; - - for (i = 0; i < nFuncs; ++i) { - delete funcs[i]; - } } GfxRadialShading *GfxRadialShading::parse(Dict *dict, Gfx *gfx) { @@ -3030,21 +3224,310 @@ GfxShading *GfxRadialShading::copy() { return new GfxRadialShading(this); } -void GfxRadialShading::getColor(double t, GfxColor *color) { - double out[gfxColorMaxComps]; - int i; +double GfxRadialShading::getDistance(double sMin, double sMax) { + double xMin, yMin, rMin, xMax, yMax, rMax; - // NB: there can be one function with n outputs or n functions with - // one output each (where n = number of color components) - for (i = 0; i < gfxColorMaxComps; ++i) { - out[i] = 0; + xMin = x0 + sMin * (x1 - x0); + yMin = y0 + sMin * (y1 - y0); + rMin = r0 + sMin * (r1 - r0); + + xMax = x0 + sMax * (x1 - x0); + yMax = y0 + sMax * (y1 - y0); + rMax = r0 + sMax * (r1 - r0); + + return hypot(xMax-xMin, yMax-yMin) + fabs(rMax-rMin); +} + +// 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; +} + +void GfxRadialShading::getParameterRange(double *lower, double *upper, + double xMin, double yMin, + double xMax, double yMax) { + double cx, cy, cr, dx, dy, dr; + double a, x_focus, y_focus; + double mindr, minx, miny, maxx, maxy; + double range[2]; + GBool valid; + + // 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 + + if (xMin >= xMax || yMin >=yMax || + (fabs (r0 - r1) < RADIAL_EPSILON && + (std::min(r0, r1) < RADIAL_EPSILON || + std::max(fabs (x0 - x1), fabs (y0 - y1)) < 2 * RADIAL_EPSILON))) { + *lower = *upper = 0; + return; } - for (i = 0; i < nFuncs; ++i) { - funcs[i]->transform(&t, &out[i]); + + range[0] = range[1] = 0; + valid = gFalse; + + 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 -= RADIAL_EPSILON; + yMin -= RADIAL_EPSILON; + xMax += RADIAL_EPSILON; + yMax += RADIAL_EPSILON; + + // enlarge boundaries even more to avoid rounding problems when + // testing if a point belongs to the box + minx = xMin - RADIAL_EPSILON; + miny = yMin - RADIAL_EPSILON; + maxx = xMax + RADIAL_EPSILON; + maxy = yMax + RADIAL_EPSILON; + + // we dont' allow negative radiuses, so we will be checking that + // t*dr >= mindr to consider t valid + mindr = -(cr + RADIAL_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) >= RADIAL_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); + } } - for (i = 0; i < gfxColorMaxComps; ++i) { - color->c[i] = dblToCol(out[i]); + + // 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) >= RADIAL_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) < RADIAL_EPSILON * RADIAL_EPSILON) { + double b; + + // 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| < RADIAL_EPSILON + // AND + // 2a) The circles are both very small: + // min (r0, r1) < RADIAL_EPSILON + // OR + // 2b) The circles are very close to each other: + // max (|dx|, |dy|) < 2 * RADIAL_EPSILON + // + // Assuming that the gradient is not degenerate, we want to + // show that |a| < RADIAL_EPSILON^2 implies |dr| >= RADIAL_EPSILON. + // + // If the gradient is not degenerate yet it has |dr| < + // RADIAL_EPSILON, (2b) is false, thus: + // + // max (|dx|, |dy|) >= 2*RADIAL_EPSILON + // which implies: + // 4*RADIAL_EPSILON^2 <= max (|dx|, |dy|)^2 <= dx^2 + dy^2 + // + // From the definition of a, we get: + // a = dx^2 + dy^2 - dr^2 < RADIAL_EPSILON^2 + // dx^2 + dy^2 - RADIAL_EPSILON^2 < dr^2 + // 3*RADIAL_EPSILON^2 < dr^2 + // + // which is inconsistent with the hypotheses, thus |dr| < + // RADIAL_EPSILON is false or the gradient is degenerate. + + assert (fabs (dr) >= RADIAL_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. We will be limiting the range to + // [0,1] anyway, so we simply add the biggest legitimate + // circle (it happens for 0 or for 1). + if (dr < 0) { + valid = radialExtendRange (range, 0, valid); + } else { + valid = radialExtendRange (range, 1, 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) >= RADIAL_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 } + + *lower = std::max(0., std::min(1., range[0])); + *upper = std::max(0., std::min(1., range[1])); } //------------------------------------------------------------------------ diff --git a/poppler/GfxState.h b/poppler/GfxState.h index b425b4a..37c3303 100644 --- a/poppler/GfxState.h +++ b/poppler/GfxState.h @@ -49,8 +49,10 @@ class Matrix { public: double m[6]; - GBool invertTo(Matrix *other); - void transform(double x, double y, double *tx, double *ty); + GBool invertTo(Matrix *other) const; + void transform(double x, double y, double *tx, double *ty) const; + double determinant() const { return m[0] * m[3] - m[1] * m[2]; } + double norm() const; }; //------------------------------------------------------------------------ @@ -746,12 +748,56 @@ protected: }; //------------------------------------------------------------------------ +// GfxUnivariateShading +//------------------------------------------------------------------------ + +class GfxUnivariateShading: public GfxShading { +public: + + GfxUnivariateShading(int typeA, + double t0A, double t1A, + Function **funcsA, int nFuncsA, + GBool extend0A, GBool extend1A); + GfxUnivariateShading(GfxUnivariateShading *shading); + virtual ~GfxUnivariateShading(); + + double getDomain0() { return t0; } + double getDomain1() { return t1; } + GBool getExtend0() { return extend0; } + GBool getExtend1() { return extend1; } + int getNFuncs() { return nFuncs; } + Function *getFunc(int i) { return funcs[i]; } + void getColor(double t, GfxColor *color); + + void setupCache(const Matrix *ctm, + double xMin, double yMin, + double xMax, double yMax); + + virtual void getParameterRange(double *lower, double *upper, + double xMin, double yMin, + double xMax, double yMax) = 0; + + virtual double getDistance(double tMin, double tMax) = 0; + +private: + + double t0, t1; + Function *funcs[gfxColorMaxComps]; + int nFuncs; + GBool extend0, extend1; + + int cacheSize, lastMatch; + double *cacheBounds; + double *cacheCoeff; + double *cacheValues; +}; + +//------------------------------------------------------------------------ // GfxFunctionShading //------------------------------------------------------------------------ class GfxFunctionShading: public GfxShading { public: - GfxFunctionShading(double x0A, double y0A, double x1A, double y1A, double *matrixA, @@ -782,7 +828,7 @@ private: // GfxAxialShading //------------------------------------------------------------------------ -class GfxAxialShading: public GfxShading { +class GfxAxialShading: public GfxUnivariateShading { public: GfxAxialShading(double x0A, double y0A, @@ -799,28 +845,23 @@ public: void getCoords(double *x0A, double *y0A, double *x1A, double *y1A) { *x0A = x0; *y0A = y0; *x1A = x1; *y1A = y1; } - double getDomain0() { return t0; } - double getDomain1() { return t1; } - GBool getExtend0() { return extend0; } - GBool getExtend1() { return extend1; } - int getNFuncs() { return nFuncs; } - Function *getFunc(int i) { return funcs[i]; } - void getColor(double t, GfxColor *color); + + virtual void getParameterRange(double *lower, double *upper, + double xMin, double yMin, + double xMax, double yMax); + + virtual double getDistance(double tMin, double tMax); private: double x0, y0, x1, y1; - double t0, t1; - Function *funcs[gfxColorMaxComps]; - int nFuncs; - GBool extend0, extend1; }; //------------------------------------------------------------------------ // GfxRadialShading //------------------------------------------------------------------------ -class GfxRadialShading: public GfxShading { +class GfxRadialShading: public GfxUnivariateShading { public: GfxRadialShading(double x0A, double y0A, double r0A, @@ -838,21 +879,16 @@ public: void getCoords(double *x0A, double *y0A, double *r0A, double *x1A, double *y1A, double *r1A) { *x0A = x0; *y0A = y0; *r0A = r0; *x1A = x1; *y1A = y1; *r1A = r1; } - double getDomain0() { return t0; } - double getDomain1() { return t1; } - GBool getExtend0() { return extend0; } - GBool getExtend1() { return extend1; } - int getNFuncs() { return nFuncs; } - Function *getFunc(int i) { return funcs[i]; } - void getColor(double t, GfxColor *color); + + virtual void getParameterRange(double *lower, double *upper, + double xMin, double yMin, + double xMax, double yMax); + + virtual double getDistance(double tMin, double tMax); private: double x0, y0, r0, x1, y1, r1; - double t0, t1; - Function *funcs[gfxColorMaxComps]; - int nFuncs; - GBool extend0, extend1; }; //------------------------------------------------------------------------ diff --git a/poppler/SplashOutputDev.cc b/poppler/SplashOutputDev.cc index 0b3722a..00aa2da 100644 --- a/poppler/SplashOutputDev.cc +++ b/poppler/SplashOutputDev.cc @@ -153,50 +153,184 @@ void SplashGouraudPattern::getParameterizedColor(double colorinterp, SplashColor } //------------------------------------------------------------------------ -// SplashAxialPattern +// SplashUnivariatePattern //------------------------------------------------------------------------ -SplashAxialPattern::SplashAxialPattern(SplashColorMode colorModeA, GfxState *stateA, GfxAxialShading *shadingA) { +SplashUnivariatePattern::SplashUnivariatePattern(SplashColorMode colorModeA, GfxState *stateA, GfxUnivariateShading *shadingA) { Matrix ctm; + double xMin, yMin, xMax, yMax; 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(); + dt = t1 - t0; + + stateA->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); + shadingA->setupCache(&ctm, xMin, yMin, xMax, yMax); } -SplashAxialPattern::~SplashAxialPattern() { +SplashUnivariatePattern::~SplashUnivariatePattern() { } -GBool SplashAxialPattern::getColor(int x, int y, SplashColorPtr c) { +GBool SplashUnivariatePattern::getColor(int x, int y, SplashColorPtr c) { GfxColor gfxColor; - double tt; - double xc, yc, xaxis; + double xc, yc, t; 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 + if (! getParameter (xc, yc, &t)) + return gFalse; + + shading->getColor(t, &gfxColor); + convertGfxColor(c, colorMode, shading->getColorSpace(), &gfxColor); + return gTrue; +} + + +//------------------------------------------------------------------------ +// SplashRadialPattern +//------------------------------------------------------------------------ +#define RADIAL_EPSILON (1. / 1024 / 1024) + +SplashRadialPattern::SplashRadialPattern(SplashColorMode colorModeA, GfxState *stateA, GfxRadialShading *shadingA): + SplashUnivariatePattern(colorModeA, stateA, shadingA) +{ + shadingA->getCoords(&x0, &y0, &r0, &dx, &dy, &dr); + dx -= x0; + dy -= y0; + dr -= r0; + a = dx*dx + dy*dy - dr*dr; + if (fabs(a) > RADIAL_EPSILON) + inva = 1.0 / a; +} + +SplashRadialPattern::~SplashRadialPattern() { +} + +GBool SplashRadialPattern::getParameter(double xs, double ys, SplashCoord *t) { + double b, c, s0, s1; + + // We want to solve this 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) + // + // To simplify the system a little, we translate + // our coordinates to have the origin in (x0,y0) + + xs -= x0; + ys -= y0; + + // Then we have to solve the equation: + // A*s^2 - 2*B*s + C = 0 + // where + // A = dx^2 + dy^2 - dr^2 + // B = xs*dx + ys*dy + r0*dr + // C = xs^2 + ys^2 - r0^2 + + b = xs*dx + ys*dy + r0*dr; + c = xs*xs + ys*ys - r0*r0; + + if (fabs(a) <= RADIAL_EPSILON) { + // A is 0, thus the equation simplifies to: + // -2*B*s + C = 0 + // If B is 0, we can either have no solution or an indeterminate + // equation, thus we behave as if we had an invalid solution + if (fabs(b) <= RADIAL_EPSILON) + return gFalse; + + s0 = s1 = 0.5 * c / b; + } else { + double d; + + d = b*b - a*c; + if (d < 0) + return gFalse; + + d = sqrt (d); + s0 = b + d; + s1 = b - d; + + // If A < 0, one of the two solutions will have negative radius, + // thus it will be ignored. Otherwise we know that s1 <= s0 + // (because d >=0 implies b - d <= b + d), so if both are valid it + // will be the true solution. + s0 *= inva; + s1 *= inva; + } + + if (r0 + s0 * dr >= 0) { + if (0 <= s0 && s0 <= 1) { + *t = t0 + dt * s0; + return gTrue; + } else if (s0 < 0 && shading->getExtend0()) { + *t = t0; + return gTrue; + } else if (s0 > 1 && shading->getExtend1()) { + *t = t1; + return gTrue; + } + } + + if (r0 + s1 * dr >= 0) { + if (0 <= s1 && s1 <= 1) { + *t = t0 + dt * s1; + return gTrue; + } else if (s1 < 0 && shading->getExtend0()) { + *t = t0; + return gTrue; + } else if (s1 > 1 && shading->getExtend1()) { + *t = t1; + return gTrue; + } + } + + return gFalse; +} + +#undef RADIAL_EPSILON + +//------------------------------------------------------------------------ +// SplashAxialPattern +//------------------------------------------------------------------------ + +SplashAxialPattern::SplashAxialPattern(SplashColorMode colorModeA, GfxState *stateA, GfxAxialShading *shadingA): + SplashUnivariatePattern(colorModeA, stateA, shadingA) +{ + shadingA->getCoords(&x0, &y0, &x1, &y1); + dx = x1 - x0; + dy = y1 - y0; + mul = 1 / (dx * dx + dy * dy); +} + +SplashAxialPattern::~SplashAxialPattern() { +} + +GBool SplashAxialPattern::getParameter(double xc, double yc, double *t) { + double s; + + xc -= x0; + yc -= y0; + + s = (xc * dx + yc * dy) * mul; + if (0 <= s && s <= 1) { + *t = t0 + dt * s; + } else if (s < 0 && shading->getExtend0()) { + *t = t0; + } else if (s > 1 && shading->getExtend1()) { + *t = t1; + } else { return gFalse; + } - shading->getColor(tt, &gfxColor); - state->setFillColor(&gfxColor); - convertGfxColor(c, colorMode, state->getFillColorSpace(), state->getFillColor()); return gTrue; } @@ -3011,23 +3145,23 @@ 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; + w = (int)ceil(xMax) - tx; if (tx + w > bitmap->getWidth()) { w = bitmap->getWidth() - tx; } if (w < 1) { w = 1; } - h = (int)ceil(yMax) - ty + 1; + h = (int)ceil(yMax) - ty; if (ty + h > bitmap->getHeight()) { h = bitmap->getHeight() - ty; } @@ -3352,14 +3486,11 @@ GBool SplashOutputDev::gouraudTriangleShadedFill(GfxState *state, GfxGouraudTria return gFalse; } -GBool SplashOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) { +GBool SplashOutputDev::univariateShadedFill(GfxState *state, SplashUnivariatePattern *pattern, double tMin, double tMax) { double xMin, yMin, xMax, yMax; SplashPath *path; - GBool vaa = getVectorAntialias(); GBool retVal = gFalse; - // restore vector antialias because we support it here - setVectorAntialias(gTrue); // get the clip region bbox state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); @@ -3371,11 +3502,31 @@ GBool SplashOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading state->closePath(); path = convertPath(state, state->getPath()); - SplashPattern *pattern = new SplashAxialPattern(colorMode, state, shading); - retVal = (splash->shadedFill(path, shading->getHasBBox(), pattern) == splashOk); - setVectorAntialias(vaa); + retVal = (splash->shadedFill(path, pattern->getShading()->getHasBBox(), pattern) == splashOk); state->clearPath(); delete path; return retVal; } + +GBool SplashOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) { + GBool vaa = getVectorAntialias(); + // restore vector antialias because we support it here + setVectorAntialias(gTrue); + SplashAxialPattern *pattern = new SplashAxialPattern(colorMode, state, shading); + GBool retVal = univariateShadedFill(state, pattern, tMin, tMax); + setVectorAntialias(vaa); + + delete pattern; + + return retVal; +} + +GBool SplashOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading, double tMin, double tMax) { + SplashRadialPattern *pattern = new SplashRadialPattern(colorMode, state, shading); + GBool retVal = univariateShadedFill(state, pattern, tMin, tMax); + + delete pattern; + + return retVal; +} diff --git a/poppler/SplashOutputDev.h b/poppler/SplashOutputDev.h index 570d036..cc409cc 100644 --- a/poppler/SplashOutputDev.h +++ b/poppler/SplashOutputDev.h @@ -52,28 +52,43 @@ struct SplashTransparencyGroup; // Splash dynamic pattern //------------------------------------------------------------------------ -class SplashAxialPattern: public SplashPattern { +class SplashUnivariatePattern: public SplashPattern { public: - SplashAxialPattern(SplashColorMode colorMode, GfxState *state, GfxAxialShading *shading); - - virtual SplashPattern *copy() { return new SplashAxialPattern(colorMode, state, shading); } + SplashUnivariatePattern(SplashColorMode colorMode, GfxState *state, GfxUnivariateShading *shading); - virtual ~SplashAxialPattern(); + virtual ~SplashUnivariatePattern(); virtual GBool getColor(int x, int y, SplashColorPtr c); virtual GBool isStatic() { return gFalse; } -private: + virtual GBool getParameter(double xs, double ys, double *t) = 0; + + virtual GfxUnivariateShading *getShading() { return shading; } + +protected: Matrix ictm; - double x0, y0, x1, y1; - double dx, dy, mul; - double t0, t1; - GfxAxialShading *shading; + double t0, t1, dt; + GfxUnivariateShading *shading; GfxState *state; SplashColorMode colorMode; - double *bbox; +}; + +class SplashAxialPattern: public SplashUnivariatePattern { +public: + + SplashAxialPattern(SplashColorMode colorMode, GfxState *state, GfxAxialShading *shading); + + virtual SplashPattern *copy() { return new SplashAxialPattern(colorMode, state, (GfxAxialShading *) shading); } + + virtual ~SplashAxialPattern(); + + virtual GBool getParameter(double xs, double ys, double *t); + +private: + double x0, y0, x1, y1; + double dx, dy, mul; }; // see GfxState.h, GfxGouraudTriangleShading @@ -105,6 +120,23 @@ private: GBool bDirectColorTranslation; }; +// see GfxState.h, GfxRadialShading +class SplashRadialPattern: public SplashUnivariatePattern { +public: + + SplashRadialPattern(SplashColorMode colorMode, GfxState *state, GfxRadialShading *shading); + + virtual SplashPattern *copy() { return new SplashRadialPattern(colorMode, state, (GfxRadialShading *) shading); } + + virtual ~SplashRadialPattern(); + + virtual GBool getParameter(double xs, double ys, double *t); + +private: + double x0, y0, r0, dx, dy, dr; + double a, inva; +}; + //------------------------------------------------------------------------ // number of Type 3 fonts to cache @@ -132,7 +164,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 +218,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 tMin, double tMax); virtual GBool gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading); //----- path clipping @@ -291,6 +324,7 @@ public: void setFreeTypeHinting(GBool enable); private: + GBool univariateShadedFill(GfxState *state, SplashUnivariatePattern *pattern, double tMin, double tMax); void setupScreenParams(double hDPI, double vDPI); #if SPLASH_CMYK diff --git a/splash/Splash.cc b/splash/Splash.cc index bc317a6..6bede7d 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; } } @@ -3950,19 +3928,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); + 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 +3961,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;