+ applied "hill shading" patch from Marcin Rudowski

a) Raster opacity
   b) Raster merging modes (TODO:add more modes, consider agg impl)
   c) Raster scaling algos: fast,bilinear,bilinear8
      (TODO: add alpha support in bilinear8) 
   d) improvements to png256

   *Great work, thanks!*
This commit is contained in:
Artem Pavlenko 2009-03-29 11:05:20 +00:00
parent 2c878b52cf
commit 2eec236fc2
10 changed files with 448 additions and 17 deletions

View File

@ -38,7 +38,7 @@
// boost
#ifdef MAPNIK_DEBUG
#ifdef MAPNIK_XML_DEBUG
#define BOOST_SPIRIT_DEBUG
#endif

View File

@ -234,6 +234,138 @@ namespace mapnik
}
}
}
struct Multiply
{
inline static void mergeRGB(unsigned const &r0, unsigned const &g0, unsigned const &b0,
unsigned &r1, unsigned &g1, unsigned &b1)
{
r1 = r1*r0/255;
g1 = g1*g0/255;
b1 = b1*b0/255;
}
};
struct Multiply2
{
inline static void mergeRGB(unsigned const &r0, unsigned const &g0, unsigned const &b0,
unsigned &r1, unsigned &g1, unsigned &b1)
{
r1 = r1*r0/128;
if (r1>255) r1=255;
g1 = g1*g0/128;
if (g1>255) g1=255;
b1 = b1*b0/128;
if (b1>255) b1=255;
}
};
struct Divide
{
inline static void mergeRGB(unsigned const &r0, unsigned const &g0, unsigned const &b0,
unsigned &r1, unsigned &g1, unsigned &b1)
{
r1 = r0*256/(r1+1);
g1 = g0*256/(g1+1);
b1 = b0*256/(b1+1);
}
};
struct Divide2
{
inline static void mergeRGB(unsigned const &r0, unsigned const &g0, unsigned const &b0,
unsigned &r1, unsigned &g1, unsigned &b1)
{
r1 = r0*128/(r1+1);
g1 = g0*128/(g1+1);
b1 = b0*128/(b1+1);
}
};
struct Screen
{
inline static void mergeRGB(unsigned const &r0, unsigned const &g0, unsigned const &b0,
unsigned &r1, unsigned &g1, unsigned &b1)
{
r1 = 255 - (255-r0)*(255-r1)/255;
g1 = 255 - (255-g0)*(255-g1)/255;
b1 = 255 - (255-b0)*(255-b1)/255;
}
};
struct HardLight
{
inline static void mergeRGB(unsigned const &r0, unsigned const &g0, unsigned const &b0,
unsigned &r1, unsigned &g1, unsigned &b1)
{
r1 = (r1>128)?255-(255-r0)*(255-2*(r1-128))/256:r0*r1*2/256;
g1 = (g1>128)?255-(255-g0)*(255-2*(g1-128))/256:g0*g1*2/256;
b1 = (b1>128)?255-(255-b0)*(255-2*(b1-128))/256:b0*b1*2/256;
}
};
struct MergeGrain
{
inline static void mergeRGB(unsigned const &r0, unsigned const &g0, unsigned const &b0,
unsigned &r1, unsigned &g1, unsigned &b1)
{
r1 = (r1+r0>128)?r1+r0-128:0;
if (r1>255) r1=255;
g1 = (g1+g0>128)?g1+g0-128:0;
if (g1>255) g1=255;
b1 = (b1+b0>128)?b1+b0-128:0;
if (b1>255) b1=255;
}
};
struct MergeGrain2
{
inline static void mergeRGB(unsigned const &r0, unsigned const &g0, unsigned const &b0,
unsigned &r1, unsigned &g1, unsigned &b1)
{
r1 = (2*r1+r0>256)?2*r1+r0-256:0;
if (r1>255) r1=255;
g1 = (2*g1+g0>256)?2*g1+g0-256:0;
if (g1>255) g1=255;
b1 = (2*b1+b0>256)?2*b1+b0-256:0;
if (b1>255) b1=255;
}
};
template <typename MergeMethod>
inline void merge_rectangle(ImageData32 const& data, unsigned x0, unsigned y0, float opacity)
{
Envelope<int> ext0(0,0,width_,height_);
Envelope<int> ext1(x0,y0,x0 + data.width(),y0 + data.height());
if (ext0.intersects(ext1))
{
Envelope<int> box = ext0.intersect(ext1);
for (int y = box.miny(); y < box.maxy(); ++y)
{
unsigned int* row_to = data_.getRow(y);
unsigned int const * row_from = data.getRow(y-y0);
for (int x = box.minx(); x < box.maxx(); ++x)
{
unsigned rgba0 = row_to[x];
unsigned rgba1 = row_from[x-x0];
unsigned a1 = int( ((rgba1 >> 24) & 0xff) * opacity );
if (a1 == 0) continue;
unsigned r1 = rgba1 & 0xff;
unsigned g1 = (rgba1 >> 8 ) & 0xff;
unsigned b1 = (rgba1 >> 16) & 0xff;
unsigned a0 = (rgba0 >> 24) & 0xff;
unsigned r0 = rgba0 & 0xff ;
unsigned g0 = (rgba0 >> 8 ) & 0xff;
unsigned b0 = (rgba0 >> 16) & 0xff;
unsigned a = (a1 * 255 + (255 - a1) * a0 + 127)/255;
MergeMethod::mergeRGB(r0,g0,b0,r1,g1,b1);
r0 = (r1*a1 + (((255 - a1) * a0 + 127)/255) * r0 + 127)/a;
g0 = (g1*a1 + (((255 - a1) * a0 + 127)/255) * g0 + 127)/a;
b0 = (b1*a1 + (((255 - a1) * a0 + 127)/255) * b0 + 127)/a;
row_to[x] = (a << 24)| (b0 << 16) | (g0 << 8) | (r0) ;
}
}
}
}
};
}
#endif //GRAPHICS_HPP

