markers: support opacity on bitmaps, and move to supporting both opacity and fill-opacity

This commit is contained in:
Dane Springmeyer 2012-07-30 18:31:21 -07:00
parent 6f174287a2
commit 0d97b851fa
13 changed files with 130 additions and 27 deletions

View file

@ -11,6 +11,10 @@ For a complete change history, see the git log.
Not yet released
- Added support for overriding fill, stroke, and opacity for svg markers using marker properties
- Added support for setting opacity dynamically on images in polygon pattern and markers symbolizers
- Added support for filtering on a features geometry type, either `point`, `linestring`, 'polygon`,
or `collection` using the expression keyword of `[mapnik::geometry_type]` (#546)

View file

@ -86,7 +86,6 @@ struct markers_symbolizer_pickle_suite : boost::python::pickle_suite
};
void export_markers_symbolizer()
{
using namespace boost::python;
@ -115,7 +114,11 @@ void export_markers_symbolizer()
.add_property("opacity",
&markers_symbolizer::get_opacity,
&markers_symbolizer::set_opacity,
"Set/get the text opacity")
"Set/get the overall opacity")
.add_property("fill_opacity",
&markers_symbolizer::get_fill_opacity,
&markers_symbolizer::set_fill_opacity,
"Set/get the fill opacity")
.add_property("ignore_placement",
&markers_symbolizer::get_ignore_placement,
&markers_symbolizer::set_ignore_placement)

View file

@ -614,6 +614,7 @@ BOOST_PYTHON_MODULE(_mapnik)
python_optional<std::string>();
python_optional<unsigned>();
python_optional<double>();
python_optional<float>();
python_optional<bool>();
python_optional<mapnik::text_transform_e>();
register_ptr_to_python<mapnik::expression_ptr>();

View file

