From a7cec31da61a783b84eced77e75f9df0ba6af14d Mon Sep 17 00:00:00 2001
From: quikee <quikee@gmail.com>
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 <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,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