View File

@ -272,6 +272,138 @@ namespace mapnik {
}
}
template <typename Image>
inline void scale_image_bilinear (Image& target,const Image& source)
{
int source_width=source.width();
int source_height=source.height();
int target_width=target.width();
int target_height=target.height();
if (source_width<1 || source_height<1 ||
target_width<1 || target_height<1) return;
int x=0,y=0,xs=0,ys=0;
int tw2 = target_width/2;
int th2 = target_height/2;
for (y=0;y<target_height;++y)
{
ys = y*source_height/target_height;
int ys1 = ys+1;
if (ys1>=source_height)
ys1--;
unsigned yprt = y*source_height%target_height;
unsigned yprt1 = target_height-yprt;
for (x=0;x<target_width;++x)
{
xs = x*source_width/target_width;
if (source_width>=target_width || source_height>=target_height){
target(x,y)=source(xs,ys);
continue;
}
unsigned xprt = x*source_width%target_width;
unsigned xprt1 = target_width-xprt;
int xs1 = xs+1;
if (xs1>=source_width)
xs1--;
unsigned a = source(xs,ys);
unsigned b = source(xs1,ys);
unsigned c = source(xs,ys1);
unsigned d = source(xs1,ys1);
unsigned out=0;
unsigned t = 0;
for(int i=0; i<4; i++){
unsigned p,r,s;
// X axis
p = a&0xff;
r = b&0xff;
if (p!=r)
r = (r*xprt+p*xprt1+tw2)/target_width;
p = c&0xff;
s = d&0xff;
if (p!=s)
s = (s*xprt+p*xprt1+tw2)/target_width;
// Y axis
if (r!=s)
r = (s*yprt+r*yprt1+th2)/target_height;
// channel up
out |= r << t;
t += 8;
a >>= 8;
b >>= 8;
c >>= 8;
d >>= 8;
}
target(x,y)=out;
}
}
}
template <typename Image>
inline void scale_image_bilinear8 (Image& target,const Image& source)
{
int source_width=source.width();
int source_height=source.height();
int target_width=target.width();
int target_height=target.height();
if (source_width<1 || source_height<1 ||
target_width<1 || target_height<1) return;
int x=0,y=0,xs=0,ys=0;
int tw2 = target_width/2;
int th2 = target_height/2;
for (y=0;y<target_height;++y)
{
ys = y*source_height/target_height;
int ys1 = ys+1;
if (ys1>=source_height)
ys1--;
unsigned yprt = y*source_height%target_height;
unsigned yprt1 = target_height-yprt;
for (x=0;x<target_width;++x)
{
xs = x*source_width/target_width;
if (source_width>=target_width || source_height>=target_height){
target(x,y)=source(xs,ys);
continue;
}
unsigned xprt = x*source_width%target_width;
unsigned xprt1 = target_width-xprt;
int xs1 = xs+1;
if (xs1>=source_width)
xs1--;
unsigned a = source(xs,ys);
unsigned b = source(xs1,ys);
unsigned c = source(xs,ys1);
unsigned d = source(xs1,ys1);
unsigned p,r,s;
// X axis
p = a&0xff;
r = b&0xff;
if (p!=r)
r = (r*xprt+p*xprt1+tw2)/target_width;
p = c&0xff;
s = d&0xff;
if (p!=s)
s = (s*xprt+p*xprt1+tw2)/target_width;
// Y axis
if (r!=s)
r = (s*yprt+r*yprt1+th2)/target_height;
target(x,y)=(0xff<<24) | (r<<16) | (r<<8) | r;
}
}
}
inline MAPNIK_DECL void save_to_file (Image32 const& image,
std::string const& file,
std::string const& type)

