diff --git a/Source/3rdpart/QColorQuantizer.cpp b/Source/3rdpart/QColorQuantizer.cpp
deleted file mode 100644
index 70d693836..000000000
--- a/Source/3rdpart/QColorQuantizer.cpp
+++ /dev/null
@@ -1,576 +0,0 @@
-/********************************************************************************
-    Copyright 2004 Sjaak Priester    
-
-    This file is part of Tinter.
-
-    Tinter is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    Tinter is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with Tinter; if not, write to the Free Software
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-********************************************************************************/
-
-// QColorQuantizer
-//
-// Version 1.0 (c) 2004, Sjaak Priester, Amsterdam.
-// mailto:sjaak@sjaakpriester.nl
-#include "atlheaders.h"
-#include <cassert>
-#include "3rdpart/GdiplusH.h"
-#include "QColorQuantizer.h"
-using namespace Gdiplus;
-QColorQuantizer::QColorQuantizer(void)
-:
- m_Message(0)
-, m_bStop(false)
-, m_pSource(NULL)
-, m_nMaxColors(0)
-,m_Mode(HalfTone)
-{
-}
-
-QColorQuantizer::~QColorQuantizer(void)
-{
-}
-
-void QColorQuantizer::MakeQuantized(Image * pSource, Mode mode, UINT nMaxColors)
-{
-    Stop();
-    m_pSource = pSource;
-    m_Mode = mode;
-    m_nMaxColors = nMaxColors;
-
-//    m_Message = message;
-ThreadProc(this);
-    //m_pThread = ::AfxBeginThread(ThreadProc, this);
-}
-
-void QColorQuantizer::Stop(void)
-{
-    /*if (m_pThread)
-    {
-        m_bStop = true;
-        ::WaitForSingleObject(m_pThread->m_hThread, INFINITE);
-    }*/
-}
-
-/* static */
-UINT QColorQuantizer::ThreadProc(LPVOID pParam)
-{
-    QColorQuantizer * pThis = (QColorQuantizer *) pParam;
-
-    
-    /*Bitmap * pResult = */pThis->Calculate();
-    
-
-
-    //pThis->Notify(pResult);
-    return 0;
-}
-
-// called by ThreadProc
-Bitmap * QColorQuantizer::Calculate(void)
-{
-    m_bStop = false;
-    return GetQuantized(m_pSource, m_Mode, m_nMaxColors);
-}
-
-// called by ThreadProc
-void QColorQuantizer::Notify(Bitmap * pResult)
-{
-    //if (pResult && m_pMsgWnd) m_pMsgWnd->PostMessage(m_Message, 0, (LPARAM) pResult);
-}
-
-Bitmap * QColorQuantizer::GetQuantized(Image * pSource, Mode mode, UINT nMaxColors)
-{
-    UINT w = pSource->GetWidth();
-    UINT h = pSource->GetHeight();
-
-    Bitmap * pSrcBitmap = NULL;
-
-    // If necessary, convert source to 24 bpp RGB bitmap
-    if (pSource->GetPixelFormat() == PixelFormat24bppRGB) pSrcBitmap = (Bitmap *) pSource;
-    else
-    {
-        pSrcBitmap = new Bitmap(w, h, PixelFormat24bppRGB);
-
-        Graphics g(pSrcBitmap);
-        if (g.DrawImage(pSource, 0, 0, w, h) != Ok)
-        {
-            delete pSrcBitmap;
-            return NULL;
-        }
-    }
-
-    // Create 8 bpp indexed bitmap of the same size
-    Bitmap * pResult = new Bitmap(w, h, PixelFormat8bppIndexed);
-
-    bool bSucceeded = false;
-
-    switch (mode)
-    {
-    case HalfTone:
-        bSucceeded = QuantizeWebSafe(pSrcBitmap, pResult);
-        break;
-    case Octree:
-        bSucceeded = QuantizeOctree(pSrcBitmap, pResult, nMaxColors);
-        break;
-    default:
-        break;
-    }
-
-    if (m_bStop || ! bSucceeded)
-    {
-        delete pResult;
-        pResult = NULL;
-    }
-    
-    if (pSource != pSrcBitmap) delete pSrcBitmap;
-
-    return pResult;
-}
-
-bool QColorQuantizer::QuantizeWebSafe(Bitmap * pSource , Bitmap * pDest)
-{
-    // It would be nice if we could simply draw an Image to an 8bpp Bitmap,
-    // but GDI+ doesn't support it. Apparently, when saving a GIF, the conversion
-    // is done by the encoder.
-    //
-    // Websafe color reduction simply uses the standard GDI/GDI+ halftone palette.
-    // Entries 40...255 are the websafe colors, arranged like this:
-    //
-    //    40 ff000000
-    //    41 ff000033
-    //    42 ff000066
-    //    43 ff000099
-    //    44 ff0000cc
-    //    45 ff0000ff
-    //    46 ff003300
-    //    47 ff003333
-    //    ...
-    //    252 ffffff66
-    //    253 ffffff99
-    //    254 ffffffcc
-    //    255 ffffffff
-    //
-    // For the 216 (= 6^3) websafe colors, the color scale in each dimension
-    // is divided in six parts.
-    // Knowing that 6 * 43 = 258 = just a little bit more than 256, we derive the
-    // following formula to calculate the color index from a 3x8 RGB color:
-    //    index = 40 + 6 * (6 * ((R + 1) / 43) + ((G + 1) / 43)) + ((B + 1) / 43)
-
-    bool r = true;
-
-    UINT w = pSource->GetWidth();
-    UINT h = pSource->GetHeight();
-    Rect rc(0, 0, w, h);
-
-    BitmapData dataSource;
-
-    // Lock bits on 3x8 source bitmap
-    if (pSource->LockBits(& rc, ImageLockModeRead, PixelFormat24bppRGB, & dataSource) == Ok)
-    {
-        BitmapData dataDest;
-
-        // Lock bits on indexed destination bitmap
-        if (pDest->LockBits(& rc, ImageLockModeWrite, PixelFormat8bppIndexed, & dataDest) == Ok)
-        {
-            BYTE * pRowSource = (BYTE *) dataSource.Scan0;
-            UINT strideSource;
-
-            if (dataSource.Stride > 0) strideSource = dataSource.Stride;
-            else
-            {
-                // Compensate for possible negative stride
-                pRowSource += h * dataSource.Stride;
-                strideSource = - dataSource.Stride;
-            }
-
-            BYTE * pRowDest = (BYTE *) dataDest.Scan0;
-            UINT strideDest;
-
-            if (dataDest.Stride > 0) strideDest = dataDest.Stride;
-            else
-            {
-                pRowDest += h * dataDest.Stride;
-                strideDest = - dataDest.Stride;
-            }
-
-            for (UINT y = 0; y < h; y++)    // For each row...
-            {
-                BYTE * pPixelSource = pRowSource;
-                BYTE * pPixelDest = pRowDest;
-
-                for (UINT x = 0; x < w; x++)    // ...for each pixel...
-                {
-                    // ...gather color information from 3x8 source bitmap...
-                    UINT b = * pPixelSource++;
-                    UINT g = * pPixelSource++;
-                    UINT r = * pPixelSource++;
-
-                    // ...calculate index in standard halftone palette...
-                    UINT index = 40 + 6 * (6 * ((r + 1) / 43) + ((g + 1) / 43)) + ((b + 1) / 43);
-
-                    // ...and put result in indexed bitmap.
-                    * pPixelDest++ = (BYTE) index;
-                }
-
-                if (m_bStop) break;
-
-                pRowSource += strideSource;
-                pRowDest += strideDest;
-            }
-            pDest->UnlockBits(& dataDest);
-        }
-        else r = false;
-
-        pSource->UnlockBits(& dataSource);
-    }
-    else r = false;
-
-    return r;
-}
-
-
-//====================
-// Octree Color quantizing
-
-
-// The work horse for Octree color quantizing.
-bool QColorQuantizer::QuantizeOctree(Bitmap * pSource, Bitmap * pDest, UINT nMaxColors)
-{
-    if (nMaxColors > 256 || nMaxColors < 8) return false;
-
-    bool r = true;
-    UINT nLeafs = 0;    // counter of leaf nodes in octree
-
-    OctreeNode head(1, NULL, nLeafs);    // head of list with reducible non-leaf nodes (dummy)
-    head.m_nLevel = 0;
-    OctreeNode root(7, NULL, nLeafs);    // root of octree
-
-    // Put tree root in reducible list. Root will always be the last node in the list,
-    // and also functions as list sentinel.
-    head.m_pNext = & root;
-    root.m_pPrev = & head;
-
-    UINT w = pSource->GetWidth();
-    UINT h = pSource->GetHeight();
-    Rect rc(0, 0, w, h);
-
-    BitmapData dataSource;
-
-    // Lock bits on 3x8 source bitmap
-    if (pSource->LockBits(& rc, ImageLockModeRead, PixelFormat24bppRGB, & dataSource) == Ok)
-    {
-        BYTE * pScan0Source = (BYTE *) dataSource.Scan0;
-        UINT strideSource;
-
-        if (dataSource.Stride > 0) strideSource = dataSource.Stride;
-        else
-        {
-            // Compensate for possible negative stride
-            // (not needed for first loop, but we have to do it
-            // for second loop anyway)
-            pScan0Source += h * dataSource.Stride;
-            strideSource = - dataSource.Stride;
-        }
-
-        BYTE * pRowSource = pScan0Source;
-
-        // First loop: gather color information, put it in tree leaves
-        for (UINT y = 0; y < h; y++)    // For each row...
-        {
-            BYTE * pPixelSource = pRowSource;
-
-            for (UINT x = 0; x < w; x++)    // ...for each pixel...
-            {
-                UINT b = * pPixelSource++;
-                UINT g = * pPixelSource++;
-                UINT r = * pPixelSource++;
-
-                root.SetColor(r, g, b, & head, nLeafs);
-                                    // ...add color to tree, make new leaf if necessary.
-
-                while (nLeafs > nMaxColors)    // If in the process too many leaves are created...
-                {
-                    // ... then, one by one, pick a non-leaf node...
-                    OctreeNode * pReducible = head.m_pNext;
-                    assert(pReducible);
-
-                    if (m_bStop || ! pReducible->m_pNext) break;
-                    pReducible->Remove();    // ...remove it from the list of reducibles...
-                    pReducible->GetQuantized(nLeafs);    // ...and reduce it to a leaf.
-                }
-                if (m_bStop) break;
-
-            }
-            if (m_bStop) break;
-
-            pRowSource += strideSource;
-        }
-
-        if (! m_bStop)
-        {
-            // We now have the important colors in the pic. Put them in a palette
-            BYTE * pPaletteBytes = new BYTE[sizeof(ColorPalette) + (nLeafs - 1) * sizeof(ARGB)];
-            {
-                ColorPalette * pPalette = (ColorPalette *) pPaletteBytes;
-                pPalette->Flags = PaletteFlagsHasAlpha;
-                pPalette->Count = nLeafs;
-
-                UINT index = 0;
-                root.FillPalette(pPalette, index);    // Let octree fill the palette
-
-                assert(index == nLeafs);
-
-                pDest->SetPalette(pPalette);
-
-                BitmapData dataDest;
-
-                // Lock bits on indexed destination bitmap
-                if (pDest->LockBits(& rc, ImageLockModeWrite, PixelFormat8bppIndexed, & dataDest) == Ok)
-                {
-                    BYTE * pRowSource = pScan0Source;
-
-                    BYTE * pRowDest = (BYTE *) dataDest.Scan0;
-                    UINT strideDest;
-
-                    // Compensate for possible negative stride
-                    if (dataDest.Stride > 0) strideDest = dataDest.Stride;
-                    else
-                    {
-                        pRowDest += h * dataDest.Stride;
-                        strideDest = - dataDest.Stride;
-                    }
-
-                    // Second loop: fill indexed bitmap
-                    for (UINT y = 0; y < h; y++)    // For each row...
-                    {
-                        BYTE * pPixelSource = pRowSource;
-                        BYTE * pPixelDest = pRowDest;
-
-                        for (UINT x = 0; x < w; x++)    // ...for each pixel...
-                        {
-                            // ...gather color information from source bitmap...
-                            UINT b = * pPixelSource++;
-                            UINT g = * pPixelSource++;
-                            UINT r = * pPixelSource++;
-
-                            // ...let octree calculate index...
-                            BYTE index = (BYTE) root.GetPaletteIndex(r, g, b);
-
-                            // ...and put index in the destination bitmap.
-                            * pPixelDest++ = index;
-                        }
-
-                        pRowSource += strideSource;
-                        pRowDest += strideDest;
-
-                        if (m_bStop) break;
-                    }
-                    pDest->UnlockBits(& dataDest);
-                }
-                else r = false;
-
-                delete[] pPaletteBytes;
-            }
-            //else r = false;
-        }
-        pSource->UnlockBits(& dataSource);
-    }
-    else r = false;
-    if (m_bStop) r = false;
-
-    return r;
-}
-
-//===============
-// OctreeNode helper class
-
-QColorQuantizer::OctreeNode::OctreeNode(UINT level, OctreeNode * pHead, UINT& nLeafs)
-: m_nPixels(0)
-, m_totalR(0)
-, m_totalG(0)
-, m_totalB(0)
-, m_nLevel(level)
-, m_bLeaf(level == 0)
-, m_pNext(NULL)
-, m_pPrev(NULL)
-{
-    for (UINT i = 0; i < 8; i++) m_pChild[i] = NULL;
-
-    if (m_bLeaf) ++nLeafs;    // If we are a leaf, increment counter
-    else if (pHead)
-    {
-        // Otherwise, put us at front of list of reducible non-leaf nodes...
-        InsertAfter(pHead);
-
-        // ...and determine our priority when it comes to reducing.
-        UpdatePosition();
-    }
-}
-
-QColorQuantizer::OctreeNode::~OctreeNode()
-{
-    for (UINT i = 0; i < 8; i++) delete m_pChild[i];
-}
-
-void QColorQuantizer::OctreeNode::UpdatePosition()
-{
-    // Determine our priority, in other words our position in the list.
-    assert(m_pNext);
-    assert(m_pPrev);
-
-    OctreeNode * pNext = m_pNext;
-
-    // Increment as long as pNext should be reduced before we are.
-    while (pNext && ! ReduceBefore(* pNext)) pNext = pNext->m_pNext;
-    assert(pNext);
-
-    if (pNext != m_pNext)
-    {
-        Remove();                // Remove us from old position in list...
-        InsertBefore(pNext);    // ...and insert us at new position.
-    }
-}
-
-void QColorQuantizer::OctreeNode::Remove()
-{
-    assert(m_pNext);
-    assert(m_pPrev);
-
-    m_pPrev->m_pNext = m_pNext;
-    m_pNext->m_pPrev = m_pPrev;
-}
-
-void QColorQuantizer::OctreeNode::InsertBefore(OctreeNode * pNext)
-{
-    assert(pNext);
-
-    m_pPrev = pNext->m_pPrev;
-    m_pNext = pNext;
-
-    m_pPrev->m_pNext = this;
-    m_pNext->m_pPrev = this;
-}
-
-UINT QColorQuantizer::OctreeNode::ChildIndex(UINT r, UINT g, UINT b) const
-{
-    // The octree trick: calculate child index based on the m_Level'th bits
-    // of the color bytes.
-    UINT rBit = (r >> m_nLevel) & 1;
-    UINT gBit = (g >> m_nLevel) & 1;
-    UINT bBit = (b >> m_nLevel) & 1;
-
-    rBit <<= 2;
-    rBit += gBit << 1;
-    rBit += bBit;
-
-    return rBit;
-}
-
-void QColorQuantizer::OctreeNode::SetColor(UINT r, UINT g, UINT b, OctreeNode * pHead, UINT& nLeafs)
-{
-    m_nPixels++;
-    if (m_bLeaf)
-    {
-        // If we are a leaf node, accumulate color bytes in totals.
-        m_totalR += r;
-        m_totalG += g;
-        m_totalB += b;
-    }
-    else
-    {
-        if (m_pNext) UpdatePosition();    // Perhaps we should move further down the list,
-                                        // because we represent more pixels.
-
-        // We are not a leaf node, so delegate to one of our children.
-        UINT index = ChildIndex(r, g, b);
-
-        // If we don't have a matching child, create it.
-        if (m_pChild[index] == NULL)
-            m_pChild[index] = new OctreeNode(m_nLevel - 1, pHead, nLeafs);
-
-        m_pChild[index]->SetColor(r, g, b, pHead, nLeafs);    // recursion
-    }
-}
-
-UINT QColorQuantizer::OctreeNode::GetPaletteIndex(UINT r, UINT g, UINT b) const
-{
-    // If we are a leaf node, the palette index is simply stored.
-    if (m_bLeaf) return m_iPaletteIndex;
-    else
-    {
-        // Otherwise, the matching child node must know more about it.
-        UINT index = ChildIndex(r, g, b);
-        assert(m_pChild[index] != NULL);
-        return m_pChild[index]->GetPaletteIndex(r, g, b);    // recursion
-    }
-}
-
-void QColorQuantizer::OctreeNode::GetQuantized(UINT& nLeafs)
-{
-    // GetQuantized a non-leaf node to a leaf
-    assert(! m_bLeaf);
-
-    UINT r = 0;
-    UINT g = 0;
-    UINT b = 0;
-
-    for (UINT i = 0; i < 8; i++)
-    {
-        OctreeNode * pChild = m_pChild[i];
-        if (pChild == NULL) continue;
-
-        assert(pChild->m_bLeaf);
-
-        // Accumulate color totals of our children...
-        r += pChild->m_totalR;
-        g += pChild->m_totalG;
-        b += pChild->m_totalB;
-
-        // ...and delete them.
-        delete pChild;
-        m_pChild[i] = NULL;
-
-        --nLeafs;
-    }
-    m_totalR = r;
-    m_totalG = g;
-    m_totalB = b;
-
-    // Now we have become a leaf node ourselves.
-    m_bLeaf = true;
-    ++nLeafs;
-}
-
-void QColorQuantizer::OctreeNode::FillPalette(ColorPalette * pPal, UINT& index)
-{
-    if (m_bLeaf)
-    {
-        // If we are a leaf, calculate the color by dividing the color totals
-        // through the number of pixels we represent...
-        Color col(255,
-            (BYTE)(m_totalR / m_nPixels),
-            (BYTE)(m_totalG / m_nPixels),
-            (BYTE)(m_totalB / m_nPixels));
-
-        // ...and put it in the palette.
-        pPal->Entries[index] = col.GetValue();
-        m_iPaletteIndex = index++;
-    }
-    else
-    {
-        // Otherwise, ask our child nodes.
-        for (UINT i = 0; i < 8; i++)
-            if (m_pChild[i] != NULL) m_pChild[i]->FillPalette(pPal, index); // recursion
-    }
-}
diff --git a/Source/3rdpart/QColorQuantizer.h b/Source/3rdpart/QColorQuantizer.h
deleted file mode 100644
index a93bbef0b..000000000
--- a/Source/3rdpart/QColorQuantizer.h
+++ /dev/null
@@ -1,146 +0,0 @@
-/********************************************************************************
-    Copyright 2004 Sjaak Priester    
-
-    This file is part of Tinter.
-
-    Tinter is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    Tinter is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with Tinter; if not, write to the Free Software
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-********************************************************************************/
-
-// QColorQuantizer
-//
-// MFC class to reduce colors of GDI+ Gdiplus::Bitmap, using the Octree algorithm or
-// the Median Cut algorithm.
-// Use at your own risk. Comments welcome.
-//
-// Version 1.0 (c) 2004, Sjaak Priester, Amsterdam.
-// mailto:sjaak@sjaakpriester.nl
-
-#pragma once
-
-#pragma warning (disable: 4201)    // nameless struct/union
-
-// Special windows message.
-// After MakeQuantized() is finished, QColorQuantizer posts this message to the
-// associated window pMsgWnd.
-// LPARAM contains a Gdiplus::Bitmap *, pointing to a new 8 bpp indixed Gdiplus::Bitmap. The window
-// gets ownership of the Gdiplus::Bitmap and is responsible for it's destruction.
-
-class QColorQuantizer
-{
-public:
-    enum Mode
-    {
-        HalfTone,
-        WebSafe = HalfTone,
-        Octree
-    };
-    QColorQuantizer(void);
-    virtual ~QColorQuantizer(void);
-
-    Gdiplus::Bitmap * GetQuantized(Gdiplus::Image * pSource, Mode mode, UINT nMaxColors = 256);
-    // Return an 8bpp indexed Gdiplus::Bitmap, based on pSource.
-    //
-    // Parameters:
-    // - pSource    points to source image, not necessarily Gdiplus::Bitmap;
-    // - mode        one of the values of enum Mode;
-    // - nMaxColors    maximum number of colors in returned Gdiplus::Bitmap.
-    //
-    // Return:
-    // - non-NULL    new 8bpp indexed Gdiplus::Bitmap, user takes ownership and should delete;
-    // - NULL        something went wrong, probably lack of memory.
-    //
-    // Remarks for different modes:
-    // - WebSafe, HalfTone    Two names for the same mode. Returned Gdiplus::Bitmap has 216 colors of the
-    //                        GDI/GDI+ standard halftone palette. nMaxColors not used. Ultrafast.
-    // - Octree                nMaxColors >= 8 and nMaxColors <= 256. Returned Gdiplus::Bitmap might have
-    //                        less than nMaxColors. Good result, relatively fast.
-
-    void MakeQuantized(Gdiplus::Image * pSource, Mode mode, UINT nMaxColors = 256);
-    // Creates an 8bpp indexed Gdiplus::Bitmap in a separate working thread. Returns immediately.
-    //
-    // Parameters:
-    // - pSource    points to source image, not necessarily Gdiplus::Bitmap;
-    // - mode        one of the values of enum Mode;
-    // - pMsgWnd    points to CWnd that will receive message when Gdiplus::Bitmap is completed;
-    // - nMaxColors    maximum number of colors in returned Gdiplus::Bitmap;
-    // - message    Windows message sent to pMsgWnd.
-
-    void Stop(void);    // Stop MakeQuantized's working thread.
-//    bool IsCalculating()    { return m_pThread != NULL; }
-
-protected:
-    bool QuantizeWebSafe(Gdiplus::Bitmap * pSource , Gdiplus::Bitmap * pDest);
-    bool QuantizeOctree(Gdiplus::Bitmap * pSource, Gdiplus::Bitmap * pDest, UINT nMaxColors = 256);
-    // Worker functions GetQuantized() delegates to.
-    // - pDest should point to a new 8 bpp indexed Gdiplus::Bitmap with size of source.
-
-    // Helper class for QuantizeOctree
-    class OctreeNode
-    {
-    public:
-        OctreeNode(UINT level, OctreeNode * pHead, UINT& nLeafs);
-        ~OctreeNode();
-        void SetColor(UINT r, UINT g, UINT b, OctreeNode * pHead, UINT& nLeafs);
-        void GetQuantized(UINT& nLeafs);
-        UINT ChildIndex(UINT r, UINT g, UINT b) const;
-        UINT GetPaletteIndex(UINT r, UINT g, UINT b) const;
-        void FillPalette(Gdiplus::ColorPalette * pPal, UINT& index);
-
-        bool ReduceBefore(const OctreeNode& other) const
-        {
-            // true if other should be reduced before we are
-            if (m_nLevel == other.m_nLevel) return m_nPixels < other.m_nPixels;
-            return m_nLevel < other.m_nLevel;
-        }
-        void UpdatePosition();
-        void Remove();
-        void InsertBefore(OctreeNode * pNext);
-        void InsertAfter(OctreeNode * pPrev)    { InsertBefore(pPrev->m_pNext); }
-
-        bool m_bLeaf;
-        UINT m_nLevel;
-        OctreeNode * m_pChild[8];    // pointers in octree
-        union    // spare some memory by declaring variables not used together as union
-        {
-            UINT m_nPixels;
-            UINT m_iPaletteIndex;    // only used in leaf, after FillPalette()
-        };
-        union
-        {
-            struct
-            {
-                OctreeNode * m_pNext;    // pointers in priority list,
-                OctreeNode * m_pPrev;    // only used in non-leaf nodes
-            };
-            struct
-            {
-                UINT m_totalR;        // color totals, only used in leaf nodes
-                UINT m_totalG;
-                UINT m_totalB;
-            };
-        };
-    };
-
-protected:
-    Gdiplus::Bitmap * Calculate(void);
-    void Notify(Gdiplus::Bitmap * pResult);
-    bool m_bStop;
-    UINT m_Message;
-    Gdiplus::Image *m_pSource;
-    Mode m_Mode;
-    UINT m_nMaxColors;
-
-    static UINT ThreadProc(LPVOID pParam);
-};
diff --git a/Source/Core/CMakeLists.txt b/Source/Core/CMakeLists.txt
index 8aec66267..2db9575e6 100644
--- a/Source/Core/CMakeLists.txt
+++ b/Source/Core/CMakeLists.txt
@@ -202,8 +202,9 @@ if(WIN32)
         Images/WebpImageReader.cpp
         Images/HeifImageReader.cpp
         Images/ImageLoader.cpp
