add support for seamless blurring - closes #1478

This commit is contained in:
Dane Springmeyer 2013-10-03 16:37:09 -07:00
parent e276d8fe35
commit 0224ce3019
14 changed files with 98 additions and 28 deletions

View file

@ -14,6 +14,8 @@ Released ...
Summary: TODO Summary: TODO
- Added support for more seamless blurring by rendering to a larger internal image to avoid edge effects (#1478)
- Fixed rendering of large shapes at high zoom levels, which might dissapear due to integer overflow. This - Fixed rendering of large shapes at high zoom levels, which might dissapear due to integer overflow. This
bug was previously fixable when geometries were clipped, but would, until now, re-appear if clipping was turned bug was previously fixable when geometries were clipped, but would, until now, re-appear if clipping was turned
off for a symbolizer (#2000) off for a symbolizer (#2000)

View file

@ -137,6 +137,21 @@ public:
return scale_factor_; return scale_factor_;
} }
inline box2d<double> clipping_extent() const
{
if (t_.offset() > 0)
{
box2d<double> box = query_extent_;
double scale = static_cast<double>(query_extent_.width())/static_cast<double>(width_);
// 3 is used here because at least 3 was needed for the 'style-level-compositing-tiled-0,1' visual test to pass
// TODO - add more tests to hone in on a more robust #
scale *= t_.offset()*3;
box.pad(scale);
return box;
}
return query_extent_;
}
protected: protected:
template <typename R> template <typename R>
void debug_draw_box(R& buf, box2d<double> const& extent, void debug_draw_box(R& buf, box2d<double> const& extent,
@ -149,11 +164,11 @@ private:
buffer_type & pixmap_; buffer_type & pixmap_;
boost::shared_ptr<buffer_type> internal_buffer_; boost::shared_ptr<buffer_type> internal_buffer_;
mutable buffer_type * current_buffer_; mutable buffer_type * current_buffer_;
CoordTransform t_;
mutable bool style_level_compositing_; mutable bool style_level_compositing_;
unsigned width_; unsigned width_;
unsigned height_; unsigned height_;
double scale_factor_; double scale_factor_;
CoordTransform t_;
freetype_engine font_engine_; freetype_engine font_engine_;
face_manager<freetype_engine> font_manager_; face_manager<freetype_engine> font_manager_;
boost::shared_ptr<label_collision_detector4> detector_; boost::shared_ptr<label_collision_detector4> detector_;

View file

@ -123,6 +123,7 @@ private:
box2d<double> extent_; box2d<double> extent_;
double offset_x_; double offset_x_;
double offset_y_; double offset_y_;
int offset_;
double sx_; double sx_;
double sy_; double sy_;
@ -134,6 +135,7 @@ public:
extent_(extent), extent_(extent),
offset_x_(offset_x), offset_x_(offset_x),
offset_y_(offset_y), offset_y_(offset_y),
offset_(0),
sx_(1.0), sx_(1.0),
sy_(1.0) sy_(1.0)
{ {
@ -143,6 +145,16 @@ public:
sy_ = static_cast<double>(height_) / extent_.height(); sy_ = static_cast<double>(height_) / extent_.height();
} }
inline int offset() const
{
return offset_;
}
inline void set_offset(int offset)
{
offset_ = offset;
}
inline double offset_x() const inline double offset_x() const
{ {
return offset_x_; return offset_x_;
@ -175,14 +187,14 @@ public:
inline void forward(double *x, double *y) const inline void forward(double *x, double *y) const
{ {
*x = (*x - extent_.minx()) * sx_ - offset_x_; *x = (*x - extent_.minx()) * sx_ - (offset_x_ - offset_);
*y = (extent_.maxy() - *y) * sy_ - offset_y_; *y = (extent_.maxy() - *y) * sy_ - (offset_y_ - offset_);
} }
inline void backward(double *x, double *y) const inline void backward(double *x, double *y) const
{ {
*x = extent_.minx() + (*x + offset_x_) / sx_; *x = extent_.minx() + (*x + (offset_x_ - offset_)) / sx_;
*y = extent_.maxy() - (*y + offset_y_) / sy_; *y = extent_.maxy() - (*y + (offset_y_ - offset_)) / sy_;
} }
inline coord2d& forward(coord2d& c) const inline coord2d& forward(coord2d& c) const

View file

@ -775,6 +775,21 @@ struct filter_visitor : boost::static_visitor<void>
Src & src_; Src & src_;
}; };
struct filter_radius_visitor : boost::static_visitor<void>
{
int & radius_;
filter_radius_visitor(int & radius)
: radius_(radius) {}
template <typename T>
void operator () (T const& /*filter*/) {}
void operator () (agg_stack_blur const& op)
{
if (op.rx > radius_) radius_ = op.rx;
if (op.ry > radius_) radius_ = op.ry;
}
};
}} }}
#endif // MAPNIK_IMAGE_FILTER_HPP #endif // MAPNIK_IMAGE_FILTER_HPP

