diff --git a/.gitignore b/.gitignore
index d000352..be47f98 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,3 +25,4 @@ poppler.pc
 stamp-h1
 gtk-doc.make
 *.o
+.*.sw?
diff --git a/poppler/Gfx.cc b/poppler/Gfx.cc
index 221caef..690b863 100644
--- a/poppler/Gfx.cc
+++ b/poppler/Gfx.cc
@@ -99,11 +99,19 @@
 #define radialColorDelta (dblToCol(1 / 256.0))
 
 // Max recursive depth for a Gouraud triangle shading fill.
+//
+// Triangles will be split at most gouraudMaxDepth times (each time into 4
+// smaller ones). That makes pow(4,gouraudMaxDepth) many triangles for
+// every triangle.
 #define gouraudMaxDepth 6
 
 // Max delta allowed in any color component for a Gouraud triangle
 // shading fill.
-#define gouraudColorDelta (dblToCol(1 / 256.0))
+#define gouraudColorDelta (dblToCol(3. / 256.0))
+
+// Gouraud triangle: if the three color parameters differ by at more than this percend of 
+// the total color parameter range, the triangle will be refined
+#define gouraudParameterizedColorDelta 5e-3
 
 // Max recursive depth for a patch mesh shading fill.
 #define patchMaxDepth 6
@@ -112,6 +120,8 @@
 // fill.
 #define patchColorDelta (dblToCol(1 / 256.0))
 
+#define GOURAUD_SAVE_ALLOC
+
 //------------------------------------------------------------------------
 // Operator table
 //------------------------------------------------------------------------
@@ -3087,22 +3097,63 @@ void Gfx::doRadialShFill(GfxRadialShading *shading) {
 
 void Gfx::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) {
   double x0, y0, x1, y1, x2, y2;
-  GfxColor color0, color1, color2;
   int i;
 
-  for (i = 0; i < shading->getNTriangles(); ++i) {
-    shading->getTriangle(i, &x0, &y0, &color0,
-			 &x1, &y1, &color1,
-			 &x2, &y2, &color2);
-    gouraudFillTriangle(x0, y0, &color0, x1, y1, &color1, x2, y2, &color2,
-			shading->getColorSpace()->getNComps(), 0);
-  }
+  // preallocate a path.
+  // Overwriting the coordinates saves about 10% at runtime for larger
+  // shadings.
+  state->moveTo(0., 0.);
+  state->lineTo(1., 0.);
+  state->lineTo(0., 1.);
+  state->closePath();
+
+  GfxState::ReusablePathIterator *reusablePath = state->getReusablePath();
+
+// Idea:
+// - state->getReferenceToReusablePath()
+// the return value has the setX/setY methods, the standard GfxState doesn't
+// - state should not delete the current path until
+// - state->freeReusablePath()
+// but that's overhead and I am not highly motivated.
+// 
+
+  if (shading->isParameterized()) {
+    // work with parameterized values:
+    double color0, color1, color2;
+    // a relative threshold:
+    const double refineColorThreshold = gouraudParameterizedColorDelta *
+                                        (shading->getParameterDomainMax() - shading->getParameterDomainMin());
+    for (i = 0; i < shading->getNTriangles(); ++i) {
+      shading->getTriangle(i, &x0, &y0, &color0,
+                              &x1, &y1, &color1,
+                              &x2, &y2, &color2);
+      gouraudFillTriangle(x0, y0, color0, x1, y1, color1, x2, y2, color2, refineColorThreshold, 0, shading, reusablePath);
+    }
+
+  } else {
+    // this always produces output -- even for parameterized ranges.
+	// But it ignores the parameterized color map (the function). 
+	//
+	// Note that using this code in for parameterized shadings might be
+	// correct in circumstances (namely if the function is linear in the actual
+	// triangle), but in general, it will simply be wrong.
+    GfxColor color0, color1, color2;
+    for (i = 0; i < shading->getNTriangles(); ++i) {
+      shading->getTriangle(i, &x0, &y0, &color0,
+                              &x1, &y1, &color1,
+                              &x2, &y2, &color2);
+      gouraudFillTriangle(x0, y0, &color0, x1, y1, &color1, x2, y2, &color2, shading->getColorSpace()->getNComps(), 0, reusablePath);
+    }
+  }
+
+  delete reusablePath;
 }
 