-        ../3rdpart/QColorQuantizer.cpp
         Images/GdiPlusImage.cpp
+        Images/ColorQuantizer.cpp
+        Images/OctreeColorQuantizer.cpp
         ../Func/GdiPlusInitializer.cpp
     )
     
@@ -236,7 +237,8 @@ if(WIN32)
         Images/HeifImageReader.h
         Images/AbstractImageReader.h
         Images/ImageLoader.h
-        ../3rdpart/QColorQuantizer.h
+        Images/ColorQuantizer.h
+        Images/OctreeColorQuantizer.h
     )
 else()
     list(APPEND SRC_LIST  
diff --git a/Source/Core/Images/ColorQuantizer.cpp b/Source/Core/Images/ColorQuantizer.cpp
new file mode 100644
index 000000000..8d1a4f54b
--- /dev/null
+++ b/Source/Core/Images/ColorQuantizer.cpp
@@ -0,0 +1,91 @@
+#include "ColorQuantizer.h"
+
+#include "OctreeColorQuantizer.h"
+
+using namespace Gdiplus;
+
+std::unique_ptr<Bitmap> ColorQuantizer::getQuantized(Bitmap* sourceBitmap, Gdiplus::Color backgroundColor, UINT nMaxColors /* = 256 */)
+{
+    BYTE alphaThreshold = 0;
+    UINT width = sourceBitmap->GetWidth();
+    UINT height = sourceBitmap->GetHeight();
+    Rect rc(0, 0, width, height);
+    std::unique_ptr<Bitmap> destBitmap;
+    BitmapData dataSource;
+
+    if (sourceBitmap->LockBits(&rc, ImageLockModeRead, PixelFormat32bppARGB, &dataSource) == Ok) {
+        BYTE* pScan0Source = (BYTE*)dataSource.Scan0;
+        UINT strideSource;
+
+        if (dataSource.Stride > 0) {
+            strideSource = dataSource.Stride;
+        } else {
+            pScan0Source += height * dataSource.Stride;
+            strideSource = -dataSource.Stride;
+        }
+
+        BYTE* pRowSource = pScan0Source;
+        OctreeColorQuantizer quantizer(nMaxColors, 8, &dataSource);
+        destBitmap = std::make_unique<Bitmap>(width, height, PixelFormat8bppIndexed);
+
+        for (UINT y = 0; y < height; y++) {
+            BYTE* pPixelSource = pRowSource;
+
+            for (UINT x = 0; x < width; x++) {
+                BYTE b = *pPixelSource++;
+                BYTE g = *pPixelSource++;
+                BYTE r = *pPixelSource++;
+                BYTE a = *pPixelSource++;
+                Gdiplus::Color c(a, r, g, b);
+                if (c.GetA() != 255) {
+                    c = c.GetA() < alphaThreshold ? Color() : BlendWithBackgroundSrgb(c, backgroundColor);
+                }
+
+                quantizer.addColor(c);
+            }
+
+            pRowSource += strideSource;
+        }
+
+        auto pallete = quantizer.generatePalette();
+        MyPalette mypal(pallete.get(), backgroundColor, alphaThreshold);
+
+        BitmapData dataDest;
+        destBitmap->SetPalette(pallete.get());
+        if (destBitmap->LockBits(&rc, ImageLockModeWrite, PixelFormat8bppIndexed, &dataDest) == Ok) {
+            BYTE* pRowSource = pScan0Source;
+
+            BYTE* pRowDest = (BYTE*)dataDest.Scan0;
+            UINT strideDest;
+
+            if (dataDest.Stride > 0) {
+                strideDest = dataDest.Stride;
+            } else {
+                pRowDest += height * dataDest.Stride;
+                strideDest = -dataDest.Stride;
+            }
+
+            for (UINT y = 0; y < height; y++) {
+                BYTE* pPixelSource = pRowSource;
+                BYTE* pPixelDest = pRowDest;
+
+                for (UINT x = 0; x < width; x++) {
+                    BYTE b = *pPixelSource++;
+                    BYTE g = *pPixelSource++;
+                    BYTE r = *pPixelSource++;
+                    BYTE a = *pPixelSource++;
+
+                    BYTE index = (BYTE)mypal.getNearestColorIndex(Gdiplus::Color(a, r, g, b));
+
+                    *pPixelDest++ = index;
+                }
+
+                pRowSource += strideSource;
+                pRowDest += strideDest;
+            }
+            destBitmap->UnlockBits(&dataDest);
+        }
+        sourceBitmap->UnlockBits(&dataSource);
+    }
+    return destBitmap;
+}
diff --git a/Source/Core/Images/ColorQuantizer.h b/Source/Core/Images/ColorQuantizer.h
new file mode 100644
index 000000000..4b40831b9
--- /dev/null
+++ b/Source/Core/Images/ColorQuantizer.h
@@ -0,0 +1,11 @@
+
+#pragma once
+#include <memory>
+
+#include <windows.h>
+#include "3rdpart/GdiplusH.h"
+
+class ColorQuantizer {
+public:
+    std::unique_ptr<Gdiplus::Bitmap> getQuantized(Gdiplus::Bitmap* pSource, Gdiplus::Color backgroundColor, UINT nMaxColors = 256);
+};
diff --git a/Source/Core/Images/OctreeColorQuantizer.cpp b/Source/Core/Images/OctreeColorQuantizer.cpp
new file mode 100644
index 000000000..09c444b8d
--- /dev/null
+++ b/Source/Core/Images/OctreeColorQuantizer.cpp
@@ -0,0 +1,362 @@
+#include "OctreeColorQuantizer.h"
+
+int ToBitsPerPixel(int colorCount) {
+    if (colorCount == 1) {
+        return 1;
+    }
+
+    // Bits per pixel is actually ceiling of log2(maxColors)
+    int bpp = 0;
+    for (int n = colorCount - 1; n > 0; n >>= 1) {
+        bpp++;
+    }
+
+    return bpp;
+}
+
+BYTE GetBrightness(Gdiplus::Color c) {
+    const float RLumSrgb = 0.299f;
+    const float GLumSrgb = 0.587f;
+    const float BLumSrgb = 0.114f;
+
+     if (c.GetR() == c.GetG() && c.GetR() == c.GetB())
+        return c.GetR();
+
+    return c.GetR() * RLumSrgb + c.GetG() * GLumSrgb + c.GetB() * BLumSrgb;
+}
+
+Gdiplus::Color BlendWithBackgroundSrgb(Gdiplus::Color c, Gdiplus::Color backColor) {
+    assert(c.GetA() != 255);
+    assert(backColor.GetA() == 255);
+
+    // The blending is applied only to the color and not the resulting alpha, which will always be opaque
+    if (c.GetA() == 0)
+        return backColor;
+    int inverseAlpha = 255 - c.GetA();
+
+    // The non-accelerated version. Division, eg. r:(byte)((c.R * c.A + backColor.R * inverseAlpha) / 255) would be more accurate
+    // but unlike for premultiplication we use the faster bit shifting because there is no inverse operation for blending.
+    return Gdiplus::Color(255,
+        (BYTE)((c.GetR() * c.GetA() + backColor.GetR() * inverseAlpha) >> 8),
+        (BYTE)((c.GetG() * c.GetA() + backColor.GetG() * inverseAlpha) >> 8),
+        (BYTE)((c.GetB() * c.GetA() + backColor.GetB() * inverseAlpha) >> 8));
+}
+
+MyPalette::MyPalette(Gdiplus::ColorPalette* palette, Gdiplus::Color backColor, BYTE alphaThreshold /*= 128*/)
+    : palette_(palette)
+    , backColor_(backColor)
+    , alphaThreshold_(alphaThreshold)
+{
+    transparentIndex_ = -1;
+    isGrayscale_ = true;
+    for (int i = 0; i < palette->Count; i++) {
+        Gdiplus::Color c = palette->Entries[i];
+        if (!colorToIndex_.count(c.GetValue()) && !(alphaThreshold_ == 0 && c.GetA() == 0 && !hasMultiLevelAlpha_)) {
+            colorToIndex_[c.GetValue()] = i;
+        }
+
+        if (c.GetA() != 255) {
+            hasAlpha_ = true;
+            if (!hasMultiLevelAlpha_) {
+                hasMultiLevelAlpha_ = c.GetA() > 0;
+            }
+
+            if (c.GetA() == 0) {
+                if (transparentIndex_ < 0)
+                    transparentIndex_ = i;
+                continue;
+            }
+        }
+
+        if (isGrayscale_) {
+            isGrayscale_ = c.GetR() == c.GetG() && c.GetR() == c.GetB();
+        }
+    }
+}
+
+int MyPalette::findNearestColorIndexSrgb(Gdiplus::Color color) {
+    assert(!hasMultiLevelAlpha_);
+    assert(color.GetA() >= alphaThreshold_ || !(transparentIndex_ >= 0));
+
+    int minDiff = INT_MAX;
+    int resultIndex = 0;
+
+    if (color.GetA() != 255) {
+        // blending the color with background and checking if there is an exact match now
+        color = BlendWithBackgroundSrgb(color, backColor_);
+        auto it = colorToIndex_.find(color.GetValue());
+        if (it != colorToIndex_.end())
+            return it->second;
+    }
+
+    // The two similar lookups could be merged, but it is faster to separate them even if some parts are duplicated
+    int len = palette_->Count;
+    if (isGrayscale_) {
+        BYTE brightness = GetBrightness(color);
+        for (int i = 0; i < len; i++) {
+            Gdiplus::Color current = palette_->Entries[i];
+
+            // Palette color with alpha
+            if (current.GetA() != 255) {
+                // Skipping fully transparent palette colors because they were handled in GetNearestColorIndex
+                continue;
+            }
+
+            // If the palette is grayscale, then distance is measured by perceived brightness.
+            int diff = std::abs(GetBrightness(current) - brightness);
+            if (diff >= minDiff)
+                continue;
+
+            // new closest match
+            if (diff == 0)
+                return i;
+            minDiff = diff;
+            resultIndex = i;
+        }
+    } else {
+
+        for (int i = 0; i < len; i++) {
+            Gdiplus::Color current = palette_->Entries[i];
+
+            // Palette color with alpha
+            if (current.GetA() != 255) {
+                // Skipping fully transparent palette colors because they were handled in GetNearestColorIndex
+                //Debug.Assert(current.A == 0, $ "If palette has partially transparent entries the {nameof(FindNearestColorIndexAlphaSrgb)} method should be used");
+                continue;
+            }
+
+            // If the palette is not grayscale, then distance is measured by Manhattan distance based on RGB components.
+            // Euclidean distance squared would provide a slightly different result in some cases but there is no good accelerated
+            // version for it using integers (DotProduct is available for floating point arguments only)
+            int diff;
+
+            {
+                diff = std::abs(current.GetR() - color.GetR()) + std::abs(current.GetG() - color.GetG()) + std::abs(current.GetB() - color.GetB());
+            }
+
+            assert(diff != 0);
+            if (diff >= minDiff)
+                continue;
+
+            // new closest match
+            minDiff = diff;
+            resultIndex = i;
+        }
+    }
+
+    return resultIndex;
+}
+
+int MyPalette::getNearestColorIndex(Gdiplus::Color c) {
+    // mapping alpha to full transparency
+    if (c.GetA() < alphaThreshold_ && transparentIndex_ >= 0)
+        return transparentIndex_;
+
+    // exact match: from the palette
+    auto it = colorToIndex_.find(c.GetValue());
+    if (it != colorToIndex_.end()) {
+        return it->second;
+    }
+
+    return findNearestColorIndexSrgb(c);
+}
+
+OctreeColorQuantizer::OctreeNode::~OctreeNode()
+{
+    for (auto* child: children_) {
+        delete child;
+    }
+}
+
+int OctreeColorQuantizer::OctreeNode::deepPixelCount()
+{
+    int result = pixelCount_;
+    /* if (children == null)
+        return result;*/
+
+    // Adding also the direct children because reducing the tree starts at level levelCount - 2.
+    // And due to reducing no more than two levels can have non-empty nodes.
+    for (int index = 0; index < 8; index++) {
+        OctreeNode* node = children_[index];
+        if (node != nullptr)
+            result += node->pixelCount_;
+    }
+
+    return result;
+}
+
+OctreeColorQuantizer::OctreeNode::OctreeNode(int level, OctreeColorQuantizer* parent) {
+    this->parent_ = parent;
+    assert(level < parent->levelCount_);
+
+    if (level >= 0) {
+        parent->levels_[level].push_back(this);
+    }
+}
+
+bool OctreeColorQuantizer::OctreeNode::addColor(Gdiplus::Color color, int level) {
+    // In the populating phase all colors are summed up in leaves at deepest level.
+    if (level == parent_->levelCount_) {
+        sumRed_ += color.GetR();
+        sumGreen_ += color.GetG();
+        sumBlue_ += color.GetB();
+        pixelCount_++;
+
+        // returning whether a new leaf has been added
+        return pixelCount_ == 1;
+    }
+
+    assert(level < parent_->levelCount_);
+
+    // Generating a 0..7 index based on the color components and adding new branches on demand.
+    int mask = 128 >> level;
+    int branchIndex = ((color.GetR() & mask) == mask ? 4 : 0)
+        | ((color.GetG() & mask) == mask ? 2 : 0)
+        | ((color.GetB() & mask) == mask ? 1 : 0);
+    OctreeNode* child = children_[branchIndex];
+
+    if (!child) {
+        child = new OctreeNode(level, parent_);
+    }
+    children_[branchIndex] = child;
+    return child->addColor(color, level + 1);
+}
+
+void OctreeColorQuantizer::OctreeNode::mergeNodes(int& leavesCount) {
+    auto CompareByBrightness = [](OctreeNode* a, OctreeNode* b) -> bool {
+        if (a == nullptr || b == nullptr)
+            return a == b ? false : a == nullptr ? true
+                                                 : false;
+
+        auto ca = a->toColor();
+        auto cb = b->toColor();
+        return GetBrightness(ca) < GetBrightness(cb);
+    };
+
+    auto CompareByWeightedBrightness = [this](OctreeNode* a, OctreeNode* b) -> bool {
+        if (a == nullptr || b == nullptr)
+            return a == b ? false : a == nullptr ? true
+                                                 : false;
+
+        auto ca = a->toColor();
+        auto cb = b->toColor();
+        return GetBrightness(ca) * (a->deepPixelCount() / (float)parent_->size_) < GetBrightness(cb) * (b->deepPixelCount() / (float)parent_->size_);
+    };
+
+    /* if (children == null)
+                return;*/
+
+    // If there are fewer than 8 removals left we sort them to merge the least relevant ones first.
+    // For 2 colors (and 3 + TR) the distance is measured purely by brightness to avoid returning very similar colors.
+    // Note: reordering children is not a problem because we don't add more colors in merging phase.
+    if (parent_->colorCount() - parent_->maxColors_ < 8) {
+        auto cc = parent_->maxColors_ - (parent_->hasTransparency_ ? 1 : 0);
+        if (cc <= 2)
+            std::sort(std::begin(children_), std::end(children_), CompareByBrightness);
+        else
+            std::sort(std::begin(children_), std::end(children_), CompareByWeightedBrightness);
+    }
+    for (int i = 0; i < 8; i++) {
+        OctreeNode* node = children_[i];
+        if (node == nullptr)
+            continue;
+
+        assert(!node->isEmpty());
+
+        // Decreasing only if this node is not becoming a "leaf" while cutting a branch down.
+        if (!isEmpty()) {
+            leavesCount--;
+        }
+
+        sumRed_ += node->sumRed_;
+        sumGreen_ += node->sumGreen_;
+        sumBlue_ += node->sumBlue_;
+        pixelCount_ += node->pixelCount_;
+
+        children_[i] = nullptr;
+        delete node;
+
+        // As we can return before merging all children,
+        // leavesCount may include "not-quite leaf" elements in the end.
+        if (parent_->colorCount() == parent_->maxColors_)
+            return;
+    }
+}
+
+void OctreeColorQuantizer::OctreeNode::populatePalette(Gdiplus::Color* result, int& palIndex, int& remainingColors) {
+    // if a non-empty node is found, adding it to the resulting palette
+    if (!isEmpty()) {
+        result[palIndex] = toColor();
+        palIndex += 1;
+        remainingColors -= 1;
+        if (remainingColors == 0)
+            return;
+    }
+
+    /* if (children == null || context.IsCancellationRequested)
+        return;*/
+
+    for (OctreeNode* child : children_) {
+        if (child == nullptr)
+            continue;
+        child->populatePalette(result, palIndex, remainingColors);
+        if (remainingColors == 0)
+            return;
+    }
+}
+
+OctreeColorQuantizer::OctreeColorQuantizer(int requestedColors, BYTE bitLevel, Gdiplus::BitmapData* source) {
+    maxColors_ = requestedColors;
+    size_ = source->Width * source->Height;
+
+    levelCount_ = bitLevel ? bitLevel : std::min<>(8, ToBitsPerPixel(requestedColors));
+    levels_.resize(levelCount_);
+
+    root_ = std::make_unique<OctreeNode>(-1, this);
+}
+
+unique_c_ptr<Gdiplus::ColorPalette> OctreeColorQuantizer::generatePalette() {
+    if (colorCount() > maxColors_)
+        reduceTree();
+
+    assert(colorCount() <= maxColors_);
+    //BYTE* result = new BYTE[sizeof(Gdiplus::ColorPalette) + leavesCount];
+    auto pal = make_unique_malloc<Gdiplus::ColorPalette>(sizeof(Gdiplus::ColorPalette) + leavesCount_ * sizeof(Gdiplus::ARGB));
+    pal->Count = leavesCount_;
+    pal->Flags = Gdiplus::PaletteFlagsHasAlpha;
+
+    if (leavesCount_ > 0) {
+        int palIndex = 0;
+        root_->populatePalette(reinterpret_cast<Gdiplus::Color*>(pal->Entries), palIndex, leavesCount_);
+        assert(leavesCount_ == 0);
+    }
+
+    // If transparent color is needed, then it will be automatically the last color in the result
+    return pal;
+}
+
+void OctreeColorQuantizer::reduceTree() {
+    // Scanning all levels towards root. Leaves are skipped (hence -2) because they are not reducible.
+    for (int level = levelCount_ - 2; level >= 0; level--) {
+        if (levels_[level].size() == 0)
+            continue;
+
+        // Sorting nodes of the current level (least significant ones first)
+        // while merging them into their parents until we go under MaxColors
+        auto& nodes = levels_[level];
+        std::sort(nodes.begin(), nodes.end(), [](auto* a, auto* b) { return a->deepPixelCount() < b->deepPixelCount(); });
+        //nodes.Sort((a, b) = > a.DeepPixelCount - b.DeepPixelCount);
+
+        for (OctreeNode* node : nodes) {
+            // As merging is stopped when we reach MaxColors.
+            // leavesCount may include some half-merged non-leaf nodes as well.
+            node->mergeNodes(leavesCount_);
+            if (colorCount() <= maxColors_)
+                return;
+        }
+    }
+
+    // If we are here, we need to reduce also the root node (less than 8 colors or 8 colors + transparency)
+    root_->mergeNodes(leavesCount_);
+    assert(colorCount() == maxColors_);
+}
diff --git a/Source/Core/Images/OctreeColorQuantizer.h b/Source/Core/Images/OctreeColorQuantizer.h
new file mode 100644
index 000000000..703177cfa
--- /dev/null
+++ b/Source/Core/Images/OctreeColorQuantizer.h
@@ -0,0 +1,101 @@
+// This class section was inspired by KGySoft.Drawing
+// https://github.com/koszeggy/KGySoft.Drawing, which is under the KGy SOFT License.
+// Original source code: https://github.com/koszeggy/KGySoft.Drawing/blob/master/KGySoft.Drawing.Core/Drawing/Imaging/_Quantizers/OptimizedPaletteQuantizer.Octree.cs
+// The KGy SOFT License is available at https://github.com/koszeggy/KGySoft.Drawing/blob/master/LICENSE
+
+#pragma once
+#include <algorithm>
+#include <vector>
+#include <cassert>
+#include <memory>
+#include <unordered_map>
+
+#include <windows.h>
+
+#include "3rdpart/GdiplusH.h"
+#include "Core/Utils/CoreUtils.h"
+
+Gdiplus::Color BlendWithBackgroundSrgb(Gdiplus::Color c, Gdiplus::Color backColor);
+
+class MyPalette {
+public:
+    explicit MyPalette(Gdiplus::ColorPalette* palette, Gdiplus::Color backColor, BYTE alphaThreshold = 128);
+    int findNearestColorIndexSrgb(Gdiplus::Color color);
+    int getNearestColorIndex(Gdiplus::Color c);
+private:
+    Gdiplus::ColorPalette* palette_;
+    std::unordered_map<Gdiplus::ARGB, int> colorToIndex_;
+    Gdiplus::Color backColor_;
+    BYTE alphaThreshold_;
+    int transparentIndex_;
+    bool hasMultiLevelAlpha_ = false;
+    bool hasAlpha_ = false;
+    bool isGrayscale_ = false;
+};
+
+class OctreeColorQuantizer {
+
+public:
+    class OctreeNode {
+    private:
+        OctreeColorQuantizer* parent_ = nullptr;
+
+        unsigned sumRed_ = 0;
+        unsigned sumGreen_ = 0;
+        unsigned sumBlue_ = 0;
+        int pixelCount_ = 0;
+        OctreeNode* children_[8] {};
+    public:
+        OctreeNode(int level, OctreeColorQuantizer* parent);
+        ~OctreeNode();
+        int deepPixelCount();
+
+        bool isEmpty()
+        {
+            return pixelCount_ == 0;
+        }
+
+        bool addColor(Gdiplus::Color color, int level);
+
+        void mergeNodes(int& leavesCount);
+
+        Gdiplus::Color toColor()
+        {
+            assert(!isEmpty());
+            return pixelCount_ == 1
+                ? Gdiplus::Color((byte)sumRed_, (byte)sumGreen_, (byte)sumBlue_)
+                : Gdiplus::Color((byte)(sumRed_ / pixelCount_), (byte)(sumGreen_ / pixelCount_), (byte)(sumBlue_ / pixelCount_));
+        }
+
+        void populatePalette(Gdiplus::Color* result, int& palIndex, int& remainingColors);
+    };
+
+    private:
+        int maxColors_ = 0;
+        int size_ = 0;
+        int levelCount_ = 0;
+        bool hasTransparency_ = false;
+        std::vector<std::vector<OctreeNode*>> levels_;
+        std::unique_ptr<OctreeNode> root_;
+        int leavesCount_ = 0;
+
+        int colorCount()
+        {
+            return leavesCount_ + (hasTransparency_ ? 1 : 0);
+        }
+
+    public:
+        explicit OctreeColorQuantizer(int requestedColors, BYTE bitLevel, Gdiplus::BitmapData* source);
+
+        void addColor(Gdiplus::Color color) {
+            if (color.GetA() == 0) {
+                hasTransparency_ = true;
+            } else if (root_->addColor(color, 0)) {
+                leavesCount_++;
+            }
+        }
+
+        unique_c_ptr<Gdiplus::ColorPalette> generatePalette();
+
+        void reduceTree();
+};
diff --git a/Source/Core/Images/Utils.cpp b/Source/Core/Images/Utils.cpp
index 4f5adad1e..ebd429cd0 100644
--- a/Source/Core/Images/Utils.cpp
+++ b/Source/Core/Images/Utils.cpp
@@ -34,7 +34,7 @@
 #include "3rdpart/GdiplusH.h"
 #include "Core/Logging.h"
 #include "Func/WinUtils.h"
-#include "3rdpart/QColorQuantizer.h"
+#include "Core/Images/ColorQuantizer.h"
 #include "Core/Utils/StringUtils.h"
 #include "Core/Utils/CoreUtils.h"
 #include "Func/IuCommonFunctions.h"
@@ -651,8 +651,8 @@ bool SaveImageToFile(Gdiplus::Bitmap* img, const CString& fileName, IStream* str
         eps.Parameter[0].NumberOfValues = 1;
         eps.Parameter[0].Value = &Quality;
     } else if (Format == sifGIF) { // GIF
-        QColorQuantizer quantizer;
-        quantizedImage.reset(quantizer.GetQuantized(img, QColorQuantizer::Octree, (Quality < 50) ? 16 : 256));
+        ColorQuantizer quantizer;
+        quantizedImage = quantizer.getQuantized(img, (Quality < 50) ? 16 : 256);
         if (quantizedImage) {
             img = quantizedImage.get();
         }
diff --git a/Source/Gui/CMakeLists.txt b/Source/Gui/CMakeLists.txt
index ee067a3df..2df3d437d 100644
--- a/Source/Gui/CMakeLists.txt
+++ b/Source/Gui/CMakeLists.txt
@@ -279,7 +279,6 @@ set(HEADER_LIST
     Helpers/LangHelper.h
     ../3rdpart/ColorButton.h
     ../3rdpart/MemberFunctionCallback.h
-    ../3rdpart/QColorQuantizer.h
     ../3rdpart/Registry.h
     ../3rdpart/Unzipper.h
     ../3rdpart/vkCodes.h
diff --git a/Source/Tests/CMakeLists.txt b/Source/Tests/CMakeLists.txt
index b68dc5203..26c5f0571 100644
--- a/Source/Tests/CMakeLists.txt
+++ b/Source/Tests/CMakeLists.txt
@@ -32,7 +32,6 @@ if(WIN32)
         ../Core/Images/Thumbnail.cpp
         ../Core/3rdpart/parser.cpp
         ../Func/MyUtils.cpp
-        ../3rdpart/QColorQuantizer.cpp
         ../Core/FileDownloader.cpp
         ../Core/FileDownloaderTest.cpp
         ../Video/Tests/VideoGrabberTest.cpp