- inaccurate palettes, holding slighty changed RGB-values
- uncalled-for dithering in the returned image, rendering it useless (unless a photo)
- wxImage_Quantize() below gives you a much better chance of receiving sensible palettes and images back from different types of images, as it will bypass wxQuantize unless really needed.
- The implementation is somewhat hampered by wx not providing the wxColourArray class, sorely missed (together with wxBitmapArray and wxIconArray - wxImageArray exists but is somewhat hidden away). Also, the helper function wxColourArray_ToPalette may hurt your eyes (the newnewnew + delete[]delete[]delete[] construct is tremendously ugly)
- Compiles with VC/gcc/MinGW, wxMSW/wxGTK, 2.8.9/2.9
- More info:
http://trac.wxwidgets.org/ticket/4539
http://trac.wxwidgets.org/ticket/9765
Code: Select all
class wxImage;
extern bool wxImage_Quantize(wxImage* image, size_t desiredNoColours,
unsigned char** eightBitData = NULL, size_t colorcount = 0);
//////////////////////////////////////////////////
#include <wx/wx.h>
#include <wx/quantize.h>
#include <wx/arrimpl.cpp>
#if wxUSE_STL
#include <vector>
#include <algorithm>
class wxColourArray : public std::vector<wxColour>
{
public:
size_t index(const wxColour& color)
{
return std::distance(begin(), std::find(begin(), end(), color));
}
};
#else
#include <wx/dynarray.h>
WX_DECLARE_OBJARRAY(wxColour, wxColourArrayBase);
class wxColourArray : public wxColourArrayBase
{
public:
void clear() { Clear(); }
const wxColour& at(size_t index) const { return Item(index); }
// According to docs wxObjArray.Index() is not working -
// and indeed it isn't. Roll our own...
size_t index(const wxColour& color)
{
size_t i;
for (i = 0; i < GetCount(); i++)
{
if (at(i) == color) break;
}
return i;
}
};
WX_DEFINE_OBJARRAY(wxColourArrayBase)
#endif
#if wxUSE_PALETTE
size_t wxColourArray_ToPalette(const wxColourArray& array, wxPalette* pal)
{
const size_t count = array.size();
unsigned char* r = new unsigned char[count];
unsigned char* g = new unsigned char[count];
unsigned char* b = new unsigned char[count];
for (size_t i = 0; i < count; i++)
{
const wxColour& col = array.at(i);
r[i] = col.Red();
g[i] = col.Green();
b[i] = col.Blue();
}
pal->operator=(wxPalette(count, r, g, b));
wxDELETEA(r);
wxDELETEA(g);
wxDELETEA(b);
return count;
}
#endif
bool wxImage_Quantize(wxImage* image, size_t desiredNoColours,
unsigned char** eightBitData, size_t colorcount)
{
bool ok = true;
wxPalette* pal = NULL;
if (0 == colorcount) colorcount = image->CountColours(); // expensive
// Only do quantizing if absolutely required -
// wxQuantize works well for jpeg-type photo quality images
// but is known to ruin images of other types
if (colorcount > desiredNoColours)
{
// reduce BPP
wxImage temp;
ok = wxQuantize::Quantize(*image, temp, &pal, desiredNoColours,
eightBitData, wxQUANTIZE_FILL_DESTINATION_IMAGE
| (eightBitData ? wxQUANTIZE_RETURN_8BIT_DATA : 0)
);
if (ok)
{
image->operator=(temp);
}
}
else if (colorcount <= 256)
{
// Quantizing not required -
// just create and attach corresponding palette
wxColourArray array;
const int width = image->GetWidth();
const int height = image->GetHeight();
if (eightBitData) *eightBitData = new wxByte[width*height];
wxByte* bits = image->GetData();
for (int j = 0; j < height; j++)
{
for (int i = 0; i < width; i++)
{
const int index = j*width + i;
const int index_rgb = index*3;
const wxColour color(
bits[index_rgb + 0],
bits[index_rgb + 1],
bits[index_rgb + 2]
);
const size_t pal_index = array.index(color);
if (pal_index == array.size())
{
array.push_back(color);
}
if (eightBitData) (*eightBitData)[index] = (wxByte)pal_index;
}
}
wxASSERT(array.size() == colorcount);
#if wxUSE_PALETTE
pal = new wxPalette;
wxColourArray_ToPalette(array, pal);
#endif // wxUSE_PALETTE/!wxUSE_PALETTE
}
#if wxUSE_PALETTE
if (pal)
{
if (ok) image->SetPalette(*pal);
wxDELETE(pal);
}
#endif // wxUSE_PALETTE/!wxUSE_PALETTE
return ok;
}