+
 void Gfx::gouraudFillTriangle(double x0, double y0, GfxColor *color0,
 			      double x1, double y1, GfxColor *color1,
 			      double x2, double y2, GfxColor *color2,
-			      int nComps, int depth) {
+			      int nComps, int depth, GfxState::ReusablePathIterator* path) {
   double x01, y01, x12, y12, x20, y20;
   GfxColor color01, color12, color20;
   int i;
@@ -3114,15 +3165,17 @@ void Gfx::gouraudFillTriangle(double x0, double y0, GfxColor *color0,
     }
   }
   if (i == nComps || depth == gouraudMaxDepth) {
+
     state->setFillColor(color0);
     out->updateFillColor(state);
-    state->moveTo(x0, y0);
-    state->lineTo(x1, y1);
-    state->lineTo(x2, y2);
-    state->closePath();
-    if (!contentIsHidden())
-      out->fill(state);
-    state->clearPath();
+
+	path->reset();
+	path->setCoord(x0,y0);	assert(!path->isEnd()); path->next();
+	path->setCoord(x1,y1);	assert(!path->isEnd()); path->next();
+	path->setCoord(x2,y2);	assert(!path->isEnd()); path->next();
+	path->setCoord(x0,y0);	assert(path->isEnd());
+
+    out->fill(state);
   } else {
     x01 = 0.5 * (x0 + x1);
     y01 = 0.5 * (y0 + y1);
@@ -3130,24 +3183,79 @@ void Gfx::gouraudFillTriangle(double x0, double y0, GfxColor *color0,
     y12 = 0.5 * (y1 + y2);
     x20 = 0.5 * (x2 + x0);
     y20 = 0.5 * (y2 + y0);
-    //~ if the shading has a Function, this should interpolate on the
-    //~ function parameter, not on the color components
     for (i = 0; i < nComps; ++i) {
-      color01.c[i] = (color0->c[i] + color1->c[i]) / 2;
-      color12.c[i] = (color1->c[i] + color2->c[i]) / 2;
-      color20.c[i] = (color2->c[i] + color0->c[i]) / 2;
+      color01.c[i] = (color0->c[i] + color1->c[i]) / 2.;
+      color12.c[i] = (color1->c[i] + color2->c[i]) / 2.;
+      color20.c[i] = (color2->c[i] + color0->c[i]) / 2.;
     }
     gouraudFillTriangle(x0, y0, color0, x01, y01, &color01,
-			x20, y20, &color20, nComps, depth + 1);
+			x20, y20, &color20, nComps, depth + 1, path);
     gouraudFillTriangle(x01, y01, &color01, x1, y1, color1,
-			x12, y12, &color12, nComps, depth + 1);
+			x12, y12, &color12, nComps, depth + 1, path);
     gouraudFillTriangle(x01, y01, &color01, x12, y12, &color12,
-			x20, y20, &color20, nComps, depth + 1);
+			x20, y20, &color20, nComps, depth + 1, path);
     gouraudFillTriangle(x20, y20, &color20, x12, y12, &color12,
-			x2, y2, color2, nComps, depth + 1);
+			x2, y2, color2, nComps, depth + 1, path);
+  }
+}
+void Gfx::gouraudFillTriangle(double x0, double y0, double color0,
+			      double x1, double y1, double color1,
+			      double x2, double y2, double color2,
+			      double refineColorThreshold, int depth, GfxGouraudTriangleShading* shading, GfxState::ReusablePathIterator* path ) {
+  const double meanColor = (color0 + color1 + color2) / 3;
+
+  const bool isFineEnough = 
+       fabs(color0 - meanColor) < refineColorThreshold &&
+       fabs(color1 - meanColor) < refineColorThreshold &&
+       fabs(color2 - meanColor) < refineColorThreshold;
+
+  if (isFineEnough || depth == gouraudMaxDepth) {
+    GfxColor color;
+
+    shading->getParameterizedColor(meanColor, &color);
+    state->setFillColor(&color);
+    out->updateFillColor(state);
+
+	path->reset();
+	path->setCoord(x0,y0);	assert(!path->isEnd()); path->next();
+	path->setCoord(x1,y1);	assert(!path->isEnd()); path->next();
+	path->setCoord(x2,y2);	assert(!path->isEnd()); path->next();
+	path->setCoord(x0,y0);	assert(path->isEnd());
+
+    out->fill(state);
+  } else {
+    double x01, y01, x12, y12, x20, y20;
+    double color01, color12, color20;
+    x01 = 0.5 * (x0 + x1);
+    y01 = 0.5 * (y0 + y1);
+    x12 = 0.5 * (x1 + x2);
+    y12 = 0.5 * (y1 + y2);
+    x20 = 0.5 * (x2 + x0);
+    y20 = 0.5 * (y2 + y0);
+    color01 = (color0 + color1) / 2.;
+    color12 = (color1 + color2) / 2.; 
+    color20 = (color2 + color0) / 2.;
+    ++depth;
+    gouraudFillTriangle(x0, y0, color0, 
+                        x01, y01, color01,
+                        x20, y20, color20, 
+                        refineColorThreshold, depth, shading, path);
+    gouraudFillTriangle(x01, y01, color01, 
+                        x1, y1, color1,
+                        x12, y12, color12, 
+                        refineColorThreshold, depth, shading, path);
+    gouraudFillTriangle(x01, y01, color01, 
+                        x12, y12, color12,
+                        x20, y20, color20, 
+                        refineColorThreshold, depth, shading, path);
+    gouraudFillTriangle(x20, y20, color20, 
+                        x12, y12, color12,
+                        x2, y2, color2, 
+                        refineColorThreshold, depth, shading, path);
   }
 }
 
