support for palette based PNGs, user 'png256' as a format parameter (see updated rundemo.py)

This commit is contained in:
Artem Pavlenko 2007-12-10 19:59:17 +00:00
parent 4586586448
commit 58f4431df1
9 changed files with 450 additions and 62 deletions

View file

@ -29,6 +29,9 @@ using mapnik::ImageData32;
using mapnik::image_view;
using mapnik::save_to_file;
void (*view_to_file1)(std::string const&,std::string const&, image_view<ImageData32> const&) = mapnik::save_to_file;
void (*view_to_file2)(std::string const&, image_view<ImageData32> const&) = mapnik::save_to_file;
void export_image_view()
{
using namespace boost::python;
@ -37,5 +40,6 @@ void export_image_view()
.def("height",&image_view<ImageData32>::height)
;
//def("save_to_file",save_to_file<image_view<ImageData32> >);
def("save_to_file",view_to_file1);
def("save_to_file",view_to_file2);
}

View file

@ -305,7 +305,7 @@ m.zoom_to_box(Envelope(1405120.04127408,-247003.813399447,1706357.31328276,-2509
# Render two maps, one PNG, one JPEG.
render_to_file(m, 'demo.png', 'png')
render_to_file(m, 'demo.png', 'png256') # save to palette based (max 256 colours) png
render_to_file(m, 'demo.jpg', 'jpeg')
print """\n\nTwo maps have been rendered in the current directory:

View file

@ -57,8 +57,10 @@ namespace mapnik
virtual ~ImageReader() {}
};
bool register_image_reader(const std::string& type,ImageReader* (*)(const std::string&));
MAPNIK_DECL ImageReader* get_image_reader(const std::string& type,const std::string& file);
bool register_image_reader(const std::string& type,ImageReader* (*)(const std::string&));
MAPNIK_DECL ImageReader* get_image_reader(const std::string& file,const std::string& type);
MAPNIK_DECL ImageReader* get_image_reader(const std::string& file);
}
#endif //IMAGE_READER_HPP

View file

@ -31,8 +31,9 @@ namespace mapnik {
template <typename T>
class image_view
{
typedef typename T::pixel_type pixel_type;
public:
public:
typedef typename T::pixel_type pixel_type;
image_view(unsigned x, unsigned y, unsigned width, unsigned height, T const& data)
: x_(x),
y_(y),

225
include/mapnik/octree.hpp Normal file
View file

@ -0,0 +1,225 @@
/*****************************************************************************
*
* 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 _OCTREE_HPP_
#define _OCTREE_HPP_
#include <boost/format.hpp>
#include <boost/utility.hpp>
#include <vector>
#include <iostream>
#include <set>
namespace mapnik {
typedef uint8_t byte ;
struct rgb
{
byte r;
byte g;
byte b;
rgb(byte r_, byte b_, byte g_)
: r(r_), g(g_), b(b_) {}
};
struct RGBPolicy
{
const static unsigned MAX_LEVELS = 6;
inline static unsigned index_from_level(unsigned level, rgb const& c)
{
unsigned shift = 7 - level;
return (((c.r >> shift) & 1) << 2)
| (((c.g >> shift) & 1) << 1)
| ((c.b >> shift) & 1);
}
};
template <typename T, typename InsertPolicy = RGBPolicy >
class octree : private boost::noncopyable
{
struct node
{
node ()
: next_node_(0),
reds(0),
greens(0),
blues(0),
count(0),
index(0)
{
memset(&children_[0],0,sizeof(children_));
}
~node ()
{
for (unsigned i = 0;i < 8; ++i) {
if (children_[i] != 0) delete children_[i],children_[i]=0;
}
}
bool is_leaf() const { return count != 0; }
node * children_[8];
node * next_node_;
unsigned reds;
unsigned greens;
unsigned blues;
unsigned count;
uint8_t index;
};
struct node_cmp
{
bool operator() ( const node * lhs,const node* rhs ) const
{
unsigned left_total=0;
unsigned right_total=0;
for (unsigned i=0; i<8;++i)
{
if (lhs->children_[i]) left_total+=lhs->children_[i]->count;
if (rhs->children_[i]) right_total+=rhs->children_[i]->count;
}
return lhs > rhs;
}
};
std::set<node*,node_cmp> reducible_[InsertPolicy::MAX_LEVELS];
unsigned max_colors_;
unsigned colors_;
unsigned leaf_level_;
public:
explicit octree(unsigned max_colors=255)
: max_colors_(max_colors),
colors_(0),
leaf_level_(InsertPolicy::MAX_LEVELS),
root_(new node())
{}
~octree() { delete root_;}
void insert(T const& data)
{
unsigned level = 0;
node * cur_node = root_;
while (true) {
if ( cur_node->count > 0 || level == leaf_level_)
{
cur_node->reds += data.r;
cur_node->greens += data.g;
cur_node->blues += data.b;
cur_node->count += 1;
if (cur_node->count == 1) ++colors_;
if (colors_ >= max_colors_ - 1)
{
reduce();
}
break;
}
unsigned idx = InsertPolicy::index_from_level(level,data);
if (cur_node->children_[idx] == 0) {
cur_node->children_[idx] = new node();
if (level < leaf_level_-1) {
reducible_[level+1].insert(cur_node->children_[idx]);
}
}
cur_node = cur_node->children_[idx];
++level;
}
}
int quantize(rgb const& c) const
{
unsigned level = 0;
node * cur_node = root_;
while (cur_node)
{
if (cur_node->count != 0) return cur_node->index;
unsigned idx = InsertPolicy::index_from_level(level,c);
cur_node = cur_node->children_[idx];
++level;
}
return -1;
}
void create_palette(std::vector<rgb> & palette) const
{
palette.reserve(colors_);
create_palette(palette, root_);
}
private:
void reduce()
{
while (leaf_level_ >0 && reducible_[leaf_level_-1].size() == 0)
{
--leaf_level_;
}
if (leaf_level_ < 1) return;
typename std::set<node*,node_cmp>::iterator pos = reducible_[leaf_level_-1].begin();
if (pos == reducible_[leaf_level_-1].end()) return;
node * cur_node = *pos;
unsigned num_children = 0;
for (unsigned idx=0; idx < 8; ++idx)
{
if (cur_node->children_[idx] != 0)
{
++num_children;
cur_node->reds += cur_node->children_[idx]->reds;
cur_node->greens += cur_node->children_[idx]->greens;
cur_node->blues += cur_node->children_[idx]->blues;
cur_node->count += cur_node->children_[idx]->count;
delete cur_node->children_[idx], cur_node->children_[idx]=0;
}
}
reducible_[leaf_level_-1].erase(pos);
if (num_children > 0 )
{
colors_ -= (num_children - 1);
}
}
void create_palette(std::vector<rgb> & palette, node * itr) const
{
if (itr->count != 0)
{
unsigned count = itr->count;
palette.push_back(rgb(uint8_t(itr->reds/float(count)),
uint8_t(itr->greens/float(count)),
uint8_t(itr->blues/float(count))));
itr->index = palette.size() - 1;
}
for (unsigned i=0; i < 8 ;++i)
{
if (itr->children_[i] != 0)
create_palette(palette, itr->children_[i]);
}
}
private:
node * root_;
};
} // namespace mapnik
#endif /* _OCTREE_HPP_ */

View file

@ -50,11 +50,16 @@ feature_ptr gdal_featureset::next()
GDALRasterBand * grey = 0;
for (int i = 0; i < dataset_.GetRasterCount() ;++i)
{
GDALRasterBand * band = dataset_.GetRasterBand(i+1);
GDALRasterBand * band = dataset_.GetRasterBand(i+1);
//if (band->GetOverviewCount() > 0)
//{
// band = band->GetOverview(0);
//}
#ifdef MAPNIK_DEBUG
int bsx,bsy;
band->GetBlockSize(&bsx,&bsy);
std::cout << boost::format("Block=%dx%d Type=%s Color=%s \n") % bsx % bsy
int bsx,bsy;
band->GetBlockSize(&bsx,&bsy);
std::cout << boost::format("Block=%dx%d Type=%s Color=%s \n") % bsx % bsy
% GDALGetDataTypeName(band->GetRasterDataType())
% GDALGetColorInterpretationName(band->GetColorInterpretation());
#endif
@ -77,30 +82,30 @@ feature_ptr gdal_featureset::next()
grey = band;
break;
case GCI_PaletteIndex:
{
grey = band;
GDALColorTable *color_table = band->GetColorTable();
{
grey = band;
GDALColorTable *color_table = band->GetColorTable();
if ( color_table)
{
int count = color_table->GetColorEntryCount();
if ( color_table)
{
int count = color_table->GetColorEntryCount();
#ifdef MAPNIK_DEBUG
std::cout << "Color Table count = " << count << "\n";´
std::cout << "Color Table count = " << count << "\n";
#endif
for ( int i = 0; i < count; i++ )
{
const GDALColorEntry *ce = color_table->GetColorEntry ( i );
if (!ce ) continue;
for ( int i = 0; i < count; i++ )
{
const GDALColorEntry *ce = color_table->GetColorEntry ( i );
if (!ce ) continue;
#ifdef MAPNIK_DEBUG
std::cout << "color entry RGB (" << ce->c1 <<"," <<ce->c2 << "," << ce->c3 << ")\n";
std::cout << "color entry RGB (" << ce->c1 <<"," <<ce->c2 << "," << ce->c3 << ")\n";
#endif
}
}
break;
}
}
}
break;
}
case GCI_Undefined:
grey = band;
break;
grey = band;
break;
default:
break;
;

View file

@ -22,7 +22,7 @@
//$Id: image_reader.cpp 17 2005-03-08 23:58:43Z pavlenko $
#include <mapnik/image_reader.hpp>
#include <mapnik/image_util.hpp>
#include <mapnik/factory.hpp>
namespace mapnik
@ -36,8 +36,14 @@ namespace mapnik
return ImageReaderFactory::instance()->register_product(type,fun);
}
ImageReader* get_image_reader(const std::string& type,const std::string& file)
ImageReader* get_image_reader(const std::string& filename,const std::string& type)
{
return ImageReaderFactory::instance()->create_object(type,file);
return ImageReaderFactory::instance()->create_object(type,filename);
}
ImageReader* get_image_reader(const std::string& filename)
{
std::string type = type_from_filename(filename);
return ImageReaderFactory::instance()->create_object(filename,type);
}
}

View file

@ -28,6 +28,7 @@
#include <mapnik/graphics.hpp>
#include <mapnik/memory.hpp>
#include <mapnik/image_view.hpp>
#include <mapnik/octree.hpp>
// jpeg png
extern "C"
{
@ -52,6 +53,7 @@ namespace mapnik
{
//all this should go into image_writer factory
if (type=="png") save_as_png(file,image);
else if (type == "png256") save_as_png256(file,image);
else if (type=="jpeg") save_as_jpeg(file,85,image);
}
}
@ -123,6 +125,150 @@ namespace mapnik
png_destroy_write_struct(&png_ptr, &info_ptr);
}
template <typename T>
void reduce_8 (T const& in, ImageData8 & out, octree<rgb> & tree)
{
unsigned width = in.width();
unsigned height = in.height();
for (unsigned y = 0; y < height; ++y)
{
mapnik::ImageData32::pixel_type const * row = in.getRow(y);
mapnik::ImageData8::pixel_type * row_out = out.getRow(y);
for (unsigned x = 0; x < width; ++x)
{
unsigned val = row[x];
mapnik::rgb c((val)&0xff, (val>>8)&0xff, (val>>16) & 0xff);
uint8_t index = tree.quantize(c);
row_out[x] = index;
}
}
}
template <typename T>
void reduce_4 (T const& in, ImageData8 & out, octree<rgb> & tree)
{
unsigned width = in.width();
unsigned height = in.height();
for (unsigned y = 0; y < height; ++y)
{
mapnik::ImageData32::pixel_type const * row = in.getRow(y);
mapnik::ImageData8::pixel_type * row_out = out.getRow(y);
for (unsigned x = 0; x < width; ++x)
{
unsigned val = row[x];
mapnik::rgb c((val)&0xff, (val>>8)&0xff, (val>>16) & 0xff);
uint8_t index = tree.quantize(c);
if (x%2 > 0) index = index<<4;
row_out[x>>1] |= index;
}
}
}
// 1-bit but only one color.
template <typename T>
void reduce_1(T const&, ImageData8 & out, octree<rgb> &)
{
out.set(0); // only one color!!!
}
template <typename T>
void save_as_png(T & file, std::vector<mapnik::rgb> & palette,
mapnik::ImageData8 const& image,
unsigned width,
unsigned height,
unsigned color_depth)
{
png_voidp error_ptr=0;
png_structp png_ptr=png_create_write_struct(PNG_LIBPNG_VER_STRING,
error_ptr,0, 0);
if (!png_ptr) return;
// switch on optimization only if supported
#if defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER >= 10200) && defined(PNG_MMX_CODE_SUPPORTED)
png_uint_32 mask, flags;
flags = png_get_asm_flags(png_ptr);
mask = png_get_asm_flagmask(PNG_SELECT_READ | PNG_SELECT_WRITE);
png_set_asm_flags(png_ptr, flags | mask);
#endif
png_set_filter (png_ptr, 0, PNG_FILTER_NONE);
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr)
{
png_destroy_write_struct(&png_ptr,(png_infopp)0);
return;
}
if (setjmp(png_jmpbuf(png_ptr)))
{
png_destroy_write_struct(&png_ptr, &info_ptr);
return;
}
png_set_write_fn (png_ptr, &file, &write_data<T>, &flush_data<T>);
png_set_IHDR(png_ptr, info_ptr,width,height,color_depth,
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_write_info(png_ptr, info_ptr);
for (unsigned i=0;i<height;i++)
{
png_write_row(png_ptr,(png_bytep)image.getRow(i));
}
png_write_end(png_ptr, info_ptr);
png_destroy_write_struct(&png_ptr, &info_ptr);
}
template <typename T1,typename T2>
void save_as_png256(T1 & file, T2 const& image)
{
octree<rgb> tree(256);
unsigned width = image.width();
unsigned height = image.height();
for (unsigned y = 0; y < height; ++y)
{
typename T2::pixel_type const * row = image.getRow(y);
for (unsigned x = 0; x < width; ++x)
{
unsigned val = row[x];
tree.insert(mapnik::rgb((val)&0xff, (val>>8)&0xff, (val>>16) & 0xff));
}
}
std::vector<rgb> palette;
tree.create_palette(palette);
assert(palette.size() <= 256);
if (palette.size() > 16 )
{
// >16 && <=256 colors -> write 8-bit color depth
ImageData8 reduced_image(width,height);
reduce_8(image,reduced_image,tree);
save_as_png(file,palette,reduced_image,width,height,8);
}
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;
ImageData8 reduced_image(image_width,image_height);
reduce_1(image,reduced_image,tree);
save_as_png(file,palette,reduced_image,width,height,1);
}
else
{
// <=16 colors -> write 4-bit color depth PNG
unsigned image_width = (int(0.5*width) + 3)&~3;
unsigned image_height = height;
ImageData8 reduced_image(image_width,image_height);
reduce_4(image,reduced_image,tree);
save_as_png(file,palette,reduced_image,width,height,4);
}
}
#define BUFFER_SIZE 4096
@ -222,7 +368,7 @@ namespace mapnik
template void save_to_file<image_view<ImageData32> > (std::string const&,
std::string const&,
image_view<ImageData32> const&);
template void save_to_file<image_view<ImageData32> > (std::string const&,
image_view<ImageData32> const&);

View file

@ -29,37 +29,36 @@
namespace mapnik {
symbolizer_with_image::symbolizer_with_image(boost::shared_ptr<ImageData32> img) :
image_( img ) {}
symbolizer_with_image::symbolizer_with_image(std::string const& file,
std::string const& type, unsigned width,unsigned height)
: image_(new ImageData32(width,height)),
image_filename_( file )
{
std::auto_ptr<ImageReader> reader(get_image_reader(type,file));
if (reader.get())
reader->read(0,0,*image_);
}
symbolizer_with_image::symbolizer_with_image( symbolizer_with_image const& rhs)
: image_(rhs.image_), image_filename_(rhs.image_filename_) {}
boost::shared_ptr<ImageData32> symbolizer_with_image::get_image() const
{
return image_;
}
void symbolizer_with_image::set_image(boost::shared_ptr<ImageData32> symbol)
{
image_ = symbol;
}
const std::string & symbolizer_with_image::get_filename() const
{
return image_filename_;
}
symbolizer_with_image::symbolizer_with_image(boost::shared_ptr<ImageData32> img) :
image_( img ) {}
symbolizer_with_image::symbolizer_with_image(std::string const& file,
std::string const& type, unsigned width,unsigned height)
: image_(new ImageData32(width,height)),
image_filename_( file )
{
std::auto_ptr<ImageReader> reader(get_image_reader(type,file));
if (reader.get())
reader->read(0,0,*image_);
}
symbolizer_with_image::symbolizer_with_image( symbolizer_with_image const& rhs)
: image_(rhs.image_), image_filename_(rhs.image_filename_) {}
boost::shared_ptr<ImageData32> symbolizer_with_image::get_image() const
{
return image_;
}
void symbolizer_with_image::set_image(boost::shared_ptr<ImageData32> symbol)
{
image_ = symbol;
}
const std::string & symbolizer_with_image::get_filename() const
{
return image_filename_;
}
} // end of namespace mapnik