View File

@ -51,9 +51,10 @@ namespace mapnik {
struct RGBPolicy
{
const static unsigned MAX_LEVELS = 6;
const static unsigned MIN_LEVELS = 3;
inline static unsigned index_from_level(unsigned level, rgb const& c)
{
unsigned shift = (MAX_LEVELS + 1) - level;
unsigned shift = 7 - level;
return (((c.r >> shift) & 1) << 2)
| (((c.g >> shift) & 1) << 1)
| ((c.b >> shift) & 1);
@ -71,6 +72,7 @@ namespace mapnik {
blues(0),
count(0),
count2(0),
children_count(0),
index(0)
{
memset(&children_[0],0,sizeof(children_));
@ -90,6 +92,7 @@ namespace mapnik {
unsigned blues;
unsigned count;
unsigned count2;
uint8_t children_count;
byte index;
};
struct node_cmp
@ -136,6 +139,7 @@ namespace mapnik {
unsigned idx = InsertPolicy::index_from_level(level,data);
if (cur_node->children_[idx] == 0) {
cur_node->children_count++;
cur_node->children_[idx] = new node();
if (level < leaf_level_-1)
{
@ -187,13 +191,30 @@ namespace mapnik {
if ( reducible_[leaf_level_-1].size() == 0) return;
typename std::deque<node*>::iterator pos = reducible_[leaf_level_-1].begin();
// select best of all reducible:
unsigned red_idx = leaf_level_-1;
unsigned bestv = (*reducible_[red_idx].begin())->count2;
for(unsigned i=red_idx; i>=InsertPolicy::MIN_LEVELS; i--) if (!reducible_[i].empty()){
node *nd = *reducible_[i].begin();
unsigned gch = 0;
for(unsigned idx=0; idx<8; idx++){
if (nd->children_[idx])
gch += nd->children_[idx]->children_count;
}
if (gch==0 && nd->count2<bestv){
bestv = nd->count2;
red_idx = i;
}
}
typename std::deque<node*>::iterator pos = reducible_[red_idx].begin();
node * cur_node = *pos;
unsigned num_children = 0;
for (unsigned idx=0; idx < 8; ++idx)
{
if (cur_node->children_[idx] != 0)
{
cur_node->children_count++;
++num_children;
cur_node->reds += cur_node->children_[idx]->reds;
cur_node->greens += cur_node->children_[idx]->greens;
@ -203,7 +224,7 @@ namespace mapnik {
}
}
reducible_[leaf_level_-1].erase(pos);
reducible_[red_idx].erase(pos);
if (num_children > 0 )
{
colors_ -= (num_children - 1);

View File

@ -28,7 +28,41 @@
namespace mapnik
{
struct MAPNIK_DECL raster_symbolizer { /* TODO */};
struct MAPNIK_DECL raster_symbolizer {
explicit raster_symbolizer()
: mode_("normal"),
scaling_("fast"),
opacity_(1.0) {}
std::string const& get_mode() const
{
return mode_;
}
void set_mode(std::string const& mode)
{
mode_ = mode;
}
std::string const& get_scaling() const
{
return scaling_;
}
void set_scaling(std::string const& scaling)
{
scaling_ = scaling;
}
void set_opacity(float opacity)
{
opacity_ = opacity;
}
float get_opacity() const
{
return opacity_;
}
private:
std::string mode_;
std::string scaling_;
float opacity_;
};
}
#endif //RASTER_SYMBOLIZER_HPP

View File

@ -660,20 +660,56 @@ namespace mapnik
}
template <typename T>
void agg_renderer<T>::process(raster_symbolizer const&,
void agg_renderer<T>::process(raster_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
// TODO -- at the moment raster_symbolizer is an empty class
// used for type dispatching, but we can have some fancy raster
// processing in a future (filters??). Just copy raster into pixmap for now.
raster_ptr const& raster=feature.get_raster();
if (raster)
{
Envelope<double> ext=t_.forward(raster->ext_);
ImageData32 target(int(ext.width() + 0.5),int(ext.height() + 0.5));
if (sym.get_scaling() == "fast"){
scale_image<ImageData32>(target,raster->data_);
} else if (sym.get_scaling() == "bilinear"){
scale_image_bilinear<ImageData32>(target,raster->data_);
} else if (sym.get_scaling() == "bilinear8"){
scale_image_bilinear8<ImageData32>(target,raster->data_);
} else {
scale_image<ImageData32>(target,raster->data_);
}
if (sym.get_mode() == "normal"){
if (sym.get_opacity() == 1.0){
pixmap_.set_rectangle(int(ext.minx()),int(ext.miny()),target);
} else {
pixmap_.set_rectangle_alpha2(target,int(ext.minx()),int(ext.miny()), sym.get_opacity());
}
} else if (sym.get_mode() == "grain_merge"){
pixmap_.template merge_rectangle<Image32::MergeGrain> (target,int(ext.minx()),int(ext.miny()), sym.get_opacity());
} else if (sym.get_mode() == "grain_merge2"){
pixmap_.template merge_rectangle<Image32::MergeGrain2> (target,int(ext.minx()),int(ext.miny()), sym.get_opacity());
} else if (sym.get_mode() == "multiply"){
pixmap_.template merge_rectangle<Image32::Multiply> (target,int(ext.minx()),int(ext.miny()), sym.get_opacity());
} else if (sym.get_mode() == "multiply2"){
pixmap_.template merge_rectangle<Image32::Multiply2> (target,int(ext.minx()),int(ext.miny()), sym.get_opacity());
} else if (sym.get_mode() == "divide"){
pixmap_.template merge_rectangle<Image32::Divide> (target,int(ext.minx()),int(ext.miny()), sym.get_opacity());
} else if (sym.get_mode() == "divide2"){
pixmap_.template merge_rectangle<Image32::Divide2> (target,int(ext.minx()),int(ext.miny()), sym.get_opacity());
} else if (sym.get_mode() == "screen"){
pixmap_.template merge_rectangle<Image32::Screen> (target,int(ext.minx()),int(ext.miny()), sym.get_opacity());
} else if (sym.get_mode() == "hard_light"){
pixmap_.template merge_rectangle<Image32::HardLight> (target,int(ext.minx()),int(ext.miny()), sym.get_opacity());
} else {
if (sym.get_opacity() == 1.0){
pixmap_.set_rectangle(int(ext.minx()),int(ext.miny()),target);
} else {
pixmap_.set_rectangle_alpha2(target,int(ext.minx()),int(ext.miny()), sym.get_opacity());
}
}
// TODO: other modes? (add,diff,sub,...)
}
}

View File

@ -961,7 +961,7 @@ namespace mapnik
}
}
void cairo_renderer_base::process(raster_symbolizer const&,
void cairo_renderer_base::process(raster_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
@ -973,10 +973,21 @@ namespace mapnik
{
Envelope<double> ext = t_.forward(raster->ext_);
ImageData32 target(int(ext.width() + 0.5), int(ext.height() + 0.5));
//TODO -- use cairo matrix transformation for scaling
if (sym.get_scaling() == "fast"){
scale_image<ImageData32>(target, raster->data_);
} else if (sym.get_scaling() == "bilinear"){
scale_image_bilinear<ImageData32>(target,raster->data_);
} else if (sym.get_scaling() == "bilinear8"){
scale_image_bilinear8<ImageData32>(target,raster->data_);
} else {
scale_image<ImageData32>(target,raster->data_);
}
cairo_context context(context_);
context.add_image(int(ext.minx()), int(ext.miny()), target);
//TODO -- support for advanced image merging
context.add_image(int(ext.minx()), int(ext.miny()), target, sym.get_opacity());
}
}

View File

@ -81,6 +81,7 @@ namespace mapnik
void parse_line_symbolizer( rule_type & rule, ptree const & sym);
void parse_polygon_symbolizer( rule_type & rule, ptree const & sym);
void parse_building_symbolizer( rule_type & rule, ptree const & sym );
void parse_raster_symbolizer( rule_type & rule, ptree const & sym );
void parse_markers_symbolizer( rule_type & rule, ptree const & sym );
void ensure_font_face( const std::string & face_name );
@ -529,7 +530,7 @@ namespace mapnik
}
else if ( sym.first == "RasterSymbolizer")
{
rule.append(raster_symbolizer());
parse_raster_symbolizer( rule, sym.second );
}
else if ( sym.first == "MarkersSymbolizer")
{
@ -1187,6 +1188,59 @@ namespace mapnik
}
}
void map_parser::parse_raster_symbolizer( rule_type & rule, ptree const & sym )
{
try
{
raster_symbolizer raster_sym;
ptree::const_iterator cssIter = sym.begin();
ptree::const_iterator endCss = sym.end();
for(; cssIter != endCss; ++cssIter)
{
ptree::value_type const& css_tag = *cssIter;
ptree const & css = cssIter->second;
if (css_tag.first == "CssParameter")
{
std::string css_name = get_attr<string>(css, "name");
if (css_name == "mode")
{
raster_sym.set_mode(get_css<string>(css, css_name));
}
else if (css_name == "scaling")
{
raster_sym.set_scaling(get_css<string>(css, css_name));
}
else if (css_name == "opacity")
{
float opacity = get_css<float>(css, css_name);
raster_sym.set_opacity(opacity);
}
else
{
throw config_error(std::string("Failed to parse unknown CSS ") +
"parameter '" + css_name + "'");
}
}
else if (css_tag.first != "<xmlcomment>" &&
css_tag.first != "<xmlattr>" )
{
throw config_error(std::string("Unknown child node. ") +
"Expected 'CssParameter' but got '" + css_tag.first + "'");
}
}
rule.append(raster_sym);
}
catch (const config_error & ex)
{
ex.append_context("in RasterSymbolizer");
throw;
}
}
void map_parser::ensure_font_face( const std::string & face_name )
{
if ( ! font_manager_.get_face( face_name ) )

View File

@ -137,8 +137,22 @@ namespace mapnik
void operator () ( const raster_symbolizer & sym )
{
rule_.push_back(
ptree::value_type("RasterSymbolizer", ptree()));
ptree & sym_node = rule_.push_back(
ptree::value_type("RasterSymbolizer", ptree()))->second;
raster_symbolizer dfl;
if ( sym.get_mode() != dfl.get_mode() )
{
set_css( sym_node, "mode", sym.get_mode() );
}
if ( sym.get_scaling() != dfl.get_scaling() )
{
set_css( sym_node, "scaling", sym.get_scaling() );
}
if ( sym.get_opacity() != dfl.get_opacity() )
{
set_css( sym_node, "opacity", sym.get_opacity() );
}
}
void operator () ( const shield_symbolizer & sym )

View File

@ -165,9 +165,6 @@ namespace mapnik {
: ok_(false),
conv_(0)
{
#ifdef MAPNIK_DEBUG
std::cerr << "ENCODING = " << encoding << "\n";
#endif
//#ifndef WORDS_BIGENDIAN
// desc_ = iconv_open("UCS-4LE",encoding.c_str());