From a013cd5032598e6285a014e7f87fff3f80740ad9 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 31 Aug 2011 02:28:14 +0000 Subject: [PATCH] add support for fixed color palettes when encoding to png --- CHANGELOG | 4 +- include/mapnik/hextree.hpp | 49 +-------- include/mapnik/image_util.hpp | 46 +++++--- include/mapnik/octree.hpp | 11 +- include/mapnik/palette.hpp | 169 +++++++++++++++++++++++++++++ include/mapnik/png_io.hpp | 144 +++++++++++++------------ src/build.py | 1 + src/image_util.cpp | 48 +++++---- src/palette.cpp | 194 ++++++++++++++++++++++++++++++++++ 9 files changed, 507 insertions(+), 159 deletions(-) create mode 100644 include/mapnik/palette.hpp create mode 100644 src/palette.cpp diff --git a/CHANGELOG b/CHANGELOG index e663998be..4532039d9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -14,9 +14,11 @@ For a complete change history, see the SVN log. Mapnik Trunk ------------ +- Add support for png quantization using fixed palettes (#843) + - Add AlsoFilter functionality - http://trac.mapnik.org/wiki/AlsoFilter -- SQLite Plugin: optimize io using shared cache and no mutexes (#797) +- SQLite Plugin: optimize i/o using shared cache and no mutexes (#797) - Directly link input plugins to libmapnik to avoid having to set dlopen flags from binding languages (#790) diff --git a/include/mapnik/hextree.hpp b/include/mapnik/hextree.hpp index 19d65c872..3fc623857 100644 --- a/include/mapnik/hextree.hpp +++ b/include/mapnik/hextree.hpp @@ -26,6 +26,7 @@ // mapnik #include +#include // boost #include @@ -40,30 +41,6 @@ namespace mapnik { -typedef boost::uint8_t byte; -struct rgba -{ - byte r; - byte g; - byte b; - byte a; - rgba(byte r_, byte g_, byte b_, byte a_) - : r(r_), g(g_), b(b_), a(a_) {} - bool operator==(const rgba& y) const - { - return r==y.r && g==y.g && b==y.b && a==y.a; - } -}; - -#define HASH_RGBA(p) (((std::size_t)p.r * 33023 + (std::size_t)p.g * 30013 + (std::size_t)p.b * 27011 + (std::size_t)p.a * 24007) % 21001) -struct rgba_hash_func : public std::unary_function -{ - std::size_t operator()(rgba const&p) const - { - return HASH_RGBA(p); - } -}; - struct RGBAPolicy { const static unsigned MAX_LEVELS = 6; @@ -130,24 +107,6 @@ class hextree : private boost::noncopyable } }; - // ordering by mean(a,r,g,b), a, r, g, b - struct rgba_mean_sort_cmp - { - bool operator() (const rgba& x, const rgba& y) const - { - int t1 = (int)x.a+x.r+x.g+x.b; - int t2 = (int)y.a+y.r+y.g+y.b; - if (t1!=t2) - return t1> 24) + - (((int)x.r-y.r) >> 16) + - (((int)x.g-y.g) >> 8) + - ((int)x.b-y.b); - } - }; - - unsigned max_colors_; unsigned colors_; // flag indicating existance of invisible pixels (a < InsertPolicy::MIN_ALPHA) @@ -158,7 +117,7 @@ class hextree : private boost::noncopyable // index remaping of sorted_pal_ indexes to indexes of returned image palette std::vector pal_remap_; // rgba hashtable for quantization - typedef boost::unordered_map rgba_hash_table; + typedef boost::unordered_map rgba_hash_table; rgba_hash_table color_hashmap_; // gamma correction to prioritize dark colors (>1.0) double gamma_; @@ -276,7 +235,7 @@ public: // find closest match based on mean of r,g,b,a std::vector::iterator pit = - std::lower_bound(sorted_pal_.begin(), sorted_pal_.end(), c, rgba_mean_sort_cmp()); + std::lower_bound(sorted_pal_.begin(), sorted_pal_.end(), c, rgba::mean_sort_cmp()); ind = pit-sorted_pal_.begin(); if (ind == sorted_pal_.size()) ind--; @@ -345,7 +304,7 @@ public: root_ = new node(); // sort palette for binary searching in quantization - std::sort(sorted_pal_.begin(), sorted_pal_.end(),rgba_mean_sort_cmp()); + std::sort(sorted_pal_.begin(), sorted_pal_.end(),rgba::mean_sort_cmp()); // returned palette is rearanged, so that colors with a<255 are at the begining pal_remap_.resize(sorted_pal_.size()); diff --git a/include/mapnik/image_util.hpp b/include/mapnik/image_util.hpp index f13328fff..e5d442def 100644 --- a/include/mapnik/image_util.hpp +++ b/include/mapnik/image_util.hpp @@ -28,6 +28,7 @@ // mapnik #include #include +#include // boost #include @@ -64,19 +65,23 @@ MAPNIK_DECL void save_to_cairo_file(mapnik::Map const& map, template MAPNIK_DECL void save_to_file(T const& image, std::string const& filename, - std::string const& type); + std::string const& type, + rgba_palette& palette = _rgba_palette); // guess type from file extension template MAPNIK_DECL void save_to_file(T const& image, - std::string const& filename); + std::string const& filename, + rgba_palette& palette = _rgba_palette); template MAPNIK_DECL std::string save_to_string(T const& image, - std::string const& type); + std::string const& type, + rgba_palette& palette = _rgba_palette); template void save_as_png(T const& image, - std::string const& filename); + std::string const& filename, + rgba_palette& palette = _rgba_palette); #if defined(HAVE_JPEG) template @@ -199,41 +204,50 @@ void scale_image_bilinear8 (Image& target,const Image& source, double x_off_f=0, inline MAPNIK_DECL void save_to_file (image_32 const& image, std::string const& file, - std::string const& type) + std::string const& type, + rgba_palette& palette = _rgba_palette) { - save_to_file(image.data(),file,type); + save_to_file(image.data(), file, type, palette); } inline MAPNIK_DECL void save_to_file(image_32 const& image, - std::string const& file) + std::string const& file, + rgba_palette& palette = _rgba_palette) { - save_to_file(image.data(),file); + save_to_file(image.data(), file, palette); } inline MAPNIK_DECL std::string save_to_string(image_32 const& image, - std::string const& type) + std::string const& type, + rgba_palette& palette = _rgba_palette) { - return save_to_string(image.data(),type); + return save_to_string(image.data(), type, palette); } #ifdef _MSC_VER template MAPNIK_DECL void save_to_file(image_data_32 const&, std::string const&, - std::string const&); + std::string const&, + rgba_palette&); template MAPNIK_DECL void save_to_file(image_data_32 const&, - std::string const&); + std::string const&, + rgba_palette&); template MAPNIK_DECL std::string save_to_string(image_data_32 const&, - std::string const&); + std::string const&, + rgba_palette&); template MAPNIK_DECL void save_to_file > (image_view const&, std::string const&, - std::string const&); + std::string const&, + rgba_palette&); template MAPNIK_DECL void save_to_file > (image_view const&, - std::string const&); + std::string const&, + rgba_palette&); template MAPNIK_DECL std::string save_to_string > (image_view const&, - std::string const&); + std::string const&, + rgba_palette&); #endif } diff --git a/include/mapnik/octree.hpp b/include/mapnik/octree.hpp index 8088877ad..fc08302aa 100644 --- a/include/mapnik/octree.hpp +++ b/include/mapnik/octree.hpp @@ -26,6 +26,7 @@ // mapnik #include +#include // boost #include @@ -36,16 +37,6 @@ #include namespace mapnik { - -typedef boost::uint8_t byte ; -struct rgb -{ - byte r; - byte g; - byte b; - rgb(byte r_, byte g_, byte b_) - : r(r_), g(g_), b(b_) {} -}; struct RGBPolicy { diff --git a/include/mapnik/palette.hpp b/include/mapnik/palette.hpp new file mode 100644 index 000000000..339a17593 --- /dev/null +++ b/include/mapnik/palette.hpp @@ -0,0 +1,169 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2006 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ +//$Id$ + +#ifndef _PALETTE_HPP_ +#define _PALETTE_HPP_ + + +// mapnik +#include +#include + +// boost +#include +#include + +// stl +#include +#include +#include +#include +#include +#include + + +#ifdef MAPNIK_BIG_ENDIAN +#define U2RED(x) (((x)>>24)&0xff) +#define U2GREEN(x) (((x)>>16)&0xff) +#define U2BLUE(x) (((x)>>8)&0xff) +#define U2ALPHA(x) ((x)&0xff) +#else +#define U2RED(x) ((x)&0xff) +#define U2GREEN(x) (((x)>>8)&0xff) +#define U2BLUE(x) (((x)>>16)&0xff) +#define U2ALPHA(x) (((x)>>24)&0xff) +#endif + + +namespace mapnik { + +typedef boost::uint8_t byte; +struct rgba; + +struct rgb { + byte r; + byte g; + byte b; + + inline rgb(byte r_, byte g_, byte b_) : r(r_), g(g_), b(b_) {}; + rgb(rgba const& c); + + inline bool operator==(const rgb& y) const + { + return r == y.r && g == y.g && b == y.b; + } +}; + +struct rgba +{ + byte r; + byte g; + byte b; + byte a; + + inline rgba(byte r_, byte g_, byte b_, byte a_) + : r(r_), g(g_), b(b_), a(a_) {} + + inline rgba(rgb const& c) + : r(c.r), g(c.g), b(c.b), a(0xFF) {} + + inline rgba(unsigned const& c) { + r = U2RED(c); + g = U2GREEN(c); + b = U2BLUE(c); + a = U2ALPHA(c); + } + + inline bool operator==(const rgba& y) const + { + return r == y.r && g == y.g && b == y.b && a == y.a; + } + + inline operator unsigned() const + { +#ifdef MAPNIK_BIG_ENDIAN + return (r << 24) | (g << 16) | (b << 8) | a; +#else + return r | (g << 8) | (b << 16) | (a << 24); +#endif + } + + // ordering by mean(a,r,g,b), a, r, g, b + struct mean_sort_cmp + { + bool operator() (const rgba& x, const rgba& y) const; + }; + + struct hash_func : public std::unary_function + { + std::size_t operator()(rgba const& p) const; + }; +}; + + +typedef boost::unordered_map rgba_hash_table; + + +class rgba_palette : private boost::noncopyable { +public: + enum palette_type { PALETTE_RGBA = 0, PALETTE_RGB = 1, PALETTE_ACT = 2 }; + + explicit rgba_palette(std::string const& pal, palette_type type = PALETTE_RGBA); + explicit rgba_palette(); + + const std::vector& palette() const; + const std::vector& alphaTable() const; + + unsigned quantize(rgba const& c); + inline unsigned quantize(unsigned const& c) + { + rgba_hash_table::iterator it = color_hashmap_.find(c); + if (it != color_hashmap_.end()) + { + return it->second; + } + else { + return quantize(rgba(U2RED(c), U2GREEN(c), U2BLUE(c), U2ALPHA(c))); + } + } + + bool valid(); + +private: + void parse(std::string const& pal, palette_type type); + +private: + std::vector sorted_pal_; + rgba_hash_table color_hashmap_; + + unsigned colors_; + std::vector rgb_pal_; + std::vector alpha_pal_; +}; + +static rgba_palette _rgba_palette; + +} // namespace mapnik + +#endif // _PALETTE_HPP_ + diff --git a/include/mapnik/png_io.hpp b/include/mapnik/png_io.hpp index 1c268af16..c7c09b681 100644 --- a/include/mapnik/png_io.hpp +++ b/include/mapnik/png_io.hpp @@ -26,9 +26,9 @@ #define MAPNIK_PNG_IO_HPP #include +#include #include #include -#include #include @@ -39,18 +39,6 @@ extern "C" #define MAX_OCTREE_LEVELS 4 -#ifdef MAPNIK_BIG_ENDIAN -#define U2RED(x) (((x)>>24)&0xff) -#define U2GREEN(x) (((x)>>16)&0xff) -#define U2BLUE(x) (((x)>>8)&0xff) -#define U2ALPHA(x) ((x)&0xff) -#else -#define U2RED(x) ((x)&0xff) -#define U2GREEN(x) (((x)>>8)&0xff) -#define U2BLUE(x) (((x)>>16)&0xff) -#define U2ALPHA(x) (((x)>>24)&0xff) -#endif - namespace mapnik { template @@ -215,14 +203,14 @@ void reduce_1(T const&, image_data_8 & out, octree /*trees*/[], unsigned /* } template -void save_as_png(T & file, std::vector & palette, +void save_as_png(T & file, std::vector const& palette, mapnik::image_data_8 const& image, unsigned width, unsigned height, unsigned color_depth, int compression, int strategy, - std::vector &alpha) + std::vector const&alpha) { png_voidp error_ptr=0; png_structp png_ptr=png_create_write_struct(PNG_LIBPNG_VER_STRING, @@ -260,7 +248,8 @@ void save_as_png(T & file, std::vector & palette, PNG_COLOR_TYPE_PALETTE,PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,PNG_FILTER_TYPE_DEFAULT); - png_set_PLTE(png_ptr,info_ptr,reinterpret_cast(&palette[0]),palette.size()); + png_color* pal = const_cast(reinterpret_cast(&palette[0])); + png_set_PLTE(png_ptr, info_ptr, pal, palette.size()); // make transparent lowest indexes, so tRNS is small if (alpha.size()>0) @@ -288,10 +277,10 @@ void save_as_png(T & file, std::vector & palette, } template -void save_as_png256(T1 & file, T2 const& image, const unsigned max_colors = 256, +void save_as_png8_oct(T1 & file, T2 const& image, const unsigned max_colors = 256, int compression = Z_DEFAULT_COMPRESSION, int strategy = Z_DEFAULT_STRATEGY, int trans_mode = -1) { - // number of alpha ranges in png256 format; 2 results in smallest image with binary transparency + // number of alpha ranges in png8 format; 2 results in smallest image with binary transparency // 3 is minimum for semitransparency, 4 is recommended, anything else is worse const unsigned TRANSPARENCY_LEVELS = (trans_mode==2||trans_mode<0)?MAX_OCTREE_LEVELS:2; unsigned width = image.width(); @@ -460,8 +449,67 @@ void save_as_png256(T1 & file, T2 const& image, const unsigned max_colors = 256, } } + +template +void save_as_png8(T1 & file, T2 const& image, T3& tree, + std::vector palette, std::vector alphaTable, + int compression = Z_DEFAULT_COMPRESSION, int strategy = Z_DEFAULT_STRATEGY) +{ + unsigned width = image.width(); + unsigned height = image.height(); + + if (palette.size() > 16 ) + { + // >16 && <=256 colors -> write 8-bit color depth + image_data_8 reduced_image(width, height); + + for (unsigned y = 0; y < height; ++y) + { + mapnik::image_data_32::pixel_type const * row = image.getRow(y); + mapnik::image_data_8::pixel_type * row_out = reduced_image.getRow(y); + + for (unsigned x = 0; x < width; ++x) + { + row_out[x] = tree.quantize(row[x]); + } + } + save_as_png(file, palette, reduced_image, width, height, 8, compression, strategy, alphaTable); + } + else if (palette.size() == 1) + { + // 1 color image -> write 1-bit color depth PNG + unsigned image_width = (int(0.125*width) + 7)&~7; + unsigned image_height = height; + image_data_8 reduced_image(image_width, image_height); + reduced_image.set(0); + save_as_png(file, palette, reduced_image, width, height, 1, compression, strategy, alphaTable); + } + else + { + // <=16 colors -> write 4-bit color depth PNG + unsigned image_width = (int(0.5*width) + 3)&~3; + unsigned image_height = height; + image_data_8 reduced_image(image_width, image_height); + for (unsigned y = 0; y < height; ++y) + { + mapnik::image_data_32::pixel_type const * row = image.getRow(y); + mapnik::image_data_8::pixel_type * row_out = reduced_image.getRow(y); + byte index = 0; + + for (unsigned x = 0; x < width; ++x) + { + + index = tree.quantize(row[x]); + if (x%2 == 0) index = index<<4; + row_out[x>>1] |= index; + } + } + save_as_png(file, palette, reduced_image, width, height, 4, compression, strategy, alphaTable); + } +} + template -void save_as_png256_hex(T1 & file, T2 const& image, int colors = 256, +void save_as_png8_hex(T1 & file, T2 const& image, int colors = 256, int compression = Z_DEFAULT_COMPRESSION, int strategy = Z_DEFAULT_STRATEGY, int trans_mode = -1, double gamma = 2.0) { @@ -498,58 +546,16 @@ void save_as_png256_hex(T1 & file, T2 const& image, int colors = 256, alphaTable.push_back(pal[i].a); } - if (palette.size() > 16 ) - { - // >16 && <=256 colors -> write 8-bit color depth - image_data_8 reduced_image(width, height); + save_as_png8 >(file, image, tree, palette, alphaTable, compression, strategy); +} - for (unsigned y = 0; y < height; ++y) - { - mapnik::image_data_32::pixel_type const * row = image.getRow(y); - mapnik::image_data_8::pixel_type * row_out = reduced_image.getRow(y); +template +void save_as_png8_pal(T1 & file, T2 const& image, rgba_palette& pal, + int compression = Z_DEFAULT_COMPRESSION, int strategy = Z_DEFAULT_STRATEGY) +{ + save_as_png8(file, image, pal, pal.palette(), pal.alphaTable(), compression, strategy); +} - for (unsigned x = 0; x < width; ++x) - { - unsigned val = row[x]; - mapnik::rgba c(U2RED(val), U2GREEN(val), U2BLUE(val), U2ALPHA(val)); - row_out[x] = tree.quantize(c); - } - } - save_as_png(file, palette, reduced_image, width, height, 8, compression, strategy, alphaTable); - } - else if (palette.size() == 1) - { - // 1 color image -> write 1-bit color depth PNG - unsigned image_width = (int(0.125*width) + 7)&~7; - unsigned image_height = height; - image_data_8 reduced_image(image_width, image_height); - reduced_image.set(0); - save_as_png(file, palette, reduced_image, width, height, 1, compression, strategy, alphaTable); - } - else - { - // <=16 colors -> write 4-bit color depth PNG - unsigned image_width = (int(0.5*width) + 3)&~3; - unsigned image_height = height; - image_data_8 reduced_image(image_width, image_height); - for (unsigned y = 0; y < height; ++y) - { - mapnik::image_data_32::pixel_type const * row = image.getRow(y); - mapnik::image_data_8::pixel_type * row_out = reduced_image.getRow(y); - byte index = 0; - - for (unsigned x = 0; x < width; ++x) - { - unsigned val = row[x]; - mapnik::rgba c(U2RED(val), U2GREEN(val), U2BLUE(val), U2ALPHA(val)); - index = tree.quantize(c); - if (x%2 == 0) index = index<<4; - row_out[x>>1] |= index; - } - } - save_as_png(file, palette, reduced_image, width, height, 4, compression, strategy, alphaTable); - } -} } #endif // MAPNIK_PNG_IO_HPP diff --git a/src/build.py b/src/build.py index 6161faf63..2aa9c20bb 100644 --- a/src/build.py +++ b/src/build.py @@ -117,6 +117,7 @@ source = Split( load_map.cpp memory.cpp parse_path.cpp + palette.cpp placement_finder.cpp plugin.cpp png_reader.cpp diff --git a/src/image_util.cpp b/src/image_util.cpp index 8b6e9d3a6..3d72407fe 100644 --- a/src/image_util.cpp +++ b/src/image_util.cpp @@ -33,6 +33,7 @@ extern "C" #include #include #include +#include #include // boost @@ -74,24 +75,27 @@ extern "C" namespace mapnik { + template std::string save_to_string(T const& image, - std::string const& type) + std::string const& type, + rgba_palette& palette) { std::ostringstream ss(std::ios::out|std::ios::binary); - save_to_stream(image, ss, type); + save_to_stream(image, ss, type, palette); return ss.str(); } template void save_to_file(T const& image, std::string const& filename, - std::string const& type) + std::string const& type, + rgba_palette& palette) { std::ofstream file (filename.c_str(), std::ios::out| std::ios::trunc|std::ios::binary); if (file) { - save_to_stream(image, file, type); + save_to_stream(image, file, type, palette); } else throw ImageWriterException("Could not write file to " + filename ); } @@ -99,7 +103,8 @@ void save_to_file(T const& image, template void save_to_stream(T const& image, std::ostream & stream, - std::string const& type) + std::string const& type, + rgba_palette& palette) { if (stream) { @@ -220,12 +225,14 @@ void save_to_stream(T const& image, } } - if (colors < 0) + if (&palette != NULL && palette.valid()) + save_as_png8_pal(stream, image, palette, compression, strategy); + else if (colors < 0) save_as_png(stream, image, compression, strategy); else if (use_octree) - save_as_png256(stream, image, colors, compression, strategy); + save_as_png8_oct(stream, image, colors, compression, strategy); else - save_as_png256_hex(stream, image, colors, compression, strategy, trans_mode, gamma); + save_as_png8_hex(stream, image, colors, compression, strategy, trans_mode, gamma); } #if defined(HAVE_JPEG) else if (boost::algorithm::istarts_with(type,std::string("jpeg"))) @@ -251,15 +258,14 @@ void save_to_stream(T const& image, } else throw ImageWriterException("Could not write to empty stream" ); } - - + template -void save_to_file(T const& image,std::string const& filename) +void save_to_file(T const& image,std::string const& filename, rgba_palette& palette) { boost::optional type = type_from_filename(filename); if (type) { - save_to_file(image,filename,*type); + save_to_file(image, filename, *type, palette); } } @@ -322,23 +328,29 @@ void save_to_cairo_file(mapnik::Map const& map, template void save_to_file(image_data_32 const&, std::string const&, - std::string const&); + std::string const&, + rgba_palette& palette); template void save_to_file(image_data_32 const&, - std::string const&); + std::string const&, + rgba_palette& palette); template std::string save_to_string(image_data_32 const&, - std::string const&); + std::string const&, + rgba_palette& palette); template void save_to_file > (image_view const&, std::string const&, - std::string const&); + std::string const&, + rgba_palette& palette); template void save_to_file > (image_view const&, - std::string const&); + std::string const&, + rgba_palette& palette); template std::string save_to_string > (image_view const&, - std::string const&); + std::string const&, + rgba_palette& palette); diff --git a/src/palette.cpp b/src/palette.cpp new file mode 100644 index 000000000..915796d37 --- /dev/null +++ b/src/palette.cpp @@ -0,0 +1,194 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2006 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#include + +namespace mapnik +{ + +rgb::rgb(rgba const& c) + : r(c.r), g(c.g), b(c.b) {} + +// ordering by mean(a,r,g,b), a, r, g, b +bool rgba::mean_sort_cmp::operator() (const rgba& x, const rgba& y) const +{ + int t1 = (int)x.a + x.r + x.g + x.b; + int t2 = (int)y.a + y.r + y.g + y.b; + if (t1 != t2) return t1 < t2; + + return (((int)x.a - y.a) >> 24) + + (((int)x.r - y.r) >> 16) + + (((int)x.g - y.g) >> 8) + + (((int)x.b - y.b)); +} + +std::size_t rgba::hash_func::operator()(rgba const& p) const +{ + return ((std::size_t)p.r * 33023 + (std::size_t)p.g * 30013 + + (std::size_t)p.b * 27011 + (std::size_t)p.a * 24007) % 21001; +} + + +rgba_palette::rgba_palette(std::string const& pal, palette_type type) + : colors_(0) +{ + parse(pal, type); +} + +rgba_palette::rgba_palette() + : colors_(0) {} + +const std::vector& rgba_palette::palette() const +{ + return rgb_pal_; +} + +const std::vector& rgba_palette::alphaTable() const +{ + return alpha_pal_; +} + +bool rgba_palette::valid() +{ + return colors_ > 0; +} + +// return color index in returned earlier palette +unsigned rgba_palette::quantize(rgba const& c) +{ + unsigned index = 0; + if (colors_ == 1) return index; + + rgba_hash_table::iterator it = color_hashmap_.find(c); + if (it != color_hashmap_.end()) + { + index = it->second; + } + else + { + int dr, dg, db, da; + int dist, newdist; + + // find closest match based on mean of r,g,b,a + std::vector::iterator pit = + std::lower_bound(sorted_pal_.begin(), sorted_pal_.end(), c, rgba::mean_sort_cmp()); + index = pit - sorted_pal_.begin(); + if (index == sorted_pal_.size()) index--; + + dr = sorted_pal_[index].r - c.r; + dg = sorted_pal_[index].g - c.g; + db = sorted_pal_[index].b - c.b; + da = sorted_pal_[index].a - c.a; + dist = dr*dr + dg*dg + db*db + da*da; + int poz = index; + + // search neighbour positions in both directions for better match + for (int i = poz - 1; i >= 0; i--) + { + dr = sorted_pal_[i].r - c.r; + dg = sorted_pal_[i].g - c.g; + db = sorted_pal_[i].b - c.b; + da = sorted_pal_[i].a - c.a; + // stop criteria based on properties of used sorting + if ((dr+db+dg+da) * (dr+db+dg+da) / 4 > dist) + break; + newdist = dr*dr + dg*dg + db*db + da*da; + if (newdist < dist) + { + index = i; + dist = newdist; + } + } + for (unsigned i = poz + 1; i < sorted_pal_.size(); i++) + { + dr = sorted_pal_[i].r - c.r; + dg = sorted_pal_[i].g - c.g; + db = sorted_pal_[i].b - c.b; + da = sorted_pal_[i].a - c.a; + // stop criteria based on properties of used sorting + if ((dr+db+dg+da) * (dr+db+dg+da) / 4 > dist) + break; + newdist = dr*dr + dg*dg + db*db + da*da; + if (newdist < dist) + { + index = i; + dist = newdist; + } + } + + // Cache found index for the color c into the hashmap. + color_hashmap_[c] = index; + } + + return index; +} + +void rgba_palette::parse(std::string const& pal, palette_type type) +{ + int length = pal.length(); + + if ((type == PALETTE_RGBA && length % 4 > 0) || + (type == PALETTE_RGB && length % 3 > 0) || + (type == PALETTE_ACT && length != 772)) + { + throw config_error("invalid palette length"); + } + + if (type == PALETTE_ACT) + { + length = (pal[768] << 8 | pal[769]) * 3; + } + + sorted_pal_.clear(); + color_hashmap_.clear(); + rgb_pal_.clear(); + alpha_pal_.clear(); + + if (type == PALETTE_RGBA) for (unsigned i = 0; i < length; i += 4) + { + sorted_pal_.push_back(rgba(pal[i], pal[i + 1], pal[i + 2], pal[i + 3])); + } + else for (unsigned i = 0; i < length; i += 3) + { + sorted_pal_.push_back(rgba(pal[i], pal[i + 1], pal[i + 2], 0xFF)); + } + + // Make sure we have at least one entry in the palette. + if (sorted_pal_.size() == 0) + sorted_pal_.push_back(rgba(0, 0, 0, 0)); + + colors_ = sorted_pal_.size(); + + // Sort palette for binary searching in quantization + std::sort(sorted_pal_.begin(), sorted_pal_.end(), rgba::mean_sort_cmp()); + + // Insert all palette colors into the hashmap and into the palette vectors. + for (unsigned i = 0; i < colors_; i++) + { + rgba c = sorted_pal_[i]; + color_hashmap_[c] = i; + rgb_pal_.push_back(rgb(c)); + if (c.a < 0xFF) alpha_pal_.push_back(c.a); + } +} + +} // namespace mapnik \ No newline at end of file