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