Dear poppler developers,

hereby I propose a new patch for a high quality, fast gouraud shader for triangle shadings.

The patch provides
- scalable triangle shadings (in contrast to the old approach),
- correct shading, no approximation using flat triangles,
- greatly (!) improved rendering speed,
- support for opacity (in contrast to the previous approach).

This patch is relative to the master branch,
git show poppler-0.14.0-91-g31ac578 .

It is unrelated to my previous patch proposals concerning shadings; the other patch proposal (my mail several weeks ago) is still important and relevant. The patch in this mail is independent and does not include my previous patch proposals.

I tested the new patch with pdftoppm and with the xpdf-poppler fork of Rogerio Brito; it appears to work reliable. I tested
- matrix shadings and triangle patch shadings (Types 4 and 5),
- degenerated triangles,
- clipping,
- opacity,
- RGB color spaces.

The implementation now supports these shadings on a display driver level and is implemented directly in the splash device (using scanline sweeps).

I've been defensive: I have no test cases for other color spaces and I have no test data for non-parametric shadings. Consequently, I disabled the shader for these cases; it will fall back to the existing solutions (approximate shading by means of many flat triangles).

I hope you find my patch proposal useful. I believe it is worthy of being part of libpoppler.

Thanks,

Christian



diff --git a/poppler/Function.cc b/poppler/Function.cc
index b7c23fe..5a026e1 100644
--- a/poppler/Function.cc
+++ b/poppler/Function.cc
@@ -519,6 +519,7 @@ ExponentialFunction::ExponentialFunction(Object *funcObj, Dict *dict) {
   e = obj1.getNum();
   obj1.free();
 
+  isLinear = fabs(e-1.) < 1e-10;
   ok = gTrue;
   return;
 
@@ -549,7 +550,7 @@ void ExponentialFunction::transform(double *in, double *out) {
     x = in[0];
   }
   for (i = 0; i < n; ++i) {
-    out[i] = c0[i] + pow(x, e) * (c1[i] - c0[i]);
+    out[i] = c0[i] + ( isLinear ? x : pow(x, e) ) * (c1[i] - c0[i]);
     if (hasRange) {
       if (out[i] < range[i][0]) {
 	out[i] = range[i][0];
diff --git a/poppler/Function.h b/poppler/Function.h
index 2dcccb0..de9a88c 100644
--- a/poppler/Function.h
+++ b/poppler/Function.h
@@ -174,6 +174,7 @@ private:
   double c0[funcMaxOutputs];
   double c1[funcMaxOutputs];
   double e;
+  bool isLinear;
   GBool ok;
 };
 
diff --git a/poppler/Gfx.cc b/poppler/Gfx.cc
index 50870cc..962cc3f 100644
--- a/poppler/Gfx.cc
+++ b/poppler/Gfx.cc
@@ -3086,6 +3086,11 @@ void Gfx::doRadialShFill(GfxRadialShading *shading) {
 }
 
 void Gfx::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) {
+  if( out->useShadingType( shading->getType() ) ) {
+    if( out->gouraudTriangleShadedFill( state, shading ) )
+      return;
+  }
+
   double x0, y0, x1, y1, x2, y2;
   GfxColor color0, color1, color2;
   int i;
diff --git a/poppler/GfxState.cc b/poppler/GfxState.cc
index fe3ee77..6bea2db 100644
--- a/poppler/GfxState.cc
+++ b/poppler/GfxState.cc
@@ -3395,6 +3395,41 @@ void GfxGouraudTriangleShading::getTriangle(
   }
 }
 
+void GfxGouraudTriangleShading::getParameterizedColor(
+					double t, GfxColor* color ) {
+
+  double out[gfxColorMaxComps];
+  int j;
+
+  for (j = 0; j < nFuncs; ++j) {
+    funcs[j]->transform(&t, &out[j]);
+  }
+  for (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;
+
+  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
 //------------------------------------------------------------------------
diff --git a/poppler/GfxState.h b/poppler/GfxState.h
index 7dccfd5..7e9508d 100644
--- a/poppler/GfxState.h
+++ b/poppler/GfxState.h
@@ -876,10 +876,35 @@ public:
   virtual GfxShading *copy();
 
   int getNTriangles() { return nTriangles; }
+
+  bool isParameterized() const { return nFuncs > 0; }
+  /**
+   * @precondition isParameterized() == true
+   */
+  double getParameterDomainMin() const { return funcs[0]->getDomainMin(0); }
+  /**
+   * @precondition isParameterized() == true
+   */
+  double getParameterDomainMax() const { 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;
diff --git a/poppler/OutputDev.h b/poppler/OutputDev.h
index cdc74cf..b6bc5ca 100644
--- a/poppler/OutputDev.h
+++ b/poppler/OutputDev.h
@@ -47,6 +47,8 @@ class GfxColorSpace;
 class GfxImageColorMap;
 class GfxFunctionShading;
 class GfxAxialShading;
+class GfxGouraudTriangleShading;
+class GfxPatchMeshShading;
 class GfxRadialShading;
 class Stream;
 class Links;
@@ -87,6 +89,9 @@ public:
   // will be reduced to a series of other drawing operations.
   virtual GBool useShadedFills() { return gFalse; }
 
+  // Does this device support specific shading types?
+  // see gouraudTriangleShadedFill() and patchMeshShadedFill()
+  virtual GBool useShadingType( int type ) { if( type < 4 ) return useShadedFills(); return gFalse; }
   // Does this device use FillColorStop()?
   virtual GBool useFillColorStop() { return gFalse; }
 
@@ -208,6 +213,10 @@ public:
     { return gFalse; }
   virtual GBool radialShadedSupportExtend(GfxState * /*state*/, GfxRadialShading * /*shading*/)
     { return gFalse; }
+  virtual GBool gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading)
+    { return gFalse; }
+  virtual GBool patchMeshShadedFill(GfxState *state, GfxPatchMeshShading *shading)
+    { return gFalse; }
 
   //----- path clipping
   virtual void clip(GfxState * /*state*/) {}
diff --git a/poppler/SplashOutputDev.cc b/poppler/SplashOutputDev.cc
index 21d7be6..04c48ee 100644
--- a/poppler/SplashOutputDev.cc
+++ b/poppler/SplashOutputDev.cc
@@ -962,6 +962,13 @@ void SplashOutputDev::startDoc(XRef *xrefA) {
   nT3Fonts = 0;
 }
 
+GBool SplashOutputDev::useShadingType( int type )
+{
+  if( type < 4 )
+    return useShadedFills();
+  return type == 4 || type == 5;
+}
+
 void SplashOutputDev::startPage(int pageNum, GfxState *state) {
   int w, h;
   double *ctm;
@@ -3177,3 +3184,8 @@ void SplashOutputDev::setFreeTypeHinting(GBool enable)
 {
   enableFreeTypeHinting = enable;
 }
+
+GBool SplashOutputDev::gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading)
+{
+	return splash->gouraudTriangleShadedFill(state,shading);
+}
diff --git a/poppler/SplashOutputDev.h b/poppler/SplashOutputDev.h
index 37b771a..825ac42 100644
--- a/poppler/SplashOutputDev.h
+++ b/poppler/SplashOutputDev.h
@@ -81,6 +81,8 @@ public:
   // text in Type 3 fonts will be drawn with drawChar/drawString.
   virtual GBool interpretType3Chars() { return gTrue; }
 
+  virtual GBool useShadingType( int type );
+
   // This device now supports text in pattern colorspace!
   virtual GBool supportTextCSPattern(GfxState *state)
   	{ return state->getFillColorSpace()->getMode() == csPattern; }
@@ -218,6 +220,8 @@ public:
 
   SplashFont *getCurrentFont() { return font; }
 
+  GBool gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading);
+
 #if 1 //~tmp: turn off anti-aliasing temporarily
   virtual GBool getVectorAntialias();
   virtual void setVectorAntialias(GBool vaa);
diff --git a/splash/Splash.cc b/splash/Splash.cc
index 4f2419f..e8f88bc 100644
--- a/splash/Splash.cc
+++ b/splash/Splash.cc
@@ -55,6 +55,10 @@ static inline Guchar div255(int x) {
   return (Guchar)((x + (x >> 8) + 0x80) >> 8);
 }
 
+template<typename T>
+inline void Guswap( T&a, T&b ) { T tmp = a; a=b; b=tmp; }
+
+
 //------------------------------------------------------------------------
 // SplashPipe
 //------------------------------------------------------------------------
@@ -3182,6 +3186,332 @@ void Splash::compositeBackground(SplashColorPtr color) {
   memset(bitmap->alpha, 255, bitmap->width * bitmap->height);
 }
 
+//#include <iostream> // FIXME
+GBool Splash::gouraudTriangleShadedFill(GfxState *gfxState, GfxGouraudTriangleShading *shading)
+{
+	// TODO : 
+	// - sanity checking between Function and colorspace is not done
+	// - did not test anything but RGB colors
+  double xdbl[3] = {0., 0., 0.};
+  double ydbl[3] = {0., 0., 0.};
+  int    x[3] = {0, 0, 0};
+  int    y[3] = {0, 0, 0};
+  double xt=0.,xa=0.,yt=0.;
+  double ca=0.,ct=0.;
+
+  // triangle interpolation:
+  //
+  double scanLimitMapL[2] = {0., 0.};
+  double scanLimitMapR[2] = {0., 0.};
+  double scanColorMapL[2] = {0., 0.};
+  double scanColorMapR[2] = {0., 0.};
+  double scanColorMap[2] = {0., 0.};
+  int scanEdgeL[2] = { 0, 0 };
+  int scanEdgeR[2] = { 0, 0 };
+  bool hasFurtherSegment = false;
+
+  int X=0,Y=0;
+  int scanLineOff=0;
+  int bitmapOff=0;
+  int scanLimitR=0,scanLimitL=0,Yt=0;
+  int i=0,m=0;
+
+  int bitmapWidth = bitmap->getWidth();
+  SplashClip* clip = getClip();
+  SplashBitmap *blitTarget = bitmap;
+  SplashColorPtr bitmapData = bitmap->getDataPtr();
+  SplashColorPtr bitmapAlpha = bitmap->getAlphaPtr();
+  SplashColorPtr cur=NULL;
+  SplashCoord* userToCanvasMatrix = getMatrix();
+  SplashColorMode bitmapMode = bitmap->getMode();
+  bool hasAlpha=(bitmapAlpha != NULL);
+  int rowSize = bitmap->getRowSize();
+  int colorComps=0;
+  switch (bitmapMode) {
+  case splashModeMono1:
+    return gFalse; // FIXME. This can't work yet.
+    break;
+  case splashModeMono8:
+    return gFalse; // FIXME I disable it just because it is not tested.
+    colorComps=1;
+    break;
+  case splashModeRGB8:
+  case splashModeBGR8:
+    colorComps=3;
+    break;
+  case splashModeXBGR8:
+    return gFalse; // FIXME I disable it just because it is not tested.
+    colorComps=3;
+	break;
+#if SPLASH_CMYK
+  case splashModeCMYK8:
+    return gFalse; // FIXME I disable it just because it is not tested.
+    colorComps=4;
+    break;
+#endif
+  }
+
+  SplashPipe pipe;
+  SplashColor cSrcVal;
+  pipeInit(&pipe, 0, 0, NULL, cSrcVal, state->strokeAlpha,
+	   gFalse, gFalse);
+
+  // idea:
+  // 1. If pipe->noTransparency && !state->blendFunc
+  //  -> blit directly into the drawing surface!
+  //  -> disable alpha manually.
+  // 2. Otherwise:
+  // - blit also directly, but into an intermediate surface.
+  // Afterwards, blit the intermediate surface using the drawing pipeline.
+  // This is necessary because triangle elements can be on top of each
+  // other, so the complete shading needs to be drawn before opacity is
+  // applied.
+  // - the final step, is performed using a SplashPipe:
+  // - assign the actual color into cSrcVal: pipe uses cSrcVal by reference
+  // - invoke drawPixel(&pipe,X,Y,bNoClip);
+  bool bDirectBlit = pipe.noTransparency && !state->blendFunc;
+  if( !bDirectBlit ) {
+	  blitTarget = new SplashBitmap( 
+			  bitmap->getWidth(),
+			  bitmap->getHeight(),
+			  bitmap->getRowPad(),
+			  bitmap->getMode(),
+			  true,
+			  bitmap->getRowSize() >= 0 );
+	  bitmapData = blitTarget->getDataPtr();
+	  bitmapAlpha = blitTarget->getAlphaPtr();
+
+	  // initialisation seems to be necessary:
+	  int S = bitmap->getWidth() * bitmap->getHeight();
+	  for( i = 0; i<S; ++i )
+		  bitmapAlpha[i] = 0;
+	  hasAlpha = true;
+  }
+
+  if( shading->isParameterized() ) {
+    double color[3];
+    double colorinterp;
+    GfxColor mappedColor;
+
+    for (i = 0; i < shading->getNTriangles(); ++i) {
+          shading->getTriangle(i, 
+                           xdbl+0, ydbl+0, color+0,
+                           xdbl+1, ydbl+1, color+1,
+                           xdbl+2, ydbl+2, color+2);
+          for( m =0; m<3; ++m ) {
+            xt= xdbl[m] * userToCanvasMatrix[0] + ydbl[m] * userToCanvasMatrix[2] + userToCanvasMatrix[4];
+            yt= xdbl[m] * userToCanvasMatrix[1] + ydbl[m] * userToCanvasMatrix[3] + userToCanvasMatrix[5];
+            xdbl[m] = xt;
+            ydbl[m] = yt;
+            // we operate on scanlines which are integer offsets into the
+            // raster image. The double offsets are of no use here.
+            x[m] = splashRound(xt);
+            y[m] = splashRound(yt);
+          }
+          // sort according to y coordinate to simplify sweep through scanlines:
+		  // INSERTION SORT.
+		  if( y[0] > y[1] ) {
+			  Guswap( x[0], x[1] );  Guswap( y[0], y[1] ); Guswap( color[0], color[1] );
+		  }
+		  // first two are sorted .
+		  assert( y[0] <= y[1] );
+		  if( y[1] > y[2] ) {
+			  int tmpX = x[2];
+			  int tmpY = y[2];
+			  double tmpC = color[2];
+			  x[2] = x[1]; y[2] = y[1]; color[2] = color[1];
+
+			  if( y[0] > tmpY ) {
+				  x[1] = x[0]; y[1] = y[0]; color[1] = color[0];
+				  x[0] = tmpX; y[0] = tmpY; color[0] = tmpC;
+			  } else {
+				  x[1] = tmpX; y[1] = tmpY; color[1] = tmpC;
+			  }
+		  }
+		  // first three are sorted
+		  assert( y[0] <= y[1] );
+		  assert( y[1] <= y[2] );
+		  /////
+
+		  // this here is det( T ) == 0
+		  // where T is the matrix to map to barycentric coordinates.
+		  if( (x[0]-x[2]) * (y[1]-y[2])
+			- (x[1]-x[2]) * (y[0]-y[2]) == 0 )
+            continue; // degenerate triangle.
+
+          // this here initialises the scanline generation.
+          // We start with low Y coordinates and sweep up to the large Y
+          // coordinates.
+          //
+          // scanEdgeL[m] in {0,1,2} m=0,1
+          // scanEdgeR[m] in {0,1,2} m=0,1
+          //
+          // are the two edges between which scanlines are (currently)
+          // sweeped. The values {0,1,2} are indices into 'x' and 'y'.
+          // scanEdgeL[0] = 0 means: the left scan edge has (x[0],y[0]) as vertex.
+          //
+          scanEdgeL[0] = 0;
+          scanEdgeR[0] = 0;
+          if( y[0] == y[1] ) {
+            scanEdgeL[0] = 1;
+            scanEdgeL[1] = scanEdgeR[1] =2;
+
+          } else {
+            scanEdgeL[1] = 1; scanEdgeR[1] = 2;
+          }
+          assert( y[scanEdgeL[0]] < y[scanEdgeL[1]] );
+          assert( y[scanEdgeR[0]] < y[scanEdgeR[1]] );
+
+		  // Ok. Now prepare the linear maps which map the y coordinate of
+		  // the current scanline to the corresponding LEFT and RIGHT x
+		  // coordinate (which define the scanline).
+          scanLimitMapL[0] = double(x[scanEdgeL[1]]  - x[scanEdgeL[0]]) / (y[scanEdgeL[1]]  - y[scanEdgeL[0]] );
+          scanLimitMapL[1] = x[scanEdgeL[0]]  - y[scanEdgeL[0]]  * scanLimitMapL[0];
+          scanLimitMapR[0] = double(x[scanEdgeR[1]] - x[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]);
+          scanLimitMapR[1] = x[scanEdgeR[0]] - y[scanEdgeR[0]] * scanLimitMapR[0];
+
+          xa = y[1] * scanLimitMapL[0] + scanLimitMapL[1];
+          xt = y[1] * scanLimitMapR[0] + scanLimitMapR[1];
+          if( xa > xt ) {
+            // I have "left" is to the right of "right".
+            // Exchange sides!
+            Guswap(scanEdgeL[0], scanEdgeR[0]);
+            Guswap(scanEdgeL[1], scanEdgeR[1]);
+            Guswap(scanLimitMapL[0], scanLimitMapR[0]);
+            Guswap(scanLimitMapL[1], scanLimitMapR[1]);
+            // FIXME I'm sure there is a more efficient way to check this.
+          }
+
+          //std::cout << "triangle " << i << "\t (" <<xdbl[0]<<", "<<ydbl[0] <<");   (" << xdbl[1] <<", "<<ydbl[1] <<");   (" << xdbl[2]<<", "<<ydbl[2]<<")\n" << "         \t (" <<x[0]<<", "<<y[0] <<" [" << color[0] <<"]);   (" << x[1] <<", "<<y[1] <<" [" << color[1] <<"]);   (" << x[2]<<", "<<y[2]<<" [" << color[2] <<"])" << std::endl;
+          //std::cout << "scanEdgeL = " << scanEdgeL[0] << " " << scanEdgeL[1] << std::endl;
+          //std::cout << "scanEdgeR = " << scanEdgeR[0] << " " << scanEdgeR[1] << std::endl;
+
+		  // Same game: we can linearly interpolate the color based on the
+		  // current y coordinate (that's correct for triangle
+		  // interpolation due to linearity. We could also have done it in
+		  // barycentric coordinates, but that's slightly more involved)
+          scanColorMapL[0] = (color[scanEdgeL[1]]  - color[scanEdgeL[0]]) / (y[scanEdgeL[1]]  - y[scanEdgeL[0]] );
+          scanColorMapL[1] = color[scanEdgeL[0]]  - y[scanEdgeL[0]]  * scanColorMapL[0];
+          scanColorMapR[0] = (color[scanEdgeR[1]] - color[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]);
+          scanColorMapR[1] = color[scanEdgeR[0]] - y[scanEdgeR[0]] * scanColorMapR[0];
+
+
+          Y = y[0];
+          Yt= y[2];
+          hasFurtherSegment = (y[1] < y[2]);
+          scanLineOff = Y * rowSize;
+
+          for( Y = y[0]; Y <= y[2]; ++Y, scanLineOff+=rowSize ) {
+            if( hasFurtherSegment && Y == y[1] ) {
+			  // SWEEP EVENT: we encountered the next segment.
+			  //
+              // switch to next segment, either at left end or at right
+              // end:
+              if( scanEdgeL[1] == 1 ) {
+                scanEdgeL[0] = 1;
+                scanEdgeL[1] = 2;
+                scanLimitMapL[0] = double(x[scanEdgeL[1]]  - x[scanEdgeL[0]]) / (y[scanEdgeL[1]]  - y[scanEdgeL[0]] );
+                scanLimitMapL[1] = x[scanEdgeL[0]]  - y[scanEdgeL[0]]  * scanLimitMapL[0];
+                
+                scanColorMapL[0] = (color[scanEdgeL[1]]  - color[scanEdgeL[0]]) / (y[scanEdgeL[1]]  - y[scanEdgeL[0]] );
+                scanColorMapL[1] = color[scanEdgeL[0]]  - y[scanEdgeL[0]]  * scanColorMapL[0];
+                //std::cout << "scanEdgeL = " << scanEdgeL[0] << " " << scanEdgeL[1] << std::endl;
+              } else if( scanEdgeR[1] == 1 ) {
+                scanEdgeR[0] = 1;
+                scanEdgeR[1] = 2;
+                scanLimitMapR[0] = double(x[scanEdgeR[1]] - x[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]);
+                scanLimitMapR[1] = x[scanEdgeR[0]] - y[scanEdgeR[0]] * scanLimitMapR[0];
+
+                scanColorMapR[0] = (color[scanEdgeR[1]] - color[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]);
+                scanColorMapR[1] = color[scanEdgeR[0]] - y[scanEdgeR[0]] * scanColorMapR[0];
+                //std::cout << "scanEdgeR = " << scanEdgeR[0] << " " << scanEdgeR[1] << std::endl;
+              }
+              assert( y[scanEdgeL[0]]  <  y[scanEdgeL[1]] );
+              assert( y[scanEdgeR[0]] <  y[scanEdgeR[1]] );
+              hasFurtherSegment = false;
+            }
+
+            yt = Y;
+
+            xa = yt * scanLimitMapL[0] + scanLimitMapL[1];
+            xt = yt * scanLimitMapR[0] + scanLimitMapR[1];
+
+            ca = yt * scanColorMapL[0] + scanColorMapL[1];
+            ct = yt * scanColorMapR[0] + scanColorMapR[1];
+
+            scanLimitL=splashRound(xa);
+            scanLimitR=splashRound(xt);
+
+			// Ok. Now: init the color interpolation depending on the X
+			// coordinate inside of the current scanline:
+            scanColorMap[0] = (scanLimitR==scanLimitL) ? 0. : ( (ct - ca) / (scanLimitR - scanLimitL) );
+            scanColorMap[1] = ca - scanLimitL * scanColorMap[0];
+
+            //std::cout << "scanline y= "<< Y << " for x = " << scanLimitL <<  " (color " <<ca <<") to " << scanLimitR << "(color " << ct << ") [ " << xa << " = " << scanLimitMapL[0] << "*y + " << scanLimitMapL[1] <<" to " << xt << " = " << scanLimitMapR[0] << "*y + " << scanLimitMapR[1] << "] hasFurtherSegment = " << hasFurtherSegment << std::endl;
+
+            // handled by clipping:
+            // assert( scanLimitL >= 0 && scanLimitR < bitmap->getWidth() );
+            assert(scanLimitL <= scanLimitR || abs( scanLimitL-scanLimitR)<=2); // allow rounding inaccuracies
+            assert( scanLineOff == Y*rowSize );
+
+			colorinterp = scanColorMap[0] * scanLimitL + scanColorMap[1];
+
+            bitmapOff = scanLineOff + scanLimitL * colorComps;
+            for( X = scanLimitL; X<=scanLimitR; ++X, colorinterp+=scanColorMap[0], bitmapOff += colorComps ) {
+              // FIXME : standard rectangular clipping can be done for a
+              // complete scanline which is faster
+              // --> see SplashClip and its methods
+              if( !clip->test(X,Y) )
+                continue;
+
+			  assert( fabs( colorinterp - (scanColorMap[0] * X + scanColorMap[1]) ) < 1e-10 );
+
+              shading->getParameterizedColor(colorinterp, &mappedColor);
+
+              // FIXME : I am pretty sure this works only for RGB color
+              // spaces. 
+              assert( bitmapOff == Y*rowSize + colorComps*X && scanLineOff == Y*rowSize);
+  			  cur = &bitmapData[bitmapOff];
+			  for( m = 0; m<colorComps; ++m )
+				  cur[m] = colToByte(mappedColor.c[m]);
+			  // make the shading visible.
+			  // Note that opacity is handled by the bDirectBlit stuff, see
+			  // above for comments and below for implementation.
+			  if(hasAlpha)
+				  bitmapAlpha[ Y*bitmapWidth + X ] = 255;
+            }
+
+          }
+    }
+
+  } else {
+    return false;
+  }
+  if( !bDirectBlit ) {
+	  // ok. Finalize the stuff by blitting the shading into the final
+	  // geometry, this time respecting the rendering pipe.
+	  int W = blitTarget->getWidth();
+	  int H = blitTarget->getHeight();
+	  cur = cSrcVal;
+
+	  for( X = 0; X<W; ++X ) {
+		  for( Y = 0; Y<H; ++Y ) {
+			  if( !bitmapAlpha[ Y*bitmapWidth + X ] )
+				  continue; // draw only parts of the shading!
+              bitmapOff = Y*rowSize + colorComps*X;
+
+			  for( m = 0; m<colorComps; ++m )
+				  cur[m] = bitmapData[bitmapOff+m];
+			  drawPixel(&pipe, X,Y, true ); // no clipping - has already been done.
+		  }
+	  }
+
+	  delete blitTarget; blitTarget=NULL;
+  }
+
+  return true;
+}
+
 SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc,
 				    int xDest, int yDest, int w, int h) {
   SplashColor pixel;
diff --git a/splash/Splash.h b/splash/Splash.h
index c9d57cc..bc3589b 100644
--- a/splash/Splash.h
+++ b/splash/Splash.h
@@ -28,6 +28,7 @@
 
 #include "SplashTypes.h"
 #include "SplashClip.h"
+#include "GfxState.h"
 
 class Splash;
 class SplashBitmap;
@@ -209,6 +210,10 @@ public:
 			int xDest, int yDest, int w, int h,
 			GBool noClip, GBool nonIsolated);
 
+  // Draw a triangle shading.
+  // Return true on success and false if the operation is not supported.
+  GBool gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading);
+
   // Composite this Splash object onto a background color.  The
   // background alpha is assumed to be 1.
   void compositeBackground(SplashColorPtr color);
diff --git a/splash/SplashBitmap.cc b/splash/SplashBitmap.cc
index f983439..4094db9 100644
--- a/splash/SplashBitmap.cc
+++ b/splash/SplashBitmap.cc
@@ -50,6 +50,7 @@ SplashBitmap::SplashBitmap(int widthA, int heightA, int rowPad,
   width = widthA;
   height = heightA;
   mode = modeA;
+  this->rowPad = rowPad;// just remember it.
   switch (mode) {
   case splashModeMono1:
     if (width > 0) {
diff --git a/splash/SplashBitmap.h b/splash/SplashBitmap.h
index e741a91..e42a49d 100644
--- a/splash/SplashBitmap.h
+++ b/splash/SplashBitmap.h
@@ -56,6 +56,7 @@ public:
   int getHeight() { return height; }
   int getRowSize() { return rowSize; }
   int getAlphaRowSize() { return width; }
+  int getRowPad() { return rowPad; }
   SplashColorMode getMode() { return mode; }
   SplashColorPtr getDataPtr() { return data; }
   Guchar *getAlphaPtr() { return alpha; }
@@ -73,6 +74,7 @@ public:
 private:
 
   int width, height;		// size of bitmap
+  int rowPad;
   int rowSize;			// size of one row of data, in bytes
 				//   - negative for bottom-up bitmaps
   SplashColorMode mode;		// color mode
_______________________________________________
poppler mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/poppler

Reply via email to