add support for fixed color palettes when encoding to png
This commit is contained in:
parent
477c317753
commit
a013cd5032
9 changed files with 507 additions and 159 deletions
|
@ -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)
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
|
||||
}
|
||||
|
|
|
@ -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
169
include/mapnik/palette.hpp
Normal 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_
|
||||
|
|
@ -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
|
||||
|
|
|
@ -117,6 +117,7 @@ source = Split(
|
|||
load_map.cpp
|
||||
memory.cpp
|
||||
parse_path.cpp
|
||||
palette.cpp
|
||||
placement_finder.cpp
|
||||
plugin.cpp
|
||||
png_reader.cpp
|
||||
|
|
|
@ -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
194
src/palette.cpp
Normal 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
|
Loading…
Reference in a new issue