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 }