From 942e93110bea4904fd8f573c145e07d8acebc482 Mon Sep 17 00:00:00 2001 From: Adrian Johnson Date: Tue, 20 Nov 2012 20:03:58 +1030 Subject: [PATCH] splash: change scaleImageYuXu to use bilinear interpolation using fixed point --- splash/Splash.cc | 238 +++++++++++++++++++++++++----------------------------- 1 file changed, 109 insertions(+), 129 deletions(-) diff --git a/splash/Splash.cc b/splash/Splash.cc index e46a496..4cbbbe6 100644 --- a/splash/Splash.cc +++ b/splash/Splash.cc @@ -35,6 +35,7 @@ #include #include #include "goo/gmem.h" +#include "goo/gtypes_p.h" #include "goo/GooLikely.h" #include "goo/GooList.h" #include "poppler/Error.h" @@ -4524,160 +4525,139 @@ void Splash::scaleImageYuXd(SplashImageSource src, void *srcData, gfree(lineBuf); } +// linear interpolation +static inline Guchar lerp(Guchar a, Guchar b, uint32_t frac) +{ + uint16_t f = frac >> 16; + uint16_t one_minus_f = 0xffff - f; + return (a*one_minus_f + b*f)>>16; +} + +// expand source row to scaledWidth using linear interpolation +static void expandRow(Guchar *srcBuf, Guchar *dstBuf, + int srcWidth, int scaledWidth, int nComps) +{ + uint64_t xSrc; // source pixel (32.32 fixed point) + uint64_t xStep; // source pixel step for each dest pixel (32.32 fixed point) + uint32_t frac; // .32 fraction + 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]; + + xStep = (((uint64_t)1<<32)*srcWidth)/scaledWidth; + xSrc = 0; + for (int x = 0; x < scaledWidth; x++) { + p = xSrc >> 32; + frac = xSrc & 0xffffffff; + for (int i = 0; i < nComps; i++) { + dstBuf[x*nComps + i] = lerp(srcBuf[p*nComps + i], srcBuf[(p+1)*nComps + i], frac); + } + xSrc += xStep; + } +} + void Splash::scaleImageYuXu(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { - Guchar *lineBuf, *alphaLineBuf; - Guint pix[splashMaxColorComps]; - Guint alpha; - Guchar *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr; - int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx; - int i, j; - - // Bresenham parameters for y scale - yp = scaledHeight / srcHeight; - yq = scaledHeight % srcHeight; - - // Bresenham parameters for x scale - xp = scaledWidth / srcWidth; - xq = scaledWidth % srcWidth; + Guchar *srcBuf, *lineBuf1, *lineBuf2, *alphaSrcBuf, *alphaLineBuf1, *alphaLineBuf2; + Guchar *destPtr, *destAlphaPtr; // allocate buffers - lineBuf = (Guchar *)gmallocn(srcWidth, nComps); + srcBuf = (Guchar *)gmallocn(srcWidth+1, nComps); // + 1 pixel of padding + lineBuf1 = (Guchar *)gmallocn(scaledWidth, nComps); + lineBuf2 = (Guchar *)gmallocn(scaledWidth, nComps); if (srcAlpha) { - alphaLineBuf = (Guchar *)gmalloc(srcWidth); + alphaSrcBuf = (Guchar *)gmalloc(srcWidth+1); // + 1 pixel of padding + alphaLineBuf1 = (Guchar *)gmalloc(scaledWidth); + alphaLineBuf2 = (Guchar *)gmalloc(scaledWidth); } else { - alphaLineBuf = NULL; - } - - // init y scale Bresenham - yt = 0; - - destPtr0 = dest->data; - destAlphaPtr0 = dest->alpha; - for (y = 0; y < srcHeight; ++y) { - - // y scale Bresenham - if ((yt += yq) >= srcHeight) { - yt -= srcHeight; - yStep = yp + 1; - } else { - yStep = yp; - } - - // read row from image - (*src)(srcData, lineBuf, alphaLineBuf); - - // init x scale Bresenham - xt = 0; - - xx = 0; - for (x = 0; x < srcWidth; ++x) { - - // x scale Bresenham - if ((xt += xq) >= srcWidth) { - xt -= srcWidth; - xStep = xp + 1; - } else { - xStep = xp; - } - - // compute the final pixel - for (i = 0; i < nComps; ++i) { - pix[i] = lineBuf[x * nComps + i]; - } + alphaSrcBuf = NULL; + alphaLineBuf1 = NULL; + alphaLineBuf2 = NULL; + } + + uint64_t ySrc; // source row (32.32 fixed point) + int r; // nearest source row + uint64_t yStep; // source row step for each dest row (32.32 fixed point) + uint32_t frac; // .32 fraction + yStep = (((uint64_t)1<<32)*srcHeight)/scaledHeight; + ySrc = 0; + int currentSrcRow = -1; + (*src)(srcData, srcBuf, alphaSrcBuf); + expandRow(srcBuf, lineBuf2, srcWidth, scaledWidth, nComps); + if (srcAlpha) + expandRow(alphaSrcBuf, alphaLineBuf2, srcWidth, scaledWidth, 1); - // store the pixel - switch (srcMode) { + destPtr = dest->data; + destAlphaPtr = dest->alpha; + for (int y = 0; y < scaledHeight; y++) { + r = ySrc >> 32; + frac = ySrc & 0xffffffff; + if (r > 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 the linear interpolation of lineBuf1 and lineBuf2 + switch (srcMode) { case splashModeMono1: // mono1 is not allowed break; - case splashModeMono8: - for (i = 0; i < yStep; ++i) { - for (j = 0; j < xStep; ++j) { - destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; - *destPtr++ = (Guchar)pix[0]; - } - } - break; - case splashModeRGB8: - for (i = 0; i < yStep; ++i) { - for (j = 0; j < xStep; ++j) { - destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; - *destPtr++ = (Guchar)pix[0]; - *destPtr++ = (Guchar)pix[1]; - *destPtr++ = (Guchar)pix[2]; - } - } - break; case splashModeXBGR8: - for (i = 0; i < yStep; ++i) { - for (j = 0; j < xStep; ++j) { - destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; - *destPtr++ = (Guchar)pix[2]; - *destPtr++ = (Guchar)pix[1]; - *destPtr++ = (Guchar)pix[0]; - *destPtr++ = (Guchar)255; - } + for (int x = 0; x < scaledWidth; x++) { + *destPtr++ = lerp(lineBuf1[3*x+2], lineBuf2[3*x+2], frac); + *destPtr++ = lerp(lineBuf1[3*x+1], lineBuf2[3*x+1], frac); + *destPtr++ = lerp(lineBuf1[3*x], lineBuf2[3*x], frac); + *destPtr++ = (Guchar)255; } break; case splashModeBGR8: - for (i = 0; i < yStep; ++i) { - for (j = 0; j < xStep; ++j) { - destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; - *destPtr++ = (Guchar)pix[2]; - *destPtr++ = (Guchar)pix[1]; - *destPtr++ = (Guchar)pix[0]; - } + for (int x = 0; x < scaledWidth; x++) { + *destPtr++ = lerp(lineBuf1[3*x+2], lineBuf2[3*x+2], frac); + *destPtr++ = lerp(lineBuf1[3*x+1], lineBuf2[3*x+1], frac); + *destPtr++ = lerp(lineBuf1[3*x], lineBuf2[3*x], frac); } break; -#if SPLASH_CMYK - case splashModeCMYK8: - for (i = 0; i < yStep; ++i) { - for (j = 0; j < xStep; ++j) { - destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; - *destPtr++ = (Guchar)pix[0]; - *destPtr++ = (Guchar)pix[1]; - *destPtr++ = (Guchar)pix[2]; - *destPtr++ = (Guchar)pix[3]; - } - } - break; - case splashModeDeviceN8: - for (i = 0; i < yStep; ++i) { - for (j = 0; j < xStep; ++j) { - destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps; - for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) - *destPtr++ = (Guchar)pix[cp]; - } - } + default: + // All other modes have a 1:1 mapping between the source and + // destination pixel format. + for (int x = 0; x < scaledWidth*nComps; x++) + *destPtr++ = lerp(lineBuf1[x], lineBuf2[x], frac); break; -#endif - } - - // process alpha - if (srcAlpha) { - alpha = alphaLineBuf[x]; - for (i = 0; i < yStep; ++i) { - for (j = 0; j < xStep; ++j) { - destAlphaPtr = destAlphaPtr0 + i * scaledWidth + xx + j; - *destAlphaPtr = (Guchar)alpha; - } - } - } - - xx += xStep; } - destPtr0 += yStep * scaledWidth * nComps; + // process alpha if (srcAlpha) { - destAlphaPtr0 += yStep * scaledWidth; + for (int x = 0; x < scaledWidth; x++) + *destAlphaPtr++ = lerp(alphaLineBuf1[x], alphaLineBuf2[x], frac); } + + ySrc += yStep; } - gfree(alphaLineBuf); - gfree(lineBuf); + gfree(alphaSrcBuf); + gfree(alphaLineBuf1); + gfree(alphaLineBuf2); + gfree(srcBuf); + gfree(lineBuf1); + gfree(lineBuf2); } void Splash::vertFlipImage(SplashBitmap *img, int width, int height, -- 1.7.10.4