add support for fixed color palettes when encoding to png

This commit is contained in:
Dane Springmeyer 2011-08-31 02:28:14 +00:00
parent 477c317753
commit a013cd5032
9 changed files with 507 additions and 159 deletions

View file

@ -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)

View file

@ -26,6 +26,7 @@
// mapnik
#include <mapnik/global.hpp>
#include <mapnik/palette.hpp>
// boost
#include <boost/utility.hpp>
@ -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<rgba, std::size_t>
{
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<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);
}
};
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<unsigned> pal_remap_;
// rgba hashtable for quantization
typedef boost::unordered_map<rgba, int, rgba_hash_func> rgba_hash_table;
typedef boost::unordered_map<rgba, int, rgba::hash_func> 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<rgba>::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());

View file

@ -28,6 +28,7 @@
// mapnik
#include <mapnik/config.hpp>
#include <mapnik/graphics.hpp>
#include <mapnik/palette.hpp>
// boost
#include <boost/algorithm/string.hpp>
@ -64,19 +65,23 @@ MAPNIK_DECL void save_to_cairo_file(mapnik::Map const& map,
template <typename T>
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 <typename T>
MAPNIK_DECL void save_to_file(T const& image,
std::string const& filename);
std::string const& filename,
rgba_palette& palette = _rgba_palette);
template <typename T>
MAPNIK_DECL std::string save_to_string(T const& image,
std::string const& type);
std::string const& type,
rgba_palette& palette = _rgba_palette);
template <typename T>
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 <typename T>
@ -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_32>(image.data(),file,type);
save_to_file<image_data_32>(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_32>(image.data(),file);
save_to_file<image_data_32>(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_32>(image.data(),type);
return save_to_string<image_data_32>(image.data(), type, palette);
}
#ifdef _MSC_VER
template MAPNIK_DECL void save_to_file<image_data_32>(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>(image_data_32 const&,
std::string const&);
std::string const&,
rgba_palette&);
template MAPNIK_DECL std::string save_to_string<image_data_32>(image_data_32 const&,
std::string const&);
std::string const&,
rgba_palette&);
template MAPNIK_DECL void save_to_file<image_view<image_data_32> > (image_view<image_data_32> const&,
std::string const&,
std::string const&);
std::string const&,
rgba_palette&);
template MAPNIK_DECL void save_to_file<image_view<image_data_32> > (image_view<image_data_32> const&,
std::string const&);
std::string const&,
rgba_palette&);
template MAPNIK_DECL std::string save_to_string<image_view<image_data_32> > (image_view<image_data_32> const&,
std::string const&);
std::string const&,
rgba_palette&);
#endif
}

View file

@ -26,6 +26,7 @@
// mapnik
#include <mapnik/global.hpp>
#include <mapnik/palette.hpp>
// boost
#include <boost/utility.hpp>
@ -37,16 +38,6 @@
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
{
const static unsigned MAX_LEVELS = 6;

169
include/mapnik/palette.hpp Normal file
View file

@ -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 <mapnik/global.hpp>
#include <mapnik/config_error.hpp>
// boost
#include <boost/utility.hpp>
#include <boost/unordered_map.hpp>
// stl
#include <vector>
#include <map>
#include <iostream>
#include <set>
#include <algorithm>
#include <cmath>
#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<rgba, std::size_t>
{
std::size_t operator()(rgba const& p) const;
};
};
typedef boost::unordered_map<unsigned, unsigned> 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<rgb>& palette() const;
const std::vector<unsigned>& 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<rgba> sorted_pal_;
rgba_hash_table color_hashmap_;
unsigned colors_;
std::vector<rgb> rgb_pal_;
std::vector<unsigned> alpha_pal_;
};
static rgba_palette _rgba_palette;
} // namespace mapnik
#endif // _PALETTE_HPP_

View file

@ -26,9 +26,9 @@
#define MAPNIK_PNG_IO_HPP
#include <mapnik/global.hpp>
#include <mapnik/palette.hpp>
#include <mapnik/octree.hpp>
#include <mapnik/hextree.hpp>
#include <mapnik/global.hpp>
#include <zlib.h>
@ -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 <typename T>
@ -215,14 +203,14 @@ void reduce_1(T const&, image_data_8 & out, octree<rgb> /*trees*/[], unsigned /*
}
template <typename T>
void save_as_png(T & file, std::vector<mapnik::rgb> & palette,
void save_as_png(T & file, std::vector<mapnik::rgb> const& palette,
mapnik::image_data_8 const& image,
unsigned width,
unsigned height,
unsigned color_depth,
int compression,
int strategy,
std::vector<unsigned> &alpha)
std::vector<unsigned> 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<mapnik::rgb> & 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<png_color*>(&palette[0]),palette.size());
png_color* pal = const_cast<png_color*>(reinterpret_cast<const png_color*>(&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<mapnik::rgb> & palette,
}
template <typename T1,typename T2>
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 <typename T1, typename T2, typename T3>
void save_as_png8(T1 & file, T2 const& image, T3& tree,
std::vector<mapnik::rgb> palette, std::vector<unsigned> 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 <typename T1,typename T2>
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);
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)
{
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);
}
save_as_png8<T1, T2, hextree<mapnik::rgba> >(file, image, tree, palette, alphaTable, compression, strategy);
}
template <typename T1, typename T2>
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<T1, T2, rgba_palette>(file, image, pal, pal.palette(), pal.alphaTable(), compression, strategy);
}
}
#endif // MAPNIK_PNG_IO_HPP

View file

@ -117,6 +117,7 @@ source = Split(
load_map.cpp
memory.cpp
parse_path.cpp
palette.cpp
placement_finder.cpp
plugin.cpp
png_reader.cpp

View file

@ -33,6 +33,7 @@ extern "C"
#include <mapnik/graphics.hpp>
#include <mapnik/memory.hpp>
#include <mapnik/image_view.hpp>
#include <mapnik/palette.hpp>
#include <mapnik/map.hpp>
// boost
@ -74,24 +75,27 @@ extern "C"
namespace mapnik
{
template <typename T>
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 <typename T>
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 <typename T>
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")))
@ -252,14 +259,13 @@ void save_to_stream(T const& image,
else throw ImageWriterException("Could not write to empty stream" );
}
template <typename T>
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<std::string> type = type_from_filename(filename);
if (type)
{
save_to_file<T>(image,filename,*type);
save_to_file<T>(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>(image_data_32 const&,
std::string const&,
std::string const&);
std::string const&,
rgba_palette& palette);
template void save_to_file<image_data_32>(image_data_32 const&,
std::string const&);
std::string const&,
rgba_palette& palette);
template std::string save_to_string<image_data_32>(image_data_32 const&,
std::string const&);
std::string const&,
rgba_palette& palette);
template void save_to_file<image_view<image_data_32> > (image_view<image_data_32> const&,
std::string const&,
std::string const&);
std::string const&,
rgba_palette& palette);
template void save_to_file<image_view<image_data_32> > (image_view<image_data_32> const&,
std::string const&);
std::string const&,
rgba_palette& palette);
template std::string save_to_string<image_view<image_data_32> > (image_view<image_data_32> const&,
std::string const&);
std::string const&,
rgba_palette& palette);

194
src/palette.cpp Normal file
View file

@ -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 <mapnik/palette.hpp>
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<rgb>& rgba_palette::palette() const
{
return rgb_pal_;
}
const std::vector<unsigned>& 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<rgba>::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