diff --git a/README.html b/README.html
index 971559d4a..8852d6f5e 100644
--- a/README.html
+++ b/README.html
@@ -175,8 +175,6 @@ data/widgets/*.xml XML files with dialogs
Jinete and Vaca are under BSD license.
Allegro is giftware license (similar to MIT license).
ALFONT is under LGPL license.
- quantize.c is copyright by Ben Davis (you need his authorization to use his code in your own program).
- quantize2.c is distributed under the MIT license
Lua-5.0 is distributed under the MIT license
GIMP is distributed under GPL license
GLib and GTK+ are distributed under LGPL license.
@@ -239,8 +237,7 @@ data/widgets/*.xml XML files with dialogs
Álvaro González
- For the other routine to generate
- optimized palettes (used in old versions of ASE).
+ For color quantization routines (used in old versions of ASE).
Angelo Mottola
@@ -249,7 +246,7 @@ data/widgets/*.xml XML files with dialogs
Ben Davis
- For his optimized palette generation routine.
+ For color quantization routines (used in old versions of ASE).
Billy Biggs and Lauris Kaplinski
diff --git a/TODO.txt b/TODO.txt
index 91710a8a0..335d3c41e 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -86,8 +86,6 @@ Low priority stuff
load/save_col_file.
- fix the fli reader (both Allegro and Gfli): when a frame hasn't
chunks means that it's looks like the last frame;
-- talk with Ben Davis, his "quantize.c" has memory leaks (test it
- more, I don't think so);
- talk with Elias Pschernig, his "do_ellipse_diameter" (algo_ellipse)
has a bug with ellipses of some dimensions (I made a test, and a
various sizes have errors).
diff --git a/src/raster/color_histogram.h b/src/raster/color_histogram.h
new file mode 100644
index 000000000..7616430e2
--- /dev/null
+++ b/src/raster/color_histogram.h
@@ -0,0 +1,142 @@
+/* ASE - Allegro Sprite Editor
+ * Copyright (C) 2001-2010 David Capello
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef RASTER_COLOR_HISTOGRAM_H_INCLUDED
+#define RASTER_COLOR_HISTOGRAM_H_INCLUDED
+
+#include
+#include "raster/image.h"
+#include "raster/image_traits.h"
+#include "raster/median_cut.h"
+#include "raster/palette.h"
+
+namespace quantization {
+
+template // Number of bits for each component in the histogram
+class ColorHistogram
+{
+public:
+ // Number of elements in histogram for each RGB component
+ enum {
+ RElements = 1 << RBits,
+ GElements = 1 << GBits,
+ BElements = 1 << BBits
+ };
+
+ ColorHistogram()
+ : m_histogram(RElements*GElements*BElements, 0)
+ , m_useHighPrecision(true)
+ {
+ }
+
+ // Returns the number of points in the specified histogram
+ // entry. Each index (i, j, k) is in the range of the
+ // histogram i=[0,RElements), etc.
+ size_t at(int i, int j, int k) const
+ {
+ return m_histogram[histogramIndex(i, j, k)];
+ }
+
+ // Add the specified "color" in the histogram as many times as the
+ // specified value in "count".
+ void addSamples(ase_uint32 color, size_t count = 1)
+ {
+ int i = histogramIndex(color);
+
+ if (m_histogram[i] < std::numeric_limits::max()-count) // Avoid overflow
+ m_histogram[i] += count;
+ else
+ m_histogram[i] = std::numeric_limits::max();
+
+ // Accurate colors are used only for less than 256 colors. If the
+ // image has more than 256 colors the m_histogram is used
+ // instead.
+ if (m_useHighPrecision) {
+ std::vector::iterator it =
+ std::find(m_highPrecision.begin(), m_highPrecision.end(), color);
+
+ // The color is not in the high-precision table
+ if (it == m_highPrecision.end()) {
+ if (m_highPrecision.size() < 256) {
+ m_highPrecision.push_back(color);
+ }
+ else {
+ // In this case we reach the limit for the high-precision histogram.
+ m_useHighPrecision = false;
+ }
+ }
+ }
+ }
+
+ // Creates a set of entries for the given palette in the given range
+ // with the more important colors in the histogram. Returns the
+ // number of used entries in the palette (maybe the range [from,to]
+ // is more than necessary).
+ int createOptimizedPalette(Palette* palette, int from, int to)
+ {
+ // Can we use the high-precision table?
+ if (m_useHighPrecision && int(m_highPrecision.size()) <= (to-from+1)) {
+ for (int i=0; i<(int)m_highPrecision.size(); ++i)
+ palette->setEntry(from+i, m_highPrecision[i]);
+
+ return m_highPrecision.size();
+ }
+ // OK, we have to use the histogram and some algorithm (like
+ // median-cut) to quantize "optimal" colors.
+ else {
+ std::vector result;
+ median_cut(*this, to-from+1, result);
+
+ for (int i=0; i<(int)result.size(); ++i)
+ palette->setEntry(from+i, result[i]);
+
+ return result.size();
+ }
+ }
+
+private:
+ // Converts input color in a index for the histogram. It reduces
+ // each 8-bit component to the resolution given in the template
+ // parameters.
+ size_t histogramIndex(ase_uint32 color) const
+ {
+ return histogramIndex((_rgba_getr(color) >> (8 - RBits)),
+ (_rgba_getg(color) >> (8 - GBits)),
+ (_rgba_getb(color) >> (8 - BBits)));
+ }
+
+ size_t histogramIndex(int i, int j, int k) const
+ {
+ return i | (j << RBits) | (k << (RBits+GBits));
+ }
+
+ // 3D histogram (the index in the histogram is calculated through histogramIndex() function).
+ std::vector m_histogram;
+
+ // High precision histogram to create an accurate palette if RGB
+ // source images contains less than 256 colors.
+ std::vector m_highPrecision;
+
+ // True if we can use m_highPrecision still (it means that the
+ // number of different samples is less than 256 colors still).
+ bool m_useHighPrecision;
+};
+
+}
+
+#endif
diff --git a/src/raster/median_cut.h b/src/raster/median_cut.h
new file mode 100644
index 000000000..1657cdcd5
--- /dev/null
+++ b/src/raster/median_cut.h
@@ -0,0 +1,302 @@
+/* ASE - Allegro Sprite Editor
+ * Copyright (C) 2001-2010 David Capello
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef RASTER_MEDIAN_CUT_H_INCLUDED
+#define RASTER_MEDIAN_CUT_H_INCLUDED
+
+#include
+#include
+
+namespace quantization {
+
+ template
+ class Box {
+
+ // These classes are used as parameters for some Box's generic
+ // member functions, so we can access to a different axis using
+ // the same generic function (i=Red channel in RAxisGetter, etc.).
+ struct RAxisGetter { static size_t at(const Histogram& h, int i, int j, int k) { return h.at(i, j, k); } };
+ struct GAxisGetter { static size_t at(const Histogram& h, int i, int j, int k) { return h.at(j, i, k); } };
+ struct BAxisGetter { static size_t at(const Histogram& h, int i, int j, int k) { return h.at(j, k, i); } };
+
+ // These classes are used as template parameter to split a Box
+ // along an axis (see splitAlongAxis)
+ struct RAxisSplitter {
+ static Box box1(const Box& box, int r) { return Box(box.r1, box.g1, box.b1, r, box.g2, box.b2); }
+ static Box box2(const Box& box, int r) { return Box(r, box.g1, box.b1, box.r2, box.g2, box.b2); }
+ };
+ struct GAxisSplitter {
+ static Box box1(const Box& box, int g) { return Box(box.r1, box.g1, box.b1, box.r2, g, box.b2); }
+ static Box box2(const Box& box, int g) { return Box(box.r1, g, box.b1, box.r2, box.g2, box.b2); }
+ };
+ struct BAxisSplitter {
+ static Box box1(const Box& box, int b) { return Box(box.r1, box.g1, box.b1, box.r2, box.g2, b ); }
+ static Box box2(const Box& box, int b) { return Box(box.r1, box.g1, b, box.r2, box.g2, box.b2); }
+ };
+
+ public:
+ Box(int r1, int g1, int b1,
+ int r2, int g2, int b2)
+ : r1(r1), g1(g1), b1(b1)
+ , r2(r2), g2(g2), b2(b2)
+ , volume(calculateVolume())
+ , points(0) { }
+
+ // Shrinks each plane of the box to a position where there are
+ // points in the histogram.
+ void shrink(const Histogram& histogram)
+ {
+ axisShrink(histogram, r1, r2, g1, g2, b1, b2);
+ axisShrink(histogram, g1, g2, r1, r2, b1, b2);
+ axisShrink(histogram, b1, b2, r1, r2, g1, g2);
+
+ // Calculate number of points inside the box (this is done by
+ // first time here, because the Box ctor didn't calculate it).
+ points = countPoints(histogram);
+
+ // Recalculate the volume (used in operator<).
+ volume = calculateVolume();
+ }
+
+ bool split(const Histogram& histogram, std::priority_queue& boxes) const
+ {
+ // Split along the largest dimension of the box.
+ if ((r2-r1) >= (g2-g1) && (r2-r1) >= (b2-b1)) {
+ return splitAlongAxis(histogram, boxes, r1, r2, g1, g2, b1, b2);
+ }
+ else if ((g2-g1) >= (r2-r1) && (g2-g1) >= (b2-b1)) {
+ return splitAlongAxis(histogram, boxes, g1, g2, r1, r2, b1, b2);
+ }
+ else {
+ return splitAlongAxis(histogram, boxes, b1, b2, r1, r2, g1, g2);
+ }
+ }
+
+ // Returns the color enclosed by the box calculating the mean of
+ // all histogram's points inside the box.
+ ase_uint32 meanColor(const Histogram& histogram) const
+ {
+ size_t r = 0, g = 0, b = 0;
+ size_t count = 0;
+ int i, j, k;
+
+ for (i=r1; i<=r2; ++i)
+ for (j=g1; j<=g2; ++j)
+ for (k=b1; k<=b2; ++k) {
+ int c = histogram.at(i, j, k);
+ r += c * i;
+ g += c * j;
+ b += c * k;
+ count += c;
+ }
+
+ // No colors in the box? This should not be possible.
+ assert(count > 0 && "Box without histogram points, you must fill the histogram before using this function.");
+ if (count == 0)
+ return _rgba(0, 0, 0, 255);
+
+ // Returns the mean.
+ return _rgba((255 * r / (Histogram::RElements-1)) / count,
+ (255 * g / (Histogram::GElements-1)) / count,
+ (255 * b / (Histogram::BElements-1)) / count, 255);
+ }
+
+ // The boxes will be sort in the priority_queue by volume.
+ bool operator<(const Box& other) const
+ {
+ return volume < other.volume;
+ }
+
+ private:
+
+ // Calculates the volume from the current box's dimensions. The
+ // value returned by this function is cached in the "volume"
+ // variable member of Box class to avoid multiplying several
+ // times.
+ int calculateVolume() const
+ {
+ return (r2-r1+1) * (g2-g1+1) * (b2-b1+1);
+ }
+
+ // Returns the number of histogram's points inside the box bounds.
+ size_t countPoints(const Histogram& histogram) const
+ {
+ size_t count = 0;
+ int i, j, k;
+
+ for (i=r1; i<=r2; ++i)
+ for (j=g1; j<=g2; ++j)
+ for (k=b1; k<=b2; ++k)
+ count += histogram.at(i, j, k);
+
+ return count;
+ }
+
+ // Reduces the specified side of the box (i1/i2) along the
+ // specified axis (if AxisGetter is RAxisGetter, then i1=r1,
+ // i2=r2; if AxisGetter is GAxisGetter, then i1=g1, i2=g2).
+ template
+ static void axisShrink(const Histogram& histogram,
+ int& i1, int& i2,
+ const int& j1, const int& j2,
+ const int& k1, const int& k2)
+ {
+ int j, k;
+
+ // Shrink i1.
+ for (; i1 0)
+ goto doneA;
+ }
+ }
+ }
+
+ doneA:;
+
+ for (; i2>i1; --i2) {
+ for (j=j1; j<=j2; ++j) {
+ for (k=k1; k<=k2; ++k) {
+ if (AxisGetter::at(histogram, i2, j, k) > 0)
+ goto doneB;
+ }
+ }
+ }
+
+ doneB:;
+ }
+
+ // Splits the box in two sub-boxes (if it's possible) along the
+ // specified axis by AxisGetter template parameter and "i1/i2"
+ // arguments. Returns true if the split was done and the "boxes"
+ // queue contains the new two sub-boxes resulting from the split
+ // operation.
+ template
+ bool splitAlongAxis(const Histogram& histogram,
+ std::priority_queue& boxes,
+ const int& i1, const int& i2,
+ const int& j1, const int& j2,
+ const int& k1, const int& k2) const
+ {
+ // These two variables will be used to count how many points are
+ // in each side of the box if we split it in "i" position.
+ size_t totalPoints1 = 0;
+ size_t totalPoints2 = this->points;
+ int i, j, k;
+
+ // We will try to split the box along the "i" axis. Imagine a
+ // plane which its normal vector is "i" axis, so we will try to
+ // move this plane from "i1" to "i2" to find the median, where
+ // the number of points in both sides of the plane are
+ // approximated the same.
+ for (i=i1; i<=i2; ++i) {
+ size_t planePoints = 0;
+
+ // We count all points in "i" plane.
+ for (j=j1; j<=j2; ++j)
+ for (k=k1; k<=k2; ++k)
+ planePoints += AxisGetter::at(histogram, i, j, k);
+
+ // As we move the plane to split through "i" axis One side is getting more points,
+ totalPoints1 += planePoints;
+ totalPoints2 -= planePoints;
+
+ if (totalPoints1 > totalPoints2) {
+ if (totalPoints2 > 0) {
+ Box box1(AxisSplitter::box1(*this, i));
+ Box box2(AxisSplitter::box2(*this, i+1));
+ box1.points = totalPoints1;
+ box2.points = totalPoints2;
+ boxes.push(box1);
+ boxes.push(box2);
+ return true;
+ }
+ else if (totalPoints1-planePoints > 0) {
+ Box box1(AxisSplitter::box1(*this, i-1));
+ Box box2(AxisSplitter::box2(*this, i));
+ box1.points = totalPoints1-planePoints;
+ box2.points = totalPoints2+planePoints;
+ boxes.push(box1);
+ boxes.push(box2);
+ return true;
+ }
+ else
+ return false;
+ }
+ }
+ return false;
+ }
+
+ int r1, g1, b1; // Min point (closest to origin)
+ int r2, g2, b2; // Max point
+ size_t points; // Number of points in the space which enclose this box
+ int volume;
+ }; // end of class Box
+
+ // Median Cut Alqorithm as described in P. Heckbert, “Color image
+ // quantization for frame buffer display,”, Computer Graphics,
+ // 16(3), pp. 297-307 (1982)
+ template
+ void median_cut(const Histogram& histogram, size_t maxBoxes, std::vector& result)
+ {
+ // We need a priority queue to split bigger boxes first (see Box::operator<).
+ std::priority_queue > boxes;
+
+ // First we start with one big box containing all histogram's samples.
+ boxes.push(Box(0, 0, 0,
+ Histogram::RElements-1,
+ Histogram::GElements-1,
+ Histogram::BElements-1));
+
+ // Then we split each box until we reach the maximum specified by
+ // the user (maxBoxes) or until there aren't more boxes to split.
+ while (!boxes.empty() && boxes.size() < maxBoxes) {
+ // Get and remove the first (bigger) box to process from "boxes" queue.
+ Box box(boxes.top());
+ boxes.pop();
+
+ // Shrink the box to the minimum, to enclose the same points in
+ // the histogram.
+ box.shrink(histogram);
+
+ // Try to split the box along the largest axis.
+ if (!box.split(histogram, boxes)) {
+ // If we were not able to split the box (maybe because it is
+ // too small or there are not enough points to split it), then
+ // we add the box's color to the "result" vector directly (the
+ // box is not in the queue anymore).
+ if (result.size() < maxBoxes)
+ result.push_back(box.meanColor(histogram));
+ else
+ return;
+ }
+ }
+
+ // When we reach the maximum number of boxes, we convert each box
+ // to a color for the "result" vector.
+ while (!boxes.empty() && result.size() < maxBoxes) {
+ const Box& box(boxes.top());
+ result.push_back(box.meanColor(histogram));
+ boxes.pop();
+ }
+ }
+
+}
+
+#endif
diff --git a/src/raster/quantization.cpp b/src/raster/quantization.cpp
index b7c1726a2..131906701 100644
--- a/src/raster/quantization.cpp
+++ b/src/raster/quantization.cpp
@@ -18,9 +18,14 @@
#include "config.h"
+#include
+#include
+#include
+
#include "gfx/hsv.h"
#include "gfx/rgb.h"
#include "raster/blend.h"
+#include "raster/color_histogram.h"
#include "raster/image.h"
#include "raster/images_collector.h"
#include "raster/palette.h"
@@ -36,10 +41,11 @@ static Image* ordered_dithering(const Image* src_image,
const RgbMap* rgbmap,
const Palette* palette);
-static int create_palette_from_bitmaps(Image** image, int nimage, RGB* pal, int* bmp_i, int fill_other);
+static void create_palette_from_bitmaps(const std::vector& images, Palette* palette, bool has_background_layer);
Palette* quantization::create_palette_from_rgb(const Sprite* sprite)
{
+ bool has_background_layer = (sprite->getBackgroundLayer() != NULL);
Palette* palette = new Palette(0, 256);
Image* flat_image;
@@ -63,20 +69,7 @@ Palette* quantization::create_palette_from_rgb(const Sprite* sprite)
image_array[c++] = flat_image; // The 'flat_image'
// Generate an optimized palette for all images
- {
- PALETTE rgbpal;
-
- for (c=0; c<256; c++)
- rgbpal[c].r = rgbpal[c].g = rgbpal[c].b = 255;
-
- std::vector ibmp(nimage);
- for (c=0; cfromAllegro(rgbpal);
- }
+ create_palette_from_bitmaps(image_array, palette, has_background_layer);
delete flat_image;
return palette;
@@ -328,299 +321,41 @@ static Image* ordered_dithering(const Image* src_image,
return dst_image;
}
-/* quantize.c
- * Copyright (C) 2000-2002 by Ben "entheh" Davis
- *
- * Nobody can use these routines without the explicit
- * permission of Ben Davis: entheh@users.sourceforge.net
- *
- * Adapted to ASE by David A. Capello.
- *
- * See "LEGAL.txt" for details.
-/ */
+//////////////////////////////////////////////////////////////////////
+// Creation of optimized palette for RGB images
+// by David Capello
-/* HACK ALERT: We use 'filler' to represent whether a colour in the palette
- * is reserved. This is only tested when the .r element can no longer
- * indicate whether an entry is reserved, i.e. when converting bitmaps.
- * NOTE: When converting bitmaps, they will not use reserved colours.
- */
-
-#define TREE_DEPTH 2
-/* TREE_DEPTH should be a power of 2. The lower it is, the deeper the tree
- * will go. This will not usually affect the accuracy of colours generated,
- * but with images with very subtle changes of colour, the variation can be
- * lost with higher values of TREE_DEPTH.
- *
- * As these trees go deeper they use an extortionate amount of memory. If it
- * runs out, you have no choice but to increase the value of TREE_DEPTH.
- */
-
-/* create_palette_from_bitmaps:
- * generates an optimised palette for the list of
- * bitmaps specified. All bitmaps must be true-colour. The number of bitmaps
- * must be passed in n_bmp, and bmp must point to an array of pointers to
- * BITMAP structures. bmp_i should point to an array parallel to bmp,
- * containing importance values for the bitmaps. pal must point to a PALETTE
- * structure which will be filled with the optimised palette.
- *
- * pal will be scanned for predefined colours. Any entry where the .r element
- * is 255 will be considered a free entry. All others are taken as predefined
- * colours. Predefined colours will not be changed, and the rest of the
- * palette will be unaffected by them. There may be copies of these colours.
- *
- * If in the bitmaps this routine finds any occurrence of the colour to which
- * palette entry 0 is set, it will be treated as a transparency colour. It
- * will not be considered when generating the palette.
- *
- * Under some circumstances there will be palette entries left over. If
- * fill_other != 0, they will be filled with black. If fill_other == 0, they
- * will be left containing whatever values they contained before.
- *
- * This function does not convert the bitmaps to 256-colour format. The
- * conversion must be done afterwards by the main program.
- */
-
-typedef struct PALETTE_NODE
+static void create_palette_from_bitmaps(const std::vector& images, Palette* palette, bool has_background_layer)
{
- unsigned int rl,gl,bl,rh,gh,bh;
- unsigned int n1,n2,Sr,Sg,Sb,E;
- struct PALETTE_NODE *parent;
- struct PALETTE_NODE *subnode[2][2][2];
-} PALETTE_NODE;
+ quantization::ColorHistogram<5, 6, 5> histogram;
+ ase_uint32 color;
+ RgbTraits::address_t address;
-static PALETTE_NODE *rgb_node[64][64][64];
+ // If the sprite has a background layer, the first entry can be
+ // used, in other case the 0 indexed will be the mask color, so it
+ // will not be used later in the color conversion (from RGB to
+ // Indexed).
+ int first_usable_entry = (has_background_layer ? 0: 1);
-static PALETTE_NODE *create_node(unsigned int rl,
- unsigned int gl,
- unsigned int bl,
- unsigned int rh,
- unsigned int gh,
- unsigned int bh,PALETTE_NODE *parent)
-{
- PALETTE_NODE *node;
- unsigned int rm,gm,bm;
- node = new PALETTE_NODE;
- node->rl=rl;
- node->gl=gl;
- node->bl=bl;
- node->rh=rh;
- node->gh=gh;
- node->bh=bh;
- node->E=node->Sb=node->Sg=node->Sr=node->n2=node->n1=0;
- node->parent=parent;
- if (rh-rl>TREE_DEPTH) {
- rm=(rl+rh)>>1;
- gm=(gl+gh)>>1;
- bm=(bl+bh)>>1;
- node->subnode[0][0][0]=create_node(rl,gl,bl,rm,gm,bm,node);
- node->subnode[0][0][1]=create_node(rm,gl,bl,rh,gm,bm,node);
- node->subnode[0][1][0]=create_node(rl,gm,bl,rm,gh,bm,node);
- node->subnode[0][1][1]=create_node(rm,gm,bl,rh,gh,bm,node);
- node->subnode[1][0][0]=create_node(rl,gl,bm,rm,gm,bh,node);
- node->subnode[1][0][1]=create_node(rm,gl,bm,rh,gm,bh,node);
- node->subnode[1][1][0]=create_node(rl,gm,bm,rm,gh,bh,node);
- node->subnode[1][1][1]=create_node(rm,gm,bm,rh,gh,bh,node);
- } else {
- for (bm=bl;bmh; ++y) {
+ address = image_address_fast(image, 0, y);
+
+ for (int x=0; xw; ++x) {
+ color = *address;
+
+ if (_rgba_geta(color) > 0) {
+ color |= _rgba(0, 0, 0, 255);
+ histogram.addSamples(color, 1);
}
- }
- }
- node->subnode[1][1][1]=
- node->subnode[1][1][0]=
- node->subnode[1][0][1]=
- node->subnode[1][0][0]=
- node->subnode[0][1][1]=
- node->subnode[0][1][0]=
- node->subnode[0][0][1]=
- node->subnode[0][0][0]=0;
- }
- return node;
-}
-static PALETTE_NODE *collapse_empty(PALETTE_NODE *node,unsigned int *n_colours)
-{
- unsigned int b,g,r;
- for (b=0;b<2;b++) {
- for (g=0;g<2;g++) {
- for (r=0;r<2;r++) {
- if (node->subnode[b][g][r])
- node->subnode[b][g][r]=collapse_empty(node->subnode[b][g][r],n_colours);
+ ++address;
}
}
}
- if (node->n1==0) {
- delete node;
- node=0;
- } else if (node->n2) (*n_colours)++;
- return node;
-}
-
-static PALETTE_NODE *collapse_nodes(PALETTE_NODE *node,unsigned int *n_colours,
- unsigned int n_entries,unsigned int Ep)
-{
- unsigned int b,g,r;
- if (node->E<=Ep) {
- for (b=0;b<2;b++) {
- for (g=0;g<2;g++) {
- for (r=0;r<2;r++) {
- if (node->subnode[b][g][r]) {
- node->subnode[b][g][r]=collapse_nodes(node->subnode[b][g][r],
- n_colours,n_entries,0);
- if (*n_colours<=n_entries) return node;
- }
- }
- }
- }
- if (node->parent->n2) (*n_colours)--;
- node->parent->n2+=node->n2;
- node->parent->Sr+=node->Sr;
- node->parent->Sg+=node->Sg;
- node->parent->Sb+=node->Sb;
- delete node;
- node=0;
- } else {
- for (b=0;b<2;b++) {
- for (g=0;g<2;g++) {
- for (r=0;r<2;r++) {
- if (node->subnode[b][g][r]) {
- node->subnode[b][g][r]=collapse_nodes(node->subnode[b][g][r],
- n_colours,n_entries,Ep);
- if (*n_colours<=n_entries) return node;
- }
- }
- }
- }
- }
- return node;
-}
-
-static unsigned int distance_squared(int r,int g,int b)
-{
- return r*r+g*g+b*b;
-}
-
-static void minimum_Ep(PALETTE_NODE *node,unsigned int *Ep)
-{
- unsigned int r,g,b;
- if (node->E<*Ep) *Ep=node->E;
- for (b=0;b<2;b++) {
- for (g=0;g<2;g++) {
- for (r=0;r<2;r++) {
- if (node->subnode[b][g][r]) minimum_Ep(node->subnode[b][g][r],Ep);
- }
- }
- }
-}
-
-static void fill_palette(PALETTE_NODE *node,unsigned int *c,RGB *pal,int depth)
-{
- unsigned int r,g,b;
- if (node->n2) {
- for (;pal[*c].r!=255;(*c)++);
- pal[*c].r=node->Sr/node->n2;
- pal[*c].g=node->Sg/node->n2;
- pal[*c].b=node->Sb/node->n2;
- (*c)++;
- }
- for (b=0;b<2;b++) {
- for (g=0;g<2;g++) {
- for (r=0;r<2;r++) {
- if (node->subnode[b][g][r])
- fill_palette(node->subnode[b][g][r],c,pal,depth+1);
- }
- }
- }
-}
-
-static void destroy_tree(PALETTE_NODE *tree)
-{
- unsigned int r,g,b;
- for (b=0;b<2;b++) {
- for (g=0;g<2;g++) {
- for (r=0;r<2;r++) {
- if (tree->subnode[b][g][r]) destroy_tree(tree->subnode[b][g][r]);
- }
- }
- }
- delete tree;
-}
-
-static int create_palette_from_bitmaps(Image** image, int nimage, RGB* pal, int* bmp_i, int fill_other)
-{
- int c_bmp,x,y,r,g,b;
- unsigned int n_colours=0;
- unsigned int n_entries=0;
- unsigned int c, Ep;
- PALETTE_NODE *tree,*node;
-
- /* only support RGB bitmaps */
- if ((nimage < 1) || (image[0]->imgtype != IMAGE_RGB))
- return 0;
-
- /*Create the tree structure*/
- tree=create_node(0,0,0,64,64,64,0);
- /*Scan the bitmaps*/
- /* add_progress(nimage+1); */
- for (c_bmp=0;c_bmph); */
- for (y=0;yh;y++) {
- for (x=0;xw;x++) {
- c=image[c_bmp]->getpixel(x,y);
- r=_rgba_getr(c)>>2;
- g=_rgba_getg(c)>>2;
- b=_rgba_getb(c)>>2;
- node=rgb_node[b][g][r];
- node->n2+=bmp_i[c_bmp];
- node->Sr+=r*bmp_i[c_bmp];
- node->Sg+=g*bmp_i[c_bmp];
- node->Sb+=b*bmp_i[c_bmp];
- do {
- node->n1+=bmp_i[c_bmp];
- node->E+=distance_squared((r<<1)-node->rl-node->rh,
- (g<<1)-node->gl-node->gh,
- (b<<1)-node->bl-node->bh)*bmp_i[c_bmp];
- node=node->parent;
- } while (node);
- }
- /* do_progress(y); */
- }
- /* del_progress(); */
- /* do_progress(c_bmp); */
- }
- /*Collapse empty nodes in the tree, and count leaves*/
- tree=collapse_empty(tree,&n_colours);
- /*Count free palette entries*/
- for (c=0;c<256;c++) {
- if (pal[c].r==255) n_entries++;
- }
- /*Collapse nodes until there are few enough to fit in the palette*/
- if (n_colours > n_entries) {
- /* int n_colours1 = n_colours; */
- /* add_progress(n_colours1 - n_entries); */
- while (n_colours>n_entries) {
- Ep=0xFFFFFFFFul;
- minimum_Ep(tree,&Ep);
- tree=collapse_nodes(tree,&n_colours,n_entries,Ep);
-
- /* if (n_colours > n_entries) */
- /* do_progress(n_colours1 - n_colours); */
- }
- /* del_progress(); */
- }
- /* del_progress(); */
- /* fill palette */
- c=0;
- fill_palette(tree,&c,pal,1);
- if (fill_other) {
- for (;c<256;c++) {
- if (pal[c].r==255)
- pal[c].b=pal[c].g=pal[c].r=0;
- }
- }
- /* free memory used by tree */
- destroy_tree(tree);
- return n_colours;
+
+ int used_colors = histogram.createOptimizedPalette(palette, first_usable_entry, 255);
+ //palette->resize(first_usable_entry+used_colors); // TODO
}