View file

@ -70,11 +70,11 @@ agg_renderer<T>::agg_renderer(Map const& m, T & pixmap, double scale_factor, uns
pixmap_(pixmap), pixmap_(pixmap),
internal_buffer_(), internal_buffer_(),
current_buffer_(&pixmap), current_buffer_(&pixmap),
t_(m.width(),m.height(),m.get_current_extent(),offset_x,offset_y),
style_level_compositing_(false), style_level_compositing_(false),
width_(pixmap_.width()), width_(pixmap_.width()),
height_(pixmap_.height()), height_(pixmap_.height()),
scale_factor_(scale_factor), scale_factor_(scale_factor),
t_(m.width(),m.height(),m.get_current_extent(),offset_x,offset_y),
font_engine_(), font_engine_(),
font_manager_(font_engine_), font_manager_(font_engine_),
detector_(boost::make_shared<label_collision_detector4>(box2d<double>(-m.buffer_size(), -m.buffer_size(), m.width() + m.buffer_size() ,m.height() + m.buffer_size()))), detector_(boost::make_shared<label_collision_detector4>(box2d<double>(-m.buffer_size(), -m.buffer_size(), m.width() + m.buffer_size() ,m.height() + m.buffer_size()))),
@ -92,11 +92,11 @@ agg_renderer<T>::agg_renderer(Map const& m, request const& req, T & pixmap, doub
pixmap_(pixmap), pixmap_(pixmap),
internal_buffer_(), internal_buffer_(),
current_buffer_(&pixmap), current_buffer_(&pixmap),
t_(req.width(),req.height(),req.extent(),offset_x,offset_y),
style_level_compositing_(false), style_level_compositing_(false),
width_(pixmap_.width()), width_(pixmap_.width()),
height_(pixmap_.height()), height_(pixmap_.height()),
scale_factor_(scale_factor), scale_factor_(scale_factor),
t_(req.width(),req.height(),req.extent(),offset_x,offset_y),
font_engine_(), font_engine_(),
font_manager_(font_engine_), font_manager_(font_engine_),
detector_(boost::make_shared<label_collision_detector4>(box2d<double>(-req.buffer_size(), -req.buffer_size(), req.width() + req.buffer_size() ,req.height() + req.buffer_size()))), detector_(boost::make_shared<label_collision_detector4>(box2d<double>(-req.buffer_size(), -req.buffer_size(), req.width() + req.buffer_size() ,req.height() + req.buffer_size()))),
@ -115,11 +115,11 @@ agg_renderer<T>::agg_renderer(Map const& m, T & pixmap, boost::shared_ptr<label_
pixmap_(pixmap), pixmap_(pixmap),
internal_buffer_(), internal_buffer_(),
current_buffer_(&pixmap), current_buffer_(&pixmap),
t_(m.width(),m.height(),m.get_current_extent(),offset_x,offset_y),
style_level_compositing_(false), style_level_compositing_(false),
width_(pixmap_.width()), width_(pixmap_.width()),
height_(pixmap_.height()), height_(pixmap_.height()),
scale_factor_(scale_factor), scale_factor_(scale_factor),
t_(m.width(),m.height(),m.get_current_extent(),offset_x,offset_y),
font_engine_(), font_engine_(),
font_manager_(font_engine_), font_manager_(font_engine_),
detector_(detector), detector_(detector),
@ -238,9 +238,27 @@ void agg_renderer<T>::start_style_processing(feature_type_style const& st)
if (style_level_compositing_) if (style_level_compositing_)
{ {
if (!internal_buffer_) int radius = 0;
mapnik::filter::filter_radius_visitor visitor(radius);
BOOST_FOREACH(mapnik::filter::filter_type const& filter_tag, st.image_filters())
{ {
internal_buffer_ = boost::make_shared<buffer_type>(pixmap_.width(),pixmap_.height()); boost::apply_visitor(visitor, filter_tag);
}
if (radius > t_.offset())
{
t_.set_offset(radius);
}
int offset = t_.offset();
unsigned target_width = width_;
unsigned target_height = height_;
target_width = width_ + (offset * 2);
target_height = height_ + (offset * 2);
ras_ptr->clip_box(-int(offset*2),-int(offset*2),target_width,target_height);
if (!internal_buffer_ ||
(internal_buffer_->width() < target_width ||
internal_buffer_->height() < target_height))
{
internal_buffer_ = boost::make_shared<buffer_type>(target_width,target_height);
} }
else else
{ {
@ -250,6 +268,8 @@ void agg_renderer<T>::start_style_processing(feature_type_style const& st)
} }
else else
{ {
t_.set_offset(0);
ras_ptr->clip_box(0,0,width_,height_);
current_buffer_ = &pixmap_; current_buffer_ = &pixmap_;
} }
} }
@ -269,14 +289,19 @@ void agg_renderer<T>::end_style_processing(feature_type_style const& st)
boost::apply_visitor(visitor, filter_tag); boost::apply_visitor(visitor, filter_tag);
} }
} }
if (st.comp_op()) if (st.comp_op())
{ {
composite(pixmap_.data(),current_buffer_->data(), *st.comp_op(), st.get_opacity(), 0, 0, false); composite(pixmap_.data(), current_buffer_->data(),
*st.comp_op(), st.get_opacity(),
-t_.offset(),
-t_.offset(), false);
} }
else if (blend_from || st.get_opacity() < 1) else if (blend_from || st.get_opacity() < 1)
{ {
composite(pixmap_.data(),current_buffer_->data(), src_over, st.get_opacity(), 0, 0, false); composite(pixmap_.data(), current_buffer_->data(),
src_over, st.get_opacity(),
-t_.offset(),
-t_.offset(), false);
} }
} }
// apply any 'direct' image filters // apply any 'direct' image filters

