vcl: move Bitmap::ImplScaleFast() and Bitmap::ImplScaleInterpolate()

Bitmap::ImplScaleFast() -> BitmapFastScaleFilter
Bitmap::ImplScaleInterpolate() -> BitmapInterpolateScaleFilter

Change-Id: Ieb39cc21df5d98563945968cec81b7c51f64177f
Reviewed-on: https://gerrit.libreoffice.org/52899
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk
index 3a819ce..93d1faf 100644
--- a/vcl/Library_vcl.mk
+++ b/vcl/Library_vcl.mk
@@ -313,10 +313,12 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\
    vcl/source/graphic/UnoGraphicTransformer \
    vcl/source/bitmap/bitmap \
    vcl/source/bitmap/bitmapfilter \
    vcl/source/bitmap/BitmapInterpolateScaleFilter \
    vcl/source/bitmap/BitmapLightenFilter \
    vcl/source/bitmap/BitmapDisabledImageFilter \
    vcl/source/bitmap/BitmapColorizeFilter \
    vcl/source/bitmap/bitmappaint \
	vcl/source/bitmap/BitmapFastScaleFilter \
    vcl/source/bitmap/BitmapScaleSuperFilter \
    vcl/source/bitmap/BitmapScaleConvolutionFilter \
    vcl/source/bitmap/BitmapSymmetryCheck \
diff --git a/vcl/inc/BitmapFastScaleFilter.hxx b/vcl/inc/BitmapFastScaleFilter.hxx
new file mode 100644
index 0000000..c1ee11b
--- /dev/null
+++ b/vcl/inc/BitmapFastScaleFilter.hxx
@@ -0,0 +1,41 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 */

#ifndef VCL_INC_BITMAPFASTSCALEFILTER_HXX
#define VCL_INC_BITMAPFASTSCALEFILTER_HXX

#include <vcl/bitmapex.hxx>
#include <vcl/BitmapFilter.hxx>

class VCL_DLLPUBLIC BitmapFastScaleFilter : public BitmapFilter
{
public:
    explicit BitmapFastScaleFilter(double fScaleX, double fScaleY)
        : mfScaleX(fScaleX)
        , mfScaleY(fScaleY)
    {
    }

    explicit BitmapFastScaleFilter(Size aSize)
        : maSize(aSize)
    {
    }

    virtual BitmapEx execute(BitmapEx const& rBitmapEx) override;

private:
    double mfScaleX;
    double mfScaleY;
    Size maSize;
};

#endif

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/inc/BitmapInterpolateScaleFilter.hxx b/vcl/inc/BitmapInterpolateScaleFilter.hxx
new file mode 100644
index 0000000..0fbe9ac3
--- /dev/null
+++ b/vcl/inc/BitmapInterpolateScaleFilter.hxx
@@ -0,0 +1,41 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 */

#ifndef VCL_INC_BITMAPINTERPOLATESCALEFILTER_HXX
#define VCL_INC_BITMAPINTERPOLATESCALEFILTER_HXX

#include <vcl/bitmapex.hxx>
#include <vcl/BitmapFilter.hxx>

class VCL_DLLPUBLIC BitmapInterpolateScaleFilter : public BitmapFilter
{
public:
    explicit BitmapInterpolateScaleFilter(double fScaleX, double fScaleY)
        : mfScaleX(fScaleX)
        , mfScaleY(fScaleY)
    {
    }

    explicit BitmapInterpolateScaleFilter(Size aSize)
        : maSize(aSize)
    {
    }

    virtual BitmapEx execute(BitmapEx const& rBitmapEx) override;

private:
    double mfScaleX;
    double mfScaleY;
    Size maSize;
};

#endif

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/BitmapFastScaleFilter.cxx b/vcl/source/bitmap/BitmapFastScaleFilter.cxx
new file mode 100644
index 0000000..6bf1700
--- /dev/null
+++ b/vcl/source/bitmap/BitmapFastScaleFilter.cxx
@@ -0,0 +1,130 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <tools/helpers.hxx>

