Skip to content

Commit

Permalink
add alternative line rasterizer that can be enabled to draw faster li…
Browse files Browse the repository at this point in the history
…nes at the expense of dropping short geometries - for thin lines has nicer look but should not be used with think lines - refs #873
  • Loading branch information
Dane Springmeyer committed Sep 9, 2011
1 parent cf384a6 commit 67d7a3a
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 87 deletions.
13 changes: 12 additions & 1 deletion bindings/python/mapnik_line_symbolizer.cpp
Expand Up @@ -22,8 +22,10 @@
//$Id$

#include <boost/python.hpp>
#include "mapnik_enumeration.hpp"
#include <mapnik/line_symbolizer.hpp>

using namespace mapnik;
using mapnik::line_symbolizer;
using mapnik::stroke;
using mapnik::color;
Expand All @@ -33,7 +35,7 @@ struct line_symbolizer_pickle_suite : boost::python::pickle_suite
static boost::python::tuple
getinitargs(const line_symbolizer& l)
{
return boost::python::make_tuple(l.get_stroke());
return boost::python::make_tuple(l.get_stroke(),l.get_rasterizer());
}

};
Expand All @@ -42,11 +44,20 @@ void export_line_symbolizer()
{
using namespace boost::python;

enumeration_<line_rasterizer_e>("line_rasterizer")
.value("FULL",RASTERIZER_FULL)
.value("FAST",RASTERIZER_FAST)
;

class_<line_symbolizer>("LineSymbolizer",
init<>("Default LineSymbolizer - 1px solid black"))
.def(init<stroke const&>("TODO"))
.def(init<color const& ,float>())
.def_pickle(line_symbolizer_pickle_suite())
.add_property("rasterizer",
&line_symbolizer::get_rasterizer,
&line_symbolizer::set_rasterizer,
"Set/get the rasterization method of the line of the point")
.add_property("stroke",make_function
(&line_symbolizer::get_stroke,
return_value_policy<copy_const_reference>()),
Expand Down
37 changes: 32 additions & 5 deletions include/mapnik/line_symbolizer.hpp
Expand Up @@ -26,32 +26,59 @@

#include <mapnik/stroke.hpp>
#include <mapnik/symbolizer.hpp>
#include <mapnik/enumeration.hpp>

namespace mapnik
{

enum line_rasterizer_enum {
RASTERIZER_FULL, // agg::renderer_scanline_aa_solid
RASTERIZER_FAST, // agg::rasterizer_outline_aa, twice as fast but only good for thin lines
line_rasterizer_enum_MAX
};

DEFINE_ENUM( line_rasterizer_e, line_rasterizer_enum );

struct MAPNIK_DECL line_symbolizer : public symbolizer_base
{
explicit line_symbolizer()
: symbolizer_base(), stroke_() {}
: symbolizer_base(),
stroke_(),
rasterizer_p_(RASTERIZER_FULL) {}

line_symbolizer(stroke const& stroke)
: symbolizer_base(), stroke_(stroke) {}
: symbolizer_base(),
stroke_(stroke),
rasterizer_p_(RASTERIZER_FULL) {}

line_symbolizer(color const& pen,float width=1.0)
: symbolizer_base(), stroke_(pen,width) {}
: symbolizer_base(),
stroke_(pen,width),
rasterizer_p_(RASTERIZER_FULL) {}

stroke const& get_stroke() const
{
return stroke_;
}

void set_stroke(stroke const& stk)
{
stroke_ = stk;
}

void set_stroke(stroke const& stroke)
void set_rasterizer(line_rasterizer_e rasterizer_p)
{
rasterizer_p_ = rasterizer_p;
}

line_rasterizer_e get_rasterizer() const
{
stroke_ = stroke;
return rasterizer_p_;
}

private:
stroke stroke_;
line_rasterizer_e rasterizer_p_;
};
}

Expand Down
191 changes: 110 additions & 81 deletions src/agg/process_line_symbolizer.cpp
Expand Up @@ -31,13 +31,13 @@
#include "agg_pixfmt_rgba.h"
#include "agg_rasterizer_scanline_aa.h"
#include "agg_scanline_u.h"
// for line_symbolizer
#include "agg_renderer_scanline.h"
//#include "agg_renderer_outline_aa.h"
//#include "agg_rasterizer_outline_aa.h"
#include "agg_scanline_p.h"
#include "agg_conv_stroke.h"
#include "agg_conv_dash.h"
#include "agg_renderer_outline_aa.h"
#include "agg_rasterizer_outline_aa.h"


// stl
#include <string>
Expand All @@ -51,101 +51,130 @@ void agg_renderer<T>::process(line_symbolizer const& sym,
{
typedef agg::renderer_base<agg::pixfmt_rgba32_plain> ren_base;
typedef coord_transform2<CoordTransform,geometry_type> path_type;
//typedef agg::renderer_outline_aa<ren_base> renderer_oaa;
//typedef agg::rasterizer_outline_aa<renderer_oaa> rasterizer_outline_aa;
typedef agg::renderer_scanline_aa_solid<ren_base> renderer;

agg::rendering_buffer buf(pixmap_.raw_data(),width_,height_, width_ * 4);
agg::pixfmt_rgba32_plain pixf(buf);

ren_base renb(pixf);
stroke const& stroke_ = sym.get_stroke();
stroke const& stroke_ = sym.get_stroke();
color const& col = stroke_.get_color();
unsigned r=col.red();
unsigned g=col.green();
unsigned b=col.blue();
unsigned a=col.alpha();
renderer ren(renb);
ras_ptr->reset();
ras_ptr->gamma(agg::gamma_linear(0.0, stroke_.get_gamma()));

agg::scanline_p8 sl;
metawriter_with_properties writer = sym.get_metawriter();
for (unsigned i=0;i<feature.num_geometries();++i)
agg::rendering_buffer buf(pixmap_.raw_data(),width_,height_, width_ * 4);
agg::pixfmt_rgba32_plain pixf(buf);

if (sym.get_rasterizer() == RASTERIZER_FAST)
{
geometry_type const& geom = feature.get_geometry(i);
if (geom.num_points() > 1)
typedef agg::renderer_outline_aa<ren_base> renderer_type;
typedef agg::rasterizer_outline_aa<renderer_type> rasterizer_type;

agg::line_profile_aa profile;
//agg::line_profile_aa profile(stroke_.get_width() * scale_factor_, agg::gamma_none());
profile.width(stroke_.get_width() * scale_factor_);
ren_base base_ren(pixf);
renderer_type ren(base_ren, profile);
ren.color(agg::rgba8(r, g, b, int(a*stroke_.get_opacity())));
ren.clip_box(0,0,width_,height_);
rasterizer_type ras(ren);
ras.line_join(agg::outline_miter_accurate_join);
ras.round_cap(true);

for (unsigned i=0;i<feature.num_geometries();++i)
{
path_type path(t_,geom,prj_trans);
geometry_type const& geom = feature.get_geometry(i);
if (geom.num_points() > 1)
{
path_type path(t_,geom,prj_trans);
ras.add_path(path);
}
}
}
else
{
typedef agg::renderer_scanline_aa_solid<ren_base> renderer;

if (stroke_.has_dash())
agg::scanline_p8 sl;

ren_base renb(pixf);
renderer ren(renb);
ras_ptr->reset();
ras_ptr->gamma(agg::gamma_linear(0.0, stroke_.get_gamma()));

metawriter_with_properties writer = sym.get_metawriter();
for (unsigned i=0;i<feature.num_geometries();++i)
{
geometry_type const& geom = feature.get_geometry(i);
if (geom.num_points() > 1)
{
agg::conv_dash<path_type> dash(path);
dash_array const& d = stroke_.get_dash_array();
dash_array::const_iterator itr = d.begin();
dash_array::const_iterator end = d.end();
for (;itr != end;++itr)
path_type path(t_,geom,prj_trans);

if (stroke_.has_dash())
{
dash.add_dash(itr->first * scale_factor_,
itr->second * scale_factor_);
agg::conv_dash<path_type> dash(path);
dash_array const& d = stroke_.get_dash_array();
dash_array::const_iterator itr = d.begin();
dash_array::const_iterator end = d.end();
for (;itr != end;++itr)
{
dash.add_dash(itr->first * scale_factor_,
itr->second * scale_factor_);
}

agg::conv_stroke<agg::conv_dash<path_type > > stroke(dash);

line_join_e join=stroke_.get_line_join();
if ( join == MITER_JOIN)
stroke.generator().line_join(agg::miter_join);
else if( join == MITER_REVERT_JOIN)
stroke.generator().line_join(agg::miter_join);
else if( join == ROUND_JOIN)
stroke.generator().line_join(agg::round_join);
else
stroke.generator().line_join(agg::bevel_join);

line_cap_e cap=stroke_.get_line_cap();
if (cap == BUTT_CAP)
stroke.generator().line_cap(agg::butt_cap);
else if (cap == SQUARE_CAP)
stroke.generator().line_cap(agg::square_cap);
else
stroke.generator().line_cap(agg::round_cap);

stroke.generator().miter_limit(4.0);
stroke.generator().width(stroke_.get_width() * scale_factor_);
ras_ptr->add_path(stroke);

}

agg::conv_stroke<agg::conv_dash<path_type > > stroke(dash);

line_join_e join=stroke_.get_line_join();
if ( join == MITER_JOIN)
stroke.generator().line_join(agg::miter_join);
else if( join == MITER_REVERT_JOIN)
stroke.generator().line_join(agg::miter_join);
else if( join == ROUND_JOIN)
stroke.generator().line_join(agg::round_join);
else
stroke.generator().line_join(agg::bevel_join);

line_cap_e cap=stroke_.get_line_cap();
if (cap == BUTT_CAP)
stroke.generator().line_cap(agg::butt_cap);
else if (cap == SQUARE_CAP)
stroke.generator().line_cap(agg::square_cap);
else
stroke.generator().line_cap(agg::round_cap);

stroke.generator().miter_limit(4.0);
stroke.generator().width(stroke_.get_width() * scale_factor_);

ras_ptr->add_path(stroke);

}
else
{
agg::conv_stroke<path_type> stroke(path);
line_join_e join=stroke_.get_line_join();
if ( join == MITER_JOIN)
stroke.generator().line_join(agg::miter_join);
else if( join == MITER_REVERT_JOIN)
stroke.generator().line_join(agg::miter_join);
else if( join == ROUND_JOIN)
stroke.generator().line_join(agg::round_join);
else
stroke.generator().line_join(agg::bevel_join);

line_cap_e cap=stroke_.get_line_cap();
if (cap == BUTT_CAP)
stroke.generator().line_cap(agg::butt_cap);
else if (cap == SQUARE_CAP)
stroke.generator().line_cap(agg::square_cap);
else
stroke.generator().line_cap(agg::round_cap);

stroke.generator().miter_limit(4.0);
stroke.generator().width(stroke_.get_width() * scale_factor_);
ras_ptr->add_path(stroke);
if (writer.first) writer.first->add_line(path, feature, t_, writer.second);
{
agg::conv_stroke<path_type> stroke(path);
line_join_e join=stroke_.get_line_join();
if ( join == MITER_JOIN)
stroke.generator().line_join(agg::miter_join);
else if( join == MITER_REVERT_JOIN)
stroke.generator().line_join(agg::miter_join);
else if( join == ROUND_JOIN)
stroke.generator().line_join(agg::round_join);
else
stroke.generator().line_join(agg::bevel_join);

line_cap_e cap=stroke_.get_line_cap();
if (cap == BUTT_CAP)
stroke.generator().line_cap(agg::butt_cap);
else if (cap == SQUARE_CAP)
stroke.generator().line_cap(agg::square_cap);
else
stroke.generator().line_cap(agg::round_cap);

stroke.generator().miter_limit(4.0);
stroke.generator().width(stroke_.get_width() * scale_factor_);
ras_ptr->add_path(stroke);
if (writer.first) writer.first->add_line(path, feature, t_, writer.second);
}
}
}
ren.color(agg::rgba8(r, g, b, int(a*stroke_.get_opacity())));
agg::render_scanlines(*ras_ptr, sl, ren);
}
ren.color(agg::rgba8(r, g, b, int(a*stroke_.get_opacity())));
agg::render_scanlines(*ras_ptr, sl, ren);
}


Expand Down
6 changes: 6 additions & 0 deletions src/load_map.cpp
Expand Up @@ -1784,6 +1784,7 @@ void map_parser::parse_line_symbolizer( rule & rule, ptree const & sym )
std::stringstream s;
s << "stroke,stroke-width,stroke-opacity,stroke-linejoin,"
<< "stroke-linecap,stroke-gamma,stroke-dash-offset,stroke-dasharray,"
<< "rasterizer,"
<< "meta-writer,meta-output";

ensure_attrs(sym, "LineSymbolizer", s.str());
Expand All @@ -1793,6 +1794,11 @@ void map_parser::parse_line_symbolizer( rule & rule, ptree const & sym )
parse_stroke(strk,sym);
line_symbolizer symbol = line_symbolizer(strk);

// rasterizer method
line_rasterizer_e rasterizer = get_attr<line_rasterizer_e>(sym, "rasterizer", RASTERIZER_FULL);
//optional<line_rasterizer_e> rasterizer_method = get_opt_attr<line_rasterizer_e>(sym, "full");
symbol.set_rasterizer(rasterizer);

parse_metawriter_in_symbolizer(symbol, sym);
rule.append(symbol);
}
Expand Down
6 changes: 6 additions & 0 deletions src/save_map.cpp
Expand Up @@ -82,6 +82,12 @@ class serialize_symbolizer : public boost::static_visitor<>
const stroke & strk = sym.get_stroke();
add_stroke_attributes(sym_node, strk);
add_metawriter_attributes(sym_node, sym);

line_symbolizer dfl;
if ( sym.get_rasterizer() != dfl.get_rasterizer() || explicit_defaults_ )
{
set_attr( sym_node, "rasterizer", sym.get_rasterizer() );
}
}

void operator () ( const line_pattern_symbolizer & sym )
Expand Down
6 changes: 6 additions & 0 deletions tests/python_tests/object_test.py
Expand Up @@ -14,6 +14,12 @@ def setup():

# Tests that exercise the functionality of Mapnik classes.

# LineSymbolizer initialization
def test_line_symbolizer_init():
s = mapnik2.LineSymbolizer()
eq_(s.stroke, mapnik2.Stroke())
eq_(s.rasterizer, mapnik2.line_rasterizer.FULL)

# ShieldSymbolizer initialization
def test_shieldsymbolizer_init():
s = mapnik2.ShieldSymbolizer(mapnik2.Expression('[Field Name]'), 'DejaVu Sans Bold', 6, mapnik2.Color('#000000'), mapnik2.PathExpression('../data/images/dummy.png'))
Expand Down

0 comments on commit 67d7a3a

Please sign in to comment.