diff --git a/CHANGELOG.md b/CHANGELOG.md index 33a7f8803..25c7cc37e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ For a complete change history, see the git log. Not yet released +- Added style-level 'opacity' (#314) + - PostGIS: Added 'simplify_geometries' option - will trigger ST_Simplify on geometries before returning to Mapnik (#1179) - Improved error feedback for invalid values passed to map.query_point diff --git a/deps/agg/include/agg_pixfmt_rgba.h b/deps/agg/include/agg_pixfmt_rgba.h index 7dad01aec..a134eb6fc 100644 --- a/deps/agg/include/agg_pixfmt_rgba.h +++ b/deps/agg/include/agg_pixfmt_rgba.h @@ -1433,9 +1433,9 @@ namespace agg } }; - // color spin + // colorize alpha values template - struct comp_op_rgba_color_spin + struct comp_op_rgba_colorize_alpha { typedef ColorT color_type; typedef Order order_type; @@ -1762,7 +1762,7 @@ namespace agg comp_op_rgba_saturation::blend_pix, comp_op_rgba_color::blend_pix, comp_op_rgba_value::blend_pix, - comp_op_rgba_color_spin::blend_pix, + comp_op_rgba_colorize_alpha::blend_pix, 0 }; @@ -1804,7 +1804,7 @@ namespace agg comp_op_saturation, //----comp_op_saturation comp_op_color, //----comp_op_color comp_op_value, //----comp_op_value - comp_op_color_spin, //----comp_op_color_spin + comp_op_colorize_alpha,//----comp_op_colorize_alpha end_of_comp_op_e }; diff --git a/include/mapnik/feature_type_style.hpp b/include/mapnik/feature_type_style.hpp index 0230c0517..45707fbdc 100644 --- a/include/mapnik/feature_type_style.hpp +++ b/include/mapnik/feature_type_style.hpp @@ -51,7 +51,7 @@ typedef std::vector rule_ptrs; class MAPNIK_DECL feature_type_style { private: - rules rules_; + rules rules_; filter_mode_e filter_mode_; // image_filters std::vector filters_; @@ -63,6 +63,7 @@ private: rule_ptrs if_rules_; rule_ptrs else_rules_; rule_ptrs also_rules_; + float opacity_; public: feature_type_style(); @@ -90,7 +91,9 @@ public: // compositing void set_comp_op(composite_mode_e comp_op); boost::optional comp_op() const; - + void set_opacity(float opacity); + float get_opacity() const; + ~feature_type_style() {} private: diff --git a/include/mapnik/image_compositing.hpp b/include/mapnik/image_compositing.hpp index 222b4d972..4bfd6bd4e 100644 --- a/include/mapnik/image_compositing.hpp +++ b/include/mapnik/image_compositing.hpp @@ -75,7 +75,7 @@ enum composite_mode_e saturation, _color, _value, - color_spin + colorize_alpha }; MAPNIK_DECL boost::optional comp_op_from_string(std::string const& name); diff --git a/include/mapnik/svg/svg_renderer.hpp b/include/mapnik/svg/svg_renderer.hpp index d375f4834..7538798ee 100644 --- a/include/mapnik/svg/svg_renderer.hpp +++ b/include/mapnik/svg/svg_renderer.hpp @@ -147,7 +147,7 @@ public: unsigned b = stop_color.blue(); unsigned a = stop_color.alpha(); //MAPNIK_LOG_DEBUG(svg_renderer) << "svg_renderer: r=" << r << ",g=" << g << ",b=" << b << ",a=" << a; - m_gradient_lut.add_color(st.first, agg::rgba8(r, g, b, int(a * opacity))); + m_gradient_lut.add_color(st.first, agg::rgba8_pre(r, g, b, int(a * opacity))); } m_gradient_lut.build_lut(); @@ -300,6 +300,7 @@ public: color = attr.fill_color; color.opacity(color.opacity() * attr.opacity * opacity); ScanlineRenderer ren_s(ren); + color.premultiply(); ren_s.color(color); render_scanlines(ras, sl, ren_s); } @@ -335,6 +336,7 @@ public: color = attr.stroke_color; color.opacity(color.opacity() * attr.opacity * opacity); ScanlineRenderer ren_s(ren); + color.premultiply(); ren_s.color(color); render_scanlines(ras, sl, ren_s); } diff --git a/src/agg/agg_renderer.cpp b/src/agg/agg_renderer.cpp index c58b5b292..2ec85cd2f 100644 --- a/src/agg/agg_renderer.cpp +++ b/src/agg/agg_renderer.cpp @@ -197,15 +197,25 @@ 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()) style_level_compositing_ = true; - else style_level_compositing_ = false; + 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_ || st.image_filters().size() > 0) + if (style_level_compositing_) { if (!internal_buffer_) + { internal_buffer_ = boost::make_shared(pixmap_.width(),pixmap_.height()); + } else + { internal_buffer_->set_background(color(0,0,0,0)); // fill with transparent colour + } current_buffer_ = internal_buffer_.get(); } else @@ -217,33 +227,35 @@ void agg_renderer::start_style_processing(feature_type_style const& st) template void agg_renderer::end_style_processing(feature_type_style const& st) { - bool blend_from = false; - if (st.image_filters().size() > 0) + if (style_level_compositing_) { - blend_from = true; - mapnik::filter::filter_visitor visitor(*current_buffer_); - BOOST_FOREACH(mapnik::filter::filter_type filter_tag, st.image_filters()) + bool blend_from = false; + if (st.image_filters().size() > 0) + { + blend_from = true; + mapnik::filter::filter_visitor visitor(*current_buffer_); + BOOST_FOREACH(mapnik::filter::filter_type filter_tag, st.image_filters()) + { + 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); + } + else if (blend_from || st.get_opacity() < 1) + { + composite(pixmap_.data(),current_buffer_->data(), src_over, st.get_opacity(), 0, 0, false); + } + + // apply any 'direct' image filters + mapnik::filter::filter_visitor visitor(pixmap_); + BOOST_FOREACH(mapnik::filter::filter_type filter_tag, st.direct_image_filters()) { boost::apply_visitor(visitor, filter_tag); - } + } } - - if (st.comp_op()) - { - composite(pixmap_.data(),current_buffer_->data(), *st.comp_op(), 1.0f, 0, 0, false); - } - else if (blend_from) - { - composite(pixmap_.data(),current_buffer_->data(), src_over, 1.0f, 0, 0, false); - } - - // apply any 'direct' image filters - mapnik::filter::filter_visitor visitor(pixmap_); - BOOST_FOREACH(mapnik::filter::filter_type filter_tag, st.direct_image_filters()) - { - boost::apply_visitor(visitor, filter_tag); - } - MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: End processing style"; } diff --git a/src/agg/process_line_symbolizer.cpp b/src/agg/process_line_symbolizer.cpp index a8e52a7c0..9edf59b4a 100644 --- a/src/agg/process_line_symbolizer.cpp +++ b/src/agg/process_line_symbolizer.cpp @@ -74,6 +74,7 @@ void agg_renderer::process(line_symbolizer const& sym, typedef agg::renderer_base renderer_base; pixfmt_comp_type pixf(buf); + pixf.comp_op(static_cast(sym.comp_op())); renderer_base renb(pixf); agg::trans_affine tr; @@ -138,7 +139,6 @@ void agg_renderer::process(line_symbolizer const& sym, } typedef agg::renderer_scanline_aa_solid renderer_type; - pixf.comp_op(static_cast(sym.comp_op())); renderer_base renb(pixf); renderer_type ren(renb); ren.color(agg::rgba8_pre(r, g, b, int(a * stroke_.get_opacity()))); diff --git a/src/cairo_renderer.cpp b/src/cairo_renderer.cpp index 48997f766..d4b27fcc2 100644 --- a/src/cairo_renderer.cpp +++ b/src/cairo_renderer.cpp @@ -389,7 +389,7 @@ public: case saturation: case _color: case _value: - case color_spin: + case colorize_alpha: break; } } diff --git a/src/feature_type_style.cpp b/src/feature_type_style.cpp index c14413e6e..2dc820895 100644 --- a/src/feature_type_style.cpp +++ b/src/feature_type_style.cpp @@ -38,7 +38,8 @@ feature_type_style::feature_type_style() : filter_mode_(FILTER_ALL), filters_(), direct_filters_(), - scale_denom_validity_(-1) + scale_denom_validity_(-1), + opacity_(1.0f) {} feature_type_style::feature_type_style(feature_type_style const& rhs, bool deep_copy) @@ -46,7 +47,8 @@ feature_type_style::feature_type_style(feature_type_style const& rhs, bool deep_ filters_(rhs.filters_), direct_filters_(rhs.direct_filters_), comp_op_(rhs.comp_op_), - scale_denom_validity_(-1) + scale_denom_validity_(-1), + opacity_(rhs.opacity_) { if (!deep_copy) { rules_ = rhs.rules_; @@ -67,6 +69,7 @@ feature_type_style& feature_type_style::operator=(feature_type_style const& rhs) direct_filters_ = rhs.direct_filters_; comp_op_ = rhs.comp_op_; scale_denom_validity_ = -1; + opacity_= rhs.opacity_; return *this; } @@ -126,6 +129,16 @@ boost::optional feature_type_style::comp_op() const return comp_op_; } +void feature_type_style::set_opacity(float opacity) +{ + opacity_ = opacity; +} + +float feature_type_style::get_opacity() const +{ + return opacity_; +} + void feature_type_style::update_rule_cache(double scale_denom) { if_rules_.clear(); diff --git a/src/image_compositing.cpp b/src/image_compositing.cpp index a02c07e11..8eff43bb5 100644 --- a/src/image_compositing.cpp +++ b/src/image_compositing.cpp @@ -74,7 +74,7 @@ static const comp_op_lookup_type comp_lookup = boost::assign::list_of comp_op_from_string(std::string const& name) diff --git a/src/load_map.cpp b/src/load_map.cpp index c1fbe21e2..a73bb3853 100644 --- a/src/load_map.cpp +++ b/src/load_map.cpp @@ -433,8 +433,13 @@ void map_parser::parse_style(Map & map, xml_node const& sty) } } - // image filters + optional opacity = sty.get_opt_attr("opacity"); + if (opacity) + { + style.set_opacity(*opacity); + } + // image filters mapnik::image_filter_grammar > filter_grammar; diff --git a/src/save_map.cpp b/src/save_map.cpp index 15e9cf127..ff0219ce5 100644 --- a/src/save_map.cpp +++ b/src/save_map.cpp @@ -518,7 +518,6 @@ void serialize_style( ptree & map_node, Map::const_style_iterator style_it, bool { feature_type_style const& style = style_it->second; std::string const& name = style_it->first; - filter_mode_e filter_mode = style.get_filter_mode(); ptree & style_node = map_node.push_back( ptree::value_type("Style", ptree()))->second; @@ -526,11 +525,18 @@ void serialize_style( ptree & map_node, Map::const_style_iterator style_it, bool set_attr(style_node, "name", name); feature_type_style dfl; + filter_mode_e filter_mode = style.get_filter_mode(); if (filter_mode != dfl.get_filter_mode() || explicit_defaults) { set_attr(style_node, "filter-mode", filter_mode); } + double opacity = style.get_opacity(); + if (opacity != dfl.get_opacity() || explicit_defaults) + { + set_attr(style_node, "opacity", opacity); + } + rules::const_iterator it = style.get_rules().begin(); rules::const_iterator end = style.get_rules().end(); for (; it != end; ++it)