vcl: move Bitmap{Ex}::ReduceColors() to BitmapColorQuantizationFilter class

Change-Id: I32b58e8d451e7303e94788a546a5b5f9a5bb4590
Reviewed-on: https://gerrit.libreoffice.org/53037
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
diff --git a/cui/source/dialogs/cuigrfflt.cxx b/cui/source/dialogs/cuigrfflt.cxx
index 9f0f60d2..406e2b6 100644
--- a/cui/source/dialogs/cuigrfflt.cxx
+++ b/cui/source/dialogs/cuigrfflt.cxx
@@ -17,6 +17,7 @@
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <vcl/BitmapColorQuantizationFilter.hxx>
#include <vcl/builderfactory.hxx>
#include <sfx2/viewfrm.hxx>
#include <sfx2/viewsh.hxx>
@@ -502,7 +503,7 @@ Graphic GraphicFilterPoster::GetFilteredGraphic( const Graphic& rGraphic, double
    {
        BitmapEx aBmpEx( rGraphic.GetBitmapEx() );

        if( aBmpEx.ReduceColors( nPosterCount ) )
        if (BitmapFilter::Filter(aBmpEx, BitmapColorQuantizationFilter(nPosterCount)))
            aRet = aBmpEx;
    }

diff --git a/include/vcl/BitmapColorQuantizationFilter.hxx b/include/vcl/BitmapColorQuantizationFilter.hxx
new file mode 100644
index 0000000..e695567
--- /dev/null
+++ b/include/vcl/BitmapColorQuantizationFilter.hxx
@@ -0,0 +1,45 @@
/* -*- 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 INCLUDED_INCLUDE_VCL_BITMAPCOLORQUANTIZATIONFILTER_HXX
#define INCLUDED_INCLUDE_VCL_BITMAPCOLORQUANTIZATIONFILTER_HXX

#include <tools/color.hxx>

#include <vcl/BitmapFilter.hxx>

class VCL_DLLPUBLIC BitmapColorQuantizationFilter : public BitmapFilter
{
public:
    /** Reduce number of colors for the bitmap using the POPULAR algorithm

        @param nNewColorCount
        Maximal number of bitmap colors after the reduce operation
     */
    BitmapColorQuantizationFilter(sal_uInt16 nNewColorCount)
        : mnNewColorCount(nNewColorCount)
    {
    }

    virtual BitmapEx execute(BitmapEx const& rBitmapEx) override;

private:
    sal_uInt16 mnNewColorCount;

    struct PopularColorCount
    {
        sal_uInt32 mnIndex;
        sal_uInt32 mnCount;
    };
};

#endif

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/vcl/BitmapMedianColorQuantizationFilter.hxx b/include/vcl/BitmapMedianColorQuantizationFilter.hxx
new file mode 100644
index 0000000..c15b0ac
--- /dev/null
+++ b/include/vcl/BitmapMedianColorQuantizationFilter.hxx
@@ -0,0 +1,49 @@
/* -*- 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 INCLUDED_INCLUDE_VCL_BITMAPMEDIANCOLORQUANTIZATIONFILTER_HXX
#define INCLUDED_INCLUDE_VCL_BITMAPMEDIANCOLORQUANTIZATIONFILTER_HXX

#include <tools/color.hxx>

#include <vcl/BitmapFilter.hxx>

#define RGB15(_def_cR, _def_cG, _def_cB)                                                           \
    ((static_cast<sal_uLong>(_def_cR) << 10) | (static_cast<sal_uLong>(_def_cG) << 5)              \
     | static_cast<sal_uLong>(_def_cB))
#define GAMMA(_def_cVal, _def_InvGamma)                                                            \
    (static_cast<sal_uInt8>(MinMax(FRound(pow(_def_cVal / 255.0, _def_InvGamma) * 255.0), 0, 255)))

class VCL_DLLPUBLIC BitmapMedianColorQuantizationFilter : public BitmapFilter
{
public:
    /** Reduce number of colors for the bitmap using the median algorithm

        @param nNewColorCount
        Maximal number of bitmap colors after the reduce operation
     */
    BitmapMedianColorQuantizationFilter(sal_uInt16 nNewColorCount)
        : mnNewColorCount(nNewColorCount)
    {
    }

    virtual BitmapEx execute(BitmapEx const& rBitmapEx) override;

private:
    sal_uInt16 mnNewColorCount;

    void medianCut(Bitmap& rBitmap, sal_uLong* pColBuf, BitmapPalette& rPal, long nR1, long nR2,
                   long nG1, long nG2, long nB1, long nB2, long nColors, long nPixels,
                   long& rIndex);
};

#endif

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/vcl/BitmapSimpleColorQuantizationFilter.hxx b/include/vcl/BitmapSimpleColorQuantizationFilter.hxx
new file mode 100644
index 0000000..b05b13f
--- /dev/null
+++ b/include/vcl/BitmapSimpleColorQuantizationFilter.hxx
@@ -0,0 +1,39 @@
/* -*- 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 INCLUDED_INCLUDE_VCL_BITMAPSIMPLECOLORQUANTIZATIONFILTER_HXX
#define INCLUDED_INCLUDE_VCL_BITMAPSIMPLECOLORQUANTIZATIONFILTER_HXX

#include <tools/color.hxx>

#include <vcl/BitmapFilter.hxx>

class VCL_DLLPUBLIC BitmapSimpleColorQuantizationFilter : public BitmapFilter
{
public:
    /** Reduce number of colors for the bitmap using the POPULAR algorithm

        @param nNewColorCount
        Maximal number of bitmap colors after the reduce operation
     */
    BitmapSimpleColorQuantizationFilter(sal_uInt16 nNewColorCount)
        : mnNewColorCount(nNewColorCount)
    {
    }

    virtual BitmapEx execute(BitmapEx const& rBitmapEx) override;

private:
    sal_uInt16 mnNewColorCount;
};

#endif

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/vcl/bitmap.hxx b/include/vcl/bitmap.hxx
index 6370a63..ad1efb8 100644
--- a/include/vcl/bitmap.hxx
+++ b/include/vcl/bitmap.hxx
@@ -89,12 +89,6 @@ enum class BmpCombine
    Or, And
};

enum BmpReduce
{
    BMP_REDUCE_SIMPLE = 0,
    BMP_REDUCE_POPULAR = 1
};

enum class BmpFilter
{
    Smooth = 0,
@@ -285,20 +279,6 @@ public:
    */
    bool                    MakeMonochrome(sal_uInt8 cThreshold);

    /** Reduce number of colors for the bitmap

        @param nNewColorCount
        Maximal number of bitmap colors after the reduce operation

        @param eReduce
        Algorithm to use for color reduction

        @return true the color reduction operation was completed successfully.
     */
    bool                    ReduceColors(
                                sal_uInt16 nNewColorCount,
                                BmpReduce eReduce = BMP_REDUCE_SIMPLE );