@ -38,24 +38,54 @@ bool push_explicit_style(Attr const& src, Attr & dst, markers_symbolizer const&
{
boost::optional<stroke> const& strk = sym.get_stroke();
boost::optional<color> const& fill = sym.get_fill();
if (strk || fill)
boost::optional<float> const& opacity = sym.get_opacity();
boost::optional<float> const& fill_opacity = sym.get_fill_opacity();
if (strk || fill || opacity || fill_opacity)
{
for(unsigned i = 0; i < src.size(); ++i)
{
mapnik::svg::path_attributes attr = src[i];
if (strk && attr.stroke_flag)
if (attr.stroke_flag)
{
// TODO - stroke attributes need to be boost::optional
// for this to work properly
if (strk)
{
attr.stroke_width = strk->get_width();
color const& s_color = strk->get_color();
attr.stroke_color = agg::rgba(s_color.red()/255.0,s_color.green()/255.0,
s_color.blue()/255.0,(s_color.alpha()*strk->get_opacity())/255.0);
attr.stroke_color = agg::rgba(s_color.red()/255.0,
s_color.green()/255.0,
s_color.blue()/255.0,
s_color.alpha()/255.0);
}
if (fill && attr.fill_flag)
if (opacity)
{
attr.stroke_opacity = *opacity;
}
else if (strk)
{
attr.stroke_opacity = strk->get_opacity();
}
}
if (attr.fill_flag)
{
if (fill)
{
color const& f_color = *fill;
attr.fill_color = agg::rgba(f_color.red()/255.0,f_color.green()/255.0,
f_color.blue()/255.0,(f_color.alpha()*sym.get_opacity())/255.0);
attr.fill_color = agg::rgba(f_color.red()/255.0,
f_color.green()/255.0,
f_color.blue()/255.0,
f_color.alpha()/255.0);
}
if (opacity)
{
attr.fill_opacity = *opacity;
}
else if (fill_opacity)
{
attr.fill_opacity = *fill_opacity;
}
}
dst.push_back(attr);
}

View file

@ -65,8 +65,12 @@ public:
double get_spacing() const;
void set_max_error(double max_error);
double get_max_error() const;
void set_opacity(float opacity);
boost::optional<float> get_opacity() const;
void set_fill(color const& fill);
boost::optional<color> get_fill() const;
void set_fill_opacity(float opacity);
boost::optional<float> get_fill_opacity() const;
void set_stroke(stroke const& stroke);
boost::optional<stroke> get_stroke() const;
void set_marker_placement(marker_placement_e marker_p);
@ -79,6 +83,8 @@ private:
double spacing_;
double max_error_;
boost::optional<color> fill_;
boost::optional<float> fill_opacity_;
boost::optional<float> opacity_;
boost::optional<stroke> stroke_;
marker_placement_e marker_p_;
};

View file

@ -64,7 +64,6 @@ struct vector_markers_rasterizer_dispatch
typedef agg::comp_op_adaptor_rgba_pre<color_type, order_type> blender_type; // comp blender
typedef agg::pixfmt_custom_blend_rgba<blender_type, agg::rendering_buffer> pixfmt_comp_type;
typedef agg::renderer_base<pixfmt_comp_type> renderer_base;
typedef agg::renderer_scanline_aa_solid<renderer_base> renderer_type;
vector_markers_rasterizer_dispatch(BufferType & image_buffer,
SvgRenderer & svg_renderer,
@ -105,7 +104,7 @@ struct vector_markers_rasterizer_dispatch
if (sym_.get_allow_overlap() ||
detector_.has_placement(transformed_bbox))
{
svg_renderer_.render(ras_, sl_, renb_, matrix, sym_.get_opacity(), bbox_);
svg_renderer_.render(ras_, sl_, renb_, matrix, 1, bbox_);
if (!sym_.get_ignore_placement())
detector_.insert(transformed_bbox);
@ -123,7 +122,7 @@ struct vector_markers_rasterizer_dispatch
agg::trans_affine matrix = marker_trans_;
matrix.rotate(angle);
matrix.translate(x, y);
svg_renderer_.render(ras_, sl_, renb_, matrix, sym_.get_opacity(), bbox_);
svg_renderer_.render(ras_, sl_, renb_, matrix, 1, bbox_);
}
}
}
@ -146,7 +145,6 @@ void render_raster_marker(Rasterizer & ras, RendererBuffer & renb,
agg::scanline_u8 & sl, image_data_32 const& src,
agg::trans_affine const& marker_tr, double opacity)
{
double width = src.width();
double height = src.height();
double p[8];
@ -180,10 +178,19 @@ void render_raster_marker(Rasterizer & ras, RendererBuffer & renb,
typedef agg::span_interpolator_linear<agg::trans_affine> interpolator_type;
typedef agg::span_image_filter_rgba_2x2<img_accessor_type,
interpolator_type> span_gen_type;
typedef agg::order_rgba order_type;
typedef agg::pixel32_type pixel_type;
typedef agg::comp_op_adaptor_rgba_pre<color_type, order_type> blender_type; // comp blender
typedef agg::pixfmt_custom_blend_rgba<blender_type, agg::rendering_buffer> pixfmt_comp_type;
typedef agg::renderer_base<pixfmt_comp_type> renderer_base;
typedef agg::renderer_scanline_aa_alpha<renderer_base,
agg::span_allocator<agg::rgba8>,
span_gen_type> renderer_type;
img_accessor_type ia(pixf);
interpolator_type interpolator(agg::trans_affine(p, 0, 0, width, height) );
span_gen_type sg(ia, interpolator, filter);
agg::render_scanlines_aa(ras, sl, renb, sa, sg);
renderer_type rp(renb,sa, sg, unsigned(opacity*255));
agg::render_scanlines(ras, sl, rp);
}
template <typename BufferType, typename Rasterizer, typename Detector>
@ -195,7 +202,6 @@ struct raster_markers_rasterizer_dispatch
typedef agg::comp_op_adaptor_rgba_pre<color_type, order_type> blender_type; // comp blender
typedef agg::pixfmt_custom_blend_rgba<blender_type, agg::rendering_buffer> pixfmt_comp_type;
typedef agg::renderer_base<pixfmt_comp_type> renderer_base;
typedef agg::renderer_scanline_aa_solid<renderer_base> renderer_type;
raster_markers_rasterizer_dispatch(BufferType & image_buffer,
Rasterizer & ras,
@ -236,8 +242,9 @@ struct raster_markers_rasterizer_dispatch
detector_.has_placement(transformed_bbox))
{
float opacity = sym_.get_opacity() ? *sym_.get_opacity() : 1;
render_raster_marker(ras_, renb_, sl_, src_,
matrix, sym_.get_opacity());
matrix, opacity);
if (!sym_.get_ignore_placement())
detector_.insert(transformed_bbox);
}
@ -254,8 +261,9 @@ struct raster_markers_rasterizer_dispatch
agg::trans_affine matrix = marker_trans_;
matrix.rotate(angle);
matrix.translate(x,y);
float opacity = sym_.get_opacity() ? *sym_.get_opacity() : 1;
render_raster_marker(ras_, renb_, sl_, src_,
matrix, sym_.get_opacity());
matrix, opacity);
}
}
}
@ -284,7 +292,6 @@ void agg_renderer<T>::process(markers_symbolizer const& sym,
typedef agg::comp_op_adaptor_rgba_pre<color_type, order_type> blender_type; // comp blender
typedef agg::pixfmt_custom_blend_rgba<blender_type, agg::rendering_buffer> pixfmt_comp_type;
typedef agg::renderer_base<pixfmt_comp_type> renderer_base;
typedef agg::renderer_scanline_aa_solid<renderer_base> renderer_type;
typedef label_collision_detector4 detector_type;
typedef boost::mpl::vector<clip_line_tag,transform_tag,smooth_tag> conv_types;
@ -311,6 +318,8 @@ void agg_renderer<T>::process(markers_symbolizer const& sym,
if ((*mark)->is_vector())
{
typedef agg::renderer_scanline_aa_solid<renderer_base> renderer_type;
using namespace mapnik::svg;
boost::optional<path_ptr> marker = (*mark)->get_vector_data();

View file

@ -1452,7 +1452,7 @@ void cairo_renderer_base::process(markers_symbolizer const& sym,
if (sym.get_allow_overlap() ||
detector_.has_placement(extent))
{
render_marker(pixel_position(x - 0.5 * w, y - 0.5 * h) ,**mark, tr, sym.get_opacity());
render_marker(pixel_position(x - 0.5 * w, y - 0.5 * h) ,**mark, tr, 1);
if (!sym.get_ignore_placement())
detector_.insert(extent);
@ -1471,7 +1471,7 @@ void cairo_renderer_base::process(markers_symbolizer const& sym,
while (placement.get_point(x, y, angle))
{
agg::trans_affine matrix = recenter * tr * agg::trans_affine_rotation(angle) * agg::trans_affine_translation(x, y);
render_marker(pixel_position(x - 0.5 * w, y - 0.5 * h), **mark, matrix, sym.get_opacity(),false);
render_marker(pixel_position(x - 0.5 * w, y - 0.5 * h), **mark, matrix, 1,false);
}
}
context.fill();

View file

@ -140,7 +140,7 @@ void grid_renderer<T>::process(markers_symbolizer const& sym,
detector_.has_placement(transformed_bbox))
{
placed = true;
svg_renderer.render_id(*ras_ptr, sl, renb, feature.id(), matrix, sym.get_opacity(), bbox);
svg_renderer.render_id(*ras_ptr, sl, renb, feature.id(), matrix, 1, bbox);
if (!sym.get_ignore_placement())
detector_.insert(transformed_bbox);
}
@ -166,7 +166,7 @@ void grid_renderer<T>::process(markers_symbolizer const& sym,
agg::trans_affine matrix = marker_trans;
matrix.rotate(angle);
matrix.translate(x, y);
svg_renderer.render_id(*ras_ptr, sl, renb, feature.id(), matrix, sym.get_opacity(), bbox);
svg_renderer.render_id(*ras_ptr, sl, renb, feature.id(), matrix, 1, bbox);
}
}
else
@ -186,7 +186,7 @@ void grid_renderer<T>::process(markers_symbolizer const& sym,
agg::trans_affine matrix = marker_trans;
matrix.rotate(angle);
matrix.translate(x, y);
svg_renderer.render_id(*ras_ptr, sl, renb, feature.id(), matrix, sym.get_opacity(), bbox);
svg_renderer.render_id(*ras_ptr, sl, renb, feature.id(), matrix, 1, bbox);
}
}
}

View file

@ -1013,9 +1013,13 @@ void map_parser::parse_markers_symbolizer(rule & rule, xml_node const& node)
sym.set_filename(expr);
}
// overall opacity - impacts both fill and stroke, like svg
optional<float> opacity = node.get_opt_attr<float>("opacity");
if (opacity) sym.set_opacity(*opacity);
optional<float> fill_opacity = node.get_opt_attr<float>("fill-opacity");
if (fill_opacity) sym.set_fill_opacity(*fill_opacity);
optional<std::string> image_transform_wkt = node.get_opt_attr<std::string>("transform");
if (image_transform_wkt)
{

View file

@ -74,6 +74,8 @@ markers_symbolizer::markers_symbolizer(markers_symbolizer const& rhs)
spacing_(rhs.spacing_),
max_error_(rhs.max_error_),
fill_(rhs.fill_),
fill_opacity_(rhs.fill_opacity_),
opacity_(rhs.opacity_),
stroke_(rhs.stroke_),
marker_p_(rhs.marker_p_) {}
@ -117,6 +119,16 @@ double markers_symbolizer::get_max_error() const
return max_error_;
}
void markers_symbolizer::set_opacity(float opacity)
{
opacity_ = opacity;
}
boost::optional<float> markers_symbolizer::get_opacity() const
{
return opacity_;
}
void markers_symbolizer::set_fill(color const& fill)
{
fill_ = fill;
@ -127,6 +139,16 @@ boost::optional<color> markers_symbolizer::get_fill() const
return fill_;
}
void markers_symbolizer::set_fill_opacity(float opacity)
{
fill_opacity_ = opacity;
}
boost::optional<float> markers_symbolizer::get_fill_opacity() const
{
return fill_opacity_;
}
void markers_symbolizer::set_width(expression_ptr const& width)
{
width_ = width;

View file

@ -283,6 +283,10 @@ public:
{
set_attr( sym_node, "fill", sym.get_fill() );
}
if (sym.get_fill_opacity() != dfl.get_fill_opacity() || explicit_defaults_)
{
set_attr( sym_node, "fill-opacity", sym.get_fill_opacity() );
}
if (sym.get_opacity() != dfl.get_opacity() || explicit_defaults_)
{
set_attr( sym_node, "opacity", sym.get_opacity() );

View file

@ -0,0 +1,17 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-0.2 -1 379 334">
<path id="puddle" fill="#9CDAF1" d="m296.94 295.43c0 20.533-47.56 37.176-106.22 37.176-58.67 0-106.23-16.643-106.23-37.176s47.558-37.18 106.23-37.18c58.66 0 106.22 16.65 106.22 37.18z"/>
<g id="shadow-legs" fill="#7DBBE6">
<path d="m161.85 331.22v-26.5c0-3.422-.619-6.284-1.653-8.701 6.853 5.322 7.316 18.695 7.316 18.695v17.004c6.166.481 12.534.773 19.053.861l-.172-16.92c-.944-23.13-20.769-25.961-20.769-25.961-7.245-1.645-7.137 1.991-6.409 4.34-7.108-12.122-26.158-10.556-26.158-10.556-6.611 2.357-.475 6.607-.475 6.607 10.387 3.775 11.33 15.105 11.33 15.105v23.622c5.72.98 11.71 1.79 17.94 2.4z"/>
<path d="m245.4 283.48s-19.053-1.566-26.16 10.559c.728-2.35.839-5.989-6.408-4.343 0 0-19.824 2.832-20.768 25.961l-.174 16.946c6.509-.025 12.876-.254 19.054-.671v-17.219s.465-13.373 7.316-18.695c-1.034 2.417-1.653 5.278-1.653 8.701v26.775c6.214-.544 12.211-1.279 17.937-2.188v-24.113s.944-11.33 11.33-15.105c0-.01 6.13-4.26-.48-6.62z"/>
</g>
<path id="cat" d="m378.18 141.32l.28-1.389c-31.162-6.231-63.141-6.294-82.487-5.49 3.178-11.451 4.134-24.627 4.134-39.32 0-21.073-7.917-37.931-20.77-50.759 2.246-7.25 5.246-23.351-2.996-43.963 0 0-14.541-4.617-47.431 17.396-12.884-3.22-26.596-4.81-40.328-4.81-15.109 0-30.376 1.924-44.615 5.83-33.94-23.154-48.923-18.411-48.923-18.411-9.78 24.457-3.733 42.566-1.896 47.063-11.495 12.406-18.513 28.243-18.513 47.659 0 14.658 1.669 27.808 5.745 39.237-19.511-.71-50.323-.437-80.373 5.572l.276 1.389c30.231-6.046 61.237-6.256 80.629-5.522.898 2.366 1.899 4.661 3.021 6.879-19.177.618-51.922 3.062-83.303 11.915l.387 1.36c31.629-8.918 64.658-11.301 83.649-11.882 11.458 21.358 34.048 35.152 74.236 39.484-5.704 3.833-11.523 10.349-13.881 21.374-7.773 3.718-32.379 12.793-47.142-12.599 0 0-8.264-15.109-24.082-16.292 0 0-15.344-.235-1.059 9.562 0 0 10.267 4.838 17.351 23.019 0 0 9.241 31.01 53.835 21.061v32.032s-.943 11.33-11.33 15.105c0 0-6.137 4.249.475 6.606 0 0 28.792 2.361 28.792-21.238v-34.929s-1.142-13.852 5.663-18.667v57.371s-.47 13.688-7.551 18.881c0 0-4.723 8.494 5.663 6.137 0 0 19.824-2.832 20.769-25.961l.449-58.06h4.765l.453 58.06c.943 23.129 20.768 25.961 20.768 25.961 10.383 2.357 5.663-6.137 5.663-6.137-7.08-5.193-7.551-18.881-7.551-18.881v-56.876c6.801 5.296 5.663 18.171 5.663 18.171v34.929c0 23.6 28.793 21.238 28.793 21.238 6.606-2.357.474-6.606.474-6.606-10.386-3.775-11.33-15.105-11.33-15.105v-45.786c0-17.854-7.518-27.309-14.87-32.3 42.859-4.25 63.426-18.089 72.903-39.591 18.773.516 52.557 2.803 84.873 11.919l.384-1.36c-32.131-9.063-65.692-11.408-84.655-11.96.898-2.172 1.682-4.431 2.378-6.755 19.25-.80 51.38-.79 82.66 5.46z"/>
<path id="face" fill="#F4CBB2" d="m258.19 94.132c9.231 8.363 14.631 18.462 14.631 29.343 0 50.804-37.872 52.181-84.585 52.181-46.721 0-84.589-7.035-84.589-52.181 0-10.809 5.324-20.845 14.441-29.174 15.208-13.881 40.946-6.531 70.147-6.531 29.07-.004 54.72-7.429 69.95 6.357z"/>
<path id="eyes" fill="#FFF" d="m160.1 126.06 c0 13.994-7.88 25.336-17.6 25.336-9.72 0-17.6-11.342-17.6-25.336 0-13.992 7.88-25.33 17.6-25.33 9.72.01 17.6 11.34 17.6 25.33z m94.43 0 c0 13.994-7.88 25.336-17.6 25.336-9.72 0-17.6-11.342-17.6-25.336 0-13.992 7.88-25.33 17.6-25.33 9.72.01 17.6 11.34 17.6 25.33z"/>
<g fill="#AD5C51">
<path id="pupils" d="m154.46 126.38 c0 9.328-5.26 16.887-11.734 16.887s-11.733-7.559-11.733-16.887c0-9.331 5.255-16.894 11.733-16.894 6.47 0 11.73 7.56 11.73 16.89z m94.42 0 c0 9.328-5.26 16.887-11.734 16.887s-11.733-7.559-11.733-16.887c0-9.331 5.255-16.894 11.733-16.894 6.47 0 11.73 7.56 11.73 16.89z"/>
<circle id="nose" cx="188.5" cy="148.56" r="4.401"/>
<path id="mouth" d="m178.23 159.69c-.26-.738.128-1.545.861-1.805.737-.26 1.546.128 1.805.861 1.134 3.198 4.167 5.346 7.551 5.346s6.417-2.147 7.551-5.346c.26-.738 1.067-1.121 1.805-.861s1.121 1.067.862 1.805c-1.529 4.324-5.639 7.229-10.218 7.229s-8.68-2.89-10.21-7.22z"/>
</g>
<path id="octo" fill="#C3E4D8" d="m80.641 179.82 c0 1.174-1.376 2.122-3.07 2.122-1.693 0-3.07-.948-3.07-2.122 0-1.175 1.377-2.127 3.07-2.127 1.694 0 3.07.95 3.07 2.13z m8.5 4.72 c0 1.174-1.376 2.122-3.07 2.122-1.693 0-3.07-.948-3.07-2.122 0-1.175 1.377-2.127 3.07-2.127 1.694 0 3.07.95 3.07 2.13z m5.193 6.14 c0 1.174-1.376 2.122-3.07 2.122-1.693 0-3.07-.948-3.07-2.122 0-1.175 1.377-2.127 3.07-2.127 1.694 0 3.07.95 3.07 2.13z m4.72 7.08 c0 1.174-1.376 2.122-3.07 2.122-1.693 0-3.07-.948-3.07-2.122 0-1.175 1.377-2.127 3.07-2.127 1.694 0 3.07.95 3.07 2.13z m5.188 6.61 c0 1.174-1.376 2.122-3.07 2.122-1.693 0-3.07-.948-3.07-2.122 0-1.175 1.377-2.127 3.07-2.127 1.694 0 3.07.95 3.07 2.13z m7.09 5.66 c0 1.174-1.376 2.122-3.07 2.122-1.693 0-3.07-.948-3.07-2.122 0-1.175 1.377-2.127 3.07-2.127 1.694 0 3.07.95 3.07 2.13z m9.91 3.78 c0 1.174-1.376 2.122-3.07 2.122-1.693 0-3.07-.948-3.07-2.122 0-1.175 1.377-2.127 3.07-2.127 1.694 0 3.07.95 3.07 2.13z m9.87 0 c0 1.174-1.376 2.122-3.07 2.122-1.693 0-3.07-.948-3.07-2.122 0-1.175 1.377-2.127 3.07-2.127 1.694 0 3.07.95 3.07 2.13z m10.01 -1.64 c0 1.174-1.376 2.122-3.07 2.122-1.693 0-3.07-.948-3.07-2.122 0-1.175 1.377-2.127 3.07-2.127 1.694 0 3.07.95 3.07 2.13z"/>
<path id="drop" fill="#9CDAF1" d="m69.369 186.12l-3.066 10.683s-.8 3.861 2.84 4.546c3.8-.074 3.486-3.627 3.223-4.781z"/>
</svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

View file

@ -126,7 +126,8 @@ def test_pointsymbolizer_init():
def test_markersymbolizer_init():
p = mapnik.MarkersSymbolizer()
eq_(p.allow_overlap, False)
eq_(p.opacity,1)
eq_(p.opacity,None)
eq_(p.fill_opacity,None)
eq_(p.filename,'shape://ellipse')
eq_(p.placement,mapnik.marker_placement.POINT_PLACEMENT)
eq_(p.fill,None)
@ -154,9 +155,11 @@ def test_markersymbolizer_init():
p.fill = mapnik.Color('white')
p.allow_overlap = True
p.opacity = 0.5
p.fill_opacity = .01
eq_(p.allow_overlap, True)
eq_(p.opacity, 0.5)
eq_(p.fill_opacity, 0.5)
# PointSymbolizer missing image file