[poppler] poppler/Function.cc poppler/Function.h poppler/GfxState.cc poppler/GfxState.h poppler/SplashOutputDev.cc poppler/SplashOutputDev.h splash/Splash.cc splash/SplashPattern.h

Albert Astals Cid aacid at kemper.freedesktop.org
Tue Feb 15 16:07:55 PST 2011


 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 <aacid at kde.org>
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 <aacid at kde.org>
 // Copyright (C) 2006 Jeff Muizelaar <jeff at infidigm.net>
 // Copyright (C) 2010 Christian Feuersänger <cfeuersaenger at googlemail.com>
+// Copyright (C) 2011 Andrea Canciani <ranma42 at gmail.com>
 //
 // 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 <aacid at kde.org>
 // Copyright (C) 2010 Christian Feuersänger <cfeuersaenger at googlemail.com>
+// Copyright (C) 2011 Andrea Canciani <ranma42 at gmail.com>
 //
 // 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 <carlosgc at gnome.org>
 // Copyright (C) 2006-2010 Albert Astals Cid <aacid at kde.org>
 // Copyright (C) 2009 Koji Otani <sho at bbr.jp>
-// Copyright (C) 2009 Thomas Freitag <Thomas.Freitag at alfa.de>
+// Copyright (C) 2009, 2011 Thomas Freitag <Thomas.Freitag at alfa.de>
 // Copyright (C) 2009 Christian Persch <chpe at gnome.org>
 // Copyright (C) 2010 Paweł Wiejacha <pawel.wiejacha at gmail.com>
 // Copyright (C) 2010 Christian Feuersänger <cfeuersaenger at googlemail.com>
+// Copyright (C) 2011 Andrea Canciani <ranma42 at gmail.com>
 //
 // 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 <sho at bbr.jp>
 // Copyright (C) 2009, 2010 Albert Astals Cid <aacid at kde.org>
 // Copyright (C) 2010 Christian Feuersänger <cfeuersaenger at googlemail.com>
+// Copyright (C) 2011 Andrea Canciani <ranma42 at gmail.com>
 //
 // 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 <pawel.wiejacha at gmail.com>
 // Copyright (C) 2010 Christian Feuersänger <cfeuersaenger at googlemail.com>
 // Copyright (C) 2011 Andreas Hartmetz <ahartmetz at gmail.com>
+// Copyright (C) 2011 Andrea Canciani <ranma42 at gmail.com>
 //
 // 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 <tiwai at suse.de>
-// Copyright (C) 2009, 2010 Thomas Freitag <Thomas.Freitag at alfa.de>
+// Copyright (C) 2009-2011 Thomas Freitag <Thomas.Freitag at alfa.de>
 // Copyright (C) 2009 Carlos Garcia Campos <carlosgc at gnome.org>
 // Copyright (C) 2010 Christian Feuersänger <cfeuersaenger at googlemail.com>
 // Copyright (C) 2011 Andreas Hartmetz <ahartmetz at gmail.com>
+// Copyright (C) 2011 Andrea Canciani <ranma42 at gmail.com>
 //
 // 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 <aacid at kde.org>
 // Copyright (C) 2005 Marco Pesenti Gritti <mpg at redhat.com>
-// Copyright (C) 2010 Thomas Freitag <Thomas.Freitag at alfa.de>
+// Copyright (C) 2010, 2011 Thomas Freitag <Thomas.Freitag at alfa.de>
 // Copyright (C) 2010 Christian Feuersänger <cfeuersaenger at googlemail.com>
 //
 // 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 <Thomas.Freitag at alfa.de>
+// Copyright (C) 2010, 2011 Thomas Freitag <Thomas.Freitag at alfa.de>
 //
 // 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:


More information about the poppler mailing list