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
- 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
bug was previously fixable when geometries were clipped, but would, until now, re-appear if clipping was turned
off for a symbolizer (#2000)

View file

@ -137,6 +137,21 @@ public:
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:
template <typename R>
void debug_draw_box(R& buf, box2d<double> const& extent,
@ -149,11 +164,11 @@ private:
buffer_type & pixmap_;
boost::shared_ptr<buffer_type> internal_buffer_;
mutable buffer_type * current_buffer_;
CoordTransform t_;
mutable bool style_level_compositing_;
unsigned width_;
unsigned height_;
double scale_factor_;
CoordTransform t_;
freetype_engine font_engine_;
face_manager<freetype_engine> font_manager_;
boost::shared_ptr<label_collision_detector4> detector_;

View file

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

View file

@ -775,6 +775,21 @@ struct filter_visitor : boost::static_visitor<void>
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

View file

@ -70,11 +70,11 @@ agg_renderer<T>::agg_renderer(Map const& m, T & pixmap, double scale_factor, uns
pixmap_(pixmap),
internal_buffer_(),
current_buffer_(&pixmap),
t_(m.width(),m.height(),m.get_current_extent(),offset_x,offset_y),
style_level_compositing_(false),
width_(pixmap_.width()),
height_(pixmap_.height()),
scale_factor_(scale_factor),
t_(m.width(),m.height(),m.get_current_extent(),offset_x,offset_y),
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()))),
@ -92,11 +92,11 @@ agg_renderer<T>::agg_renderer(Map const& m, request const& req, T & pixmap, doub
pixmap_(pixmap),
internal_buffer_(),
current_buffer_(&pixmap),
t_(req.width(),req.height(),req.extent(),offset_x,offset_y),
style_level_compositing_(false),
width_(pixmap_.width()),
height_(pixmap_.height()),
scale_factor_(scale_factor),
t_(req.width(),req.height(),req.extent(),offset_x,offset_y),
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()))),
@ -115,11 +115,11 @@ agg_renderer<T>::agg_renderer(Map const& m, T & pixmap, boost::shared_ptr<label_
pixmap_(pixmap),
internal_buffer_(),
current_buffer_(&pixmap),
t_(m.width(),m.height(),m.get_current_extent(),offset_x,offset_y),
style_level_compositing_(false),
width_(pixmap_.width()),
height_(pixmap_.height()),
scale_factor_(scale_factor),
t_(m.width(),m.height(),m.get_current_extent(),offset_x,offset_y),
font_engine_(),
font_manager_(font_engine_),
detector_(detector),
@ -238,9 +238,27 @@ void agg_renderer<T>::start_style_processing(feature_type_style const& st)
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
{
@ -250,6 +268,8 @@ void agg_renderer<T>::start_style_processing(feature_type_style const& st)
}
else
{
t_.set_offset(0);
ras_ptr->clip_box(0,0,width_,height_);
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);
}
}
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)
{
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

View file

@ -127,7 +127,7 @@ void agg_renderer<T>::process(line_pattern_symbolizer const& sym,
agg::trans_affine tr;
evaluate_transform(tr, feature, sym.get_transform());
box2d<double> clipping_extent = query_extent_;
box2d<double> clip_box = clipping_extent();
if (sym.clip())
{
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)
padding *= std::fabs(sym.offset()) * 1.2;
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;
vertex_converter<box2d<double>, rasterizer_type, line_pattern_symbolizer,
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)
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;
evaluate_transform(tr, feature, sym.get_transform());
box2d<double> clipping_extent = query_extent_;
box2d<double> clip_box = clipping_extent();
if (sym.clip())
{
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)
padding *= std::fabs(sym.offset()) * 1.2;
padding *= scale_factor_;
clipping_extent.pad(padding);
clip_box.pad(padding);
// debugging
//box2d<double> inverse = query_extent_;
//inverse.pad(-padding);
@ -121,7 +121,7 @@ void agg_renderer<T>::process(line_symbolizer const& sym,
vertex_converter<box2d<double>, rasterizer_type, line_symbolizer,
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)
converter.set<transform_tag>(); // always transform
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,
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)
converter.set<transform_tag>(); // always transform

View file

@ -139,7 +139,7 @@ void agg_renderer<T>::process(markers_symbolizer const& sym,
snap_pixels);
vertex_converter<box2d<double>, dispatch_type, markers_symbolizer,
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)
{
eGeomType type = feature.paths()[0].type();
@ -179,7 +179,7 @@ void agg_renderer<T>::process(markers_symbolizer const& sym,
snap_pixels);
vertex_converter<box2d<double>, dispatch_type, markers_symbolizer,
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)
{
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*/);
vertex_converter<box2d<double>, dispatch_type, markers_symbolizer,
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)
{

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();
if (!pat) return;
box2d<double> clip_box = clipping_extent();
typedef agg::rgba8 color;
typedef agg::order_rgba order;
@ -131,7 +132,7 @@ void agg_renderer<T>::process(polygon_pattern_symbolizer const& sym,
if (feature.num_geometries() > 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.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;
vertex_converter<box2d<double>, rasterizer, polygon_pattern_symbolizer,
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)
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;
vertex_converter<box2d<double>, rasterizer, polygon_symbolizer,
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)
converter.set<transform_tag>(); //always transform

View file

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

View file

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