+
 void Gfx::doPatchMeshShFill(GfxPatchMeshShading *shading) {
   int start, i;
 
diff --git a/poppler/Gfx.h b/poppler/Gfx.h
index f1a8964..604f97f 100644
--- a/poppler/Gfx.h
+++ b/poppler/Gfx.h
@@ -299,7 +299,11 @@ private:
   void gouraudFillTriangle(double x0, double y0, GfxColor *color0,
 			   double x1, double y1, GfxColor *color1,
 			   double x2, double y2, GfxColor *color2,
-			   int nComps, int depth);
+			   int nComps, int depth, GfxState::ReusablePathIterator* path);
+  void gouraudFillTriangle(double x0, double y0, double color0,
+			   double x1, double y1, double color1,
+			   double x2, double y2, double color2,
+			   double refineColorThreshold, int depth, GfxGouraudTriangleShading* shading, GfxState::ReusablePathIterator* path);
   void doPatchMeshShFill(GfxPatchMeshShading *shading);
   void fillPatch(GfxPatch *patch, int nComps, int depth);
   void doEndPath();
diff --git a/poppler/GfxState.cc b/poppler/GfxState.cc
index 12e14d6..185754f 100644
--- a/poppler/GfxState.cc
+++ b/poppler/GfxState.cc
@@ -3362,6 +3362,8 @@ void GfxGouraudTriangleShading::getTriangle(
   double out[gfxColorMaxComps];
   int v, j;
 
+  assert(!isParameterized()); 
+
   v = triangles[i][0];
   *x0 = vertices[v].x;
   *y0 = vertices[v].y;
@@ -3406,6 +3408,40 @@ void GfxGouraudTriangleShading::getTriangle(
   }
 }
 
+void GfxGouraudTriangleShading::getParameterizedColor(double t, GfxColor *color) {
+  double out[gfxColorMaxComps];
+
+  for (int j = 0; j < nFuncs; ++j) {
+    funcs[j]->transform(&t, &out[j]);
+  }
+  for (int j = 0; j < gfxColorMaxComps; ++j) {
+    color->c[j] = dblToCol(out[j]);
+  }
+}
+
+void GfxGouraudTriangleShading::getTriangle(
+				    int i,
+				    double *x0, double *y0, double *color0,
+				    double *x1, double *y1, double *color1,
+				    double *x2, double *y2, double *color2) {
+  int v;
+
+  assert(isParameterized()); 
+
+  v = triangles[i][0];
+  *x0 = vertices[v].x;
+  *y0 = vertices[v].y;
+  *color0 = colToDbl(vertices[v].color.c[0]);
+  v = triangles[i][1];
+  *x1 = vertices[v].x;
+  *y1 = vertices[v].y;
+  *color1 = colToDbl(vertices[v].color.c[0]);
+  v = triangles[i][2];
+  *x2 = vertices[v].x;
+  *y2 = vertices[v].y;
+  *color2 = colToDbl(vertices[v].color.c[0]);
+}
+
 //------------------------------------------------------------------------
 // GfxPatchMeshShading
 //------------------------------------------------------------------------
