Am 27.01.2011 00:44, schrieb Albert Astals Cid:
A Dimecres, 26 de gener de 2011, Albert Astals Cid va escriure:
The altona file shows a vertical line that wasn't present before.
Speaking with Andrea on IRC he blames it on a clip issue that seems to be
present on axial shadings too.

He says he'll try to find and fix the issue in the comming days.

My suggestion is wait to see if he (or maybe you Thomas?) can find that
clipping issue and then commit all the patches so we end up with no regression
and only improvements.
I fear that this is not such a trivial issue. I think we have here at least three different problems, I try to explain what I mean respectively already encountered ( and what were the reasons for my compromises): a) The way how antialiasing is implemented it splash works well if there is a high contrast, i.e. drawing a dark object on light background. But it gets worser if background colors and object colors are nearly the same in brightness. That probably produces the glitches in Andrea's test PDFs and in the altona PDF. b) If two PDF objects are close together (without any distance) but not on pixel boundaries, we can see sometimes two different effects: Either we have glitches like in a) or we get white (or light) lines as everyone probably encountered in several PDFs. Also the Acrobat Reader has sometimes this problem, but solves it in a better way (I don't know, how). c) If no antialiasing is used, but the objects are used in softmasks or in transparency groups, we get also problems if the same resulting pixel is drawn more than once. This was the effect in wine glass of ducks & roses: because of the alpha channel implementation the pixel becomes darker and darker the more often it is painted. That was the reason I started implementation of radial shading in splash. So this probably leads to not changing only a few statements but to completely rewrite antialiasing in splash, and on the other hand any changes will not only effect radial shading but all objects, so it will be hard to test it. So in my company we use in such case often the dictum: It's not a bug, it's a feature.
But let us see what Andrea will discover...

Of course if we see that poppler 0.17 release date nears and the clip issue is
not found-fixed yet we'll have to rethink since this patch adds better radial
shadings.
Regarding that time schedule I I suggest once again the following:
a) we move the switch on of antialiasing from univariateShadedFill back to axialShadedFill. b) that means that axial shading behaves like it has done since I implemented it last year and also with my patch here, but has the optimize of speed from Andrea c) that means that radial shading behaves like Albert already regtest it with my patch, so antialiasing is not used in radial shading, but has the optimize of speed from Andrea. Of course it then still have the glitches in Andrea's PDF, but that's not a problem of radial shading. We still get the improvement of that implementation. d) the regtest should be quite easy and can be done automatically, comparing the new results with results from Albert's last regtesting of my patch. (This assumes that the cache that Andrea implements is an exact cache, otherwise we could have some very small differences, which I suppose would be acceptable but would break the automatization of the regtest) e) if Andrea (or someone else) is able to solve the clipping problem, we can just move back the switch on of antialiasing to univariateShadedFill

On the assumption that everyone (or at least Albert :-) ) agrees, I attach a complete patch which should fulfil that (hopefully not missing something) . I'm not able (or at least don't know) how to produce the small patch segments how Andrea did it without having an own git repository, otherwise I would have attached only the changes to Andrea's proposal.

One last point: Andrea removed my code changes from Splash::shadedFill which allows the use of it with antialiasing switched off. Of course it is not needed if antialiasing would be always switched on, so in my suggestion we definitely need it again. But on the other hand it would also be needed if someone uses SplashOutputDev without any antialiasing, i.e. switch off antialiasing in GlobalParams. If we remove it, radial shading (and also axial shading, but in this case it doesn't matter) would fall back to the Gfx routines and that would produce not only uglier but wrong results. I use this sometimes to speed up rendering, and I suggest we should use this code changes in either case, Splash does it also in all other routines.

Another (German?) dictum says: The whole life is a compromise :-)

Thomas
Anyone disagrees?

Albert

Albert
_______________________________________________
poppler mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/poppler

.


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<int>
     }
   }
 
+  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<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 +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<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..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 <algorithm>
 #include <stddef.h>
 #include <math.h>
 #include <string.h>
@@ -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<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);
+  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 {
+    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<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 +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<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 < 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<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..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;
 
_______________________________________________
poppler mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/poppler

Reply via email to