    /** Apply a dither algorithm to the bitmap

        This method dithers the bitmap inplace, i.e. a true color
@@ -680,14 +660,6 @@ public:
    SAL_DLLPRIVATE bool     ImplDitherMatrix();
    SAL_DLLPRIVATE bool     ImplDitherFloyd();
    SAL_DLLPRIVATE bool     ImplDitherFloyd16();
    SAL_DLLPRIVATE bool     ImplReduceSimple( sal_uInt16 nColorCount );
    SAL_DLLPRIVATE bool     ImplReducePopular( sal_uInt16 nColorCount );
    SAL_DLLPRIVATE bool     ImplReduceMedian( sal_uInt16 nColorCount );
    SAL_DLLPRIVATE void     ImplMedianCut(
                                sal_uLong* pColBuf,
                                BitmapPalette& rPal,
                                long nR1, long nR2, long nG1, long nG2, long nB1, long nB2,
                                long nColors, long nPixels, long& rIndex );

    SAL_DLLPRIVATE bool     ImplConvolute3( const long* pMatrix );

diff --git a/include/vcl/bitmapex.hxx b/include/vcl/bitmapex.hxx
index 41b1fb2..390a2c0 100644
--- a/include/vcl/bitmapex.hxx
+++ b/include/vcl/bitmapex.hxx
@@ -109,15 +109,6 @@ public:
     */
    bool                Convert( BmpConversion eConversion );

    /** Reduce number of colors for the bitmap using the POPULAR algorithm

        @param nNewColorCount
        Maximal number of bitmap colors after the reduce operation

        @return true, if the color reduction operation was completed successfully.
     */
    bool                ReduceColors( sal_uInt16 nNewColorCount );