@@ -4519,6 +4555,51 @@ void GfxPath::offset(double dx, double dy) {
 //------------------------------------------------------------------------
 // GfxState
 //------------------------------------------------------------------------
+GfxState::ReusablePathIterator::ReusablePathIterator( GfxPath *path )
+	: 
+	path(path),
+	subPathOff(0),
+	coordOff(0),
+	numCoords(0),
+	curSubPath(NULL)
+{
+	if( path->getNumSubpaths() ) {
+		curSubPath = path->getSubpath(subPathOff);
+		numCoords = curSubPath->getNumPoints();
+	}
+}
+
+bool
+GfxState::ReusablePathIterator::isEnd() {
+	return coordOff < numCoords;
+}
+
+void
+GfxState::ReusablePathIterator::next() {
+	++coordOff;
+	if( coordOff == numCoords ) {
+		++subPathOff;
+		if( subPathOff < path->getNumSubpaths() ) {
+			coordOff=0;
+			curSubPath = path->getSubpath(subPathOff);
+			numCoords = curSubPath->getNumPoints();
+		}
+	}
+}
+
+void 
+GfxState::ReusablePathIterator::setCoord( double x, double y ) {
+	curSubPath->setX( coordOff, x );
+	curSubPath->setY( coordOff, y );
+}
+
+void 
+GfxState::ReusablePathIterator::reset() {
+	coordOff = 0;
+	subPathOff = 0;
+	curSubPath = path->getSubpath(0);
+	numCoords = curSubPath->getNumPoints();
+}
 
 GfxState::GfxState(double hDPIA, double vDPIA, PDFRectangle *pageBox,
 		   int rotateA, GBool upsideDown) {
diff --git a/poppler/GfxState.h b/poppler/GfxState.h
index 7dccfd5..d647f98 100644
--- a/poppler/GfxState.h
+++ b/poppler/GfxState.h
@@ -35,6 +35,8 @@
 #include "Object.h"
 #include "Function.h"
 
+#include <assert.h>
+
 class Array;
 class Gfx;
 class GfxFont;
@@ -876,10 +878,37 @@ public:
   virtual GfxShading *copy();
 
   int getNTriangles() { return nTriangles; }
+
+  bool isParameterized() const { return nFuncs > 0; }
+
+  /**
+   * @precondition isParameterized() == true
+   */
+  double getParameterDomainMin() const { assert(isParameterized()); return funcs[0]->getDomainMin(0); }
+
+  /**
+   * @precondition isParameterized() == true
+   */
+  double getParameterDomainMax() const { assert(isParameterized()); return funcs[0]->getDomainMax(0); }
+
+  /**
+   * @precondition isParameterized() == false
+   */
   void getTriangle(int i, double *x0, double *y0, GfxColor *color0,
 		   double *x1, double *y1, GfxColor *color1,
 		   double *x2, double *y2, GfxColor *color2);
 
+  /**
+   * Variant for functions.
+   *
+   * @precondition isParameterized() == true
+   */
+  void getTriangle(int i, double *x0, double *y0, double *color0,
+		   double *x1, double *y1, double *color1,
+		   double *x2, double *y2, double *color2);
+
+  void getParameterizedColor(double t, GfxColor *color);
+
 private:
 
   GfxGouraudVertex *vertices;
@@ -1002,6 +1031,9 @@ public:
   double getY(int i) { return y[i]; }
   GBool getCurve(int i) { return curve[i]; }
 
+  void setX(int i, double a) { x[i] = a; }
+  void setY(int i, double a) { y[i] = a; }
+
   // Get last point.
   double getLastX() { return x[n-1]; }
   double getLastY() { return y[n-1]; }
@@ -1096,6 +1128,60 @@ private:
 
 class GfxState {
 public:
+  /**
+   * When GfxState::getReusablePath() is invoked, the currently active
+   * path is taken per reference and its coordinates can be re-edited.
+   *
+   * A ReusablePathIterator is intented to reduce overhead when the same
+   * path type is used a lot of times, only with different coordinates. It
+   * allows just to update the coordinates (occuring in the same order as
+   * in the original path).
+   */
+  class ReusablePathIterator {
+	  GfxPath *path;
+	  int subPathOff;
+
+	  int coordOff;
+	  int numCoords;
+
+	  GfxSubpath *curSubPath;
+  public:
+	  /**
+	   * Creates the ReusablePathIterator. This should only be done from
+	   * GfxState::getReusablePath().
+	   *
+	   * @param path the path as it is used so far. Changing this path,
+	   * deleting it or starting a new path from scratch will most likely
+	   * invalidate the iterator (and may cause serious problems). Make
+	   * sure the path's memory structure is not changed during the
+	   * lifetime of the ReusablePathIterator.
+	   */
+	  ReusablePathIterator( GfxPath* path );
+
+	  /**
+	   * Returns true if and only if the current iterator position is
+	   * beyond the last valid point.
+	   *
+	   * A call to setCoord() will be undefined.
+	   */
+	  bool isEnd();
+
+	  /**
+	   * Advances the iterator.
+	   */
+	  void next();
+
+	  /**
+	   * Updates the coordinates associated to the current iterator
+	   * position.
+	   */
+	  void setCoord( double x, double y );
+
+	  /**
+	   * Resets the iterator.
+	   */
+	  void reset();
+  };
 
   // Construct a default GfxState, for a device with resolution <hDPI>
   // x <vDPI>, page box <pageBox>, page rotation <rotateA>, and
@@ -1274,6 +1360,7 @@ public:
   // Misc
   GBool parseBlendMode(Object *obj, GfxBlendMode *mode);
 
+  ReusablePathIterator *getReusablePath() { return new ReusablePathIterator(path); }
 private:
 
   double hDPI, vDPI;		// resolution
