summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomaz Vajngerl <quikee@gmail.com>2012-06-02 18:16:36 (GMT)
committerJan Holesovsky <kendy@suse.cz>2012-06-04 14:12:43 (GMT)
commit47e0df5cebc05576e55210c5dd408a6f3eb91700 (patch)
tree4c0db048267ba6064b1cea4915e81f6b381927e3
parent0b638638ef6e8c93318cb898ef8312e0e197b45e (diff)
fdo#46378: Lanczos3 resampling of images added to Bitmap.
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
-rw-r--r--vcl/inc/vcl/bitmap.hxx13
-rw-r--r--vcl/source/gdi/bitmap3.cxx237
2 files changed, 250 insertions, 0 deletions
diff --git a/vcl/inc/vcl/bitmap.hxx b/vcl/inc/vcl/bitmap.hxx
index 17041d8..14aae27 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 bool ImplScaleLanczos( const double& rScaleX, const double& rScaleY );
+
+ SAL_DLLPRIVATE void ImplCalculateContributions( const int aSourceSize, const int aDestinationSize,
+ const double aSupport, const int aNumberOfContributions,
+ double* pWeights, int* pPixels, int* pCount );
+ SAL_DLLPRIVATE bool ImplHorizontalConvolution( Bitmap& aNewBitmap, BitmapReadAccess* pReadAcc,
+ int aNumberOfContributions, double* pWeights, int* pPixels, int* pCount );
+ SAL_DLLPRIVATE 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..2d35c6a 100644
--- a/vcl/source/gdi/bitmap3.cxx
+++ b/vcl/source/gdi/bitmap3.cxx
@@ -36,6 +36,7 @@
#include <impoct.hxx>
#include <impvect.hxx>
+#include <math.h>
// -----------
// - 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,238 @@ sal_Bool Bitmap::Adjust( short nLuminancePercent, short nContrastPercent,
return bRet;
}
+//-----------------------------------------------------------------------------------
+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() );
+ 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 true;
+}
+
+void 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 ]++;
+ }
+ }
+}
+
+bool Bitmap::ImplHorizontalConvolution(Bitmap& aNewBitmap, BitmapReadAccess* pReadAcc, int aNumberOfContributions, double* pWeights, int* pPixels, int* pCount)
+{
+ BitmapWriteAccess* pWriteAcc = aNewBitmap.AcquireWriteAccess();
+
+ if (!pReadAcc || !pWriteAcc)
+ {
+ return 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 true;
+}
+
+bool Bitmap::ImplVerticalConvolution(Bitmap& aNewBitmap, BitmapReadAccess* pReadAcc, int aNumberOfContributions, double* pWeights, int* pPixels, int* pCount)
+{
+ BitmapWriteAccess* pWriteAcc = aNewBitmap.AcquireWriteAccess();
+
+ if (!pReadAcc || !pWriteAcc)
+ {
+ return 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 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: */