    /** Apply a dither algorithm to the bitmap

        This method dithers the bitmap inplace, i.e. a true color
diff --git a/sd/source/ui/dlg/vectdlg.cxx b/sd/source/ui/dlg/vectdlg.cxx
index 2c64760..44ed925 100644
--- a/sd/source/ui/dlg/vectdlg.cxx
+++ b/sd/source/ui/dlg/vectdlg.cxx
@@ -19,13 +19,14 @@

#include <vcl/vclenum.hxx>
#include <vcl/wrkwin.hxx>
#include <vcl/bitmapaccess.hxx>
#include <vcl/metaact.hxx>
#include <vcl/BitmapSimpleColorQuantizationFilter.hxx>

#include <DrawDocShell.hxx>
#include <sdmod.hxx>
#include <sdiocmpt.hxx>
#include <vectdlg.hxx>
#include <vcl/bitmapaccess.hxx>
#include <vcl/metaact.hxx>

#define VECTORIZE_MAX_EXTENT 512

@@ -140,7 +141,9 @@ Bitmap SdVectorizeDlg::GetPreparedBitmap( Bitmap const & rBmp, Fraction& rScale 
    else
        rScale = Fraction( 1, 1 );

    aNew.ReduceColors( static_cast<sal_uInt16>(m_pNmLayers->GetValue()) );
    BitmapEx aNewBmpEx(aNew);
    BitmapFilter::Filter(aNewBmpEx, BitmapSimpleColorQuantizationFilter(static_cast<sal_uInt16>(m_pNmLayers->GetValue())));
    aNew = aNewBmpEx.GetBitmap();

    return aNew;
}
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk
index 03c705a..cfd5da3 100644
--- a/vcl/Library_vcl.mk
+++ b/vcl/Library_vcl.mk
@@ -322,6 +322,9 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\
    vcl/source/bitmap/BitmapScaleSuperFilter \
    vcl/source/bitmap/BitmapScaleConvolutionFilter \
    vcl/source/bitmap/BitmapSymmetryCheck \
    vcl/source/bitmap/BitmapColorQuantizationFilter \
    vcl/source/bitmap/BitmapSimpleColorQuantizationFilter \
    vcl/source/bitmap/BitmapMedianColorQuantizationFilter \
    vcl/source/bitmap/BitmapTools \
    vcl/source/bitmap/checksum \
    vcl/source/image/Image \
diff --git a/vcl/source/bitmap/BitmapColorQuantizationFilter.cxx b/vcl/source/bitmap/BitmapColorQuantizationFilter.cxx
new file mode 100644
index 0000000..13e8834
--- /dev/null
+++ b/vcl/source/bitmap/BitmapColorQuantizationFilter.cxx
@@ -0,0 +1,229 @@
/* -*- 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/.
 *
 */

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

#include <bitmapwriteaccess.hxx>

#include <cstdlib>

BitmapEx BitmapColorQuantizationFilter::execute(BitmapEx const& aBitmapEx)
{
    Bitmap aBitmap = aBitmapEx.GetBitmap();

    bool bRet = false;

    if (aBitmap.GetColorCount() <= static_cast<sal_uLong>(mnNewColorCount))
    {
        bRet = true;
    }
    else
    {
        Bitmap::ScopedReadAccess pRAcc(aBitmap);
        sal_uInt16 nBitCount;

        if (mnNewColorCount > 256)
            mnNewColorCount = 256;

        if (mnNewColorCount < 17)
            nBitCount = 4;
        else
            nBitCount = 8;

        if (pRAcc)
        {
            const sal_uInt32 nValidBits = 4;
            const sal_uInt32 nRightShiftBits = 8 - nValidBits;
            const sal_uInt32 nLeftShiftBits1 = nValidBits;
            const sal_uInt32 nLeftShiftBits2 = nValidBits << 1;
            const sal_uInt32 nColorsPerComponent = 1 << nValidBits;
            const sal_uInt32 nColorOffset = 256 / nColorsPerComponent;
            const sal_uInt32 nTotalColors
                = nColorsPerComponent * nColorsPerComponent * nColorsPerComponent;
            const long nWidth = pRAcc->Width();
            const long nHeight = pRAcc->Height();
            std::unique_ptr<PopularColorCount[]> pCountTable(new PopularColorCount[nTotalColors]);

            memset(pCountTable.get(), 0, nTotalColors * sizeof(PopularColorCount));

            for (long nR = 0, nIndex = 0; nR < 256; nR += nColorOffset)
            {
                for (long nG = 0; nG < 256; nG += nColorOffset)
                {
                    for (long nB = 0; nB < 256; nB += nColorOffset)
                    {
                        pCountTable[nIndex].mnIndex = nIndex;
                        nIndex++;
                    }
                }
            }

            if (pRAcc->HasPalette())
            {
                for (long nY = 0; nY < nHeight; nY++)
                {
                    Scanline pScanlineRead = pRAcc->GetScanline(nY);
                    for (long nX = 0; nX < nWidth; nX++)
                    {
                        const BitmapColor& rCol
                            = pRAcc->GetPaletteColor(pRAcc->GetIndexFromData(pScanlineRead, nX));
                        pCountTable[((static_cast<sal_uInt32>(rCol.GetRed()) >> nRightShiftBits)
                                     << nLeftShiftBits2)
                                    | ((static_cast<sal_uInt32>(rCol.GetGreen()) >> nRightShiftBits)
                                       << nLeftShiftBits1)
                                    | (static_cast<sal_uInt32>(rCol.GetBlue()) >> nRightShiftBits)]
                            .mnCount++;
                    }
                }
            }
            else
            {
                for (long nY = 0; nY < nHeight; nY++)
                {
                    Scanline pScanlineRead = pRAcc->GetScanline(nY);
                    for (long nX = 0; nX < nWidth; nX++)
                    {
                        const BitmapColor aCol(pRAcc->GetPixelFromData(pScanlineRead, nX));
                        pCountTable[((static_cast<sal_uInt32>(aCol.GetRed()) >> nRightShiftBits)
                                     << nLeftShiftBits2)
                                    | ((static_cast<sal_uInt32>(aCol.GetGreen()) >> nRightShiftBits)
                                       << nLeftShiftBits1)
                                    | (static_cast<sal_uInt32>(aCol.GetBlue()) >> nRightShiftBits)]
                            .mnCount++;
                    }
                }
            }

            BitmapPalette aNewPal(mnNewColorCount);

            std::qsort(pCountTable.get(), nTotalColors, sizeof(PopularColorCount),
                       [](const void* p1, const void* p2) {
                           int nRet;

                           if (static_cast<PopularColorCount const*>(p1)->mnCount
                               < static_cast<PopularColorCount const*>(p2)->mnCount)
                               nRet = 1;
                           else if (static_cast<PopularColorCount const*>(p1)->mnCount
                                    == static_cast<PopularColorCount const*>(p2)->mnCount)
                               nRet = 0;
                           else
                               nRet = -1;

                           return nRet;
                       });

            for (sal_uInt16 n = 0; n < mnNewColorCount; n++)
            {
                const PopularColorCount& rPop = pCountTable[n];
                aNewPal[n] = BitmapColor(
                    static_cast<sal_uInt8>((rPop.mnIndex >> nLeftShiftBits2) << nRightShiftBits),
                    static_cast<sal_uInt8>(
                        ((rPop.mnIndex >> nLeftShiftBits1) & (nColorsPerComponent - 1))
                        << nRightShiftBits),
                    static_cast<sal_uInt8>((rPop.mnIndex & (nColorsPerComponent - 1))
                                           << nRightShiftBits));
            }

            Bitmap aNewBmp(aBitmap.GetSizePixel(), nBitCount, &aNewPal);
            BitmapScopedWriteAccess pWAcc(aNewBmp);

            if (pWAcc)
            {
                BitmapColor aDstCol(sal_uInt8(0));
                std::unique_ptr<sal_uInt8[]> pIndexMap(new sal_uInt8[nTotalColors]);

                for (long nR = 0, nIndex = 0; nR < 256; nR += nColorOffset)
                {
                    for (long nG = 0; nG < 256; nG += nColorOffset)
                    {
                        for (long nB = 0; nB < 256; nB += nColorOffset)
                        {
                            pIndexMap[nIndex++] = static_cast<sal_uInt8>(aNewPal.GetBestIndex(
                                BitmapColor(static_cast<sal_uInt8>(nR), static_cast<sal_uInt8>(nG),
                                            static_cast<sal_uInt8>(nB))));
                        }
                    }
                }

                if (pRAcc->HasPalette())
                {
                    for (long nY = 0; nY < nHeight; nY++)
                    {
                        Scanline pScanline = pWAcc->GetScanline(nY);
                        Scanline pScanlineRead = pRAcc->GetScanline(nY);
                        for (long nX = 0; nX < nWidth; nX++)
                        {
                            const BitmapColor& rCol = pRAcc->GetPaletteColor(
                                pRAcc->GetIndexFromData(pScanlineRead, nX));
                            aDstCol.SetIndex(pIndexMap[((static_cast<sal_uInt32>(rCol.GetRed())
                                                         >> nRightShiftBits)
                                                        << nLeftShiftBits2)
                                                       | ((static_cast<sal_uInt32>(rCol.GetGreen())
                                                           >> nRightShiftBits)
                                                          << nLeftShiftBits1)
                                                       | (static_cast<sal_uInt32>(rCol.GetBlue())
                                                          >> nRightShiftBits)]);
                            pWAcc->SetPixelOnData(pScanline, nX, aDstCol);
                        }
                    }
                }
                else
                {
                    for (long nY = 0; nY < nHeight; nY++)
                    {
                        Scanline pScanline = pWAcc->GetScanline(nY);
                        Scanline pScanlineRead = pRAcc->GetScanline(nY);

                        for (long nX = 0; nX < nWidth; nX++)
                        {
                            const BitmapColor aCol(pRAcc->GetPixelFromData(pScanlineRead, nX));
                            aDstCol.SetIndex(pIndexMap[((static_cast<sal_uInt32>(aCol.GetRed())
                                                         >> nRightShiftBits)
                                                        << nLeftShiftBits2)
                                                       | ((static_cast<sal_uInt32>(aCol.GetGreen())
                                                           >> nRightShiftBits)
                                                          << nLeftShiftBits1)
                                                       | (static_cast<sal_uInt32>(aCol.GetBlue())
                                                          >> nRightShiftBits)]);
                            pWAcc->SetPixelOnData(pScanline, nX, aDstCol);
                        }
                    }
                }

                pWAcc.reset();
                bRet = true;
            }

            pCountTable.reset();
            pRAcc.reset();

            if (bRet)
            {
                const MapMode aMap(aBitmap.GetPrefMapMode());
                const Size aSize(aBitmap.GetPrefSize());

                aBitmap = aNewBmp;

                aBitmap.SetPrefMapMode(aMap);
                aBitmap.SetPrefSize(aSize);
            }
        }
    }

    if (bRet)
        return BitmapEx(aBitmap);

    return BitmapEx();
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/BitmapMedianColorQuantizationFilter.cxx b/vcl/source/bitmap/BitmapMedianColorQuantizationFilter.cxx
new file mode 100644
index 0000000..807e8a0
--- /dev/null
+++ b/vcl/source/bitmap/BitmapMedianColorQuantizationFilter.cxx
@@ -0,0 +1,300 @@
/* -*- 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/.
 *
 */

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

#include <bitmapwriteaccess.hxx>
#include <impoctree.hxx>

#include <cstdlib>

BitmapEx BitmapMedianColorQuantizationFilter::execute(BitmapEx const& aBitmapEx)
{
    Bitmap aBitmap = aBitmapEx.GetBitmap();

    bool bRet = false;

    if (aBitmap.GetColorCount() <= static_cast<sal_uLong>(mnNewColorCount))
    {
        bRet = true;
    }
    else
    {
        Bitmap::ScopedReadAccess pRAcc(aBitmap);
        sal_uInt16 nBitCount;

        if (mnNewColorCount < 17)
        {
            nBitCount = 4;
        }
        else if (mnNewColorCount < 257)
        {
            nBitCount = 8;
        }
        else
        {
            OSL_FAIL("Bitmap::ImplReduceMedian(): invalid color count!");
            nBitCount = 8;
            mnNewColorCount = 256;
        }

        if (pRAcc)
        {
            Bitmap aNewBmp(aBitmap.GetSizePixel(), nBitCount);
            BitmapScopedWriteAccess pWAcc(aNewBmp);

            if (pWAcc)
            {
                const sal_uLong nSize = 32768 * sizeof(sal_uLong);
                sal_uLong* pColBuf = static_cast<sal_uLong*>(rtl_allocateMemory(nSize));
                const long nWidth = pWAcc->Width();
                const long nHeight = pWAcc->Height();
                long nIndex = 0;

                memset(pColBuf, 0, nSize);

                // create Buffer
                if (pRAcc->HasPalette())
                {
                    for (long nY = 0; nY < nHeight; nY++)
                    {
                        Scanline pScanlineRead = pRAcc->GetScanline(nY);
                        for (long nX = 0; nX < nWidth; nX++)
                        {
                            const BitmapColor& rCol = pRAcc->GetPaletteColor(
                                pRAcc->GetIndexFromData(pScanlineRead, nX));

                            pColBuf[RGB15(rCol.GetRed() >> 3, rCol.GetGreen() >> 3,
                                          rCol.GetBlue() >> 3)]++;
                        }
                    }
                }
                else
                {
                    for (long nY = 0; nY < nHeight; nY++)
                    {
                        Scanline pScanlineRead = pRAcc->GetScanline(nY);
                        for (long nX = 0; nX < nWidth; nX++)
                        {
                            const BitmapColor aCol(pRAcc->GetPixelFromData(pScanlineRead, nX));
                            pColBuf[RGB15(aCol.GetRed() >> 3, aCol.GetGreen() >> 3,
                                          aCol.GetBlue() >> 3)]++;
                        }
                    }
                }

                // create palette via median cut
                BitmapPalette aPal(pWAcc->GetPaletteEntryCount());
                medianCut(aBitmap, pColBuf, aPal, 0, 31, 0, 31, 0, 31, mnNewColorCount,
                          nWidth * nHeight, nIndex);

                // do mapping of colors to palette
                InverseColorMap aMap(aPal);
                pWAcc->SetPalette(aPal);
                for (long nY = 0; nY < nHeight; nY++)
                {
                    Scanline pScanline = pWAcc->GetScanline(nY);
                    for (long nX = 0; nX < nWidth; nX++)
                    {
                        pWAcc->SetPixelOnData(
                            pScanline, nX,
                            BitmapColor(static_cast<sal_uInt8>(
                                aMap.GetBestPaletteIndex(pRAcc->GetColor(nY, nX)))));
                    }
                }

                rtl_freeMemory(pColBuf);
                pWAcc.reset();
                bRet = true;
            }

            pRAcc.reset();
            if (bRet)
            {
                const MapMode aMap(aBitmap.GetPrefMapMode());
                const Size aSize(aBitmap.GetPrefSize());

                aBitmap = aNewBmp;

                aBitmap.SetPrefMapMode(aMap);
                aBitmap.SetPrefSize(aSize);
            }
        }
    }

    if (bRet)
        return BitmapEx(aBitmap);

    return BitmapEx();
}

void BitmapMedianColorQuantizationFilter::medianCut(Bitmap& rBitmap, sal_uLong* pColBuf,
                                                    BitmapPalette& rPal, long nR1, long nR2,
                                                    long nG1, long nG2, long nB1, long nB2,
                                                    long nColors, long nPixels, long& rIndex)
{
    if (!nPixels)
        return;

    BitmapColor aCol;
    const long nRLen = nR2 - nR1;
    const long nGLen = nG2 - nG1;
    const long nBLen = nB2 - nB1;
    sal_uLong* pBuf = pColBuf;

    if (!nRLen && !nGLen && !nBLen)
    {
        if (pBuf[RGB15(nR1, nG1, nB1)])
        {
            aCol.SetRed(static_cast<sal_uInt8>(nR1 << 3));
            aCol.SetGreen(static_cast<sal_uInt8>(nG1 << 3));
            aCol.SetBlue(static_cast<sal_uInt8>(nB1 << 3));
            rPal[static_cast<sal_uInt16>(rIndex++)] = aCol;
        }
    }
    else
    {
        if (nColors == 1 || nPixels == 1)
        {
            long nPixSum = 0, nRSum = 0, nGSum = 0, nBSum = 0;

            for (long nR = nR1; nR <= nR2; nR++)
            {
                for (long nG = nG1; nG <= nG2; nG++)
                {
                    for (long nB = nB1; nB <= nB2; nB++)
                    {
                        nPixSum = pBuf[RGB15(nR, nG, nB)];

                        if (nPixSum)
                        {
                            nRSum += nR * nPixSum;
                            nGSum += nG * nPixSum;
                            nBSum += nB * nPixSum;
                        }
                    }
                }
            }

            aCol.SetRed(static_cast<sal_uInt8>((nRSum / nPixels) << 3));
            aCol.SetGreen(static_cast<sal_uInt8>((nGSum / nPixels) << 3));
            aCol.SetBlue(static_cast<sal_uInt8>((nBSum / nPixels) << 3));
            rPal[static_cast<sal_uInt16>(rIndex++)] = aCol;
        }
        else
        {
            const long nTest = (nPixels >> 1);
            long nPixOld = 0;
            long nPixNew = 0;

            if (nBLen > nGLen && nBLen > nRLen)
            {
                long nB = nB1 - 1;

                while (nPixNew < nTest)
                {
                    nB++;
                    nPixOld = nPixNew;
                    for (long nR = nR1; nR <= nR2; nR++)
                    {
                        for (long nG = nG1; nG <= nG2; nG++)
                        {
                            nPixNew += pBuf[RGB15(nR, nG, nB)];
                        }
                    }
                }

                if (nB < nB2)
                {
                    medianCut(rBitmap, pBuf, rPal, nR1, nR2, nG1, nG2, nB1, nB, nColors >> 1,
                              nPixNew, rIndex);
                    medianCut(rBitmap, pBuf, rPal, nR1, nR2, nG1, nG2, nB + 1, nB2, nColors >> 1,
                              nPixels - nPixNew, rIndex);
                }
                else
                {
                    medianCut(rBitmap, pBuf, rPal, nR1, nR2, nG1, nG2, nB1, nB - 1, nColors >> 1,
                              nPixOld, rIndex);
                    medianCut(rBitmap, pBuf, rPal, nR1, nR2, nG1, nG2, nB, nB2, nColors >> 1,
                              nPixels - nPixOld, rIndex);
                }
            }
            else if (nGLen > nRLen)
            {
                long nG = nG1 - 1;

                while (nPixNew < nTest)
                {
                    nG++;
                    nPixOld = nPixNew;
                    for (long nR = nR1; nR <= nR2; nR++)
                    {
                        for (long nB = nB1; nB <= nB2; nB++)
                        {
                            nPixNew += pBuf[RGB15(nR, nG, nB)];
                        }
                    }
                }

                if (nG < nG2)
                {
                    medianCut(rBitmap, pBuf, rPal, nR1, nR2, nG1, nG, nB1, nB2, nColors >> 1,
                              nPixNew, rIndex);
                    medianCut(rBitmap, pBuf, rPal, nR1, nR2, nG + 1, nG2, nB1, nB2, nColors >> 1,
                              nPixels - nPixNew, rIndex);
                }
                else
                {
                    medianCut(rBitmap, pBuf, rPal, nR1, nR2, nG1, nG - 1, nB1, nB2, nColors >> 1,
                              nPixOld, rIndex);
                    medianCut(rBitmap, pBuf, rPal, nR1, nR2, nG, nG2, nB1, nB2, nColors >> 1,
                              nPixels - nPixOld, rIndex);
                }
            }
            else
            {
                long nR = nR1 - 1;

                while (nPixNew < nTest)
                {
                    nR++;
                    nPixOld = nPixNew;
                    for (long nG = nG1; nG <= nG2; nG++)
                    {
                        for (long nB = nB1; nB <= nB2; nB++)
                        {
                            nPixNew += pBuf[RGB15(nR, nG, nB)];
                        }
                    }
                }

                if (nR < nR2)
                {
                    medianCut(rBitmap, pBuf, rPal, nR1, nR, nG1, nG2, nB1, nB2, nColors >> 1,
                              nPixNew, rIndex);
                    medianCut(rBitmap, pBuf, rPal, nR1 + 1, nR2, nG1, nG2, nB1, nB2, nColors >> 1,
                              nPixels - nPixNew, rIndex);
                }
                else
                {
                    medianCut(rBitmap, pBuf, rPal, nR1, nR - 1, nG1, nG2, nB1, nB2, nColors >> 1,
                              nPixOld, rIndex);
                    medianCut(rBitmap, pBuf, rPal, nR, nR2, nG1, nG2, nB1, nB2, nColors >> 1,
                              nPixels - nPixOld, rIndex);
                }
            }
        }
    }
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/bitmap/BitmapSimpleColorQuantizationFilter.cxx b/vcl/source/bitmap/BitmapSimpleColorQuantizationFilter.cxx
new file mode 100644
index 0000000..3ffade5
--- /dev/null
+++ b/vcl/source/bitmap/BitmapSimpleColorQuantizationFilter.cxx
@@ -0,0 +1,108 @@
/* -*- 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/.
 *
 */

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

#include <bitmapwriteaccess.hxx>
#include <impoctree.hxx>

#include <cstdlib>

BitmapEx BitmapSimpleColorQuantizationFilter::execute(BitmapEx const& aBitmapEx)
{
    Bitmap aBitmap = aBitmapEx.GetBitmap();

    bool bRet = false;

    if (aBitmap.GetColorCount() <= static_cast<sal_uLong>(mnNewColorCount))
    {
        bRet = true;
    }
    else
    {
        Bitmap aNewBmp;
        Bitmap::ScopedReadAccess pRAcc(aBitmap);
        const sal_uInt16 nColorCount = std::min(nColorCount, sal_uInt16(256));
        sal_uInt16 nBitCount = 0;

        if (pRAcc)
        {
            Octree aOct(*pRAcc, nColorCount);
            const BitmapPalette& rPal = aOct.GetPalette();

            aNewBmp = Bitmap(aBitmap.GetSizePixel(), nBitCount, &rPal);
            BitmapScopedWriteAccess pWAcc(aNewBmp);

            if (pWAcc)
            {
                const long nWidth = pRAcc->Width();
                const long nHeight = pRAcc->Height();

                if (pRAcc->HasPalette())
                {
                    for (long nY = 0; nY < nHeight; nY++)
                    {
                        Scanline pScanline = pWAcc->GetScanline(nY);
                        Scanline pScanlineRead = pRAcc->GetScanline(nY);
                        for (long nX = 0; nX < nWidth; nX++)
                        {
                            auto c = pRAcc->GetPaletteColor(
                                pRAcc->GetIndexFromData(pScanlineRead, nX));
                            pWAcc->SetPixelOnData(
                                pScanline, nX,
                                BitmapColor(static_cast<sal_uInt8>(aOct.GetBestPaletteIndex(c))));
                        }
                    }
                }
                else
                {
                    for (long nY = 0; nY < nHeight; nY++)
                    {
                        Scanline pScanline = pWAcc->GetScanline(nY);
                        Scanline pScanlineRead = pRAcc->GetScanline(nY);
                        for (long nX = 0; nX < nWidth; nX++)
                        {
                            auto c = pRAcc->GetPixelFromData(pScanlineRead, nX);
                            pWAcc->SetPixelOnData(
                                pScanline, nX,
                                BitmapColor(static_cast<sal_uInt8>(aOct.GetBestPaletteIndex(c))));
                        }
                    }
                }

                pWAcc.reset();
                bRet = true;
            }

            pRAcc.reset();
        }

        if (bRet)
        {
            const MapMode aMap(aBitmap.GetPrefMapMode());
            const Size aSize(aBitmap.GetPrefSize());

            aBitmap = aNewBmp;

            aBitmap.SetPrefMapMode(aMap);
            aBitmap.SetPrefSize(aSize);
        }
    }

    if (bRet)
        return BitmapEx(aBitmap);

    return BitmapEx();
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/gdi/animate.cxx b/vcl/source/gdi/animate.cxx
index d3c5031..5adfc6c 100644
--- a/vcl/source/gdi/animate.cxx
+++ b/vcl/source/gdi/animate.cxx
@@ -17,12 +17,14 @@
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <vcl/animate.hxx>
#include <tools/stream.hxx>
#include <rtl/crc.h>

#include <vcl/animate.hxx>
#include <vcl/virdev.hxx>
#include <vcl/window.hxx>
#include <vcl/dibtools.hxx>
#include <vcl/BitmapColorQuantizationFilter.hxx>

#include <impanmvw.hxx>

@@ -527,13 +529,17 @@ bool Animation::ReduceColors( sal_uInt16 nNewColorCount )
    {
        bRet = true;

        for( size_t i = 0, n = maList.size(); ( i < n ) && bRet; ++i )
            bRet = maList[ i ]->aBmpEx.ReduceColors( nNewColorCount );
        for (size_t i = 0, n = maList.size(); (i < n) && bRet; ++i)
        {
            bRet = BitmapFilter::Filter(maList[i]->aBmpEx, BitmapColorQuantizationFilter(nNewColorCount));
        }

        maBitmapEx.ReduceColors( nNewColorCount );
        BitmapFilter::Filter(maBitmapEx, BitmapColorQuantizationFilter(nNewColorCount));
    }
    else
    {
        bRet = false;
    }

    return bRet;
}
diff --git a/vcl/source/gdi/bitmap3.cxx b/vcl/source/gdi/bitmap3.cxx
index 3bf068c..8503db8 100644
--- a/vcl/source/gdi/bitmap3.cxx
+++ b/vcl/source/gdi/bitmap3.cxx
@@ -43,7 +43,6 @@

#include <memory>

#define RGB15( _def_cR, _def_cG, _def_cB )  ((static_cast<sal_uLong>(_def_cR)<<10)|(static_cast<sal_uLong>(_def_cG)<<5)|static_cast<sal_uLong>(_def_cB))
#define GAMMA( _def_cVal, _def_InvGamma )   (static_cast<sal_uInt8>(MinMax(FRound(pow( _def_cVal/255.0,_def_InvGamma)*255.0),0,255)))

#define CALC_ERRORS                                                             \
@@ -1209,498 +1208,6 @@ bool Bitmap::ImplDitherFloyd16()
    return bRet;
}

bool Bitmap::ReduceColors( sal_uInt16 nColorCount, BmpReduce eReduce )
{
    bool bRet;

    if( GetColorCount() <= static_cast<sal_uLong>(nColorCount) )
        bRet = true;
    else if( nColorCount )
    {
        if( BMP_REDUCE_SIMPLE == eReduce )
            bRet = ImplReduceSimple( nColorCount );
        else if( BMP_REDUCE_POPULAR == eReduce )
            bRet = ImplReducePopular( nColorCount );
        else
            bRet = ImplReduceMedian( nColorCount );
    }
    else
        bRet = false;

    return bRet;
}

bool Bitmap::ImplReduceSimple( sal_uInt16 nColorCount )
{
    Bitmap aNewBmp;
    ScopedReadAccess pRAcc(*this);
    const sal_uInt16 nColCount = std::min( nColorCount, sal_uInt16(256) );
    sal_uInt16 nBitCount;
    bool bRet = false;

    if( nColCount <= 2 )
        nBitCount = 1;
    else if( nColCount <= 16 )
        nBitCount = 4;
    else
        nBitCount = 8;

    if( pRAcc )
    {
        Octree aOct( *pRAcc, nColCount );
        const BitmapPalette& rPal = aOct.GetPalette();

        aNewBmp = Bitmap( GetSizePixel(), nBitCount, &rPal );
        BitmapScopedWriteAccess pWAcc(aNewBmp);

        if( pWAcc )
        {
            const long nWidth = pRAcc->Width();
            const long nHeight = pRAcc->Height();

            if( pRAcc->HasPalette() )
            {
                for( long nY = 0; nY < nHeight; nY++ )
                {
                    Scanline pScanline = pWAcc->GetScanline(nY);
                    Scanline pScanlineRead = pRAcc->GetScanline(nY);
                    for( long nX =0; nX < nWidth; nX++ )
                    {
                        auto c = pRAcc->GetPaletteColor( pRAcc->GetIndexFromData( pScanlineRead, nX ) );
                        pWAcc->SetPixelOnData( pScanline, nX, BitmapColor(static_cast<sal_uInt8>(aOct.GetBestPaletteIndex( c ))) );
                    }
                }
            }
            else
            {
                for( long nY = 0; nY < nHeight; nY++ )
                {
                    Scanline pScanline = pWAcc->GetScanline(nY);
                    Scanline pScanlineRead = pRAcc->GetScanline(nY);
                    for( long nX =0; nX < nWidth; nX++ )
                    {
                        auto c = pRAcc->GetPixelFromData( pScanlineRead, nX );
                        pWAcc->SetPixelOnData( pScanline, nX, BitmapColor(static_cast<sal_uInt8>(aOct.GetBestPaletteIndex( c ))) );
                    }
                }
            }

            pWAcc.reset();
            bRet = true;
        }

        pRAcc.reset();
    }

    if( bRet )
    {
        const MapMode aMap( maPrefMapMode );
        const Size aSize( maPrefSize );

        *this = aNewBmp;
        maPrefMapMode = aMap;
        maPrefSize = aSize;
    }

    return bRet;
}

struct PopularColorCount
{
    sal_uInt32 mnIndex;
    sal_uInt32 mnCount;
};

extern "C" int ImplPopularCmpFnc( const void* p1, const void* p2 )
{
    int nRet;

    if( static_cast<PopularColorCount const *>(p1)->mnCount < static_cast<PopularColorCount const *>(p2)->mnCount )
        nRet = 1;
    else if( static_cast<PopularColorCount const *>(p1)->mnCount == static_cast<PopularColorCount const *>(p2)->mnCount )
        nRet = 0;
    else
        nRet = -1;

    return nRet;
}

bool Bitmap::ImplReducePopular( sal_uInt16 nColCount )
{
    ScopedReadAccess pRAcc(*this);
    sal_uInt16 nBitCount;
    bool bRet = false;

    if( nColCount > 256 )
        nColCount = 256;

    if( nColCount < 17 )
        nBitCount = 4;
    else
        nBitCount = 8;

    if( pRAcc )
    {
        const sal_uInt32 nValidBits = 4;
        const sal_uInt32 nRightShiftBits = 8 - nValidBits;
        const sal_uInt32 nLeftShiftBits1 = nValidBits;
        const sal_uInt32 nLeftShiftBits2 = nValidBits << 1;
        const sal_uInt32 nColorsPerComponent = 1 << nValidBits;
        const sal_uInt32 nColorOffset = 256 / nColorsPerComponent;
        const sal_uInt32 nTotalColors = nColorsPerComponent * nColorsPerComponent * nColorsPerComponent;
        const long nWidth = pRAcc->Width();
        const long nHeight = pRAcc->Height();
        std::unique_ptr<PopularColorCount[]> pCountTable(new PopularColorCount[ nTotalColors ]);

        memset( pCountTable.get(), 0, nTotalColors * sizeof( PopularColorCount ) );

        for( long nR = 0, nIndex = 0; nR < 256; nR += nColorOffset )
        {
            for( long nG = 0; nG < 256; nG += nColorOffset )
            {
                for( long nB = 0; nB < 256; nB += nColorOffset )
                {
                    pCountTable[ nIndex ].mnIndex = nIndex;
                    nIndex++;
                }
            }
        }

        if( pRAcc->HasPalette() )
        {
            for( long nY = 0; nY < nHeight; nY++ )
            {
                Scanline pScanlineRead = pRAcc->GetScanline(nY);
                for( long nX = 0; nX < nWidth; nX++ )
                {
                    const BitmapColor& rCol = pRAcc->GetPaletteColor( pRAcc->GetIndexFromData( pScanlineRead, nX ) );
                    pCountTable[ ( ( static_cast<sal_uInt32>(rCol.GetRed()) >> nRightShiftBits ) << nLeftShiftBits2 ) |
                                 ( ( static_cast<sal_uInt32>(rCol.GetGreen()) >> nRightShiftBits ) << nLeftShiftBits1 ) |
                                 ( static_cast<sal_uInt32>(rCol.GetBlue()) >> nRightShiftBits ) ].mnCount++;
                }
            }
        }
        else
        {
            for( long nY = 0; nY < nHeight; nY++ )
            {
                Scanline pScanlineRead = pRAcc->GetScanline(nY);
                for( long nX = 0; nX < nWidth; nX++ )
                {
                    const BitmapColor aCol( pRAcc->GetPixelFromData( pScanlineRead, nX ) );
                    pCountTable[ ( ( static_cast<sal_uInt32>(aCol.GetRed()) >> nRightShiftBits ) << nLeftShiftBits2 ) |
                                 ( ( static_cast<sal_uInt32>(aCol.GetGreen()) >> nRightShiftBits ) << nLeftShiftBits1 ) |
                                 ( static_cast<sal_uInt32>(aCol.GetBlue()) >> nRightShiftBits ) ].mnCount++;
                }
            }
        }

        BitmapPalette aNewPal( nColCount );

        qsort( pCountTable.get(), nTotalColors, sizeof( PopularColorCount ), ImplPopularCmpFnc );

        for( sal_uInt16 n = 0; n < nColCount; n++ )
        {
            const PopularColorCount& rPop = pCountTable[ n ];
            aNewPal[ n ] = BitmapColor( static_cast<sal_uInt8>( ( rPop.mnIndex >> nLeftShiftBits2 ) << nRightShiftBits ),
                                        static_cast<sal_uInt8>( ( ( rPop.mnIndex >> nLeftShiftBits1 ) & ( nColorsPerComponent - 1 ) ) << nRightShiftBits ),
                                        static_cast<sal_uInt8>( ( rPop.mnIndex & ( nColorsPerComponent - 1 ) ) << nRightShiftBits ) );
        }

        Bitmap aNewBmp( GetSizePixel(), nBitCount, &aNewPal );
        BitmapScopedWriteAccess pWAcc(aNewBmp);

        if( pWAcc )
        {
            BitmapColor aDstCol( sal_uInt8(0) );
            std::unique_ptr<sal_uInt8[]> pIndexMap(new sal_uInt8[ nTotalColors ]);

            for( long nR = 0, nIndex = 0; nR < 256; nR += nColorOffset )
                for( long nG = 0; nG < 256; nG += nColorOffset )
                    for( long nB = 0; nB < 256; nB += nColorOffset )
                        pIndexMap[ nIndex++ ] = static_cast<sal_uInt8>(aNewPal.GetBestIndex( BitmapColor( static_cast<sal_uInt8>(nR), static_cast<sal_uInt8>(nG), static_cast<sal_uInt8>(nB) ) ));

            if( pRAcc->HasPalette() )
            {
                for( long nY = 0; nY < nHeight; nY++ )
                {
                    Scanline pScanline = pWAcc->GetScanline(nY);
                    Scanline pScanlineRead = pRAcc->GetScanline(nY);
                    for( long nX = 0; nX < nWidth; nX++ )
                    {
                        const BitmapColor& rCol = pRAcc->GetPaletteColor( pRAcc->GetIndexFromData( pScanlineRead, nX ) );
                        aDstCol.SetIndex( pIndexMap[ ( ( static_cast<sal_uInt32>(rCol.GetRed()) >> nRightShiftBits ) << nLeftShiftBits2 ) |
                                                     ( ( static_cast<sal_uInt32>(rCol.GetGreen()) >> nRightShiftBits ) << nLeftShiftBits1 ) |
                                                     ( static_cast<sal_uInt32>(rCol.GetBlue()) >> nRightShiftBits ) ] );
                        pWAcc->SetPixelOnData( pScanline, nX, aDstCol );
                    }
                }
            }
            else
            {
                for( long nY = 0; nY < nHeight; nY++ )
                {
                    Scanline pScanline = pWAcc->GetScanline(nY);
                    Scanline pScanlineRead = pRAcc->GetScanline(nY);
                    for( long nX = 0; nX < nWidth; nX++ )
                    {
                        const BitmapColor aCol( pRAcc->GetPixelFromData( pScanlineRead, nX ) );
                        aDstCol.SetIndex( pIndexMap[ ( ( static_cast<sal_uInt32>(aCol.GetRed()) >> nRightShiftBits ) << nLeftShiftBits2 ) |
                                                     ( ( static_cast<sal_uInt32>(aCol.GetGreen()) >> nRightShiftBits ) << nLeftShiftBits1 ) |
                                                     ( static_cast<sal_uInt32>(aCol.GetBlue()) >> nRightShiftBits ) ] );
                        pWAcc->SetPixelOnData( pScanline, nX, aDstCol );
                    }
                }
            }

            pWAcc.reset();
            bRet = true;
        }

        pCountTable.reset();
        pRAcc.reset();

        if( bRet )
        {
            const MapMode aMap( maPrefMapMode );
            const Size aSize( maPrefSize );

            *this = aNewBmp;
            maPrefMapMode = aMap;
            maPrefSize = aSize;
        }
    }

    return bRet;
}

bool Bitmap::ImplReduceMedian( sal_uInt16 nColCount )
{
    ScopedReadAccess pRAcc(*this);
    sal_uInt16 nBitCount;
    bool bRet = false;

    if( nColCount < 17 )
        nBitCount = 4;
    else if( nColCount < 257 )
        nBitCount = 8;
    else
    {
        OSL_FAIL( "Bitmap::ImplReduceMedian(): invalid color count!" );
        nBitCount = 8;
        nColCount = 256;
    }

    if( pRAcc )
    {
        Bitmap aNewBmp( GetSizePixel(), nBitCount );
        BitmapScopedWriteAccess pWAcc(aNewBmp);

        if( pWAcc )
        {
            const sal_uLong nSize = 32768 * sizeof( sal_uLong );
            sal_uLong* pColBuf = static_cast<sal_uLong*>(rtl_allocateMemory( nSize ));
            const long nWidth = pWAcc->Width();
            const long nHeight = pWAcc->Height();
            long nIndex = 0;

            memset( pColBuf, 0, nSize );

            // create Buffer
            if( pRAcc->HasPalette() )
            {
                for( long nY = 0; nY < nHeight; nY++ )
                {
                    Scanline pScanlineRead = pRAcc->GetScanline(nY);
                    for( long nX = 0; nX < nWidth; nX++ )
                    {
                        const BitmapColor& rCol = pRAcc->GetPaletteColor( pRAcc->GetIndexFromData( pScanlineRead, nX ) );
                        pColBuf[ RGB15( rCol.GetRed() >> 3, rCol.GetGreen() >> 3, rCol.GetBlue() >> 3 ) ]++;
                    }
                }
            }
            else
            {
                for( long nY = 0; nY < nHeight; nY++ )
                {
                    Scanline pScanlineRead = pRAcc->GetScanline(nY);
                    for( long nX = 0; nX < nWidth; nX++ )
                    {
                        const BitmapColor aCol( pRAcc->GetPixelFromData( pScanlineRead, nX ) );
                        pColBuf[ RGB15( aCol.GetRed() >> 3, aCol.GetGreen() >> 3, aCol.GetBlue() >> 3 ) ]++;
                    }
                }
            }

            // create palette via median cut
            BitmapPalette aPal( pWAcc->GetPaletteEntryCount() );
            ImplMedianCut( pColBuf, aPal, 0, 31, 0, 31, 0, 31,
                           nColCount, nWidth * nHeight, nIndex );

            // do mapping of colors to palette
            InverseColorMap aMap( aPal );
            pWAcc->SetPalette( aPal );
            for( long nY = 0; nY < nHeight; nY++ )
            {
                Scanline pScanline = pWAcc->GetScanline(nY);
                for( long nX = 0; nX < nWidth; nX++ )
                    pWAcc->SetPixelOnData( pScanline, nX, BitmapColor(static_cast<sal_uInt8>( aMap.GetBestPaletteIndex( pRAcc->GetColor( nY, nX ) ))) );
            }

            rtl_freeMemory( pColBuf );
            pWAcc.reset();
            bRet = true;
        }

        pRAcc.reset();

        if( bRet )
        {
            const MapMode aMap( maPrefMapMode );
            const Size aSize( maPrefSize );

            *this = aNewBmp;
            maPrefMapMode = aMap;
            maPrefSize = aSize;
        }
    }

    return bRet;
}

void Bitmap::ImplMedianCut( sal_uLong* pColBuf, BitmapPalette& rPal,
                            long nR1, long nR2, long nG1, long nG2, long nB1, long nB2,
                            long nColors, long nPixels, long& rIndex )
{
    if( !nPixels )
        return;

    BitmapColor aCol;
    const long nRLen = nR2 - nR1;
    const long nGLen = nG2 - nG1;
    const long nBLen = nB2 - nB1;
    sal_uLong* pBuf = pColBuf;

    if( !nRLen && !nGLen && !nBLen )
    {
        if( pBuf[ RGB15( nR1, nG1, nB1 ) ] )
        {
            aCol.SetRed( static_cast<sal_uInt8>( nR1 << 3 ) );
            aCol.SetGreen( static_cast<sal_uInt8>( nG1 << 3 ) );
            aCol.SetBlue( static_cast<sal_uInt8>( nB1 << 3 ) );
            rPal[ static_cast<sal_uInt16>(rIndex++) ] = aCol;
        }
    }
    else
    {
        if( 1 == nColors || 1 == nPixels )
        {
            long nPixSum = 0, nRSum = 0, nGSum = 0, nBSum = 0;

            for( long nR = nR1; nR <= nR2; nR++ )
            {
                for( long nG = nG1; nG <= nG2; nG++ )
                {
                    for( long nB = nB1; nB <= nB2; nB++ )
                    {
                        nPixSum = pBuf[ RGB15( nR, nG, nB ) ];

                        if( nPixSum )
                        {
                            nRSum += nR * nPixSum;
                            nGSum += nG * nPixSum;
                            nBSum += nB * nPixSum;
                        }
                    }
                }
            }

            aCol.SetRed( static_cast<sal_uInt8>( ( nRSum / nPixels ) << 3 ) );
            aCol.SetGreen( static_cast<sal_uInt8>( ( nGSum / nPixels ) << 3 ) );
            aCol.SetBlue( static_cast<sal_uInt8>( ( nBSum / nPixels ) << 3 ) );
            rPal[ static_cast<sal_uInt16>(rIndex++) ] = aCol;
        }
        else
        {
            const long nTest = ( nPixels >> 1 );
            long nPixOld = 0;
            long nPixNew = 0;

            if( nBLen > nGLen && nBLen > nRLen )
            {
                long nB = nB1 - 1;

                while( nPixNew < nTest )
                {
                    nB++;
                    nPixOld = nPixNew;
                    for( long nR = nR1; nR <= nR2; nR++ )
                        for( long nG = nG1; nG <= nG2; nG++ )
                            nPixNew += pBuf[ RGB15( nR, nG, nB ) ];
                }

                if( nB < nB2 )
                {
                    ImplMedianCut( pBuf, rPal, nR1, nR2, nG1, nG2, nB1, nB, nColors >> 1, nPixNew, rIndex );
                    ImplMedianCut( pBuf, rPal, nR1, nR2, nG1, nG2, nB + 1, nB2, nColors >> 1, nPixels - nPixNew, rIndex );
                }
                else
                {
                    ImplMedianCut( pBuf, rPal, nR1, nR2, nG1, nG2, nB1, nB - 1, nColors >> 1, nPixOld, rIndex );
                    ImplMedianCut( pBuf, rPal, nR1, nR2, nG1, nG2, nB, nB2, nColors >> 1, nPixels - nPixOld, rIndex );
                }
            }
            else if( nGLen > nRLen )
            {
                long nG = nG1 - 1;

                while( nPixNew < nTest )
                {
                    nG++;
                    nPixOld = nPixNew;
                    for( long nR = nR1; nR <= nR2; nR++ )
                        for( long nB = nB1; nB <= nB2; nB++ )
                            nPixNew += pBuf[ RGB15( nR, nG, nB ) ];
                }

                if( nG < nG2 )
                {
                    ImplMedianCut( pBuf, rPal, nR1, nR2, nG1, nG, nB1, nB2, nColors >> 1, nPixNew, rIndex );
                    ImplMedianCut( pBuf, rPal, nR1, nR2, nG + 1, nG2, nB1, nB2, nColors >> 1, nPixels - nPixNew, rIndex );
                }
                else
                {
                    ImplMedianCut( pBuf, rPal, nR1, nR2, nG1, nG - 1, nB1, nB2, nColors >> 1, nPixOld, rIndex );
                    ImplMedianCut( pBuf, rPal, nR1, nR2, nG, nG2, nB1, nB2, nColors >> 1, nPixels - nPixOld, rIndex );
                }
            }
            else
            {
                long nR = nR1 - 1;

                while( nPixNew < nTest )
                {
                    nR++;
                    nPixOld = nPixNew;
                    for( long nG = nG1; nG <= nG2; nG++ )
                        for( long nB = nB1; nB <= nB2; nB++ )
                            nPixNew += pBuf[ RGB15( nR, nG, nB ) ];
                }

                if( nR < nR2 )
                {
                    ImplMedianCut( pBuf, rPal, nR1, nR, nG1, nG2, nB1, nB2, nColors >> 1, nPixNew, rIndex );
                    ImplMedianCut( pBuf, rPal, nR1 + 1, nR2, nG1, nG2, nB1, nB2, nColors >> 1, nPixels - nPixNew, rIndex );
                }
                else
                {
                    ImplMedianCut( pBuf, rPal, nR1, nR - 1, nG1, nG2, nB1, nB2, nColors >> 1, nPixOld, rIndex );
                    ImplMedianCut( pBuf, rPal, nR, nR2, nG1, nG2, nB1, nB2, nColors >> 1, nPixels - nPixOld, rIndex );
                }
            }
        }
    }
}

void Bitmap::Vectorize( GDIMetaFile& rMtf, sal_uInt8 cReduce, const Link<long,void>* pProgress )
{
diff --git a/vcl/source/gdi/bitmapex.cxx b/vcl/source/gdi/bitmapex.cxx
index 5b03870..2f6f9bf 100644
--- a/vcl/source/gdi/bitmapex.cxx
+++ b/vcl/source/gdi/bitmapex.cxx
@@ -446,11 +446,6 @@ bool BitmapEx::Convert( BmpConversion eConversion )
    return !!maBitmap && maBitmap.Convert( eConversion );
}

bool BitmapEx::ReduceColors( sal_uInt16 nNewColorCount )
{
    return !!maBitmap && maBitmap.ReduceColors( nNewColorCount, BMP_REDUCE_POPULAR );
}

void BitmapEx::Expand( sal_uLong nDX, sal_uLong nDY, bool bExpandTransparent )
{
    bool bRet = false;
diff --git a/vcl/unx/generic/dtrans/bmp.cxx b/vcl/unx/generic/dtrans/bmp.cxx
index c72e931..b6b5d18 100644
--- a/vcl/unx/generic/dtrans/bmp.cxx
+++ b/vcl/unx/generic/dtrans/bmp.cxx
@@ -17,20 +17,23 @@
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <unistd.h>
#include <cstdio>
#include <cstring>

#include "bmp.hxx"

#include "X11_selection.hxx"
#include <unx/x11/xlimits.hxx>

#include <sal/macros.h>
#include <tools/stream.hxx>

#include <vcl/dibtools.hxx>
#include <vcl/svapp.hxx>
#include <vcl/bitmap.hxx>
#include <vcl/bitmapex.hxx>
#include <vcl/BitmapSimpleColorQuantizationFilter.hxx>

#include <unx/x11/xlimits.hxx>

#include "bmp.hxx"
#include "X11_selection.hxx"

#include <unistd.h>
#include <cstdio>
#include <cstring>

using namespace x11;

@@ -753,11 +756,21 @@ css::uno::Sequence<sal_Int8> x11::convertBitmapDepth(
            bm.Convert(BmpConversion::N1BitThreshold);
            break;
        case 4:
            bm.ReduceColors(1<<4);
            break;
        {
            BitmapEx aBmpEx(bm);
            BitmapFilter::Filter(aBmpEx, BitmapSimpleColorQuantizationFilter(1<<4));
            bm = aBmpEx.GetBitmap();
        }
        break;

        case 8:
            bm.ReduceColors(1<<8);
            break;
        {
            BitmapEx aBmpEx(bm);
            BitmapFilter::Filter(aBmpEx, BitmapSimpleColorQuantizationFilter(1<<8));
            bm = aBmpEx.GetBitmap();
        }
        break;

        case 24:
            bm.Convert(BmpConversion::N24Bit);
            break;