View file

@ -127,7 +127,7 @@ void agg_renderer<T>::process(line_pattern_symbolizer const& sym,
agg::trans_affine tr; agg::trans_affine tr;
evaluate_transform(tr, feature, sym.get_transform()); evaluate_transform(tr, feature, sym.get_transform());
box2d<double> clipping_extent = query_extent_; box2d<double> clip_box = clipping_extent();
if (sym.clip()) if (sym.clip())
{ {
double padding = (double)(query_extent_.width()/pixmap_.width()); double padding = (double)(query_extent_.width()/pixmap_.width());
@ -137,13 +137,13 @@ void agg_renderer<T>::process(line_pattern_symbolizer const& sym,
if (std::fabs(sym.offset()) > 0) if (std::fabs(sym.offset()) > 0)
padding *= std::fabs(sym.offset()) * 1.2; padding *= std::fabs(sym.offset()) * 1.2;
padding *= scale_factor_; padding *= scale_factor_;
clipping_extent.pad(padding); clip_box.pad(padding);
} }
typedef boost::mpl::vector<clip_line_tag,transform_tag,offset_transform_tag,simplify_tag,smooth_tag> conv_types; typedef boost::mpl::vector<clip_line_tag,transform_tag,offset_transform_tag,simplify_tag,smooth_tag> conv_types;
vertex_converter<box2d<double>, rasterizer_type, line_pattern_symbolizer, vertex_converter<box2d<double>, rasterizer_type, line_pattern_symbolizer,
CoordTransform, proj_transform, agg::trans_affine, conv_types> CoordTransform, proj_transform, agg::trans_affine, conv_types>
converter(clipping_extent,ras,sym,t_,prj_trans,tr,scale_factor_); converter(clip_box,ras,sym,t_,prj_trans,tr,scale_factor_);
if (sym.clip()) converter.set<clip_line_tag>(); //optional clip (default: true) if (sym.clip()) converter.set<clip_line_tag>(); //optional clip (default: true)
converter.set<transform_tag>(); //always transform converter.set<transform_tag>(); //always transform

View file

@ -92,7 +92,7 @@ void agg_renderer<T>::process(line_symbolizer const& sym,
agg::trans_affine tr; agg::trans_affine tr;
evaluate_transform(tr, feature, sym.get_transform()); evaluate_transform(tr, feature, sym.get_transform());
box2d<double> clipping_extent = query_extent_; box2d<double> clip_box = clipping_extent();
if (sym.clip()) if (sym.clip())
{ {
double padding = (double)(query_extent_.width()/pixmap_.width()); double padding = (double)(query_extent_.width()/pixmap_.width());
@ -102,7 +102,7 @@ void agg_renderer<T>::process(line_symbolizer const& sym,
if (std::fabs(sym.offset()) > 0) if (std::fabs(sym.offset()) > 0)
padding *= std::fabs(sym.offset()) * 1.2; padding *= std::fabs(sym.offset()) * 1.2;
padding *= scale_factor_; padding *= scale_factor_;
clipping_extent.pad(padding); clip_box.pad(padding);
// debugging // debugging
//box2d<double> inverse = query_extent_; //box2d<double> inverse = query_extent_;
//inverse.pad(-padding); //inverse.pad(-padding);
@ -121,7 +121,7 @@ void agg_renderer<T>::process(line_symbolizer const& sym,
vertex_converter<box2d<double>, rasterizer_type, line_symbolizer, vertex_converter<box2d<double>, rasterizer_type, line_symbolizer,
CoordTransform, proj_transform, agg::trans_affine, conv_types> CoordTransform, proj_transform, agg::trans_affine, conv_types>
converter(clipping_extent,ras,sym,t_,prj_trans,tr,scale_factor_); converter(clip_box,ras,sym,t_,prj_trans,tr,scale_factor_);
if (sym.clip()) converter.set<clip_line_tag>(); // optional clip (default: true) if (sym.clip()) converter.set<clip_line_tag>(); // optional clip (default: true)
converter.set<transform_tag>(); // always transform converter.set<transform_tag>(); // always transform
if (std::fabs(sym.offset()) > 0.0) converter.set<offset_transform_tag>(); // parallel offset if (std::fabs(sym.offset()) > 0.0) converter.set<offset_transform_tag>(); // parallel offset
@ -141,7 +141,7 @@ void agg_renderer<T>::process(line_symbolizer const& sym,
{ {
vertex_converter<box2d<double>, rasterizer, line_symbolizer, vertex_converter<box2d<double>, rasterizer, line_symbolizer,
CoordTransform, proj_transform, agg::trans_affine, conv_types> CoordTransform, proj_transform, agg::trans_affine, conv_types>
converter(clipping_extent,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_); converter(clip_box,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_);
if (sym.clip()) converter.set<clip_line_tag>(); // optional clip (default: true) if (sym.clip()) converter.set<clip_line_tag>(); // optional clip (default: true)
converter.set<transform_tag>(); // always transform converter.set<transform_tag>(); // always transform

View file

@ -139,7 +139,7 @@ void agg_renderer<T>::process(markers_symbolizer const& sym,
snap_pixels); snap_pixels);
vertex_converter<box2d<double>, dispatch_type, markers_symbolizer, vertex_converter<box2d<double>, dispatch_type, markers_symbolizer,
CoordTransform, proj_transform, agg::trans_affine, conv_types> CoordTransform, proj_transform, agg::trans_affine, conv_types>
converter(query_extent_, rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_); converter(clipping_extent(), rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_);
if (sym.clip() && feature.paths().size() > 0) // optional clip (default: true) if (sym.clip() && feature.paths().size() > 0) // optional clip (default: true)
{ {
eGeomType type = feature.paths()[0].type(); eGeomType type = feature.paths()[0].type();
@ -179,7 +179,7 @@ void agg_renderer<T>::process(markers_symbolizer const& sym,
snap_pixels); snap_pixels);
vertex_converter<box2d<double>, dispatch_type, markers_symbolizer, vertex_converter<box2d<double>, dispatch_type, markers_symbolizer,
CoordTransform, proj_transform, agg::trans_affine, conv_types> CoordTransform, proj_transform, agg::trans_affine, conv_types>
converter(query_extent_, rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_); converter(clipping_extent(), rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_);
if (sym.clip() && feature.paths().size() > 0) // optional clip (default: true) if (sym.clip() && feature.paths().size() > 0) // optional clip (default: true)
{ {
eGeomType type = feature.paths()[0].type(); eGeomType type = feature.paths()[0].type();
@ -216,7 +216,7 @@ void agg_renderer<T>::process(markers_symbolizer const& sym,
true /*snap rasters no matter what*/); true /*snap rasters no matter what*/);
vertex_converter<box2d<double>, dispatch_type, markers_symbolizer, vertex_converter<box2d<double>, dispatch_type, markers_symbolizer,
CoordTransform, proj_transform, agg::trans_affine, conv_types> CoordTransform, proj_transform, agg::trans_affine, conv_types>
converter(query_extent_, rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_); converter(clipping_extent(), rasterizer_dispatch, sym,t_,prj_trans,tr,scale_factor_);
if (sym.clip() && feature.paths().size() > 0) // optional clip (default: true) if (sym.clip() && feature.paths().size() > 0) // optional clip (default: true)
{ {

View file

@ -91,6 +91,7 @@ void agg_renderer<T>::process(polygon_pattern_symbolizer const& sym,
boost::optional<image_ptr> pat = (*marker)->get_bitmap_data(); boost::optional<image_ptr> pat = (*marker)->get_bitmap_data();
if (!pat) return; if (!pat) return;
box2d<double> clip_box = clipping_extent();
typedef agg::rgba8 color; typedef agg::rgba8 color;
typedef agg::order_rgba order; typedef agg::order_rgba order;
@ -131,7 +132,7 @@ void agg_renderer<T>::process(polygon_pattern_symbolizer const& sym,
if (feature.num_geometries() > 0) if (feature.num_geometries() > 0)
{ {
clipped_geometry_type clipped(feature.get_geometry(0)); clipped_geometry_type clipped(feature.get_geometry(0));
clipped.clip_box(query_extent_.minx(),query_extent_.miny(),query_extent_.maxx(),query_extent_.maxy()); clipped.clip_box(clip_box.minx(),clip_box.miny(),clip_box.maxx(),clip_box.maxy());
path_type path(t_,clipped,prj_trans); path_type path(t_,clipped,prj_trans);
path.vertex(&x0,&y0); path.vertex(&x0,&y0);
} }
@ -150,7 +151,7 @@ void agg_renderer<T>::process(polygon_pattern_symbolizer const& sym,
typedef boost::mpl::vector<clip_poly_tag,transform_tag,simplify_tag,smooth_tag> conv_types; typedef boost::mpl::vector<clip_poly_tag,transform_tag,simplify_tag,smooth_tag> conv_types;
vertex_converter<box2d<double>, rasterizer, polygon_pattern_symbolizer, vertex_converter<box2d<double>, rasterizer, polygon_pattern_symbolizer,
CoordTransform, proj_transform, agg::trans_affine, conv_types> CoordTransform, proj_transform, agg::trans_affine, conv_types>
converter(query_extent_,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_); converter(clip_box,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_);
if (prj_trans.equal() && sym.clip()) converter.set<clip_poly_tag>(); //optional clip (default: true) if (prj_trans.equal() && sym.clip()) converter.set<clip_poly_tag>(); //optional clip (default: true)
converter.set<transform_tag>(); //always transform converter.set<transform_tag>(); //always transform

View file

@ -62,7 +62,7 @@ void agg_renderer<T>::process(polygon_symbolizer const& sym,
typedef boost::mpl::vector<clip_poly_tag,transform_tag,affine_transform_tag,simplify_tag,smooth_tag> conv_types; typedef boost::mpl::vector<clip_poly_tag,transform_tag,affine_transform_tag,simplify_tag,smooth_tag> conv_types;
vertex_converter<box2d<double>, rasterizer, polygon_symbolizer, vertex_converter<box2d<double>, rasterizer, polygon_symbolizer,
CoordTransform, proj_transform, agg::trans_affine, conv_types> CoordTransform, proj_transform, agg::trans_affine, conv_types>
converter(query_extent_,*ras_ptr,sym,t_,prj_trans,tr,scale_factor_); converter(clipping_extent(),*ras_ptr,sym,t_,prj_trans,tr,scale_factor_);
if (prj_trans.equal() && sym.clip()) converter.set<clip_poly_tag>(); //optional clip (default: true) if (prj_trans.equal() && sym.clip()) converter.set<clip_poly_tag>(); //optional clip (default: true)
converter.set<transform_tag>(); //always transform converter.set<transform_tag>(); //always transform

View file

@ -45,7 +45,7 @@ void agg_renderer<T>::process(shield_symbolizer const& sym,
width_, height_, width_, height_,
scale_factor_, scale_factor_,
t_, font_manager_, *detector_, t_, font_manager_, *detector_,
query_extent_); clipping_extent());
text_renderer<T> ren(*current_buffer_, text_renderer<T> ren(*current_buffer_,
font_manager_, font_manager_,

View file

@ -41,7 +41,7 @@ void agg_renderer<T>::process(text_symbolizer const& sym,
width_,height_, width_,height_,
scale_factor_, scale_factor_,
t_, font_manager_, *detector_, t_, font_manager_, *detector_,
query_extent_); clipping_extent());
text_renderer<T> ren(*current_buffer_, text_renderer<T> ren(*current_buffer_,
font_manager_, font_manager_,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB