[poppler] poppler/SplashOutputDev.cc poppler/SplashOutputDev.h splash/Splash.cc splash/SplashPattern.h splash/SplashTypes.h

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Thu Feb 27 13:17:20 UTC 2020


 poppler/SplashOutputDev.cc |   51 ++++----
 poppler/SplashOutputDev.h  |   11 +
 splash/Splash.cc           |  271 ++++++++++++++++++++++++++++++++-------------
 splash/SplashPattern.h     |    7 -
 splash/SplashTypes.h       |    7 +
 5 files changed, 246 insertions(+), 101 deletions(-)

New commits:
commit 68af136fb2934a65f912d84a619c39c75d6d90b9
Author: Albert Astals Cid <aacid at kde.org>
Date:   Sat Feb 22 10:05:25 2020 +0100

    Implement Splash::gouraudTriangleShadedFill for non parametrized shadings
    
    Fixes #881
    
    Unfortunately only implemented for shadings where the 3 vertices of the
    triangle have the same color for now since i got lost trying to
    implement the coloring (and also have no pdf to check against)
    
    The reason this fixes #881 is because if Splash::gouraudTriangleShadedFill
    returns false because it doesn't natively support this shading, the
    default rendering algorithm of Gfx.cc kicks in, and that rendering what
    does is render different triangles without them knowing they belong to
    the same shading, meaning that if you have some opacity the edges of the
    triangles will overlap and and up having different color than the one
    you really wanted

diff --git a/poppler/SplashOutputDev.cc b/poppler/SplashOutputDev.cc
index dbbd29a9..047b2d2d 100644
--- a/poppler/SplashOutputDev.cc
+++ b/poppler/SplashOutputDev.cc
@@ -82,9 +82,9 @@
 static const double s_minLineWidth = 0.0;
 
 static inline void convertGfxColor(SplashColorPtr dest,
-                                   SplashColorMode colorMode,
-                                   GfxColorSpace *colorSpace,
-                                   GfxColor *src) {
+                                   const SplashColorMode colorMode,
+                                   const GfxColorSpace *colorSpace,
+                                   const GfxColor *src) {
   SplashColor color;
   GfxGray gray;
   GfxRGB rgb;
@@ -134,9 +134,9 @@ static inline void convertGfxColor(SplashColorPtr dest,
 // to ensure that everything is initialized.
 
 static inline void convertGfxShortColor(SplashColorPtr dest,
-                                   SplashColorMode colorMode,
-                                   GfxColorSpace *colorSpace,
-                                   GfxColor *src) {
+                                   const SplashColorMode colorMode,
+                                   const GfxColorSpace *colorSpace,
+                                   const GfxColor *src) {
   switch (colorMode) {
     case splashModeMono1:
     case splashModeMono8:
@@ -194,21 +194,29 @@ SplashGouraudPattern::SplashGouraudPattern(bool bDirectColorTranslationA,
 SplashGouraudPattern::~SplashGouraudPattern() {
 }
 
+void SplashGouraudPattern::getNonParametrizedTriangle(int i, SplashColorMode mode, double *x0, double *y0, SplashColorPtr color0,
+                                        double *x1, double *y1, SplashColorPtr color1,
+                                        double *x2, double *y2, SplashColorPtr color2) {
+  GfxColor c0, c1, c2;
+  shading->getTriangle(i, x0, y0, &c0, x1, y1, &c1, x2, y2, &c2);
+
+  const GfxColorSpace* srcColorSpace = shading->getColorSpace();
+  convertGfxColor(color0, mode, srcColorSpace, &c0);
+  convertGfxColor(color1, mode, srcColorSpace, &c1);
+  convertGfxColor(color2, mode, srcColorSpace, &c2);
+}
+
+
 void SplashGouraudPattern::getParameterizedColor(double colorinterp, SplashColorMode mode, SplashColorPtr dest) {
   GfxColor src;
-  GfxColorSpace* srcColorSpace = shading->getColorSpace();
-  int colorComps = 3;
-  if (mode == splashModeCMYK8)
-    colorComps=4;
-  else if (mode == splashModeDeviceN8)
-    colorComps=4 + SPOT_NCOMPS;
-
   shading->getParameterizedColor(colorinterp, &src);
 
   if (bDirectColorTranslation) {
+    const int colorComps = splashColorModeNComps[mode];
     for (int m = 0; m < colorComps; ++m)
       dest[m] = colToByte(src.c[m]);
   } else {
+    GfxColorSpace* srcColorSpace = shading->getColorSpace();
     convertGfxShortColor(dest, mode, srcColorSpace, &src);
   }
 }
@@ -4540,17 +4548,12 @@ bool SplashOutputDev::gouraudTriangleShadedFill(GfxState *state, GfxGouraudTrian
     break;
   }
   // restore vector antialias because we support it here
-  if (shading->isParameterized()) {
-    SplashGouraudColor *splashShading = new SplashGouraudPattern(bDirectColorTranslation, state, shading);
-    bool vaa = getVectorAntialias();
-    bool retVal = false;
-    setVectorAntialias(true);
-    retVal = splash->gouraudTriangleShadedFill(splashShading);
-    setVectorAntialias(vaa);
-    delete splashShading;
-    return retVal;
-  }
-  return false;
+  SplashGouraudPattern splashShading(bDirectColorTranslation, state, shading);
+  const bool vaa = getVectorAntialias();
+  setVectorAntialias(true);
+  const bool retVal = splash->gouraudTriangleShadedFill(&splashShading);
+  setVectorAntialias(vaa);
+  return retVal;
 }
 
 bool SplashOutputDev::univariateShadedFill(GfxState *state, SplashUnivariatePattern *pattern, double tMin, double tMax) {
diff --git a/poppler/SplashOutputDev.h b/poppler/SplashOutputDev.h
index 7a670d1b..a42e724c 100644
--- a/poppler/SplashOutputDev.h
+++ b/poppler/SplashOutputDev.h
@@ -147,11 +147,16 @@ public:
 
   bool isParameterized() override { return shading->isParameterized(); }
   int getNTriangles() override { return shading->getNTriangles(); }
-   void getTriangle(int i, double *x0, double *y0, double *color0,
-                            double *x1, double *y1, double *color1,
-                            double *x2, double *y2, double *color2) override
+  void getParametrizedTriangle(int i, double *x0, double *y0, double *color0,
+                          double *x1, double *y1, double *color1,
+                          double *x2, double *y2, double *color2) override
   { shading->getTriangle(i, x0, y0, color0, x1, y1, color1, x2, y2, color2); }
 
+  void getNonParametrizedTriangle(int i, SplashColorMode mode,
+                    double *x0, double *y0, SplashColorPtr color0,
+                    double *x1, double *y1, SplashColorPtr color1,
+                    double *x2, double *y2, SplashColorPtr color2) override;
+
   void getParameterizedColor(double colorinterp, SplashColorMode mode, SplashColorPtr dest) override;
 
 private:
diff --git a/splash/Splash.cc b/splash/Splash.cc
index 3d4e6177..70584ec4 100644
--- a/splash/Splash.cc
+++ b/splash/Splash.cc
@@ -2385,7 +2385,7 @@ SplashError Splash::fillWithPattern(SplashPath *path, bool eo,
   SplashPipe pipe = {};
   int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
   SplashClipResult clipRes, clipRes2;
-  bool adjustLine = false; 
+  bool adjustLine = false;
   int linePosI = 0;
 
   if (path->length == 0) {
@@ -5381,62 +5381,23 @@ bool Splash::gouraudTriangleShadedFill(SplashGouraudColor *shading)
   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 scanLineOff = 0;
-  int bitmapOff = 0;
-  int scanLimitR = 0, scanLimitL = 0;
-
-  int bitmapWidth = bitmap->getWidth();
+
+  const int bitmapWidth = bitmap->getWidth();
   SplashClip* clip = getClip();
   SplashBitmap *blitTarget = bitmap;
   SplashColorPtr bitmapData = bitmap->getDataPtr();
-  int bitmapOffLimit = bitmap->getHeight() * bitmap->getRowSize();
+  const int bitmapOffLimit = bitmap->getHeight() * bitmap->getRowSize();
   SplashColorPtr bitmapAlpha = bitmap->getAlphaPtr();
-  SplashColorPtr cur = nullptr;
   SplashCoord* userToCanvasMatrix = getMatrix();
-  SplashColorMode bitmapMode = bitmap->getMode();
+  const SplashColorMode bitmapMode = bitmap->getMode();
   bool hasAlpha = (bitmapAlpha != nullptr);
-  int rowSize = bitmap->getRowSize();
-  int colorComps = 0;
-  switch (bitmapMode) {
-    case splashModeMono1:
-    break;
-    case splashModeMono8:
-      colorComps=1;
-    break;
-    case splashModeRGB8:
-      colorComps=3;
-    break;
-    case splashModeBGR8:
-      colorComps=3;
-    break;
-    case splashModeXBGR8:
-      colorComps=4;
-    break;
-    case splashModeCMYK8:
-      colorComps=4;
-    break;
-    case splashModeDeviceN8:
-      colorComps=SPOT_NCOMPS+4;
-    break;
-  }
+  const int rowSize = bitmap->getRowSize();
+  const int colorComps = splashColorModeNComps[bitmapMode];
 
   SplashPipe pipe;
   SplashColor cSrcVal;
 
-  pipeInit(&pipe, 0, 0, nullptr, cSrcVal, (unsigned char)splashRound(state->strokeAlpha * 255), false, false);
+  pipeInit(&pipe, 0, 0, nullptr, cSrcVal, (unsigned char)splashRound(state->fillAlpha * 255), false, false);
 
   if (vectorAntialias) {
     if (aaBuf == nullptr)
@@ -5457,7 +5418,7 @@ bool Splash::gouraudTriangleShadedFill(SplashGouraudColor *shading)
   // - 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 = vectorAntialias ? false : pipe.noTransparency && !state->blendFunc;
+  const bool bDirectBlit = vectorAntialias ? false : pipe.noTransparency && !state->blendFunc && !shading->isParameterized();
   if (!bDirectBlit) {
     blitTarget = new SplashBitmap(bitmap->getWidth(),
                                   bitmap->getHeight(),
@@ -5469,7 +5430,7 @@ bool Splash::gouraudTriangleShadedFill(SplashGouraudColor *shading)
     bitmapAlpha = blitTarget->getAlphaPtr();
 
     // initialisation seems to be necessary:
-    int S = bitmap->getWidth() * bitmap->getHeight();
+    const int S = bitmap->getWidth() * bitmap->getHeight();
     for (int i = 0; i < S; ++i)
       bitmapAlpha[i] = 0;
     hasAlpha = true;
@@ -5477,10 +5438,15 @@ bool Splash::gouraudTriangleShadedFill(SplashGouraudColor *shading)
 
   if (shading->isParameterized()) {
     double color[3];
-    double colorinterp;
+    double scanLimitMapL[2] = {0., 0.};
+    double scanLimitMapR[2] = {0., 0.};
+    double scanColorMapL[2] = {0., 0.};
+    double scanColorMapR[2] = {0., 0.};
+    int scanEdgeL[2] = { 0, 0 };
+    int scanEdgeR[2] = { 0, 0 };
 
     for (int i = 0; i < shading->getNTriangles(); ++i) {
-      shading->getTriangle(i,
+      shading->getParametrizedTriangle(i,
                            xdbl + 0, ydbl + 0, color + 0,
                            xdbl + 1, ydbl + 1, color + 1,
                            xdbl + 2, ydbl + 2, color + 2);
@@ -5504,9 +5470,9 @@ bool Splash::gouraudTriangleShadedFill(SplashGouraudColor *shading)
       // 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];
+        const int tmpX = x[2];
+        const int tmpY = y[2];
+        const double tmpC = color[2];
         x[2] = x[1]; y[2] = y[1]; color[2] = color[1];
 
         if (y[0] > tmpY) {
@@ -5578,8 +5544,8 @@ bool Splash::gouraudTriangleShadedFill(SplashGouraudColor *shading)
       scanColorMapR[0] = (color[scanEdgeR[1]] - color[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]);
       scanColorMapR[1] = color[scanEdgeR[0]] - y[scanEdgeR[0]] * scanColorMapR[0];
 
-      hasFurtherSegment = (y[1] < y[2]);
-      scanLineOff = y[0] * rowSize;
+      bool hasFurtherSegment = (y[1] < y[2]);
+      int scanLineOff = y[0] * rowSize;
 
       for (int Y = y[0]; Y <= y[2]; ++Y, scanLineOff += rowSize) {
         if (hasFurtherSegment && Y == y[1]) {
@@ -5614,34 +5580,34 @@ bool Splash::gouraudTriangleShadedFill(SplashGouraudColor *shading)
         xa = yt * scanLimitMapL[0] + scanLimitMapL[1];
         xt = yt * scanLimitMapR[0] + scanLimitMapR[1];
 
-        ca = yt * scanColorMapL[0] + scanColorMapL[1];
-        ct = yt * scanColorMapR[0] + scanColorMapR[1];
+        const double ca = yt * scanColorMapL[0] + scanColorMapL[1];
+        const double ct = yt * scanColorMapR[0] + scanColorMapR[1];
 
-        scanLimitL = splashRound(xa);
-        scanLimitR = splashRound(xt);
+        const int scanLimitL = splashRound(xa);
+        const int 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];
+        const double scanColorMap0 = (scanLimitR == scanLimitL) ? 0. : ((ct - ca) / (scanLimitR - scanLimitL));
+        const double scanColorMap1 = ca - scanLimitL * scanColorMap0;
 
         // 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];
+        double colorinterp = scanColorMap0 * scanLimitL + scanColorMap1;
 
-        bitmapOff = scanLineOff + scanLimitL * colorComps;
+        int bitmapOff = scanLineOff + scanLimitL * colorComps;
         if (likely(bitmapOff >= 0)) {
-	  for (int X = scanLimitL; X <= scanLimitR && bitmapOff + colorComps <= bitmapOffLimit; ++X, colorinterp += scanColorMap[0], bitmapOff += colorComps) {
+	  for (int X = scanLimitL; X <= scanLimitR && bitmapOff + colorComps <= bitmapOffLimit; ++X, colorinterp += scanColorMap0, 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);
+	    assert(fabs(colorinterp - (scanColorMap0 * X + scanColorMap1)) < 1e-10);
 	    assert(bitmapOff == Y * rowSize + colorComps * X && scanLineOff == Y * rowSize);
 
 	    shading->getParameterizedColor(colorinterp, bitmapMode, &bitmapData[bitmapOff]);
@@ -5656,24 +5622,183 @@ bool Splash::gouraudTriangleShadedFill(SplashGouraudColor *shading)
       }
     }
   } else {
-    if (!bDirectBlit) {
-      delete blitTarget;
+    SplashColor color, auxColor1, auxColor2;
+    double scanLimitMapL[2] = {0., 0.};
+    double scanLimitMapR[2] = {0., 0.};
+    int scanEdgeL[2] = { 0, 0 };
+    int scanEdgeR[2] = { 0, 0 };
+
+    for (int i = 0; i < shading->getNTriangles(); ++i) {
+      // Sadly this current algorithm only supports shadings where the three triangle vertices have the same color
+      shading->getNonParametrizedTriangle(i, bitmapMode,
+                           xdbl + 0, ydbl + 0, (SplashColorPtr)&color,
+                           xdbl + 1, ydbl + 1, (SplashColorPtr)&auxColor1,
+                           xdbl + 2, ydbl + 2, (SplashColorPtr)&auxColor2);
+      if (!splashColorEqual(color, auxColor1) ||
+	  !splashColorEqual(color, auxColor2))
+      {
+	  delete blitTarget;
+	  return false;
+      }
+      for (int m = 0; m < 3; ++m) {
+        xt = xdbl[m] * (double)userToCanvasMatrix[0] + ydbl[m] * (double)userToCanvasMatrix[2] + (double)userToCanvasMatrix[4];
+        yt = xdbl[m] * (double)userToCanvasMatrix[1] + ydbl[m] * (double)userToCanvasMatrix[3] + (double)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]);
+      }
+      // first two are sorted.
+      assert(y[0] <= y[1]);
+      if (y[1] > y[2]) {
+	const int tmpX = x[2];
+	const int tmpY = y[2];
+	x[2] = x[1]; y[2] = y[1];
+
+	if (y[0] > tmpY) {
+	  x[1] = x[0]; y[1] = y[0];
+	  x[0] = tmpX; y[0] = tmpY;
+	} else {
+	  x[1] = tmpX; y[1] = tmpY;
+	}
+      }
+      // 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.
+      }
+
+      bool hasFurtherSegment = (y[1] < y[2]);
+      int scanLineOff = y[0] * rowSize;
+
+      for (int 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];
+          } 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];
+          }
+          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];
+
+        const int scanLimitL = splashRound(xa);
+        const int scanLimitR = splashRound(xt);
+
+        // handled by clipping:
+        // assert( scanLimitL >= 0 && scanLimitR < bitmap->getWidth() );
+        assert(scanLimitL <= scanLimitR || abs(scanLimitL - scanLimitR) <= 2); // allow rounding inaccuracies
+        assert(scanLineOff == Y * rowSize);
+
+        int bitmapOff = scanLineOff + scanLimitL * colorComps;
+        if (likely(bitmapOff >= 0)) {
+	  for (int X = scanLimitL; X <= scanLimitR && bitmapOff + colorComps <= bitmapOffLimit; ++X, 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(bitmapOff == Y * rowSize + colorComps * X && scanLineOff == Y * rowSize);
+
+	    for (int k = 0; k < colorComps; ++k) {
+	      bitmapData[bitmapOff + k] = color[k];
+	    }
+
+	    // 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;
+	  }
+	}
+      }
     }
-    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;
+    const int W = blitTarget->getWidth();
+    const int H = blitTarget->getHeight();
+    SplashColorPtr cur = cSrcVal;
 
     for (int X = 0; X < W; ++X) {
       for (int Y = 0; Y < H; ++Y) {
         if (!bitmapAlpha[Y * bitmapWidth + X])
           continue; // draw only parts of the shading!
-        bitmapOff = Y * rowSize + colorComps * X;
+        const int bitmapOff = Y * rowSize + colorComps * X;
 
         for (int m = 0; m < colorComps; ++m)
           cur[m] = bitmapData[bitmapOff + m];
diff --git a/splash/SplashPattern.h b/splash/SplashPattern.h
index ce7eb446..1545c097 100644
--- a/splash/SplashPattern.h
+++ b/splash/SplashPattern.h
@@ -94,10 +94,15 @@ public:
 
   virtual int getNTriangles() = 0;
 
-  virtual  void getTriangle(int i, double *x0, double *y0, double *color0,
+  virtual void getParametrizedTriangle(int i, double *x0, double *y0, double *color0,
                             double *x1, double *y1, double *color1,
                             double *x2, double *y2, double *color2) = 0;
 
+  virtual void getNonParametrizedTriangle(int i, SplashColorMode mode,
+                            double *x0, double *y0, SplashColorPtr color0,
+                            double *x1, double *y1, SplashColorPtr color1,
+                            double *x2, double *y2, SplashColorPtr color2) = 0;
+
   virtual void getParameterizedColor(double t, SplashColorMode mode, SplashColorPtr c) = 0;
 };
 
diff --git a/splash/SplashTypes.h b/splash/SplashTypes.h
index 7500f588..c036a56e 100644
--- a/splash/SplashTypes.h
+++ b/splash/SplashTypes.h
@@ -128,6 +128,13 @@ static inline void splashColorCopy(SplashColorPtr dest, SplashColorConstPtr src)
     dest[i] = src[i];
 }
 
+static inline bool splashColorEqual(SplashColorConstPtr dest, SplashColorConstPtr src) {
+  for (int i = 0; i < SPOT_NCOMPS + 4; i++)
+    if (dest[i] != src[i])
+        return false;
+  return true;
+}
+
 static inline void splashColorXor(SplashColorPtr dest, SplashColorConstPtr src) {
   dest[0] ^= src[0];
   dest[1] ^= src[1];


More information about the poppler mailing list