diff --git a/include/mapnik/agg_renderer.hpp b/include/mapnik/agg_renderer.hpp index 0eef75d29..27a22b74a 100644 --- a/include/mapnik/agg_renderer.hpp +++ b/include/mapnik/agg_renderer.hpp @@ -35,8 +35,10 @@ #include #include #include +#include // stl #include +#include // fwd declaration to avoid dependence on agg headers namespace agg { struct trans_affine; } @@ -58,6 +60,53 @@ namespace mapnik { namespace mapnik { +template +class buffer_stack +{ +public: + buffer_stack(std::size_t width, std::size_t height) + : width_(width), + height_(height), + buffers_(), + position_(buffers_.begin()) + { + } + + T & push() + { + if (position_ == buffers_.begin()) + { + buffers_.emplace_front(width_, height_); + position_ = buffers_.begin(); + } + else + { + position_--; + mapnik::fill(*position_, 0); // fill with transparent colour + } + return *position_; + } + + void pop() + { + if (position_ != buffers_.end()) + { + position_++; + } + } + + T & top() const + { + return *position_; + } + +private: + const std::size_t width_; + const std::size_t height_; + std::deque buffers_; + typename std::deque::iterator position_; +}; + template class MAPNIK_DECL agg_renderer : public feature_style_processor >, private util::noncopyable @@ -160,15 +209,14 @@ protected: void draw_geo_extent(box2d const& extent,mapnik::color const& color); private: - buffer_type & pixmap_; - std::shared_ptr internal_buffer_; - mutable buffer_type * current_buffer_; - mutable bool style_level_compositing_; + std::stack> buffers_; + buffer_stack internal_buffers_; + std::unique_ptr inflated_buffer_; const std::unique_ptr ras_ptr; gamma_method_enum gamma_method_; double gamma_; renderer_common common_; - void setup(Map const& m); + void setup(Map const & m, buffer_type & pixmap); }; extern template class MAPNIK_DECL agg_renderer>; diff --git a/include/mapnik/feature_style_processor.hpp b/include/mapnik/feature_style_processor.hpp index f0cf91b6c..4ae4fa9c0 100644 --- a/include/mapnik/feature_style_processor.hpp +++ b/include/mapnik/feature_style_processor.hpp @@ -30,6 +30,7 @@ #include // stl +#include #include #include @@ -93,6 +94,12 @@ private: featureset_ptr features, proj_transform const& prj_trans); + void prepare_layers(layer_rendering_material & parent_mat, + std::vector const & layers, + feature_style_context_map & ctx_map, + Processor & p, + double scale_denom); + /*! * \brief prepare features for rendering asynchronously. */ @@ -111,6 +118,7 @@ private: * \brief render features list queued when they are available. */ void render_material(layer_rendering_material const & mat, Processor & p ); + void render_submaterials(layer_rendering_material const & mat, Processor & p); Map const& m_; }; diff --git a/include/mapnik/feature_style_processor_impl.hpp b/include/mapnik/feature_style_processor_impl.hpp index 3e3743b72..981595147 100644 --- a/include/mapnik/feature_style_processor_impl.hpp +++ b/include/mapnik/feature_style_processor_impl.hpp @@ -63,6 +63,7 @@ struct layer_rendering_material std::vector active_styles_; std::vector featureset_ptr_list_; std::vector rule_caches_; + std::vector materials_; layer_rendering_material(layer const& lay, projection const& dest) : @@ -84,6 +85,41 @@ feature_style_processor::feature_style_processor(Map const& m, double } } +template +void feature_style_processor::prepare_layers(layer_rendering_material & parent_mat, + std::vector const & layers, + feature_style_context_map & ctx_map, + Processor & p, + double scale_denom) +{ + for (layer const& lyr : layers) + { + if (lyr.visible(scale_denom)) + { + std::set names; + layer_rendering_material mat(lyr, parent_mat.proj0_); + + prepare_layer(mat, + ctx_map, + p, + m_.scale(), + scale_denom, + m_.width(), + m_.height(), + m_.get_current_extent(), + m_.buffer_size(), + names); + + // Store active material + if (!mat.active_styles_.empty()) + { + prepare_layers(mat, lyr.layers(), ctx_map, p, scale_denom); + parent_mat.materials_.emplace_back(std::move(mat)); + } + } + } +} + template void feature_style_processor::apply(double scale_denom) { @@ -101,44 +137,16 @@ void feature_style_processor::apply(double scale_denom) // in a second time, we fetch the results and // do the actual rendering - std::vector mat_list; - // Define processing context map used by datasources // implementing asynchronous queries feature_style_context_map ctx_map; - for ( layer const& lyr : m_.layers() ) + if (!m_.layers().empty()) { - if (lyr.visible(scale_denom)) - { - std::set names; - layer_rendering_material mat(lyr, proj); + layer_rendering_material root_mat(m_.layers().front(), proj); + prepare_layers(root_mat, m_.layers(), ctx_map, p, scale_denom); - prepare_layer(mat, - ctx_map, - p, - m_.scale(), - scale_denom, - m_.width(), - m_.height(), - m_.get_current_extent(), - m_.buffer_size(), - names); - - // Store active material - if (!mat.active_styles_.empty()) - { - mat_list.emplace_back(std::move(mat)); - } - } - } - - for ( layer_rendering_material const & mat : mat_list ) - { - if (!mat.active_styles_.empty()) - { - render_material(mat, p); - } + render_submaterials(root_mat, p); } p.end_map_processing(m_); @@ -201,9 +209,12 @@ void feature_style_processor::apply_to_layer(layer const& lay, buffer_size, names); + prepare_layers(mat, lay.layers(), ctx_map, p, scale_denom); + if (!mat.active_styles_.empty()) { render_material(mat,p); + render_submaterials(mat, p); } } @@ -441,10 +452,27 @@ void feature_style_processor::prepare_layer(layer_rendering_material } } +template +void feature_style_processor::render_submaterials(layer_rendering_material const & parent_mat, + Processor & p) +{ + for (layer_rendering_material const & mat : parent_mat.materials_) + { + if (!mat.active_styles_.empty()) + { + p.start_layer_processing(mat.lay_, mat.layer_ext2_); + + render_material(mat, p); + render_submaterials(mat, p); + + p.end_layer_processing(mat.lay_); + } + } +} template void feature_style_processor::render_material(layer_rendering_material const & mat, - Processor & p ) + Processor & p) { std::vector const & active_styles = mat.active_styles_; std::vector const & featureset_ptr_list = mat.featureset_ptr_list_; @@ -460,8 +488,6 @@ void feature_style_processor::render_material(layer_rendering_materia return; } - p.start_layer_processing(mat.lay_, mat.layer_ext2_); - layer const& lay = mat.lay_; std::vector const & rule_caches = mat.rule_caches_; @@ -555,7 +581,6 @@ void feature_style_processor::render_material(layer_rendering_materia ++i; } } - p.end_layer_processing(mat.lay_); } template diff --git a/include/mapnik/layer.hpp b/include/mapnik/layer.hpp index 68746431f..43e8760c2 100644 --- a/include/mapnik/layer.hpp +++ b/include/mapnik/layer.hpp @@ -26,6 +26,7 @@ // mapnik #include #include +#include // stl #include @@ -96,6 +97,18 @@ public: */ std::vector& styles(); + /*! \brief Add a child layer by copying it. + * @param l The layer to add. + */ + void add_layer(layer const& l); + + /*! \brief Add a child layer by moving it. + * @param l The layer to add. + */ + void add_layer(layer && l); + + std::vector const& layers() const; + /*! * @param minimum_scale_denom The minimum scale denominator */ @@ -197,6 +210,12 @@ public: */ box2d envelope() const; + // compositing + void set_comp_op(composite_mode_e comp_op); + boost::optional comp_op() const; + void set_opacity(double opacity); + double get_opacity() const; + void set_maximum_extent(box2d const& box); boost::optional > const& maximum_extent() const; void reset_maximum_extent(); @@ -215,9 +234,12 @@ private: bool cache_features_; std::string group_by_; std::vector styles_; + std::vector layers_; datasource_ptr ds_; boost::optional buffer_size_; boost::optional > maximum_extent_; + boost::optional comp_op_; + double opacity_; }; } diff --git a/src/agg/agg_renderer.cpp b/src/agg/agg_renderer.cpp index e9ea69526..ed28335ef 100644 --- a/src/agg/agg_renderer.cpp +++ b/src/agg/agg_renderer.cpp @@ -41,8 +41,8 @@ #include #include #include -#include #include +#include #pragma GCC diagnostic push #include @@ -71,47 +71,44 @@ namespace mapnik template agg_renderer::agg_renderer(Map const& m, T0 & pixmap, double scale_factor, unsigned offset_x, unsigned offset_y) : feature_style_processor(m, scale_factor), - pixmap_(pixmap), - internal_buffer_(), - current_buffer_(&pixmap), - style_level_compositing_(false), + buffers_(), + internal_buffers_(m.width(), m.height()), + inflated_buffer_(), ras_ptr(new rasterizer), gamma_method_(GAMMA_POWER), gamma_(1.0), common_(m, attributes(), offset_x, offset_y, m.width(), m.height(), scale_factor) { - setup(m); + setup(m, pixmap); } template agg_renderer::agg_renderer(Map const& m, request const& req, attributes const& vars, T0 & pixmap, double scale_factor, unsigned offset_x, unsigned offset_y) : feature_style_processor(m, scale_factor), - pixmap_(pixmap), - internal_buffer_(), - current_buffer_(&pixmap), - style_level_compositing_(false), + buffers_(), + internal_buffers_(req.width(), req.height()), + inflated_buffer_(), ras_ptr(new rasterizer), gamma_method_(GAMMA_POWER), gamma_(1.0), common_(m, req, vars, offset_x, offset_y, req.width(), req.height(), scale_factor) { - setup(m); + setup(m, pixmap); } template agg_renderer::agg_renderer(Map const& m, T0 & pixmap, std::shared_ptr detector, double scale_factor, unsigned offset_x, unsigned offset_y) : feature_style_processor(m, scale_factor), - pixmap_(pixmap), - internal_buffer_(), - current_buffer_(&pixmap), - style_level_compositing_(false), + buffers_(), + internal_buffers_(m.width(), m.height()), + inflated_buffer_(), ras_ptr(new rasterizer), gamma_method_(GAMMA_POWER), gamma_(1.0), common_(m, attributes(), offset_x, offset_y, m.width(), m.height(), scale_factor, detector) { - setup(m); + setup(m, pixmap); } template @@ -157,9 +154,11 @@ struct setup_agg_bg_visitor }; template -void agg_renderer::setup(Map const &m) +void agg_renderer::setup(Map const &m, buffer_type & pixmap) { - mapnik::set_premultiplied_alpha(pixmap_, true); + buffers_.emplace(pixmap); + + mapnik::set_premultiplied_alpha(pixmap, true); boost::optional const& bg = m.background(); if (bg) { @@ -167,13 +166,13 @@ void agg_renderer::setup(Map const &m) { mapnik::color bg_color = *bg; bg_color.premultiply(); - mapnik::fill(pixmap_, bg_color); + mapnik::fill(pixmap, bg_color); } else { mapnik::color bg_color = *bg; bg_color.set_premultiplied(true); - mapnik::fill(pixmap_,bg_color); + mapnik::fill(pixmap, bg_color); } } @@ -182,7 +181,7 @@ void agg_renderer::setup(Map const &m) { // NOTE: marker_cache returns premultiplied image, if needed std::shared_ptr bg_marker = mapnik::marker_cache::instance().find(*image_filename,true); - setup_agg_bg_visitor visitor(pixmap_, + setup_agg_bg_visitor visitor(pixmap, common_, m.background_image_comp_op(), m.background_image_opacity()); @@ -204,7 +203,7 @@ void agg_renderer::start_map_processing(Map const& map) template void agg_renderer::end_map_processing(Map const& map) { - mapnik::demultiply_alpha(pixmap_); + mapnik::demultiply_alpha(buffers_.top().get()); MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: End map processing"; } @@ -226,28 +225,44 @@ void agg_renderer::start_layer_processing(layer const& lay, box2d { common_.query_extent_.clip(*maximum_extent); } + + if (lay.comp_op() || lay.get_opacity() < 1.0) + { + buffers_.emplace(internal_buffers_.push()); + set_premultiplied_alpha(buffers_.top().get(), true); + } + else + { + buffers_.emplace(buffers_.top().get()); + } } template -void agg_renderer::end_layer_processing(layer const&) +void agg_renderer::end_layer_processing(layer const& lyr) { MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: End layer processing"; + + buffer_type & current_buffer = buffers_.top().get(); + buffers_.pop(); + buffer_type & previous_buffer = buffers_.top().get(); + + if (¤t_buffer != &previous_buffer) + { + composite_mode_e comp_op = lyr.comp_op() ? *lyr.comp_op() : src_over; + composite(previous_buffer, current_buffer, + comp_op, lyr.get_opacity(), + -common_.t_.offset(), + -common_.t_.offset()); + internal_buffers_.pop(); + } } template void agg_renderer::start_style_processing(feature_type_style const& st) { MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: Start processing style"; - if (st.comp_op() || st.image_filters().size() > 0 || st.get_opacity() < 1) - { - style_level_compositing_ = true; - } - else - { - style_level_compositing_ = false; - } - if (style_level_compositing_) + if (st.comp_op() || st.image_filters().size() > 0 || st.get_opacity() < 1) { if (st.image_filters_inflate()) { @@ -266,81 +281,81 @@ void agg_renderer::start_style_processing(feature_type_style const& st) unsigned target_width = common_.width_ + (offset * 2); unsigned target_height = common_.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)) + if (!inflated_buffer_ || + (inflated_buffer_->width() < target_width || + inflated_buffer_->height() < target_height)) { - internal_buffer_ = std::make_shared(target_width,target_height); + inflated_buffer_ = std::make_unique(target_width, target_height); } else { - mapnik::fill(*internal_buffer_, 0); // fill with transparent colour + mapnik::fill(*inflated_buffer_, 0); // fill with transparent colour } + buffers_.emplace(*inflated_buffer_); } else { - if (!internal_buffer_) - { - internal_buffer_ = std::make_shared(common_.width_,common_.height_); - } - else - { - mapnik::fill(*internal_buffer_, 0); // fill with transparent colour - } + buffers_.emplace(internal_buffers_.push()); common_.t_.set_offset(0); ras_ptr->clip_box(0,0,common_.width_,common_.height_); } - current_buffer_ = internal_buffer_.get(); - set_premultiplied_alpha(*current_buffer_,true); + set_premultiplied_alpha(buffers_.top().get(), true); } else { common_.t_.set_offset(0); ras_ptr->clip_box(0,0,common_.width_,common_.height_); - current_buffer_ = &pixmap_; + buffers_.emplace(buffers_.top().get()); } } template void agg_renderer::end_style_processing(feature_type_style const& st) { - if (style_level_compositing_) + buffer_type & current_buffer = buffers_.top().get(); + buffers_.pop(); + buffer_type & previous_buffer = buffers_.top().get(); + if (¤t_buffer != &previous_buffer) { bool blend_from = false; if (st.image_filters().size() > 0) { blend_from = true; - mapnik::filter::filter_visitor visitor(*current_buffer_, common_.scale_factor_); + mapnik::filter::filter_visitor visitor(current_buffer, common_.scale_factor_); for (mapnik::filter::filter_type const& filter_tag : st.image_filters()) { util::apply_visitor(visitor, filter_tag); } - mapnik::premultiply_alpha(*current_buffer_); + mapnik::premultiply_alpha(current_buffer); } if (st.comp_op()) { - composite(pixmap_, *current_buffer_, + composite(previous_buffer, current_buffer, *st.comp_op(), st.get_opacity(), -common_.t_.offset(), -common_.t_.offset()); } else if (blend_from || st.get_opacity() < 1.0) { - composite(pixmap_, *current_buffer_, + composite(previous_buffer, current_buffer, src_over, st.get_opacity(), -common_.t_.offset(), -common_.t_.offset()); } + if (¤t_buffer == &internal_buffers_.top()) + { + internal_buffers_.pop(); + } } if (st.direct_image_filters().size() > 0) { // apply any 'direct' image filters - mapnik::filter::filter_visitor visitor(pixmap_, common_.scale_factor_); + mapnik::filter::filter_visitor visitor(previous_buffer, common_.scale_factor_); for (mapnik::filter::filter_type const& filter_tag : st.direct_image_filters()) { util::apply_visitor(visitor, filter_tag); } - mapnik::premultiply_alpha(pixmap_); + mapnik::premultiply_alpha(previous_buffer); } MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: End processing style"; } @@ -349,7 +364,7 @@ template struct agg_render_marker_visitor { agg_render_marker_visitor(renderer_common & common, - buffer_type * current_buffer, + buffer_type & current_buffer, std::unique_ptr const& ras_ptr, gamma_method_enum & gamma_method, double & gamma, @@ -386,10 +401,10 @@ struct agg_render_marker_visitor gamma_ = 1.0; } agg::scanline_u8 sl; - agg::rendering_buffer buf(current_buffer_->bytes(), - current_buffer_->width(), - current_buffer_->height(), - current_buffer_->row_size()); + agg::rendering_buffer buf(current_buffer_.bytes(), + current_buffer_.width(), + current_buffer_.height(), + current_buffer_.row_size()); pixfmt_comp_type pixf(buf); pixf.comp_op(static_cast(comp_op_)); renderer_base renb(pixf); @@ -435,10 +450,10 @@ struct agg_render_marker_visitor gamma_ = 1.0; } agg::scanline_u8 sl; - agg::rendering_buffer buf(current_buffer_->bytes(), - current_buffer_->width(), - current_buffer_->height(), - current_buffer_->row_size()); + agg::rendering_buffer buf(current_buffer_.bytes(), + current_buffer_.width(), + current_buffer_.height(), + current_buffer_.row_size()); pixfmt_comp_type pixf(buf); pixf.comp_op(static_cast(comp_op_)); renderer_base renb(pixf); @@ -453,7 +468,7 @@ struct agg_render_marker_visitor { double cx = 0.5 * width; double cy = 0.5 * height; - composite(*current_buffer_, marker.get_data(), + composite(current_buffer_, marker.get_data(), comp_op_, opacity_, std::floor(pos_.x - cx + .5), std::floor(pos_.y - cy + .5)); @@ -517,7 +532,7 @@ struct agg_render_marker_visitor private: renderer_common & common_; - buffer_type * current_buffer_; + buffer_type & current_buffer_; std::unique_ptr const& ras_ptr_; gamma_method_enum & gamma_method_; double & gamma_; @@ -536,7 +551,7 @@ void agg_renderer::render_marker(pixel_position const& pos, composite_mode_e comp_op) { agg_render_marker_visitor visitor(common_, - current_buffer_, + buffers_.top().get(), ras_ptr, gamma_method_, gamma_, @@ -550,23 +565,24 @@ void agg_renderer::render_marker(pixel_position const& pos, template bool agg_renderer::painted() { - return pixmap_.painted(); + return buffers_.top().get().painted(); } template void agg_renderer::painted(bool painted) { - pixmap_.painted(painted); + buffers_.top().get().painted(painted); } template void agg_renderer::debug_draw_box(box2d const& box, double x, double y, double angle) { - agg::rendering_buffer buf(current_buffer_->bytes(), - current_buffer_->width(), - current_buffer_->height(), - current_buffer_->row_size()); + buffer_type & current_buffer = buffers_.top().get(); + agg::rendering_buffer buf(current_buffer.bytes(), + current_buffer.width(), + current_buffer.height(), + current_buffer.row_size()); debug_draw_box(buf, box, x, y, angle); } @@ -619,13 +635,13 @@ void agg_renderer::draw_geo_extent(box2d const& extent, mapnik::c unsigned rgba = color.rgba(); for (double x=x0; x::process(building_symbolizer const& sym, using ren_base = agg::renderer_base; using renderer = agg::renderer_scanline_aa_solid; - agg::rendering_buffer buf(current_buffer_->bytes(),current_buffer_->width(),current_buffer_->height(), current_buffer_->row_size()); + buffer_type & current_buffer = buffers_.top().get(); + agg::rendering_buffer buf(current_buffer.bytes(), current_buffer.width(), current_buffer.height(), current_buffer.row_size()); agg::pixfmt_rgba32_pre pixf(buf); ren_base renb(pixf); diff --git a/src/agg/process_debug_symbolizer.cpp b/src/agg/process_debug_symbolizer.cpp index df5e980c7..42b45cd61 100644 --- a/src/agg/process_debug_symbolizer.cpp +++ b/src/agg/process_debug_symbolizer.cpp @@ -226,7 +226,7 @@ void agg_renderer::process(debug_symbolizer const& sym, if (mode == DEBUG_SYM_MODE_RINGS) { - RingRenderer renderer(*ras_ptr,*current_buffer_,common_.t_,prj_trans); + RingRenderer renderer(*ras_ptr, buffers_.top().get(), common_.t_, prj_trans); render_ring_visitor apply(renderer); mapnik::util::apply_visitor(apply,feature.get_geometry()); } @@ -234,13 +234,13 @@ void agg_renderer::process(debug_symbolizer const& sym, { for (auto const& n : *common_.detector_) { - draw_rect(pixmap_, n.get().box); + draw_rect(buffers_.top().get(), n.get().box); } } else if (mode == DEBUG_SYM_MODE_VERTEX) { using apply_vertex_mode = apply_vertex_mode; - apply_vertex_mode apply(pixmap_, common_.t_, prj_trans); + apply_vertex_mode apply(buffers_.top().get(), common_.t_, prj_trans); util::apply_visitor(geometry::vertex_processor(apply), feature.get_geometry()); } } diff --git a/src/agg/process_dot_symbolizer.cpp b/src/agg/process_dot_symbolizer.cpp index c612a0e26..7f5291806 100644 --- a/src/agg/process_dot_symbolizer.cpp +++ b/src/agg/process_dot_symbolizer.cpp @@ -119,7 +119,8 @@ void agg_renderer::process(dot_symbolizer const& sym, gamma_method_ = GAMMA_POWER; gamma_ = 1.0; } - agg::rendering_buffer buf(current_buffer_->bytes(),current_buffer_->width(),current_buffer_->height(),current_buffer_->row_size()); + buffer_type & current_buffer = buffers_.top().get(); + agg::rendering_buffer buf(current_buffer.bytes(), current_buffer.width(), current_buffer.height(), current_buffer.row_size()); using blender_type = agg::comp_op_adaptor_rgba_pre; using pixfmt_comp_type = agg::pixfmt_custom_blend_rgba; using renderer_base = agg::renderer_base; diff --git a/src/agg/process_group_symbolizer.cpp b/src/agg/process_group_symbolizer.cpp index b00557eeb..486d714f2 100644 --- a/src/agg/process_group_symbolizer.cpp +++ b/src/agg/process_group_symbolizer.cpp @@ -61,10 +61,10 @@ struct thunk_renderer : render_thunk_list_dispatch thunk_renderer(renderer_type &ren, std::unique_ptr const& ras_ptr, - buffer_type *buf, + buffer_type & buf, renderer_common &common) : ren_(ren), ras_ptr_(ras_ptr), buf_(buf), common_(common), - tex_(*buf, HALO_RASTERIZER_FULL, src_over, src_over, + tex_(buf, HALO_RASTERIZER_FULL, src_over, src_over, common.scale_factor_, common.font_manager_.get_stroker()) {} @@ -80,7 +80,7 @@ struct thunk_renderer : render_thunk_list_dispatch renderer_type, pixfmt_comp_type>; ras_ptr_->reset(); - buf_type render_buffer(buf_->bytes(), buf_->width(), buf_->height(), buf_->row_size()); + buf_type render_buffer(buf_.bytes(), buf_.width(), buf_.height(), buf_.row_size()); pixfmt_comp_type pixf(render_buffer); pixf.comp_op(static_cast(thunk.comp_op_)); renderer_base renb(pixf); @@ -101,7 +101,7 @@ struct thunk_renderer : render_thunk_list_dispatch using renderer_base = agg::renderer_base; ras_ptr_->reset(); - buf_type render_buffer(buf_->bytes(), buf_->width(), buf_->height(), buf_->row_size()); + buf_type render_buffer(buf_.bytes(), buf_.width(), buf_.height(), buf_.row_size()); pixfmt_comp_type pixf(render_buffer); pixf.comp_op(static_cast(thunk.comp_op_)); renderer_base renb(pixf); @@ -135,7 +135,7 @@ struct thunk_renderer : render_thunk_list_dispatch private: renderer_type &ren_; std::unique_ptr const& ras_ptr_; - buffer_type *buf_; + buffer_type & buf_; renderer_common &common_; text_renderer_type tex_; }; @@ -145,7 +145,7 @@ void agg_renderer::process(group_symbolizer const& sym, mapnik::feature_impl & feature, proj_transform const& prj_trans) { - thunk_renderer ren(*this, ras_ptr, current_buffer_, common_); + thunk_renderer ren(*this, ras_ptr, buffers_.top().get(), common_); render_group_symbolizer( sym, feature, common_.vars_, prj_trans, clipping_extent(common_), common_, diff --git a/src/agg/process_line_pattern_symbolizer.cpp b/src/agg/process_line_pattern_symbolizer.cpp index 4af6a0074..5e304cb8f 100644 --- a/src/agg/process_line_pattern_symbolizer.cpp +++ b/src/agg/process_line_pattern_symbolizer.cpp @@ -61,14 +61,12 @@ template struct agg_renderer_process_visitor_l { agg_renderer_process_visitor_l(renderer_common & common, - buffer_type & pixmap, - buffer_type * current_buffer, + buffer_type & current_buffer, std::unique_ptr const& ras_ptr, line_pattern_symbolizer const& sym, mapnik::feature_impl & feature, proj_transform const& prj_trans) : common_(common), - pixmap_(pixmap), current_buffer_(current_buffer), ras_ptr_(ras_ptr), sym_(sym), @@ -112,8 +110,8 @@ private: value_double simplify_tolerance = get(sym_, feature_, common_.vars_); value_double smooth = get(sym_, feature_, common_.vars_); - agg::rendering_buffer buf(current_buffer_->bytes(),current_buffer_->width(), - current_buffer_->height(), current_buffer_->row_size()); + agg::rendering_buffer buf(current_buffer_.bytes(), current_buffer_.width(), + current_buffer_.height(), current_buffer_.row_size()); pixfmt_type pixf(buf); pixf.comp_op(static_cast(get(sym_, feature_, common_.vars_))); renderer_base ren_base(pixf); @@ -134,7 +132,7 @@ private: box2d clip_box = clipping_extent(common_); if (clip) { - double padding = (double)(common_.query_extent_.width()/pixmap_.width()); + double padding = (double)(common_.query_extent_.width() / common_.width_); if (half_stroke > 1) padding *= half_stroke; if (std::fabs(offset) > 0) @@ -163,8 +161,7 @@ private: } renderer_common & common_; - buffer_type & pixmap_; - buffer_type * current_buffer_; + buffer_type & current_buffer_; std::unique_ptr const& ras_ptr_; line_pattern_symbolizer const& sym_; mapnik::feature_impl & feature_; @@ -189,8 +186,7 @@ void agg_renderer::process(line_pattern_symbolizer const& sym, } std::shared_ptr marker = marker_cache::instance().find(filename, true); agg_renderer_process_visitor_l visitor(common_, - pixmap_, - current_buffer_, + buffers_.top().get(), ras_ptr, sym, feature, diff --git a/src/agg/process_line_symbolizer.cpp b/src/agg/process_line_symbolizer.cpp index 8bc73c2a7..5d8ebf073 100644 --- a/src/agg/process_line_symbolizer.cpp +++ b/src/agg/process_line_symbolizer.cpp @@ -112,7 +112,8 @@ void agg_renderer::process(line_symbolizer const& sym, gamma_ = gamma; } - agg::rendering_buffer buf(current_buffer_->bytes(),current_buffer_->width(),current_buffer_->height(), current_buffer_->row_size()); + buffer_type & current_buffer = buffers_.top().get(); + agg::rendering_buffer buf(current_buffer.bytes(), current_buffer.width(), current_buffer.height(), current_buffer.row_size()); using color_type = agg::rgba8; using order_type = agg::order_rgba; @@ -139,7 +140,7 @@ void agg_renderer::process(line_symbolizer const& sym, line_rasterizer_enum rasterizer_e = get(sym, feature, common_.vars_); if (clip) { - double padding = static_cast(common_.query_extent_.width()/pixmap_.width()); + double padding = static_cast(common_.query_extent_.width() / common_.width_); double half_stroke = 0.5 * width; if (half_stroke > 1) { diff --git a/src/agg/process_markers_symbolizer.cpp b/src/agg/process_markers_symbolizer.cpp index 81a2d6989..8546f042b 100644 --- a/src/agg/process_markers_symbolizer.cpp +++ b/src/agg/process_markers_symbolizer.cpp @@ -132,7 +132,8 @@ void agg_renderer::process(markers_symbolizer const& sym, gamma_ = gamma; } - buf_type render_buffer(current_buffer_->bytes(), current_buffer_->width(), current_buffer_->height(), current_buffer_->row_size()); + buffer_type & current_buffer = buffers_.top().get(); + buf_type render_buffer(current_buffer.bytes(), current_buffer.width(), current_buffer.height(), current_buffer.row_size()); box2d clip_box = clipping_extent(common_); using renderer_context_type = detail::agg_markers_renderer_context struct agg_renderer_process_visitor_p { agg_renderer_process_visitor_p(renderer_common & common, - buffer_type * current_buffer, + buffer_type & current_buffer, std::unique_ptr const& ras_ptr, gamma_method_enum & gamma_method, double & gamma, @@ -99,8 +99,8 @@ struct agg_renderer_process_visitor_p private: void render(mapnik::image_rgba8 const& image) const { - agg::rendering_buffer buf(current_buffer_->bytes(), current_buffer_->width(), - current_buffer_->height(), current_buffer_->row_size()); + agg::rendering_buffer buf(current_buffer_.bytes(), current_buffer_.width(), + current_buffer_.height(), current_buffer_.row_size()); ras_ptr_->reset(); value_double gamma = get(sym_, feature_, common_.vars_); gamma_method_enum gamma_method = get(sym_, feature_, common_.vars_); @@ -158,8 +158,8 @@ private: apply_local_alignment apply(common_.t_,prj_trans_, clip_box, x0, y0); util::apply_visitor(geometry::vertex_processor(apply), feature_.get_geometry()); - offset_x = unsigned(current_buffer_->width() - x0); - offset_y = unsigned(current_buffer_->height() - y0); + offset_x = unsigned(current_buffer_.width() - x0); + offset_y = unsigned(current_buffer_.height() - y0); } span_gen_type sg(img_src, offset_x, offset_y); @@ -194,7 +194,7 @@ private: } renderer_common & common_; - buffer_type * current_buffer_; + buffer_type & current_buffer_; std::unique_ptr const& ras_ptr_; gamma_method_enum & gamma_method_; double & gamma_; @@ -212,7 +212,7 @@ void agg_renderer::process(polygon_pattern_symbolizer const& sym, if (filename.empty()) return; std::shared_ptr marker = marker_cache::instance().find(filename, true); agg_renderer_process_visitor_p visitor(common_, - current_buffer_, + buffers_.top().get(), ras_ptr, gamma_method_, gamma_, diff --git a/src/agg/process_polygon_symbolizer.cpp b/src/agg/process_polygon_symbolizer.cpp index 94451641f..a3cf32fe0 100644 --- a/src/agg/process_polygon_symbolizer.cpp +++ b/src/agg/process_polygon_symbolizer.cpp @@ -63,9 +63,10 @@ void agg_renderer::process(polygon_symbolizer const& sym, gamma_ = gamma; } - box2d clip_box = clipping_extent(common_); - agg::rendering_buffer buf(current_buffer_->bytes(),current_buffer_->width(),current_buffer_->height(), current_buffer_->row_size()); + buffer_type & current_buffer = buffers_.top().get(); + agg::rendering_buffer buf(current_buffer.bytes(), current_buffer.width(), current_buffer.height(), current_buffer.row_size()); + box2d clip_box = clipping_extent(common_); render_polygon_symbolizer( sym, feature, prj_trans, common_, clip_box, *ras_ptr, [&](color const &fill, double opacity) { diff --git a/src/agg/process_raster_symbolizer.cpp b/src/agg/process_raster_symbolizer.cpp index 9f9c9f8df..ad3581065 100644 --- a/src/agg/process_raster_symbolizer.cpp +++ b/src/agg/process_raster_symbolizer.cpp @@ -55,7 +55,7 @@ void agg_renderer::process(raster_symbolizer const& sym, sym, feature, prj_trans, common_, [&](image_rgba8 const & target, composite_mode_e comp_op, double opacity, int start_x, int start_y) { - composite(*current_buffer_, target, + composite(buffers_.top().get(), target, comp_op, opacity, start_x, start_y); } ); diff --git a/src/agg/process_shield_symbolizer.cpp b/src/agg/process_shield_symbolizer.cpp index c583a6a34..e331121e9 100644 --- a/src/agg/process_shield_symbolizer.cpp +++ b/src/agg/process_shield_symbolizer.cpp @@ -51,7 +51,7 @@ void agg_renderer::process(shield_symbolizer const& sym, halo_rasterizer_enum halo_rasterizer = get(sym, keys::halo_rasterizer, feature, common_.vars_, HALO_RASTERIZER_FULL); composite_mode_e comp_op = get(sym, keys::comp_op, feature, common_.vars_, src_over); composite_mode_e halo_comp_op = get(sym, keys::halo_comp_op, feature, common_.vars_, src_over); - agg_text_renderer ren(*current_buffer_, + agg_text_renderer ren(buffers_.top().get(), halo_rasterizer, comp_op, halo_comp_op, diff --git a/src/agg/process_text_symbolizer.cpp b/src/agg/process_text_symbolizer.cpp index e9b3d8f0f..afb6c85a3 100644 --- a/src/agg/process_text_symbolizer.cpp +++ b/src/agg/process_text_symbolizer.cpp @@ -52,7 +52,7 @@ void agg_renderer::process(text_symbolizer const& sym, halo_rasterizer_enum halo_rasterizer = get(sym, keys::halo_rasterizer,feature, common_.vars_, HALO_RASTERIZER_FULL); composite_mode_e comp_op = get(sym, keys::comp_op, feature, common_.vars_, src_over); composite_mode_e halo_comp_op = get(sym, keys::halo_comp_op, feature, common_.vars_, src_over); - agg_text_renderer ren(*current_buffer_, + agg_text_renderer ren(buffers_.top().get(), halo_rasterizer, comp_op, halo_comp_op, diff --git a/src/cairo/cairo_renderer.cpp b/src/cairo/cairo_renderer.cpp index 0fbb00972..43c3ffef4 100644 --- a/src/cairo/cairo_renderer.cpp +++ b/src/cairo/cairo_renderer.cpp @@ -188,12 +188,25 @@ void cairo_renderer::start_layer_processing(layer const& lay, box2d c common_.detector_->clear(); } common_.query_extent_ = query_extent; + + if (lay.comp_op() || lay.get_opacity() < 1.0) + { + context_.push_group(); + } } template -void cairo_renderer::end_layer_processing(layer const&) +void cairo_renderer::end_layer_processing(layer const& lay) { MAPNIK_LOG_DEBUG(cairo_renderer) << "cairo_renderer: End layer processing"; + + if (lay.comp_op() || lay.get_opacity() < 1.0) + { + context_.pop_group(); + composite_mode_e comp_op = lay.comp_op() ? *lay.comp_op() : src_over; + context_.set_operator(comp_op); + context_.paint(lay.get_opacity()); + } } template diff --git a/src/layer.cpp b/src/layer.cpp index 78cdd2cd2..aa781860d 100644 --- a/src/layer.cpp +++ b/src/layer.cpp @@ -42,9 +42,13 @@ layer::layer(std::string const& _name, std::string const& _srs) cache_features_(false), group_by_(), styles_(), + layers_(), ds_(), buffer_size_(), - maximum_extent_() {} + maximum_extent_(), + comp_op_(), + opacity_(1.0f) +{} layer::layer(layer const& rhs) : name_(rhs.name_), @@ -57,9 +61,13 @@ layer::layer(layer const& rhs) cache_features_(rhs.cache_features_), group_by_(rhs.group_by_), styles_(rhs.styles_), + layers_(rhs.layers_), ds_(rhs.ds_), buffer_size_(rhs.buffer_size_), - maximum_extent_(rhs.maximum_extent_) {} + maximum_extent_(rhs.maximum_extent_), + comp_op_(rhs.comp_op_), + opacity_(rhs.opacity_) +{} layer::layer(layer && rhs) : name_(std::move(rhs.name_)), @@ -72,9 +80,13 @@ layer::layer(layer && rhs) cache_features_(std::move(rhs.cache_features_)), group_by_(std::move(rhs.group_by_)), styles_(std::move(rhs.styles_)), + layers_(std::move(rhs.layers_)), ds_(std::move(rhs.ds_)), buffer_size_(std::move(rhs.buffer_size_)), - maximum_extent_(std::move(rhs.maximum_extent_)) {} + maximum_extent_(std::move(rhs.maximum_extent_)), + comp_op_(std::move(rhs.comp_op_)), + opacity_(std::move(rhs.opacity_)) +{} layer& layer::operator=(layer rhs) { @@ -92,6 +104,8 @@ layer& layer::operator=(layer rhs) std::swap(this->ds_, rhs.ds_); std::swap(this->buffer_size_, rhs.buffer_size_); std::swap(this->maximum_extent_, rhs.maximum_extent_); + std::swap(this->comp_op_, rhs.comp_op_); + std::swap(this->opacity_, rhs.opacity_); return *this; } @@ -109,7 +123,9 @@ bool layer::operator==(layer const& rhs) const (styles_ == rhs.styles_) && ((ds_ && rhs.ds_) ? *ds_ == *rhs.ds_ : ds_ == rhs.ds_) && (buffer_size_ == rhs.buffer_size_) && - (maximum_extent_ == rhs.maximum_extent_); + (maximum_extent_ == rhs.maximum_extent_) && + (comp_op_ == rhs.comp_op_) && + (opacity_ == rhs.opacity_); } layer::~layer() {} @@ -149,6 +165,21 @@ std::vector & layer::styles() return styles_; } +void layer::add_layer(layer const& l) +{ + layers_.emplace_back(l); +} + +void layer::add_layer(layer && l) +{ + layers_.push_back(std::move(l)); +} + +std::vector const& layer::layers() const +{ + return layers_; +} + void layer::set_minimum_scale_denominator(double minimum_scale_denom) { minimum_scale_denom_=minimum_scale_denom; @@ -270,4 +301,24 @@ std::string const& layer::group_by() const return group_by_; } +void layer::set_comp_op(composite_mode_e comp_op) +{ + comp_op_ = comp_op; +} + +boost::optional layer::comp_op() const +{ + return comp_op_; +} + +void layer::set_opacity(double opacity) +{ + opacity_ = opacity; +} + +double layer::get_opacity() const +{ + return opacity_; +} + } diff --git a/src/load_map.cpp b/src/load_map.cpp index c5f48e4ed..767d32366 100644 --- a/src/load_map.cpp +++ b/src/load_map.cpp @@ -104,7 +104,10 @@ public: private: void parse_map_include(Map & map, xml_node const& node); void parse_style(Map & map, xml_node const& node); - void parse_layer(Map & map, xml_node const& node); + + template + void parse_layer(Parent & parent, xml_node const& node); + void parse_symbolizer_base(symbolizer_base &sym, xml_node const& node); void parse_fontset(Map & map, xml_node const & node); bool parse_font(font_set & fset, xml_node const& f); @@ -559,7 +562,8 @@ bool map_parser::parse_font(font_set & fset, xml_node const& f) return false; } -void map_parser::parse_layer(Map & map, xml_node const& node) +template +void map_parser::parse_layer(Parent & parent, xml_node const& node) { std::string name; try @@ -575,7 +579,7 @@ void map_parser::parse_layer(Map & map, xml_node const& node) name = node.get_attr("name", std::string("Unnamed")); // If no projection is given inherit from map - std::string srs = node.get_attr("srs", map.srs()); + std::string srs = node.get_attr("srs", parent.srs()); try { // create throwaway projection object here to ensure it is valid @@ -679,6 +683,24 @@ void map_parser::parse_layer(Map & map, xml_node const& node) } } + // compositing + optional comp_op_name = node.get_opt_attr("comp-op"); + if (comp_op_name) + { + optional comp_op = comp_op_from_string(*comp_op_name); + if (comp_op) + { + lyr.set_comp_op(*comp_op); + } + else + { + throw config_error("failed to parse comp-op: '" + *comp_op_name + "'"); + } + } + + optional opacity = node.get_opt_attr("opacity"); + if (opacity) lyr.set_opacity(*opacity); + for (auto const& child: node) { @@ -758,8 +780,12 @@ void map_parser::parse_layer(Map & map, xml_node const& node) throw config_error("Unknown exception occurred attempting to create datasoure for layer '" + lyr.name() + "'"); } } + else if (child.is("Layer")) + { + parse_layer(lyr, child); + } } - map.add_layer(std::move(lyr)); + parent.add_layer(std::move(lyr)); } catch (config_error const& ex) {