poppler/Function.cc | 122 -------- poppler/Function.h | 3 poppler/GfxState.cc | 634 +++++++++++++++++++++++++++++++++++++++------ poppler/GfxState.h | 90 ++++-- poppler/SplashOutputDev.cc | 253 +++++++++++++++-- poppler/SplashOutputDev.h | 67 +++- splash/Splash.cc | 126 ++++++-- splash/SplashPattern.h | 7 8 files changed, 1006 insertions(+), 296 deletions(-)
New commits: commit e1a56d73b066e7152ccf6ccf36206def7956cb00 Author: Albert Astals Cid <[email protected]> Date: Wed Feb 16 00:06:45 2011 +0000 Lots of rendering improvements by Thomas and Andrea Function.cc: Stitching functions incorrectly reported 0 as output size. Function.cc: Remove cache from PostScriptFunction Function.cc: Make PSStack stack allocated GfxState.cc & GfxState.h: Abstract GfxSimpleShading, add Matrix::norm method, add simple caching, parameter range computation SplashOutputDev.cc & SplashOutputDev..h & Splash.cc & SplashPattern.h: Improve splash rendering, implement radial and abstract simple shadings in splash And maybe something more, look at the Followup Bug 32349 & Poppler: More shading fun ;-) thread for more info diff --git a/poppler/Function.cc b/poppler/Function.cc index 0a72278..b63ae81 100644 --- a/poppler/Function.cc +++ b/poppler/Function.cc @@ -16,6 +16,7 @@ // Copyright (C) 2006, 2008-2010 Albert Astals Cid <[email protected]> // Copyright (C) 2006 Jeff Muizelaar <[email protected]> // Copyright (C) 2010 Christian Feuersänger <[email protected]> +// Copyright (C) 2011 Andrea Canciani <[email protected]> // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -39,7 +40,6 @@ #include "Stream.h" #include "Error.h" #include "Function.h" -#include "PopplerCache.h" #ifndef M_PI #define M_PI 3.14159265358979323846 @@ -678,6 +678,7 @@ StitchingFunction::StitchingFunction(Object *funcObj, Dict *dict, std::set<int> } } + n = funcs[0]->getOutputSize(); ok = gTrue; return; @@ -1048,84 +1049,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<const PostScriptFunctionKey*>(&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 +1057,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 +1094,6 @@ PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) { ok = gTrue; - stack = new PSStack(); - err2: str->close(); err1: @@ -1186,58 +1105,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<PostScriptFunctionItem *>(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..d6ac893 100644 --- a/poppler/Function.h +++ b/poppler/Function.h @@ -15,6 +15,7 @@ // // Copyright (C) 2009, 2010 Albert Astals Cid <[email protected]> // Copyright (C) 2010 Christian Feuersänger <[email protected]> +// Copyright (C) 2011 Andrea Canciani <[email protected]> // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -239,10 +240,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..5716f26 100644 --- a/poppler/GfxState.cc +++ b/poppler/GfxState.cc @@ -18,10 +18,11 @@ // Copyright (C) 2006, 2010 Carlos Garcia Campos <[email protected]> // Copyright (C) 2006-2010 Albert Astals Cid <[email protected]> // Copyright (C) 2009 Koji Otani <[email protected]> -// Copyright (C) 2009 Thomas Freitag <[email protected]> +// Copyright (C) 2009, 2011 Thomas Freitag <[email protected]> // Copyright (C) 2009 Christian Persch <[email protected]> // Copyright (C) 2010 PaweÅ Wiejacha <[email protected]> // Copyright (C) 2010 Christian Feuersänger <[email protected]> +// Copyright (C) 2011 Andrea Canciani <[email protected]> // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -34,6 +35,7 @@ #pragma implementation #endif +#include <algorithm> #include <stddef.h> #include <math.h> #include <string.h> @@ -51,11 +53,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 +68,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 +79,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 +2725,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 +2744,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 +2765,174 @@ 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<int>(std::max<int>(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<int>(std::max<int>(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); + cacheBounds = NULL; + 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<int>(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<double>(xMin, x[i]); + yMin = std::min<double>(yMin, y[i]); + xMax = std::max<double>(xMax, x[i]); + yMax = std::max<double>(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 if (tMax != tMin) { + 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 +3036,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<double>(0., std::min<double>(1., range[0])); + *upper = std::max<double>(0., std::min<double>(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 +3226,313 @@ 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; +} + +inline void radialEdge(double num, double den, double delta, double lower, double upper, + double dr, double mindr, GBool &valid, double *range) +{ + 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); } - for (i = 0; i < nFuncs; ++i) { - funcs[i]->transform(&t, &out[i]); +} + +inline void radialCorner1(double x, double y, double &b, double dx, double dy, double cr, + double dr, double mindr, GBool &valid, double *range) +{ + 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); + } +} + +inline void radialCorner2(double x, double y, double a, double &b, double &c, double &d, double dx, double dy, double cr, + double inva, double dr, double mindr, GBool &valid, double *range) +{ + 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); + } +} +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<double>(r0, r1) < RADIAL_EPSILON || + std::max<double>(fabs (x0 - x1), fabs (y0 - y1)) < 2 * RADIAL_EPSILON))) { + *lower = *upper = 0; + return; } - for (i = 0; i < gfxColorMaxComps; ++i) { - color->c[i] = dblToCol(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); + } } + + // 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. + + // circles tangent (externally) to left/right/top/bottom edge + radialEdge(xMin - cr, dx + dr, dy, miny, maxy, dr, mindr, valid, range); + radialEdge(xMax + cr, dx - dr, dy, miny, maxy, dr, mindr, valid, range); + radialEdge(yMin - cr, dy + dr, dx, minx, maxx, dr, mindr, valid, range); + radialEdge(yMax + cr, dy - dr, dx, minx, maxx, dr, mindr, valid, range); + + // 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. + + // circles touching each corner + radialCorner1(xMin, yMin, b, dx, dy, cr, dr, mindr, valid, range); + radialCorner1(xMin, yMax, b, dx, dy, cr, dr, mindr, valid, range); + radialCorner1(xMax, yMin, b, dx, dy, cr, dr, mindr, valid, range); + radialCorner1(xMax, yMax, b, dx, dy, cr, dr, mindr, valid, range); + } 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. + + // circles touching each corner + radialCorner2(xMin, yMin, a, b, c, d, dx, dy, cr, inva, dr, mindr, valid, range); + radialCorner2(xMin, yMax, a, b, c, d, dx, dy, cr, inva, dr, mindr, valid, range); + radialCorner2(xMax, yMin, a, b, c, d, dx, dy, cr, inva, dr, mindr, valid, range); + radialCorner2(xMax, yMax, a, b, c, d, dx, dy, cr, inva, dr, mindr, valid, range); + } + + *lower = std::max<double>(0., std::min<double>(1., range[0])); + *upper = std::max<double>(0., std::min<double>(1., range[1])); } //------------------------------------------------------------------------ diff --git a/poppler/GfxState.h b/poppler/GfxState.h index b425b4a..8d853fa 100644 --- a/poppler/GfxState.h +++ b/poppler/GfxState.h @@ -19,6 +19,7 @@ // Copyright (C) 2009 Koji Otani <[email protected]> // Copyright (C) 2009, 2010 Albert Astals Cid <[email protected]> // Copyright (C) 2010 Christian Feuersänger <[email protected]> +// Copyright (C) 2011 Andrea Canciani <[email protected]> // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -49,8 +50,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,6 +749,51 @@ 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 //------------------------------------------------------------------------ @@ -782,7 +830,7 @@ private: // GfxAxialShading //------------------------------------------------------------------------ -class GfxAxialShading: public GfxShading { +class GfxAxialShading: public GfxUnivariateShading { public: GfxAxialShading(double x0A, double y0A, @@ -799,28 +847,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 +881,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 63a471c..fc838d3 100644 --- a/poppler/SplashOutputDev.cc +++ b/poppler/SplashOutputDev.cc @@ -28,6 +28,7 @@ // Copyright (C) 2010 PaweÅ Wiejacha <[email protected]> // Copyright (C) 2010 Christian Feuersänger <[email protected]> // Copyright (C) 2011 Andreas Hartmetz <[email protected]> +// Copyright (C) 2011 Andrea Canciani <[email protected]> // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -154,50 +155,193 @@ 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); + if (! getParameter (xc, yc, &t)) + return gFalse; + + shading->getColor(t, &gfxColor); + convertGfxColor(c, colorMode, shading->getColorSpace(), &gfxColor); + return gTrue; +} + +GBool SplashUnivariatePattern::testPosition(int x, int y) { + 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; + return (t0 < t1) ? (t > t0 && t < t1) : (t > t1 && t < t0); +} + + +//------------------------------------------------------------------------ +// 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; } @@ -3317,6 +3461,7 @@ GBool SplashOutputDev::getVectorAntialias() { } void SplashOutputDev::setVectorAntialias(GBool vaa) { + vectorAntialias = vaa; splash->setVectorAntialias(vaa); } #endif @@ -3356,16 +3501,48 @@ 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); + + GBool retVal = gFalse; // get the clip region bbox - state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); + if (pattern->getShading()->getHasBBox()) { + pattern->getShading()->getBBox(&xMin, &yMin, &xMax, &yMax); + } else { + state->getClipBBox(&xMin, &yMin, &xMax, &yMax); + + xMin = floor (xMin); + yMin = floor (yMin); + xMax = ceil (xMax); + yMax = ceil (yMax); + + { + Matrix ctm, ictm; + double x[4], y[4]; + int i; + + state->getCTM(&ctm); + ctm.invertTo(&ictm); + + ictm.transform(xMin, yMin, &x[0], &y[0]); + ictm.transform(xMax, yMin, &x[1], &y[1]); + ictm.transform(xMin, yMax, &x[2], &y[2]); + ictm.transform(xMax, yMax, &x[3], &y[3]); + + xMin = xMax = x[0]; + yMin = yMax = y[0]; + for (i = 1; i < 4; i++) { + xMin = std::min<double>(xMin, x[i]); + yMin = std::min<double>(yMin, y[i]); + xMax = std::max<double>(xMax, x[i]); + yMax = std::max<double>(yMax, y[i]); + } + } + } // fill the region state->moveTo(xMin, yMin); @@ -3375,12 +3552,28 @@ 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 pattern; + setVectorAntialias(vaa); delete path; return retVal; } + +GBool SplashOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) { + SplashAxialPattern *pattern = new SplashAxialPattern(colorMode, state, shading); + GBool retVal = univariateShadedFill(state, pattern, tMin, tMax); + + 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 47161c0..c8cb84f 100644 --- a/poppler/SplashOutputDev.h +++ b/poppler/SplashOutputDev.h @@ -14,10 +14,11 @@ // under GPL version 2 or later // // Copyright (C) 2005 Takashi Iwai <[email protected]> -// Copyright (C) 2009, 2010 Thomas Freitag <[email protected]> +// Copyright (C) 2009-2011 Thomas Freitag <[email protected]> // Copyright (C) 2009 Carlos Garcia Campos <[email protected]> // Copyright (C) 2010 Christian Feuersänger <[email protected]> // Copyright (C) 2011 Andreas Hartmetz <[email protected]> +// Copyright (C) 2011 Andrea Canciani <[email protected]> // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -53,28 +54,45 @@ 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 testPosition(int x, int y); + 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 @@ -89,13 +107,15 @@ public: virtual GBool getColor(int x, int y, SplashColorPtr c) { return gFalse; } + virtual GBool testPosition(int x, int y) { return gFalse; } + virtual GBool isStatic() { return gFalse; } virtual GBool isParameterized() { return shading->isParameterized(); } virtual int getNTriangles() { return shading->getNTriangles(); } virtual void getTriangle(int i, double *x0, double *y0, double *color0, double *x1, double *y1, double *color1, - double *x2, double *y2, double *color2) + double *x2, double *y2, double *color2) { return shading->getTriangle(i, x0, y0, color0, x1, y1, color1, x2, y2, color2); } virtual void getParameterizedColor(double t, SplashColorMode mode, SplashColorPtr c); @@ -106,6 +126,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 @@ -133,7 +170,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.) @@ -187,6 +224,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 @@ -292,6 +330,7 @@ public: void setFreeTypeHinting(GBool enable, GBool enableSlightHinting); 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..bf24892 100644 --- a/splash/Splash.cc +++ b/splash/Splash.cc @@ -13,7 +13,7 @@ // // Copyright (C) 2005-2010 Albert Astals Cid <[email protected]> // Copyright (C) 2005 Marco Pesenti Gritti <[email protected]> -// Copyright (C) 2010 Thomas Freitag <[email protected]> +// Copyright (C) 2010, 2011 Thomas Freitag <[email protected]> // Copyright (C) 2010 Christian Feuersänger <[email protected]> // // To see a description of the changes please see the Changelog file that @@ -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; } } @@ -2395,7 +2373,7 @@ SplashError Splash::drawImage(SplashImageSource src, void *srcData, // loop-invariant constants k1 = splashRound(xShear * ySign * y); - + // clipping test if (clipRes != splashClipAllInside && !rot && @@ -3097,7 +3075,7 @@ void Splash::compositeBackground(SplashColorPtr color) { Guchar color3; #endif int x, y, mask; - + if (unlikely(bitmap->alpha == NULL)) { error(-1, "bitmap->alpha is NULL in Splash::compositeBackground"); return; @@ -3282,7 +3260,7 @@ GBool Splash::gouraudTriangleShadedFill(SplashGouraudColor *shading) pipeInit(&pipe, 0, 0, NULL, cSrcVal, state->strokeAlpha, gFalse, gFalse); if (vectorAntialias) { - if (aaBuf == NULL) + if (aaBuf == NULL) return gFalse; // fall back to old behaviour drawAAPixelInit(); } @@ -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,12 +3961,82 @@ 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); + if (vectorAntialias) { + for (y = yMinI; y <= yMaxI; ++y) { + scanner->renderAALine(aaBuf, &x0, &x1, y); + if (clipRes != splashClipAllInside) { + state->clip->clipAALine(aaBuf, &x0, &x1, y); + } +#if splashAASize == 4 + if (!hasBBox && y > yMinI && y < yMaxI) { + // correct shape on left side if clip is + // vertical through the middle of shading: + Guchar *p0, *p1, *p2, *p3; + Guchar c1, c2, c3, c4; + p0 = aaBuf->getDataPtr() + (x0 >> 1); + p1 = p0 + aaBuf->getRowSize(); + p2 = p1 + aaBuf->getRowSize(); + p3 = p2 + aaBuf->getRowSize(); + if (x0 & 1) { + c1 = (*p0 & 0x0f); c2 =(*p1 & 0x0f); c3 = (*p2 & 0x0f) ; c4 = (*p3 & 0x0f); + } else { + c1 = (*p0 >> 4); c2 = (*p1 >> 4); c3 = (*p2 >> 4); c4 = (*p3 >> 4); + } + if ( (c1 & 0x03) == 0x03 && (c2 & 0x03) == 0x03 && (c3 & 0x03) == 0x03 && (c4 & 0x03) == 0x03 + && c1 == c2 && c2 == c3 && c3 == c4 && + pattern->testPosition(x0 - 1, y) ) + { + Guchar shapeCorrection = (x0 & 1) ? 0x0f : 0xf0; + *p0 |= shapeCorrection; + *p1 |= shapeCorrection; + *p2 |= shapeCorrection; + *p3 |= shapeCorrection; + } + // correct shape on right side if clip is + // through the middle of shading: + p0 = aaBuf->getDataPtr() + (x1 >> 1); + p1 = p0 + aaBuf->getRowSize(); + p2 = p1 + aaBuf->getRowSize(); + p3 = p2 + aaBuf->getRowSize(); + if (x1 & 1) { + c1 = (*p0 & 0x0f); c2 =(*p1 & 0x0f); c3 = (*p2 & 0x0f) ; c4 = (*p3 & 0x0f); + } else { + c1 = (*p0 >> 4); c2 = (*p1 >> 4); c3 = (*p2 >> 4); c4 = (*p3 >> 4); + } + + if ( (c1 & 0xc) == 0x0c && (c2 & 0x0c) == 0x0c && (c3 & 0x0c) == 0x0c && (c4 & 0x0c) == 0x0c + && c1 == c2 && c2 == c3 && c3 == c4 && + pattern->testPosition(x1 + 1, y) ) + { + Guchar shapeCorrection = (x1 & 1) ? 0x0f : 0xf0; + *p0 |= shapeCorrection; + *p1 |= shapeCorrection; + *p2 |= shapeCorrection; + *p3 |= shapeCorrection; + } + } +#endif + 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); + } + } } - drawAALine(&pipe, x0, x1, y); } } opClipRes = clipRes; diff --git a/splash/SplashPattern.h b/splash/SplashPattern.h index 09e9b1a..42c1660 100644 --- a/splash/SplashPattern.h +++ b/splash/SplashPattern.h @@ -11,7 +11,7 @@ // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // -// Copyright (C) 2010 Thomas Freitag <[email protected]> +// Copyright (C) 2010, 2011 Thomas Freitag <[email protected]> // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -45,6 +45,9 @@ public: // Return the color value for a specific pixel. virtual GBool getColor(int x, int y, SplashColorPtr c) = 0; + // Test if x,y-position is inside pattern. + virtual GBool testPosition(int x, int y) = 0; + // Returns true if this pattern object will return the same color // value for all pixels. virtual GBool isStatic() = 0; @@ -67,6 +70,8 @@ public: virtual GBool getColor(int x, int y, SplashColorPtr c); + virtual GBool testPosition(int x, int y) { return gFalse; } + virtual GBool isStatic() { return gTrue; } private:
_______________________________________________ poppler mailing list [email protected] http://lists.freedesktop.org/mailman/listinfo/poppler
