[poppler] poppler/SplashOutputDev.cc splash/Splash.cc splash/Splash.h
Albert Astals Cid
aacid at kemper.freedesktop.org
Fri Nov 30 12:32:22 PST 2012
poppler/SplashOutputDev.cc | 10 +-
splash/Splash.cc | 183 ++++++++++++++++++++++++++++++++++++++++++---
splash/Splash.h | 13 ++-
3 files changed, 188 insertions(+), 18 deletions(-)
New commits:
commit a97aead193a927b91a4e33d7b5d2ba7179e664df
Author: Adrian Johnson <ajohnson at redneon.com>
Date: Fri Nov 30 21:30:19 2012 +0100
Splash: Implement bilinear image scaling
Bug #22138
diff --git a/poppler/SplashOutputDev.cc b/poppler/SplashOutputDev.cc
index de09f49..3687676 100644
--- a/poppler/SplashOutputDev.cc
+++ b/poppler/SplashOutputDev.cc
@@ -29,7 +29,7 @@
// Copyright (C) 2010 Christian Feuersänger <cfeuersaenger at googlemail.com>
// Copyright (C) 2011 Andreas Hartmetz <ahartmetz at gmail.com>
// Copyright (C) 2011 Andrea Canciani <ranma42 at gmail.com>
-// Copyright (C) 2011 Adrian Johnson <ajohnson at redneon.com>
+// Copyright (C) 2011, 2012 Adrian Johnson <ajohnson at redneon.com>
//
// To see a description of the changes please see the Changelog file that
// came with your tarball or type make ChangeLog if you are building from git
@@ -3215,7 +3215,7 @@ void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
}
src = maskColors ? &alphaImageSrc : &imageSrc;
splash->drawImage(src, &imgData, srcMode, maskColors ? gTrue : gFalse,
- width, height, mat);
+ width, height, mat, interpolate);
if (inlineImg) {
while (imgData.y < height) {
imgData.imgStr->getLine();
@@ -3517,7 +3517,7 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref,
srcMode = colorMode;
}
splash->drawImage(&maskedImageSrc, &imgData, srcMode, gTrue,
- width, height, mat);
+ width, height, mat, interpolate);
delete maskBitmap;
gfree(imgData.lookup);
delete imgData.imgStr;
@@ -3592,7 +3592,7 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
maskColor[0] = 0;
maskSplash->clear(maskColor);
maskSplash->drawImage(&imageSrc, &imgMaskData, splashModeMono8, gFalse,
- maskWidth, maskHeight, mat);
+ maskWidth, maskHeight, mat, maskInterpolate);
delete imgMaskData.imgStr;
maskStr->close();
gfree(imgMaskData.lookup);
@@ -3679,7 +3679,7 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
} else {
srcMode = colorMode;
}
- splash->drawImage(&imageSrc, &imgData, srcMode, gFalse, width, height, mat);
+ splash->drawImage(&imageSrc, &imgData, srcMode, gFalse, width, height, mat, interpolate);
splash->setSoftMask(NULL);
gfree(imgData.lookup);
delete imgData.imgStr;
diff --git a/splash/Splash.cc b/splash/Splash.cc
index e46a496..7fa509c 100644
--- a/splash/Splash.cc
+++ b/splash/Splash.cc
@@ -17,6 +17,7 @@
// Copyright (C) 2010 Christian Feuersänger <cfeuersaenger at googlemail.com>
// Copyright (C) 2011, 2012 William Bader <williambader at hotmail.com>
// Copyright (C) 2012 Markus Trippelsdorf <markus at trippelsdorf.de>
+// Copyright (C) 2012 Adrian Johnson <ajohnson at redneon.com>
//
// To see a description of the changes please see the Changelog file that
// came with your tarball or type make ChangeLog if you are building from git
@@ -3502,8 +3503,8 @@ void Splash::blitMask(SplashBitmap *src, int xDest, int yDest,
SplashError Splash::drawImage(SplashImageSource src, void *srcData,
SplashColorMode srcMode, GBool srcAlpha,
- int w, int h, SplashCoord *mat,
- GBool tilingPattern) {
+ int w, int h, SplashCoord *mat, GBool interpolate,
+ GBool tilingPattern) {
GBool ok;
SplashBitmap *scaledImg;
SplashClipResult clipRes;
@@ -3587,7 +3588,7 @@ SplashError Splash::drawImage(SplashImageSource src, void *srcData,
return splashErrBadArg;
}
scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h,
- scaledWidth, scaledHeight);
+ scaledWidth, scaledHeight, interpolate);
if (scaledImg == NULL) {
return splashErrBadArg;
}
@@ -3625,7 +3626,7 @@ SplashError Splash::drawImage(SplashImageSource src, void *srcData,
return splashErrBadArg;
}
scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h,
- scaledWidth, scaledHeight);
+ scaledWidth, scaledHeight, interpolate);
if (scaledImg == NULL) {
return splashErrBadArg;
}
@@ -3637,7 +3638,7 @@ SplashError Splash::drawImage(SplashImageSource src, void *srcData,
// all other cases
} else {
return arbitraryTransformImage(src, srcData, srcMode, nComps, srcAlpha,
- w, h, mat, tilingPattern);
+ w, h, mat, interpolate, tilingPattern);
}
return splashOk;
@@ -3647,7 +3648,7 @@ SplashError Splash::arbitraryTransformImage(SplashImageSource src, void *srcData
SplashColorMode srcMode, int nComps,
GBool srcAlpha,
int srcWidth, int srcHeight,
- SplashCoord *mat,
+ SplashCoord *mat, GBool interpolate,
GBool tilingPattern) {
SplashBitmap *scaledImg;
SplashClipResult clipRes, clipRes2;
@@ -3772,7 +3773,7 @@ SplashError Splash::arbitraryTransformImage(SplashImageSource src, void *srcData
return splashErrBadArg;
}
scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha,
- srcWidth, srcHeight, scaledWidth, scaledHeight);
+ srcWidth, srcHeight, scaledWidth, scaledHeight, interpolate);
if (scaledImg == NULL) {
return splashErrBadArg;
@@ -3947,11 +3948,26 @@ SplashError Splash::arbitraryTransformImage(SplashImageSource src, void *srcData
return splashOk;
}
+// determine if a scaled image requires interpolation based on the scale and
+// the interpolate flag from the image dictionary
+static GBool isImageInterpolationRequired(int srcWidth, int srcHeight,
+ int scaledWidth, int scaledHeight,
+ GBool interpolate) {
+ if (interpolate)
+ return gTrue;
+
+ /* When scale factor is >= 400% we don't interpolate. See bugs #25268, #9860 */
+ if (scaledWidth / srcWidth >= 4 || scaledHeight / srcHeight >= 4)
+ return gFalse;
+
+ return gTrue;
+}
+
// Scale an image into a SplashBitmap.
SplashBitmap *Splash::scaleImage(SplashImageSource src, void *srcData,
SplashColorMode srcMode, int nComps,
GBool srcAlpha, int srcWidth, int srcHeight,
- int scaledWidth, int scaledHeight) {
+ int scaledWidth, int scaledHeight, GBool interpolate) {
SplashBitmap *dest;
dest = new SplashBitmap(scaledWidth, scaledHeight, 1, srcMode, srcAlpha, gTrue, bitmap->getSeparationList());
@@ -3969,8 +3985,13 @@ SplashBitmap *Splash::scaleImage(SplashImageSource src, void *srcData,
scaleImageYuXd(src, srcData, srcMode, nComps, srcAlpha,
srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
} else {
- scaleImageYuXu(src, srcData, srcMode, nComps, srcAlpha,
- srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
+ if (isImageInterpolationRequired(srcWidth, srcHeight, scaledWidth, scaledHeight, interpolate)) {
+ scaleImageYuXuBilinear(src, srcData, srcMode, nComps, srcAlpha,
+ srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
+ } else {
+ scaleImageYuXu(src, srcData, srcMode, nComps, srcAlpha,
+ srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
+ }
}
}
} else {
@@ -4680,6 +4701,148 @@ void Splash::scaleImageYuXu(SplashImageSource src, void *srcData,
gfree(lineBuf);
}
+// expand source row to scaledWidth using linear interpolation
+static void expandRow(Guchar *srcBuf, Guchar *dstBuf, int srcWidth, int scaledWidth, int nComps)
+{
+ double xStep = (double)srcWidth/scaledWidth;
+ double xSrc = 0.0;
+ double xFrac, xInt;
+ int p;
+
+ // pad the source with an extra pixel equal to the last pixel
+ // so that when xStep is inside the last pixel we still have two
+ // pixels to interpolate between.
+ for (int i = 0; i < nComps; i++)
+ srcBuf[srcWidth*nComps + i] = srcBuf[(srcWidth-1)*nComps + i];
+
+ for (int x = 0; x < scaledWidth; x++) {
+ xFrac = modf(xSrc, &xInt);
+ p = (int)xInt;
+ for (int c = 0; c < nComps; c++) {
+ dstBuf[nComps*x + c] = srcBuf[nComps*p + c]*(1.0 - xFrac) + srcBuf[nComps*(p+1) + c]*xFrac;
+ }
+ xSrc += xStep;
+ }
+}
+
+// Scale up image using bilinear interpolation
+void Splash::scaleImageYuXuBilinear(SplashImageSource src, void *srcData,
+ SplashColorMode srcMode, int nComps,
+ GBool srcAlpha, int srcWidth, int srcHeight,
+ int scaledWidth, int scaledHeight,
+ SplashBitmap *dest) {
+ Guchar *srcBuf, *lineBuf1, *lineBuf2, *alphaSrcBuf, *alphaLineBuf1, *alphaLineBuf2;
+ Guint pix[splashMaxColorComps];
+ Guchar *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr;
+ int i;
+
+ // allocate buffers
+ srcBuf = (Guchar *)gmallocn(srcWidth+1, nComps); // + 1 pixel of padding
+ lineBuf1 = (Guchar *)gmallocn(scaledWidth, nComps);
+ lineBuf2 = (Guchar *)gmallocn(scaledWidth, nComps);
+ if (srcAlpha) {
+ alphaSrcBuf = (Guchar *)gmalloc(srcWidth+1); // + 1 pixel of padding
+ alphaLineBuf1 = (Guchar *)gmalloc(scaledWidth);
+ alphaLineBuf2 = (Guchar *)gmalloc(scaledWidth);
+ } else {
+ alphaSrcBuf = NULL;
+ alphaLineBuf1 = NULL;
+ alphaLineBuf2 = NULL;
+ }
+
+ double ySrc = 0.0;
+ double yStep = (double)srcHeight/scaledHeight;
+ double yFrac, yInt;
+ int currentSrcRow = -1;
+ (*src)(srcData, srcBuf, alphaSrcBuf);
+ expandRow(srcBuf, lineBuf2, srcWidth, scaledWidth, nComps);
+ if (srcAlpha)
+ expandRow(alphaSrcBuf, alphaLineBuf2, srcWidth, scaledWidth, 1);
+
+ destPtr0 = dest->data;
+ destAlphaPtr0 = dest->alpha;
+ for (int y = 0; y < scaledHeight; y++) {
+ yFrac = modf(ySrc, &yInt);
+ if ((int)yInt > currentSrcRow) {
+ currentSrcRow++;
+ // Copy line2 data to line1 and get next line2 data.
+ // If line2 already contains the last source row we don't touch it.
+ // This effectively adds an extra row of padding for interpolating the
+ // last source row with.
+ memcpy(lineBuf1, lineBuf2, scaledWidth * nComps);
+ if (srcAlpha)
+ memcpy(alphaLineBuf1, alphaLineBuf2, scaledWidth);
+ if (currentSrcRow < srcHeight) {
+ (*src)(srcData, srcBuf, alphaSrcBuf);
+ expandRow(srcBuf, lineBuf2, srcWidth, scaledWidth, nComps);
+ if (srcAlpha)
+ expandRow(alphaSrcBuf, alphaLineBuf2, srcWidth, scaledWidth, 1);
+ }
+ }
+
+ // write row y using linear interpolation on lineBuf1 and lineBuf2
+ for (int x = 0; x < scaledWidth; ++x) {
+ // compute the final pixel
+ for (i = 0; i < nComps; ++i) {
+ pix[i] = lineBuf1[x*nComps + i]*(1.0 - yFrac) + lineBuf2[x*nComps + i]*yFrac;
+ }
+
+ // store the pixel
+ destPtr = destPtr0 + (y * scaledWidth + x) * nComps;
+ switch (srcMode) {
+ case splashModeMono1: // mono1 is not allowed
+ break;
+ case splashModeMono8:
+ *destPtr++ = (Guchar)pix[0];
+ break;
+ case splashModeRGB8:
+ *destPtr++ = (Guchar)pix[0];
+ *destPtr++ = (Guchar)pix[1];
+ *destPtr++ = (Guchar)pix[2];
+ break;
+ case splashModeXBGR8:
+ *destPtr++ = (Guchar)pix[2];
+ *destPtr++ = (Guchar)pix[1];
+ *destPtr++ = (Guchar)pix[0];
+ *destPtr++ = (Guchar)255;
+ break;
+ case splashModeBGR8:
+ *destPtr++ = (Guchar)pix[2];
+ *destPtr++ = (Guchar)pix[1];
+ *destPtr++ = (Guchar)pix[0];
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ *destPtr++ = (Guchar)pix[0];
+ *destPtr++ = (Guchar)pix[1];
+ *destPtr++ = (Guchar)pix[2];
+ *destPtr++ = (Guchar)pix[3];
+ break;
+ case splashModeDeviceN8:
+ for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
+ *destPtr++ = (Guchar)pix[cp];
+ break;
+#endif
+ }
+
+ // process alpha
+ if (srcAlpha) {
+ destAlphaPtr = destAlphaPtr0 + y*scaledWidth + x;
+ *destAlphaPtr = alphaLineBuf1[x]*(1.0 - yFrac) + alphaLineBuf2[x]*yFrac;
+ }
+ }
+
+ ySrc += yStep;
+ }
+
+ gfree(alphaSrcBuf);
+ gfree(alphaLineBuf1);
+ gfree(alphaLineBuf2);
+ gfree(srcBuf);
+ gfree(lineBuf1);
+ gfree(lineBuf2);
+}
+
void Splash::vertFlipImage(SplashBitmap *img, int width, int height,
int nComps) {
Guchar *lineBuf;
diff --git a/splash/Splash.h b/splash/Splash.h
index 223afdd..c31dbc8 100644
--- a/splash/Splash.h
+++ b/splash/Splash.h
@@ -15,6 +15,7 @@
// Copyright (C) 2007, 2011 Albert Astals Cid <aacid at kde.org>
// Copyright (C) 2010-2012 Thomas Freitag <Thomas.Freitag at alfa.de>
// Copyright (C) 2010 Christian Feuersänger <cfeuersaenger at googlemail.com>
+// Copyright (C) 2012 Adrian Johnson <ajohnson at redneon.com>
//
// To see a description of the changes please see the Changelog file that
// came with your tarball or type make ChangeLog if you are building from git
@@ -212,7 +213,8 @@ public:
// The matrix behaves as for fillImageMask.
SplashError drawImage(SplashImageSource src, void *srcData,
SplashColorMode srcMode, GBool srcAlpha,
- int w, int h, SplashCoord *mat, GBool tilingPattern = gFalse);
+ int w, int h, SplashCoord *mat, GBool interpolate,
+ GBool tilingPattern = gFalse);
// Composite a rectangular region from <src> onto this Splash
// object.
@@ -353,11 +355,11 @@ private:
SplashColorMode srcMode, int nComps,
GBool srcAlpha,
int srcWidth, int srcHeight,
- SplashCoord *mat, GBool tilingPattern = gFalse);
+ SplashCoord *mat, GBool interpolate, GBool tilingPattern = gFalse);
SplashBitmap *scaleImage(SplashImageSource src, void *srcData,
SplashColorMode srcMode, int nComps,
GBool srcAlpha, int srcWidth, int srcHeight,
- int scaledWidth, int scaledHeight);
+ int scaledWidth, int scaledHeight, GBool interpolate);
void scaleImageYdXd(SplashImageSource src, void *srcData,
SplashColorMode srcMode, int nComps,
GBool srcAlpha, int srcWidth, int srcHeight,
@@ -378,6 +380,11 @@ private:
GBool srcAlpha, int srcWidth, int srcHeight,
int scaledWidth, int scaledHeight,
SplashBitmap *dest);
+ void scaleImageYuXuBilinear(SplashImageSource src, void *srcData,
+ SplashColorMode srcMode, int nComps,
+ GBool srcAlpha, int srcWidth, int srcHeight,
+ int scaledWidth, int scaledHeight,
+ SplashBitmap *dest);
void vertFlipImage(SplashBitmap *img, int width, int height,
int nComps);
void blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest,
More information about the poppler
mailing list