#include <vcl/bitmapex.hxx>
#include <vcl/bitmapaccess.hxx>

#include <bitmapwriteaccess.hxx>
#include <BitmapFastScaleFilter.hxx>

BitmapEx BitmapFastScaleFilter::execute(BitmapEx const& rBitmapEx)
{
    SAL_INFO("vcl.gdi", "BitmapFastScaleFilter::execute()");

    Bitmap aBitmap(rBitmapEx.GetBitmap());

    const Size aSizePix(aBitmap.GetSizePixel());
    const long nNewWidth = FRound(aSizePix.Width() * mfScaleX);
    const long nNewHeight = FRound(aSizePix.Height() * mfScaleY);
    bool bRet = false;

    SAL_INFO("vcl.gdi", "New width: " << nNewWidth << "\nNew height: " << nNewHeight);

    if (nNewWidth && nNewHeight)
    {
        Bitmap::ScopedReadAccess pReadAcc(aBitmap);

        if (pReadAcc)
        {
            Bitmap aNewBmp(Size(nNewWidth, nNewHeight), aBitmap.GetBitCount(),
                           &pReadAcc->GetPalette());
            BitmapScopedWriteAccess pWriteAcc(aNewBmp);

            if (pWriteAcc)
            {
                const long nScanlineSize = pWriteAcc->GetScanlineSize();
                const long nNewWidth1 = nNewWidth - 1;
                const long nNewHeight1 = nNewHeight - 1;

                if (nNewWidth1 && nNewHeight1)
                {
                    const double nWidth = pReadAcc->Width();
                    const double nHeight = pReadAcc->Height();
                    std::unique_ptr<long[]> pLutX(new long[nNewWidth]);
                    std::unique_ptr<long[]> pLutY(new long[nNewHeight]);

                    for (long nX = 0; nX < nNewWidth; nX++)
                    {
                        pLutX[nX] = long(nX * nWidth / nNewWidth);
                    }

                    for (long nY = 0; nY < nNewHeight; nY++)
                    {
                        pLutY[nY] = long(nY * nHeight / nNewHeight);
                    }

                    long nActY = 0;
                    while (nActY < nNewHeight)
                    {
                        long nMapY = pLutY[nActY];
                        Scanline pScanline = pWriteAcc->GetScanline(nActY);
                        Scanline pScanlineRead = pReadAcc->GetScanline(nMapY);

                        for (long nX = 0; nX < nNewWidth; nX++)
                        {
                            pWriteAcc->SetPixelOnData(
                                pScanline, nX,
                                pReadAcc->GetPixelFromData(pScanlineRead, pLutX[nX]));
                        }

                        while ((nActY < nNewHeight1) && (pLutY[nActY + 1] == nMapY))
                        {
                            memcpy(pWriteAcc->GetScanline(nActY + 1), pWriteAcc->GetScanline(nActY),
                                   nScanlineSize);
                            nActY++;
                        }
                        nActY++;
                    }

                    bRet = true;
                }

                pWriteAcc.reset();
            }
            pReadAcc.reset();

            if (bRet)
            {
                aBitmap.ReassignWithSize(aNewBmp);
                SAL_INFO("vcl.gdi", "Bitmap size: " << aBitmap.GetSizePixel());
            }
            else
            {
                SAL_WARN("vcl.gdi", "no resize");
            }
        }
    }

    Bitmap aMask(rBitmapEx.GetMask());

    if (bRet && (rBitmapEx.GetTransparentType() == TransparentType::Bitmap) && !aMask.IsEmpty())
        bRet = aMask.Scale(maSize, BmpScaleFlag::Fast);

    SAL_WARN_IF(!aMask.IsEmpty() && aBitmap.GetSizePixel() != aMask.GetSizePixel(), "vcl",
                "BitmapEx::Scale(): size mismatch for bitmap and alpha mask.");

    if (bRet)
        return BitmapEx(aBitmap, aMask);

    return BitmapEx();
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/BitmapInterpolateScaleFilter.cxx b/vcl/source/bitmap/BitmapInterpolateScaleFilter.cxx
new file mode 100644
index 0000000..43e4d2f
--- /dev/null
+++ b/vcl/source/bitmap/BitmapInterpolateScaleFilter.cxx
@@ -0,0 +1,236 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <tools/helpers.hxx>

#include <vcl/bitmapex.hxx>
#include <vcl/bitmapaccess.hxx>

#include <bitmapwriteaccess.hxx>
#include <BitmapFastScaleFilter.hxx>
#include <BitmapInterpolateScaleFilter.hxx>

BitmapEx BitmapInterpolateScaleFilter::execute(BitmapEx const& rBitmapEx)
{
    Bitmap aBitmap(rBitmapEx.GetBitmap());

    const Size aSizePix(aBitmap.GetSizePixel());
    const long nNewWidth = FRound(aSizePix.Width() * mfScaleX);
    const long nNewHeight = FRound(aSizePix.Height() * mfScaleY);
    bool bRet = false;

    if ((nNewWidth > 1) && (nNewHeight > 1))
    {
        Bitmap::ScopedReadAccess pReadAcc(aBitmap);
        if (pReadAcc)
        {
            long nWidth = pReadAcc->Width();
            long nHeight = pReadAcc->Height();
            Bitmap aNewBmp(Size(nNewWidth, nHeight), 24);
            BitmapScopedWriteAccess pWriteAcc(aNewBmp);

            if (pWriteAcc)
            {
                const long nNewWidth1 = nNewWidth - 1;
                const long nWidth1 = pReadAcc->Width() - 1;
                const double fRevScaleX = static_cast<double>(nWidth1) / nNewWidth1;

                std::unique_ptr<long[]> pLutInt(new long[nNewWidth]);
                std::unique_ptr<long[]> pLutFrac(new long[nNewWidth]);

                for (long nX = 0, nTemp = nWidth - 2; nX < nNewWidth; nX++)
                {
                    double fTemp = nX * fRevScaleX;
                    pLutInt[nX] = MinMax(static_cast<long>(fTemp), 0, nTemp);
                    fTemp -= pLutInt[nX];
                    pLutFrac[nX] = static_cast<long>(fTemp * 1024.);
                }

                for (long nY = 0; nY < nHeight; nY++)
                {
                    Scanline pScanlineRead = pReadAcc->GetScanline(nY);
                    if (1 == nWidth)
                    {
                        BitmapColor aCol0;
                        if (pReadAcc->HasPalette())
                        {
                            aCol0 = pReadAcc->GetPaletteColor(
                                pReadAcc->GetIndexFromData(pScanlineRead, 0));
                        }
                        else
                        {
                            aCol0 = pReadAcc->GetPixelFromData(pScanlineRead, 0);
                        }

                        Scanline pScanline = pWriteAcc->GetScanline(nY);
                        for (long nX = 0; nX < nNewWidth; nX++)
                        {
                            pWriteAcc->SetPixelOnData(pScanline, nX, aCol0);
                        }
                    }
                    else
                    {
                        Scanline pScanline = pWriteAcc->GetScanline(nY);
                        for (long nX = 0; nX < nNewWidth; nX++)
                        {
                            long nTemp = pLutInt[nX];

                            BitmapColor aCol0, aCol1;
                            if (pReadAcc->HasPalette())
                            {
                                aCol0 = pReadAcc->GetPaletteColor(
                                    pReadAcc->GetIndexFromData(pScanlineRead, nTemp++));
                                aCol1 = pReadAcc->GetPaletteColor(
                                    pReadAcc->GetIndexFromData(pScanlineRead, nTemp));
                            }
                            else
                            {
                                aCol0 = pReadAcc->GetPixelFromData(pScanlineRead, nTemp++);
                                aCol1 = pReadAcc->GetPixelFromData(pScanlineRead, nTemp);
                            }

                            nTemp = pLutFrac[nX];

                            long lXR0 = aCol0.GetRed();
                            long lXG0 = aCol0.GetGreen();
                            long lXB0 = aCol0.GetBlue();
                            long lXR1 = aCol1.GetRed() - lXR0;
                            long lXG1 = aCol1.GetGreen() - lXG0;
                            long lXB1 = aCol1.GetBlue() - lXB0;

                            aCol0.SetRed(
                                static_cast<sal_uInt8>((lXR1 * nTemp + (lXR0 << 10)) >> 10));
                            aCol0.SetGreen(
                                static_cast<sal_uInt8>((lXG1 * nTemp + (lXG0 << 10)) >> 10));
                            aCol0.SetBlue(
                                static_cast<sal_uInt8>((lXB1 * nTemp + (lXB0 << 10)) >> 10));

                            pWriteAcc->SetPixelOnData(pScanline, nX, aCol0);
                        }
                    }
                }

                bRet = true;
            }

            pReadAcc.reset();
            pWriteAcc.reset();

            if (bRet)
            {
                bRet = false;
                const Bitmap aOriginal(aBitmap);
                aBitmap = aNewBmp;
                aNewBmp = Bitmap(Size(nNewWidth, nNewHeight), 24);
                pReadAcc = Bitmap::ScopedReadAccess(aBitmap);
                pWriteAcc = BitmapScopedWriteAccess(aNewBmp);

                if (pReadAcc && pWriteAcc)
                {
                    const long nNewHeight1 = nNewHeight - 1;
                    const long nHeight1 = pReadAcc->Height() - 1;
                    const double fRevScaleY = static_cast<double>(nHeight1) / nNewHeight1;

                    std::unique_ptr<long[]> pLutInt(new long[nNewHeight]);
                    std::unique_ptr<long[]> pLutFrac(new long[nNewHeight]);

                    for (long nY = 0, nTemp = nHeight - 2; nY < nNewHeight; nY++)
                    {
                        double fTemp = nY * fRevScaleY;
                        pLutInt[nY] = MinMax(static_cast<long>(fTemp), 0, nTemp);
                        fTemp -= pLutInt[nY];
                        pLutFrac[nY] = static_cast<long>(fTemp * 1024.);
                    }

                    // after 1st step, bitmap *is* 24bit format (see above)
                    OSL_ENSURE(!pReadAcc->HasPalette(), "OOps, somehow ImplScaleInterpolate "
                                                        "in-between format has palette, should not "
                                                        "happen (!)");

                    for (long nX = 0; nX < nNewWidth; nX++)
                    {
                        if (1 == nHeight)
                        {
                            BitmapColor aCol0 = pReadAcc->GetPixel(0, nX);

                            for (long nY = 0; nY < nNewHeight; nY++)
                            {
                                pWriteAcc->SetPixel(nY, nX, aCol0);
                            }
                        }
                        else
                        {
                            for (long nY = 0; nY < nNewHeight; nY++)
                            {
                                long nTemp = pLutInt[nY];

                                BitmapColor aCol0 = pReadAcc->GetPixel(nTemp++, nX);
                                BitmapColor aCol1 = pReadAcc->GetPixel(nTemp, nX);

                                nTemp = pLutFrac[nY];

                                long lXR0 = aCol0.GetRed();
                                long lXG0 = aCol0.GetGreen();
                                long lXB0 = aCol0.GetBlue();
                                long lXR1 = aCol1.GetRed() - lXR0;
                                long lXG1 = aCol1.GetGreen() - lXG0;
                                long lXB1 = aCol1.GetBlue() - lXB0;

                                aCol0.SetRed(
                                    static_cast<sal_uInt8>((lXR1 * nTemp + (lXR0 << 10)) >> 10));
                                aCol0.SetGreen(
                                    static_cast<sal_uInt8>((lXG1 * nTemp + (lXG0 << 10)) >> 10));
                                aCol0.SetBlue(
                                    static_cast<sal_uInt8>((lXB1 * nTemp + (lXB0 << 10)) >> 10));

                                pWriteAcc->SetPixel(nY, nX, aCol0);
                            }
                        }
                    }

                    bRet = true;
                }

                pReadAcc.reset();
                pWriteAcc.reset();

                if (bRet)
                {
                    aOriginal.AdaptBitCount(aNewBmp);
                    aBitmap = aNewBmp;
                }
            }
        }
    }

    if (!bRet)
    {
        // fallback to fast scale filter
        BitmapEx aBmpEx(aBitmap);
        bRet = BitmapFilter::Filter(aBmpEx, BitmapFastScaleFilter(mfScaleX, mfScaleY));
        aBitmap = aBmpEx.GetBitmap();
    }

    if (bRet)
        return BitmapEx(aBitmap);

    return BitmapEx();
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/gdi/bitmap3.cxx b/vcl/source/gdi/bitmap3.cxx
index b933e41..b42731e 100755
--- a/vcl/source/gdi/bitmap3.cxx
+++ b/vcl/source/gdi/bitmap3.cxx
@@ -32,6 +32,8 @@
#include <impoctree.hxx>
#include <BitmapScaleSuperFilter.hxx>
#include <BitmapScaleConvolutionFilter.hxx>
#include <BitmapFastScaleFilter.hxx>
#include <BitmapInterpolateScaleFilter.hxx>
#include <bitmapwriteaccess.hxx>
#include <octree.hxx>

@@ -761,16 +763,16 @@ bool Bitmap::Scale( const double& rScaleX, const double& rScaleY, BmpScaleFlag n
    switch(nScaleFlag)
    {
        case BmpScaleFlag::Fast:
            bRetval = ImplScaleFast( rScaleX, rScaleY );
            bRetval = BitmapFilter::Filter(aBmpEx, BitmapFastScaleFilter(rScaleX, rScaleY));
            break;

        case BmpScaleFlag::Interpolate:
            bRetval = ImplScaleInterpolate(rScaleX, rScaleY);
            bRetval = BitmapFilter::Filter(aBmpEx, BitmapInterpolateScaleFilter(rScaleX, rScaleY));
            break;

        case BmpScaleFlag::Default:
            if (GetSizePixel().Width() < 2 || GetSizePixel().Height() < 2)
                bRetval = ImplScaleFast(rScaleX, rScaleY);
                bRetval = BitmapFilter::Filter(aBmpEx, BitmapFastScaleFilter(rScaleX, rScaleY));
            else
                bRetval = BitmapFilter::Filter(aBmpEx, BitmapScaleSuperFilter(rScaleX, rScaleY));
            break;
@@ -789,7 +791,7 @@ bool Bitmap::Scale( const double& rScaleX, const double& rScaleY, BmpScaleFlag n
            break;
    }

    if (bRetval && nScaleFlag != BmpScaleFlag::Fast && nScaleFlag != BmpScaleFlag::Interpolate)
    if (bRetval)
        *this = aBmpEx.GetBitmapRef();

    OSL_ENSURE(!bRetval || nStartCount == GetBitCount(), "Bitmap::Scale has changed the ColorDepth, this should *not* happen (!)");
@@ -872,261 +874,6 @@ void Bitmap::AdaptBitCount(Bitmap& rNew) const
    }
}

bool Bitmap::ImplScaleFast( const double& rScaleX, const double& rScaleY )
{
    const Size aSizePix( GetSizePixel() );
    const long nNewWidth = FRound( aSizePix.Width() * rScaleX );
    const long nNewHeight = FRound( aSizePix.Height() * rScaleY );
    bool bRet = false;

    if( nNewWidth && nNewHeight )
    {
        ScopedReadAccess pReadAcc(*this);

        if(pReadAcc)
        {
            Bitmap aNewBmp( Size( nNewWidth, nNewHeight ), GetBitCount(), &pReadAcc->GetPalette() );
            BitmapScopedWriteAccess pWriteAcc(aNewBmp);

            if( pWriteAcc )
            {
                const long nScanlineSize = pWriteAcc->GetScanlineSize();
                const long nNewWidth1 = nNewWidth - 1;
                const long nNewHeight1 = nNewHeight - 1;

                if( nNewWidth1 && nNewHeight1 )
                {
                    const double nWidth = pReadAcc->Width();
                    const double nHeight = pReadAcc->Height();
                    std::unique_ptr<long[]> pLutX(new long[ nNewWidth ]);
                    std::unique_ptr<long[]> pLutY(new long[ nNewHeight ]);

                    for( long nX = 0; nX < nNewWidth; nX++ )
                        pLutX[ nX ] = long(nX * nWidth / nNewWidth);

                    for( long nY = 0; nY < nNewHeight; nY++ )
                        pLutY[ nY ] = long(nY * nHeight / nNewHeight);

                    long nActY = 0;
                    while( nActY < nNewHeight )
                    {
                        long nMapY = pLutY[ nActY ];
                        Scanline pScanline = pWriteAcc->GetScanline(nActY);
                        Scanline pScanlineRead = pReadAcc->GetScanline(nMapY);

                        for( long nX = 0; nX < nNewWidth; nX++ )
                            pWriteAcc->SetPixelOnData( pScanline, nX, pReadAcc->GetPixelFromData( pScanlineRead , pLutX[ nX ] ) );

                        while( ( nActY < nNewHeight1 ) && ( pLutY[ nActY + 1 ] == nMapY ) )
                        {
                            memcpy( pWriteAcc->GetScanline( nActY + 1 ),
                                    pWriteAcc->GetScanline( nActY ), nScanlineSize );
                            nActY++;
                        }
                        nActY++;
                    }

                    bRet = true;
                }

                pWriteAcc.reset();
            }
            pReadAcc.reset();

            if (bRet)
                ReassignWithSize(aNewBmp);
        }
    }

    return bRet;
}

bool Bitmap::ImplScaleInterpolate( const double& rScaleX, const double& rScaleY )
{
    const Size aSizePix( GetSizePixel() );
    const long nNewWidth = FRound( aSizePix.Width() * rScaleX );
    const long nNewHeight = FRound( aSizePix.Height() * rScaleY );
    bool bRet = false;

    if( ( nNewWidth > 1 ) && ( nNewHeight > 1 ) )
    {
        ScopedReadAccess pReadAcc(*this);
        if( pReadAcc )
        {
            long nWidth = pReadAcc->Width();
            long nHeight = pReadAcc->Height();
            Bitmap aNewBmp( Size( nNewWidth, nHeight ), 24 );
            BitmapScopedWriteAccess pWriteAcc(aNewBmp);

            if( pWriteAcc )
            {
                const long nNewWidth1 = nNewWidth - 1;
                const long nWidth1 = pReadAcc->Width() - 1;
                const double fRevScaleX = static_cast<double>(nWidth1) / nNewWidth1;

                std::unique_ptr<long[]> pLutInt(new long[ nNewWidth ]);
                std::unique_ptr<long[]> pLutFrac(new long[ nNewWidth ]);

                for( long nX = 0, nTemp = nWidth - 2; nX < nNewWidth; nX++ )
                {
                    double fTemp = nX * fRevScaleX;
                    pLutInt[ nX ] = MinMax( static_cast<long>(fTemp), 0, nTemp );
                    fTemp -= pLutInt[ nX ];
                    pLutFrac[ nX ] = static_cast<long>( fTemp * 1024. );
                }

                for( long nY = 0; nY < nHeight; nY++ )
                {
                    Scanline pScanlineRead = pReadAcc->GetScanline(nY);
                    if( 1 == nWidth )
                    {
                        BitmapColor aCol0;
                        if( pReadAcc->HasPalette() )
                        {
                            aCol0 = pReadAcc->GetPaletteColor( pReadAcc->GetIndexFromData( pScanlineRead, 0 ) );
                        }
                        else
                        {
                            aCol0 = pReadAcc->GetPixelFromData( pScanlineRead, 0 );
                        }

                        Scanline pScanline = pWriteAcc->GetScanline(nY);
                        for( long nX = 0; nX < nNewWidth; nX++ )
                        {
                            pWriteAcc->SetPixelOnData( pScanline, nX, aCol0 );
                        }
                    }
                    else
                    {
                        Scanline pScanline = pWriteAcc->GetScanline(nY);
                        for( long nX = 0; nX < nNewWidth; nX++ )
                        {
                            long nTemp = pLutInt[ nX ];

                            BitmapColor aCol0, aCol1;
                            if( pReadAcc->HasPalette() )
                            {
                                aCol0 = pReadAcc->GetPaletteColor( pReadAcc->GetIndexFromData( pScanlineRead, nTemp++ ) );
                                aCol1 = pReadAcc->GetPaletteColor( pReadAcc->GetIndexFromData( pScanlineRead, nTemp ) );
                            }
                            else
                            {
                                aCol0 = pReadAcc->GetPixelFromData( pScanlineRead, nTemp++ );
                                aCol1 = pReadAcc->GetPixelFromData( pScanlineRead, nTemp );
                            }

                            nTemp = pLutFrac[ nX ];

                            long lXR0 = aCol0.GetRed();
                            long lXG0 = aCol0.GetGreen();
                            long lXB0 = aCol0.GetBlue();
                            long lXR1 = aCol1.GetRed() - lXR0;
                            long lXG1 = aCol1.GetGreen() - lXG0;
                            long lXB1 = aCol1.GetBlue() - lXB0;

                            aCol0.SetRed( static_cast<sal_uInt8>( ( lXR1 * nTemp + ( lXR0 << 10 ) ) >> 10 ) );
                            aCol0.SetGreen( static_cast<sal_uInt8>( ( lXG1 * nTemp + ( lXG0 << 10 ) ) >> 10 ) );
                            aCol0.SetBlue( static_cast<sal_uInt8>( ( lXB1 * nTemp + ( lXB0 << 10 ) ) >> 10 ) );

                            pWriteAcc->SetPixelOnData( pScanline, nX, aCol0 );
                        }
                    }
                }

                bRet = true;
            }

            pReadAcc.reset();
            pWriteAcc.reset();

            if( bRet )
            {
                bRet = false;
                const Bitmap aOriginal(*this);
                *this = aNewBmp;
                aNewBmp = Bitmap( Size( nNewWidth, nNewHeight ), 24 );
                pReadAcc = ScopedReadAccess(*this);
                pWriteAcc = BitmapScopedWriteAccess(aNewBmp);

                if( pReadAcc && pWriteAcc )
                {
                    const long nNewHeight1 = nNewHeight - 1;
                    const long nHeight1 = pReadAcc->Height() - 1;
                    const double fRevScaleY = static_cast<double>(nHeight1) / nNewHeight1;

                    std::unique_ptr<long[]> pLutInt(new long[ nNewHeight ]);
                    std::unique_ptr<long[]> pLutFrac(new long[ nNewHeight ]);

                    for( long nY = 0, nTemp = nHeight - 2; nY < nNewHeight; nY++ )
                    {
                        double fTemp = nY * fRevScaleY;
                        pLutInt[ nY ] = MinMax( static_cast<long>(fTemp), 0, nTemp );
                        fTemp -= pLutInt[ nY ];
                        pLutFrac[ nY ] = static_cast<long>( fTemp * 1024. );
                    }

                    // after 1st step, bitmap *is* 24bit format (see above)
                    OSL_ENSURE(!pReadAcc->HasPalette(), "OOps, somehow ImplScaleInterpolate in-between format has palette, should not happen (!)");

                    for( long nX = 0; nX < nNewWidth; nX++ )
                    {
                        if( 1 == nHeight )
                        {
                            BitmapColor aCol0 = pReadAcc->GetPixel( 0, nX );

                            for( long nY = 0; nY < nNewHeight; nY++ )
                            {
                                pWriteAcc->SetPixel( nY, nX, aCol0 );
                            }
                        }
                        else
                        {
                            for( long nY = 0; nY < nNewHeight; nY++ )
                            {
                                long nTemp = pLutInt[ nY ];

                                BitmapColor aCol0 = pReadAcc->GetPixel( nTemp++, nX );
                                BitmapColor aCol1 = pReadAcc->GetPixel( nTemp, nX );

                                nTemp = pLutFrac[ nY ];

                                long lXR0 = aCol0.GetRed();
                                long lXG0 = aCol0.GetGreen();
                                long lXB0 = aCol0.GetBlue();
                                long lXR1 = aCol1.GetRed() - lXR0;
                                long lXG1 = aCol1.GetGreen() - lXG0;
                                long lXB1 = aCol1.GetBlue() - lXB0;

                                aCol0.SetRed( static_cast<sal_uInt8>( ( lXR1 * nTemp + ( lXR0 << 10 ) ) >> 10 ) );
                                aCol0.SetGreen( static_cast<sal_uInt8>( ( lXG1 * nTemp + ( lXG0 << 10 ) ) >> 10 ) );
                                aCol0.SetBlue( static_cast<sal_uInt8>( ( lXB1 * nTemp + ( lXB0 << 10 ) ) >> 10 ) );

                                pWriteAcc->SetPixel( nY, nX, aCol0 );
                            }
                        }
                    }

                    bRet = true;
                }

                pReadAcc.reset();
                pWriteAcc.reset();

                if( bRet )
                {
                    aOriginal.AdaptBitCount(aNewBmp);
                    *this = aNewBmp;
                }
            }
        }
    }

    if (!bRet)
        bRet = ImplScaleFast(rScaleX, rScaleY);

    return bRet;
}

bool Bitmap::Dither( BmpDitherFlags nDitherFlags )
{
    bool bRet = false;
diff --git a/vcl/source/gdi/bitmapex.cxx b/vcl/source/gdi/bitmapex.cxx
old mode 100644
new mode 100755
index d107c55..5b03870
--- a/vcl/source/gdi/bitmapex.cxx
+++ b/vcl/source/gdi/bitmapex.cxx
@@ -19,11 +19,10 @@

#include <rtl/crc.h>
#include <rtl/strbuf.hxx>
#include <o3tl/any.hxx>
#include <tools/debug.hxx>
#include <unotools/resmgr.hxx>
#include <tools/stream.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <unotools/resmgr.hxx>

#include <vcl/ImageTree.hxx>
#include <vcl/salbtype.hxx>
@@ -40,9 +39,12 @@
#include <salbmp.hxx>
#include <salinst.hxx>
#include <svdata.hxx>
#include <BitmapFastScaleFilter.hxx>
#include <bitmapwriteaccess.hxx>
#include <image.h>

#include <o3tl/any.hxx>

#include <com/sun/star/beans/XFastPropertySet.hpp>

#include <memory>
@@ -119,7 +121,6 @@ BitmapEx::BitmapEx( const Bitmap& rBmp, const Bitmap& rMask ) :
        meTransparent    ( !rMask ? TransparentType::NONE : TransparentType::Bitmap ),
        mbAlpha          ( false )
{
    SAL_INFO("vcl.gdi", "Bitmap size: " << maBitmapSize);
    // Ensure a mask is exactly one bit deep
    if( !!maMask && maMask.GetBitCount() != 1 )
    {