From a7cec31da61a783b84eced77e75f9df0ba6af14d Mon Sep 17 00:00:00 2001 From: quikee Date: Sat, 2 Jun 2012 20:16:36 +0200 Subject: [PATCH] Lanczos3 resampling of images added to Bitmap and enabled in PDF export. Current resampling methods for images are FAST and INTERPOLATE. FAST is used where speed of resampling is required, on the other hand INTERPOLATE resampling is used when we need quality. For example INTERPOLATE resampling method is used at PDF export. INTERPOLATE resampling uses bilinear interpolation which is known to be lower quality as other modern (and slower) resampling algorithms such as Lanczos, Mitchell or BiCubic resampling. This change adds Lanczos resampling to the Bitmap class and enables Lanczos resampling in PDF export. Lanczos3 resampling is implmented using separable convolution with which it is also possible to easily add other resampling methods like BiCubic just by changing the kernel function. Change-Id: I8dff5b65753b09dffd5bc34f2343d9818efb3e58 --- vcl/inc/vcl/bitmap.hxx | 13 ++ vcl/source/gdi/bitmap3.cxx | 238 ++++++++++++++++++++++++++++++++++++ vcl/source/gdi/pdfwriter_impl2.cxx | 4 +- 3 files changed, 254 insertions(+), 1 deletion(-) diff --git a/vcl/inc/vcl/bitmap.hxx b/vcl/inc/vcl/bitmap.hxx index 17041d8..28fff13 100644 --- a/vcl/inc/vcl/bitmap.hxx +++ b/vcl/inc/vcl/bitmap.hxx @@ -49,6 +49,7 @@ #define BMP_SCALE_NONE 0x00000000UL #define BMP_SCALE_FAST 0x00000001UL #define BMP_SCALE_INTERPOLATE 0x00000002UL +#define BMP_SCALE_LANCZOS 0x00000003UL // ----------------------------------------------------------------------------- @@ -276,6 +277,18 @@ public: SAL_DLLPRIVATE sal_Bool ImplScaleFast( const double& rScaleX, const double& rScaleY ); SAL_DLLPRIVATE sal_Bool ImplScaleInterpolate( const double& rScaleX, const double& rScaleY ); + SAL_DLLPRIVATE sal_Bool ImplScaleLanczos( const double& rScaleX, const double& rScaleY ); + + SAL_DLLPRIVATE sal_Bool ImplCalculateContributions( const int aSourceSize, const int aDestinationSize, + const double aSupport, const int aNumberOfContributions, + double* pWeights, int* pPixels, int* pCount ); + SAL_DLLPRIVATE sal_Bool ImplHorizontalConvolution( Bitmap& aNewBitmap, BitmapReadAccess* pReadAcc, + int aNumberOfContributions, double* pWeights, int* pPixels, int* pCount ); + SAL_DLLPRIVATE sal_Bool ImplVerticalConvolution( Bitmap& aNewBitmap, BitmapReadAccess* pReadAcc, + int aNumberOfContributions, double* pWeights, int* pPixels, int* pCount ); + + SAL_DLLPRIVATE static double ImplLanczosKernel( const double aValue, const double aSupport ); + SAL_DLLPRIVATE sal_Bool ImplMakeMono( sal_uInt8 cThreshold ); SAL_DLLPRIVATE sal_Bool ImplMakeMonoDither(); SAL_DLLPRIVATE sal_Bool ImplMakeGreyscales( sal_uInt16 nGreyscales ); diff --git a/vcl/source/gdi/bitmap3.cxx b/vcl/source/gdi/bitmap3.cxx index a2b8587..5e60b97 100644 --- a/vcl/source/gdi/bitmap3.cxx +++ b/vcl/source/gdi/bitmap3.cxx @@ -36,6 +36,7 @@ #include #include +#include // ----------- // - Defines - @@ -914,6 +915,8 @@ sal_Bool Bitmap::Scale( const double& rScaleX, const double& rScaleY, sal_uLong bRet = ImplScaleFast( rScaleX, rScaleY ); else if( BMP_SCALE_INTERPOLATE == nScaleFlag ) bRet = ImplScaleInterpolate( rScaleX, rScaleY ); + else if( BMP_SCALE_LANCZOS == nScaleFlag ) + bRet = ImplScaleLanczos( rScaleX, rScaleY ); else bRet = sal_False; } @@ -2205,4 +2208,239 @@ sal_Bool Bitmap::Adjust( short nLuminancePercent, short nContrastPercent, return bRet; } +//----------------------------------------------------------------------------------- +sal_Bool Bitmap::ImplScaleLanczos( const double& rScaleX, const double& rScaleY ) +{ + const Size aSizePix( GetSizePixel() ); + const long nWidth = aSizePix.Width(); + const long nHeight = aSizePix.Height(); + const long nNewWidth = FRound( nWidth * rScaleX ); + const long nNewHeight = FRound( nHeight * rScaleY ); + + double aSupport = 3.0; // Sampling radius + + // Do horizontal filtering + double aScale = nNewWidth / (double) nWidth; + double aScaledRadius = aSupport / aScale; + int aNumberOfContributions = (int) ( 2 * aScaledRadius + 1 ); + + double* pWeights = new double[ nNewWidth*aNumberOfContributions ]; + int* pPixels = new int[ nNewWidth*aNumberOfContributions ]; + int* pCount = new int[ nNewWidth ]; + + ImplCalculateContributions( nWidth, nNewWidth, aSupport, aNumberOfContributions, pWeights, pPixels, pCount ); + + BitmapReadAccess* pReadAcc = AcquireReadAccess(); + Bitmap aNewBitmap( Size( nNewWidth, nHeight ), GetBitCount(), &pReadAcc->GetPalette() ); + sal_Bool bResult = ImplHorizontalConvolution( aNewBitmap, pReadAcc, aNumberOfContributions, pWeights, pPixels, pCount ); + + // Cleanup + ReleaseAccess( pReadAcc ); + delete[] pWeights; + delete[] pCount; + delete[] pPixels; + + if ( !bResult ) + return bResult; + + // Swap current bitmap with new bitmap + ImplAssignWithSize( aNewBitmap ); + + // Do vertical filtering + aScale = nNewHeight / (double) nHeight; + aScaledRadius = aSupport / aScale; + aNumberOfContributions = (int) ( 2 * aScaledRadius + 1 ); + + pWeights = new double[ nNewHeight*aNumberOfContributions ]; + pPixels = new int[ nNewHeight*aNumberOfContributions ]; + pCount = new int[ nNewHeight ]; + + ImplCalculateContributions(nHeight, nNewHeight, aSupport, aNumberOfContributions, pWeights, pPixels, pCount ); + + pReadAcc = AcquireReadAccess(); + aNewBitmap = Bitmap( Size( nNewWidth, nNewHeight ), GetBitCount(), &pReadAcc->GetPalette() ); + bResult = ImplVerticalConvolution( aNewBitmap, pReadAcc, aNumberOfContributions, pWeights, pPixels, pCount ); + + // Cleanup + ReleaseAccess( pReadAcc ); + delete[] pWeights; + delete[] pCount; + delete[] pPixels; + + if ( !bResult ) + return bResult; + + // Swap current bitmap with new bitmap + ImplAssignWithSize( aNewBitmap ); + + return sal_True; +} + +sal_Bool Bitmap::ImplCalculateContributions( const int aSourceSize, const int aDestinationSize, const double aSupport, + const int aNumberOfContributions, double* pWeights, int* pPixels, + int* pCount ) +{ + const double aScale = aDestinationSize / (double) aSourceSize; + const double aScaledRadius = aSupport / aScale; + const double aFilterFactor = aScale; + + double aWeight, aCenter; + int aIndex, aLeft, aRight; + + for ( int i = 0; i < aDestinationSize; i++ ) { + aIndex = i * aNumberOfContributions; + pCount[i] = 0; + aCenter = ((double)i) / aScale; + + aLeft = (int)((aCenter + 0.5) - aScaledRadius); + aRight = (int)(aLeft + 2 * aScaledRadius); + + for ( int j = aLeft; j<= aRight; j++ ) { + if ( j < 0 || j >= aSourceSize ) { + continue; + } + + aWeight = ImplLanczosKernel( (aCenter - j) * aFilterFactor, aSupport ); + if (aWeight == 0.0) { + continue; + } + + int currentCount = pCount[ i ]; + pWeights[ aIndex + currentCount ] = aWeight; + pPixels[ aIndex + currentCount ] = j; + pCount[ i ]++; + } + } + return sal_True; +} + +sal_Bool Bitmap::ImplHorizontalConvolution(Bitmap& aNewBitmap, BitmapReadAccess* pReadAcc, int aNumberOfContributions, double* pWeights, int* pPixels, int* pCount) +{ + BitmapWriteAccess* pWriteAcc = aNewBitmap.AcquireWriteAccess(); + + if (!pReadAcc || !pWriteAcc) + { + return sal_False; + } + + const int nHeight = GetSizePixel().Height(); + const int nNewWidth = aNewBitmap.GetSizePixel().Width(); + + BitmapColor aColor; + double aValueRed, aValueGreen, aValueBlue; + double aSum, aWeight; + int aBaseIndex, aIndex; + + for ( int y = 0; y < nHeight; y++ ) + { + for ( int i = 0; i < nNewWidth; i++ ) + { + aBaseIndex = i * aNumberOfContributions; + aValueRed = aValueGreen = aValueBlue = 0.0; + aSum = 0.0; + + for ( int j=0; j < pCount[i]; j++ ) + { + aIndex = aBaseIndex + j; + aWeight = pWeights[ aIndex ]; + aSum += aWeight; + if( pReadAcc->HasPalette() ) + { + aColor = pReadAcc->GetPaletteColor( pReadAcc->GetPixel( y , pPixels[ aIndex ] ) ); + } + else + { + aColor = pReadAcc->GetPixel( y , pPixels[ aIndex ] ); + } + + aValueRed += aWeight * aColor.GetRed(); + aValueGreen += aWeight * aColor.GetGreen(); + aValueBlue += aWeight * aColor.GetBlue(); + } + + BitmapColor aResultColor( + (sal_uInt8) MinMax( aValueRed / aSum, 0, 255 ), + (sal_uInt8) MinMax( aValueGreen / aSum, 0, 255 ), + (sal_uInt8) MinMax( aValueBlue / aSum, 0, 255 ) ); + pWriteAcc->SetPixel( y, i, aResultColor ); + } + } + aNewBitmap.ReleaseAccess( pWriteAcc ); + return sal_True; +} + +sal_Bool Bitmap::ImplVerticalConvolution(Bitmap& aNewBitmap, BitmapReadAccess* pReadAcc, int aNumberOfContributions, double* pWeights, int* pPixels, int* pCount) +{ + BitmapWriteAccess* pWriteAcc = aNewBitmap.AcquireWriteAccess(); + + if (!pReadAcc || !pWriteAcc) + { + return sal_False; + } + + const int nWidth = GetSizePixel().Width(); + const int nNewHeight = aNewBitmap.GetSizePixel().Height(); + + BitmapColor aColor; + double aValueRed, aValueGreen, aValueBlue; + double aSum, aWeight; + int aBaseIndex, aIndex; + for (int x = 0; x < nWidth; x++) + { + for (int i = 0; i < nNewHeight; i++) + { + aBaseIndex = i * aNumberOfContributions; + aSum = 0.0; + aValueRed = aValueGreen = aValueBlue = 0.0; + + for (int j=0; j < pCount[i]; j++) + { + aIndex = aBaseIndex + j; + aWeight = pWeights[ aIndex ]; + aSum += aWeight; + if( pReadAcc->HasPalette() ) + { + aColor = pReadAcc->GetPaletteColor( pReadAcc->GetPixel( pPixels[ aIndex ] , x ) ); + } + else + { + aColor = pReadAcc->GetPixel( pPixels[ aIndex ] , x ); + } + aValueRed += aWeight * aColor.GetRed(); + aValueGreen += aWeight * aColor.GetGreen(); + aValueBlue += aWeight * aColor.GetBlue(); + } + + BitmapColor aResultColor( + (sal_uInt8) MinMax( aValueRed / aSum, 0, 255 ), + (sal_uInt8) MinMax( aValueGreen / aSum, 0, 255 ), + (sal_uInt8) MinMax( aValueBlue / aSum, 0, 255 ) ); + pWriteAcc->SetPixel( i, x, aResultColor ); + } + } + + aNewBitmap.ReleaseAccess( pWriteAcc ); + return sal_True; +} + +double Bitmap::ImplLanczosKernel( const double aValue, const double aSupport ) { + double x = aValue; + if (x == 0.0) + { + return 1.0; + } + if (x < 0.0) + { + x = -x; + } + + x *= M_PI; + if (x < aSupport) + { + double x3 = x / 3.0; + return (sin(x) / x) * sin(x3) / x3; + } + return 0.0; +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/pdfwriter_impl2.cxx b/vcl/source/gdi/pdfwriter_impl2.cxx index 4ba5e2c..12af40e 100644 --- a/vcl/source/gdi/pdfwriter_impl2.cxx +++ b/vcl/source/gdi/pdfwriter_impl2.cxx @@ -137,7 +137,9 @@ void PDFWriterImpl::implWriteBitmapEx( const Point& i_rPoint, const Size& i_rSiz aNewBmpSize.Height() = FRound( fMaxPixelX / fBmpWH); } if( aNewBmpSize.Width() && aNewBmpSize.Height() ) - aBitmapEx.Scale( aNewBmpSize ); + { + aBitmapEx.Scale( aNewBmpSize, BMP_SCALE_LANCZOS ); + } else aBitmapEx.SetEmpty(); } -- 1.7.9.5