diff options
-rw-r--r-- | poppler/SplashOutputDev.cc | 51 | ||||
-rw-r--r-- | poppler/SplashOutputDev.h | 11 | ||||
-rw-r--r-- | splash/Splash.cc | 271 | ||||
-rw-r--r-- | splash/SplashPattern.h | 7 | ||||
-rw-r--r-- | splash/SplashTypes.h | 7 |
5 files changed, 246 insertions, 101 deletions
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]; |