/***************************************************************************** * * This file is part of Mapnik (c++ mapping toolkit) * * Copyright (C) 2011 Artem Pavlenko * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #if defined(HAVE_CAIRO) // mapnik #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // cairo #include #include #include // boost #include // agg #include "agg_conv_clip_polyline.h" #include "agg_conv_clip_polygon.h" #include "agg_conv_smooth_poly1.h" #include "agg_rendering_buffer.h" #include "agg_pixfmt_rgba.h" // markers #include "agg_path_storage.h" #include "agg_ellipse.h" // stl #include #include namespace mapnik { struct cairo_save_restore { cairo_save_restore(cairo_context & context) : context_(context) { context_.save(); } ~cairo_save_restore() { context_.restore(); } cairo_context & context_; }; cairo_face_manager::cairo_face_manager(std::shared_ptr engine) : font_engine_(engine) { } cairo_face_ptr cairo_face_manager::get_face(face_ptr face) { cairo_face_cache::iterator itr = cache_.find(face); cairo_face_ptr entry; if (itr != cache_.end()) { entry = itr->second; } else { entry = std::make_shared(font_engine_, face); cache_.insert(std::make_pair(face, entry)); } return entry; } cairo_renderer_base::cairo_renderer_base(Map const& m, cairo_ptr const& cairo, double scale_factor, unsigned offset_x, unsigned offset_y) : m_(m), context_(cairo), width_(m.width()), height_(m.height()), scale_factor_(scale_factor), t_(m.width(),m.height(),m.get_current_extent(),offset_x,offset_y), font_engine_(std::make_shared()), font_manager_(*font_engine_), face_manager_(font_engine_), detector_(std::make_shared( box2d(-m.buffer_size(), -m.buffer_size(), m.width() + m.buffer_size(), m.height() + m.buffer_size()))) { setup(m); } cairo_renderer_base::cairo_renderer_base(Map const& m, request const& req, cairo_ptr const& cairo, double scale_factor, unsigned offset_x, unsigned offset_y) : m_(m), context_(cairo), width_(req.width()), height_(req.height()), scale_factor_(scale_factor), t_(req.width(),req.height(),req.extent(),offset_x,offset_y), font_engine_(std::make_shared()), font_manager_(*font_engine_), face_manager_(font_engine_), detector_(std::make_shared( box2d(-m.buffer_size(), -m.buffer_size(), m.width() + m.buffer_size(), m.height() + m.buffer_size()))) { setup(m); } cairo_renderer_base::cairo_renderer_base(Map const& m, cairo_ptr const& cairo, std::shared_ptr detector, double scale_factor, unsigned offset_x, unsigned offset_y) : m_(m), context_(cairo), width_(m.width()), height_(m.height()), scale_factor_(scale_factor), t_(m.width(),m.height(),m.get_current_extent(),offset_x,offset_y), font_engine_(std::make_shared()), font_manager_(*font_engine_), face_manager_(font_engine_), detector_(detector) { MAPNIK_LOG_DEBUG(cairo_renderer) << "cairo_renderer_base: Scale=" << m.scale(); } template <> cairo_renderer::cairo_renderer(Map const& m, cairo_ptr const& cairo, double scale_factor, unsigned offset_x, unsigned offset_y) : feature_style_processor(m,scale_factor), cairo_renderer_base(m,cairo,scale_factor,offset_x,offset_y) {} template <> cairo_renderer::cairo_renderer(Map const& m, cairo_surface_ptr const& surface, double scale_factor, unsigned offset_x, unsigned offset_y) : feature_style_processor(m,scale_factor), cairo_renderer_base(m,create_context(surface),scale_factor,offset_x,offset_y) {} template <> cairo_renderer::cairo_renderer(Map const& m, request const& req, cairo_ptr const& cairo, double scale_factor, unsigned offset_x, unsigned offset_y) : feature_style_processor(m,scale_factor), cairo_renderer_base(m,req,cairo,scale_factor,offset_x,offset_y) {} template <> cairo_renderer::cairo_renderer(Map const& m, request const& req, cairo_surface_ptr const& surface, double scale_factor, unsigned offset_x, unsigned offset_y) : feature_style_processor(m,scale_factor), cairo_renderer_base(m,req, create_context(surface),scale_factor,offset_x,offset_y) {} template <> cairo_renderer::cairo_renderer(Map const& m, cairo_ptr const& cairo, std::shared_ptr detector, double scale_factor, unsigned offset_x, unsigned offset_y) : feature_style_processor(m,scale_factor), cairo_renderer_base(m,cairo,detector,scale_factor,offset_x,offset_y) {} template <> cairo_renderer::cairo_renderer(Map const& m, cairo_surface_ptr const& surface, std::shared_ptr detector, double scale_factor, unsigned offset_x, unsigned offset_y) : feature_style_processor(m,scale_factor), cairo_renderer_base(m,create_context(surface),detector,scale_factor,offset_x,offset_y) {} cairo_renderer_base::~cairo_renderer_base() {} void cairo_renderer_base::setup(Map const& map) { boost::optional bg = m_.background(); if (bg) { cairo_save_restore guard(context_); context_.set_color(*bg); context_.paint(); } boost::optional const& image_filename = map.background_image(); if (image_filename) { // NOTE: marker_cache returns premultiplied image, if needed boost::optional bg_marker = mapnik::marker_cache::instance().find(*image_filename,true); if (bg_marker && (*bg_marker)->is_bitmap()) { mapnik::image_ptr bg_image = *(*bg_marker)->get_bitmap_data(); int w = bg_image->width(); int h = bg_image->height(); if ( w > 0 && h > 0) { // repeat background-image both vertically and horizontally unsigned x_steps = unsigned(std::ceil(width_/double(w))); unsigned y_steps = unsigned(std::ceil(height_/double(h))); for (unsigned x=0;x void cairo_renderer::end_map_processing(Map const& ) { MAPNIK_LOG_DEBUG(cairo_renderer) << "cairo_renderer_base: End map processing"; context_.show_page(); } void cairo_renderer_base::start_layer_processing(layer const& lay, box2d const& query_extent) { MAPNIK_LOG_DEBUG(cairo_renderer) << "cairo_renderer_base: Start processing layer=" << lay.name() ; MAPNIK_LOG_DEBUG(cairo_renderer) << "cairo_renderer_base: -- datasource=" << lay.datasource().get(); MAPNIK_LOG_DEBUG(cairo_renderer) << "cairo_renderer_base: -- query_extent=" << query_extent; if (lay.clear_label_cache()) { detector_->clear(); } query_extent_ = query_extent; } void cairo_renderer_base::end_layer_processing(layer const&) { MAPNIK_LOG_DEBUG(cairo_renderer) << "cairo_renderer_base: End layer processing"; } void cairo_renderer_base::start_style_processing(feature_type_style const& st) { MAPNIK_LOG_DEBUG(cairo_renderer) << "cairo_renderer:start style processing"; } void cairo_renderer_base::end_style_processing(feature_type_style const& st) { MAPNIK_LOG_DEBUG(cairo_renderer) << "cairo_renderer:end style processing"; } void cairo_renderer_base::process(polygon_symbolizer const& sym, mapnik::feature_impl & feature, proj_transform const& prj_trans) { cairo_save_restore guard(context_); composite_mode_e comp_op = get(sym, keys::comp_op, feature, src_over); mapnik::color fill = get(sym, keys::fill, feature, mapnik::color(128,128,128)); double opacity = get(sym, keys::fill_opacity, feature, 1.0); auto geom_transform = get_optional(sym, keys::geometry_transform); bool clip = get(sym, keys::clip, feature, false); double simplify_tolerance = get(sym, keys::simplify_tolerance, feature, 0.0); double smooth = get(sym, keys::smooth, feature, 0.0); context_.set_operator(comp_op); context_.set_color(fill, opacity); agg::trans_affine tr; if (geom_transform) { evaluate_transform(tr, feature, *geom_transform); } typedef boost::mpl::vector conv_types; vertex_converter, cairo_context, polygon_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> converter(query_extent_,context_,sym,t_,prj_trans,tr,1.0); if (prj_trans.equal() && clip) converter.set(); //optional clip (default: true) converter.set(); //always transform converter.set(); if (simplify_tolerance > 0.0) converter.set(); // optional simplify converter if (smooth > 0.0) converter.set(); // optional smooth converter for ( geometry_type & geom : feature.paths()) { if (geom.size() > 2) { converter.apply(geom); } } // fill polygon context_.set_fill_rule(CAIRO_FILL_RULE_EVEN_ODD); context_.fill(); } void cairo_renderer_base::process(building_symbolizer const& sym, mapnik::feature_impl & feature, proj_transform const& prj_trans) { typedef coord_transform path_type; cairo_save_restore guard(context_); composite_mode_e comp_op = get(sym, keys::comp_op, feature, src_over); mapnik::color fill = get(sym, keys::fill, feature); double opacity = get(sym, keys::fill_opacity, feature, 1.0); double height = get(sym, keys::height, feature, 0.0); context_.set_operator(comp_op); for (std::size_t i = 0; i < feature.num_geometries(); ++i) { geometry_type const& geom = feature.get_geometry(i); if (geom.size() > 2) { const std::unique_ptr frame(new geometry_type(geometry_type::types::LineString)); const std::unique_ptr roof(new geometry_type(geometry_type::types::Polygon)); std::deque face_segments; double x0 = 0; double y0 = 0; double x, y; geom.rewind(0); for (unsigned cm = geom.vertex(&x, &y); cm != SEG_END; cm = geom.vertex(&x, &y)) { if (cm == SEG_MOVETO) { frame->move_to(x,y); } else if (cm == SEG_LINETO) { frame->line_to(x,y); face_segments.push_back(segment_t(x0,y0,x,y)); } else if (cm == SEG_CLOSE) { frame->close_path(); } x0 = x; y0 = y; } std::sort(face_segments.begin(), face_segments.end(), y_order); for (auto const& seg : face_segments) { const std::unique_ptr faces(new geometry_type(geometry_type::types::Polygon)); faces->move_to(std::get<0>(seg), std::get<1>(seg)); faces->line_to(std::get<2>(seg), std::get<3>(seg)); faces->line_to(std::get<2>(seg), std::get<3>(seg) + height); faces->line_to(std::get<0>(seg), std::get<1>(seg) + height); path_type faces_path(t_, *faces, prj_trans); context_.set_color(fill.red() * 0.8 / 255.0, fill.green() * 0.8 / 255.0, fill.blue() * 0.8 / 255.0, fill.alpha() * opacity / 255.0); context_.add_path(faces_path); context_.fill(); frame->move_to(std::get<0>(seg), std::get<1>(seg)); frame->line_to(std::get<0>(seg), std::get<1>(seg) + height); } geom.rewind(0); for (unsigned cm = geom.vertex(&x, &y); cm != SEG_END; cm = geom.vertex(&x, &y)) { if (cm == SEG_MOVETO) { frame->move_to(x,y+height); roof->move_to(x,y+height); } else if (cm == SEG_LINETO) { frame->line_to(x,y+height); roof->line_to(x,y+height); } else if (cm == SEG_CLOSE) { frame->close_path(); roof->close_path(); } } path_type path(t_, *frame, prj_trans); context_.set_color(fill.red() * 0.8 / 255.0, fill.green() * 0.8/255.0, fill.blue() * 0.8 / 255.0, fill.alpha() * opacity / 255.0); context_.set_line_width(scale_factor_); context_.add_path(path); context_.stroke(); path_type roof_path(t_, *roof, prj_trans); context_.set_color(fill, opacity); context_.add_path(roof_path); context_.fill(); } } } void cairo_renderer_base::process(line_symbolizer const& sym, mapnik::feature_impl & feature, proj_transform const& prj_trans) { typedef boost::mpl::vector conv_types; cairo_save_restore guard(context_); composite_mode_e comp_op = get(sym, keys::comp_op, feature, src_over); auto geom_transform = get_optional(sym, keys::geometry_transform); bool clip = get(sym, keys::clip, feature, false); double offset = get(sym, keys::offset, feature, 0.0); double simplify_tolerance = get(sym, keys::simplify_tolerance, feature, 0.0); double smooth = get(sym, keys::smooth, feature, 0.0); mapnik::color stroke = get(sym, keys::stroke, feature, mapnik::color(0,0,0)); double stroke_opacity = get(sym, keys::stroke_opacity, feature, 1.0); line_join_enum stroke_join = get(sym, keys::stroke_linejoin, MITER_JOIN); line_cap_enum stroke_cap = get(sym, keys::stroke_linecap, BUTT_CAP); auto dash = get_optional(sym, keys::stroke_dasharray); double miterlimit = get(sym, keys::stroke_miterlimit, 4.0); double width = get(sym, keys::stroke_width, 1.0); context_.set_operator(comp_op); context_.set_color(stroke, stroke_opacity); context_.set_line_join(stroke_join); context_.set_line_cap(stroke_cap); context_.set_miter_limit(miterlimit); context_.set_line_width(width * scale_factor_); if (dash) { context_.set_dash(*dash, scale_factor_); } agg::trans_affine tr; if (geom_transform) { evaluate_transform(tr, feature, *geom_transform); } box2d clipping_extent = query_extent_; if (clip) { double padding = (double)(query_extent_.width()/width_); double half_stroke = width/2.0; if (half_stroke > 1) padding *= half_stroke; if (std::fabs(offset) > 0) padding *= std::fabs(offset) * 1.2; padding *= scale_factor_; clipping_extent.pad(padding); } vertex_converter, cairo_context, line_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> converter(clipping_extent,context_,sym,t_,prj_trans,tr,scale_factor_); if (clip) converter.set(); // optional clip (default: true) converter.set(); // always transform if (std::fabs(offset) > 0.0) converter.set(); // parallel offset converter.set(); // optional affine transform if (simplify_tolerance > 0.0) converter.set(); // optional simplify converter if (smooth > 0.0) converter.set(); // optional smooth converter for (geometry_type & geom : feature.paths()) { if (geom.size() > 1) { converter.apply(geom); } } // stroke context_.set_fill_rule(CAIRO_FILL_RULE_WINDING); context_.stroke(); } void cairo_renderer_base::render_box(box2d const& b) { cairo_save_restore guard(context_); context_.move_to(b.minx(), b.miny()); context_.line_to(b.minx(), b.maxy()); context_.line_to(b.maxx(), b.maxy()); context_.line_to(b.maxx(), b.miny()); context_.close_path(); context_.stroke(); } void render_vector_marker(cairo_context & context, pixel_position const& pos, mapnik::svg_storage_type & vmarker, agg::pod_bvector const & attributes, agg::trans_affine const& tr, double opacity, bool recenter) { using namespace mapnik::svg; box2d bbox = vmarker.bounding_box(); agg::trans_affine mtx = tr; if (recenter) { coord c = bbox.center(); mtx = agg::trans_affine_translation(-c.x,-c.y); mtx *= tr; mtx.translate(pos.x, pos.y); } agg::trans_affine transform; for(unsigned i = 0; i < attributes.size(); ++i) { mapnik::svg::path_attributes const& attr = attributes[i]; if (!attr.visibility_flag) continue; cairo_save_restore guard(context); transform = attr.transform; transform *= mtx; // TODO - this 'is_valid' check is not used in the AGG renderer and also // appears to lead to bogus results with // tests/data/good_maps/markers_symbolizer_lines_file.xml if (/*transform.is_valid() && */ !transform.is_identity()) { double m[6]; transform.store_to(m); cairo_matrix_t matrix; cairo_matrix_init(&matrix,m[0],m[1],m[2],m[3],m[4],m[5]); context.transform(matrix); } vertex_stl_adapter stl_storage(vmarker.source()); svg_path_adapter svg_path(stl_storage); if (attr.fill_flag || attr.fill_gradient.get_gradient_type() != NO_GRADIENT) { context.add_agg_path(svg_path,attr.index); if (attr.even_odd_flag) { context.set_fill_rule(CAIRO_FILL_RULE_EVEN_ODD); } else { context.set_fill_rule(CAIRO_FILL_RULE_WINDING); } if(attr.fill_gradient.get_gradient_type() != NO_GRADIENT) { cairo_gradient g(attr.fill_gradient,attr.fill_opacity * attr.opacity * opacity); context.set_gradient(g,bbox); context.fill(); } else if(attr.fill_flag) { double fill_opacity = attr.fill_opacity * attr.opacity * opacity * attr.fill_color.opacity(); context.set_color(attr.fill_color.r/255.0,attr.fill_color.g/255.0, attr.fill_color.b/255.0, fill_opacity); context.fill(); } } if (attr.stroke_gradient.get_gradient_type() != NO_GRADIENT || attr.stroke_flag) { context.add_agg_path(svg_path,attr.index); if(attr.stroke_gradient.get_gradient_type() != NO_GRADIENT) { context.set_line_width(attr.stroke_width); context.set_line_cap(line_cap_enum(attr.line_cap)); context.set_line_join(line_join_enum(attr.line_join)); context.set_miter_limit(attr.miter_limit); cairo_gradient g(attr.stroke_gradient,attr.fill_opacity * attr.opacity * opacity); context.set_gradient(g,bbox); context.stroke(); } else if (attr.stroke_flag) { double stroke_opacity = attr.stroke_opacity * attr.opacity * opacity * attr.stroke_color.opacity(); context.set_color(attr.stroke_color.r/255.0,attr.stroke_color.g/255.0, attr.stroke_color.b/255.0, stroke_opacity); context.set_line_width(attr.stroke_width); context.set_line_cap(line_cap_enum(attr.line_cap)); context.set_line_join(line_join_enum(attr.line_join)); context.set_miter_limit(attr.miter_limit); context.stroke(); } } } } void cairo_renderer_base::render_marker(pixel_position const& pos, marker const& marker, agg::trans_affine const& tr, double opacity, bool recenter) { cairo_save_restore guard(context_); if (marker.is_vector()) { mapnik::svg_path_ptr vmarker = *marker.get_vector_data(); if (vmarker) { agg::trans_affine marker_tr = tr; marker_tr *=agg::trans_affine_scaling(scale_factor_); agg::pod_bvector const & attributes = vmarker->attributes(); render_vector_marker(context_, pos, *vmarker, attributes, marker_tr, opacity, recenter); } } else if (marker.is_bitmap()) { double width = (*marker.get_bitmap_data())->width(); double height = (*marker.get_bitmap_data())->height(); double cx = 0.5 * width; double cy = 0.5 * height; agg::trans_affine marker_tr; marker_tr *= agg::trans_affine_translation(-cx,-cy); marker_tr *= tr; marker_tr *= agg::trans_affine_scaling(scale_factor_); marker_tr *= agg::trans_affine_translation(pos.x,pos.y); context_.add_image(marker_tr, **marker.get_bitmap_data(), opacity); } } void cairo_renderer_base::process(point_symbolizer const& sym, mapnik::feature_impl & feature, proj_transform const& prj_trans) { std::string filename = get(sym, keys::file, feature); double opacity = get(sym, keys::opacity, feature, 1.0); point_placement_enum placement = get(sym, keys::point_placement_type, feature, CENTROID_POINT_PLACEMENT); bool allow_overlap = get(sym, keys::allow_overlap, feature, false); bool ignore_placement = get(sym, keys::ignore_placement, feature, false); auto geom_transform = get_optional(sym, keys::geometry_transform); composite_mode_e comp_op = get(sym, keys::comp_op, feature, src_over); cairo_save_restore guard(context_); context_.set_operator(comp_op); boost::optional marker; if ( !filename.empty() ) { marker = marker_cache::instance().find(filename, true); } else { marker.reset(std::make_shared()); } if (marker) { box2d const& bbox = (*marker)->bounding_box(); coord2d center = bbox.center(); agg::trans_affine tr; if (geom_transform) { evaluate_transform(tr, feature, *geom_transform); } agg::trans_affine_translation recenter(-center.x, -center.y); agg::trans_affine recenter_tr = recenter * tr; box2d label_ext = bbox * recenter_tr * agg::trans_affine_scaling(scale_factor_); for (std::size_t i = 0; i < feature.num_geometries(); ++i) { geometry_type const& geom = feature.get_geometry(i); double x; double y; double z = 0; if (placement == CENTROID_POINT_PLACEMENT) { if (!label::centroid(geom, x, y)) return; } else { if (!label::interior_position(geom ,x, y)) return; } prj_trans.backward(x, y, z); t_.forward(&x, &y); label_ext.re_center(x,y); if (allow_overlap || detector_->has_placement(label_ext)) { render_marker(pixel_position(x,y),**marker, tr, opacity); if (!ignore_placement) detector_->insert(label_ext); } } } } void cairo_renderer_base::process(shield_symbolizer const& sym, mapnik::feature_impl & feature, proj_transform const& prj_trans) { text_symbolizer_helper helper( sym, feature, prj_trans, width_, height_, scale_factor_, t_, font_manager_, *detector_, query_extent_); cairo_save_restore guard(context_); composite_mode_e comp_op = get(sym, keys::comp_op, feature, src_over); double opacity = get(sym,keys::opacity,feature, 1.0); context_.set_operator(comp_op); placements_list const &placements = helper.get(); for (glyph_positions_ptr glyphs : placements) { if (glyphs->marker()) { pixel_position pos = glyphs->marker_pos(); render_marker(pos, *(glyphs->marker()->marker), glyphs->marker()->transform, opacity); } context_.add_text(glyphs, face_manager_, font_manager_, scale_factor_); } } void cairo_renderer_base::process(line_pattern_symbolizer const& sym, mapnik::feature_impl & feature, proj_transform const& prj_trans) { typedef agg::conv_clip_polyline clipped_geometry_type; typedef coord_transform path_type; std::string filename = get(sym, keys::file, feature); composite_mode_e comp_op = get(sym, keys::comp_op, feature, src_over); boost::optional marker; if ( !filename.empty() ) { marker = marker_cache::instance().find(filename, true); } else { marker.reset(std::make_shared()); } if (!marker && !(*marker)->is_bitmap()) return; unsigned width((*marker)->width()); unsigned height((*marker)->height()); cairo_save_restore guard(context_); context_.set_operator(comp_op); cairo_pattern pattern(**((*marker)->get_bitmap_data())); pattern.set_extend(CAIRO_EXTEND_REPEAT); pattern.set_filter(CAIRO_FILTER_BILINEAR); context_.set_line_width(height * scale_factor_); for (std::size_t i = 0; i < feature.num_geometries(); ++i) { geometry_type & geom = feature.get_geometry(i); if (geom.size() > 1) { clipped_geometry_type clipped(geom); clipped.clip_box(query_extent_.minx(),query_extent_.miny(),query_extent_.maxx(),query_extent_.maxy()); path_type path(t_,clipped,prj_trans); double length(0); double x0(0), y0(0); double x, y; for (unsigned cm = path.vertex(&x, &y); cm != SEG_END; cm = path.vertex(&x, &y)) { if (cm == SEG_MOVETO) { length = 0.0; } else if (cm == SEG_LINETO) { double dx = x - x0; double dy = y - y0; double angle = std::atan2(dy, dx); double offset = std::fmod(length, width); cairo_matrix_t matrix; cairo_matrix_init_identity(&matrix); cairo_matrix_translate(&matrix,x0,y0); cairo_matrix_rotate(&matrix,angle); cairo_matrix_translate(&matrix,-offset,0.5*height); cairo_matrix_invert(&matrix); pattern.set_matrix(matrix); context_.set_pattern(pattern); context_.move_to(x0, y0); context_.line_to(x, y); context_.stroke(); length = length + hypot(x - x0, y - y0); } x0 = x; y0 = y; } } } } void cairo_renderer_base::process(polygon_pattern_symbolizer const& sym, mapnik::feature_impl & feature, proj_transform const& prj_trans) { //typedef agg::conv_clip_polygon clipped_geometry_type; //typedef coord_transform path_type; cairo_save_restore guard(context_); composite_mode_e comp_op = get(sym, keys::comp_op, feature, src_over); std::string filename = get(sym, keys::file, feature); auto geom_transform = get_optional(sym, keys::geometry_transform); bool clip = get(sym, keys::clip, feature, false); double simplify_tolerance = get(sym, keys::simplify_tolerance, feature, 0.0); double smooth = get(sym, keys::smooth, feature, 0.0); context_.set_operator(comp_op); boost::optional marker = mapnik::marker_cache::instance().find(filename,true); if (!marker && !(*marker)->is_bitmap()) return; cairo_pattern pattern(**((*marker)->get_bitmap_data())); pattern.set_extend(CAIRO_EXTEND_REPEAT); context_.set_pattern(pattern); //pattern_alignment_e align = sym.get_alignment(); //unsigned offset_x=0; //unsigned offset_y=0; //if (align == LOCAL_ALIGNMENT) //{ // double x0 = 0; // double y0 = 0; // 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()); // path_type path(t_,clipped,prj_trans); // path.vertex(&x0,&y0); // } // offset_x = unsigned(width_ - x0); // offset_y = unsigned(height_ - y0); //} agg::trans_affine tr; if (geom_transform) { evaluate_transform(tr, feature, *geom_transform); } typedef boost::mpl::vector conv_types; vertex_converter, cairo_context, polygon_pattern_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> converter(query_extent_,context_,sym,t_,prj_trans,tr, scale_factor_); if (prj_trans.equal() && clip) converter.set(); //optional clip (default: true) converter.set(); //always transform converter.set(); if (simplify_tolerance > 0.0) converter.set(); // optional simplify converter if (smooth > 0.0) converter.set(); // optional smooth converter for ( geometry_type & geom : feature.paths()) { if (geom.size() > 2) { converter.apply(geom); } } // fill polygon context_.set_fill_rule(CAIRO_FILL_RULE_EVEN_ODD); context_.fill(); } void cairo_renderer_base::process(raster_symbolizer const& sym, mapnik::feature_impl & feature, proj_transform const& prj_trans) { raster_ptr const& source = feature.get_raster(); if (source) { // If there's a colorizer defined, use it to color the raster in-place raster_colorizer_ptr colorizer = get(sym, keys::colorizer); scaling_method_e scaling_method = get(sym, keys::scaling, feature, SCALING_NEAR); double filter_factor = get(sym, keys::filter_factor, feature); boost::optional is_premultiplied = get_optional(sym, keys::premultiplied, feature); unsigned mesh_size = static_cast(get(sym,keys::mesh_size,feature, 16)); composite_mode_e comp_op = get(sym, keys::comp_op, feature, src_over); double opacity = get(sym, keys::opacity, feature, 1.0); if (colorizer) colorizer->colorize(source,feature); box2d target_ext = box2d(source->ext_); prj_trans.backward(target_ext, PROJ_ENVELOPE_POINTS); box2d ext = t_.forward(target_ext); int start_x = static_cast(std::floor(ext.minx()+.5)); int start_y = static_cast(std::floor(ext.miny()+.5)); int end_x = static_cast(std::floor(ext.maxx()+.5)); int end_y = static_cast(std::floor(ext.maxy()+.5)); int raster_width = end_x - start_x; int raster_height = end_y - start_y; if (raster_width > 0 && raster_height > 0) { raster target(target_ext, raster_width, raster_height, filter_factor, true); bool premultiply_source = !source->premultiplied_alpha_; if (is_premultiplied) { premultiply_source = !(*is_premultiplied); } if (premultiply_source) { agg::rendering_buffer buffer(source->data_.getBytes(), source->data_.width(), source->data_.height(), source->data_.width() * 4); agg::pixfmt_rgba32 pixf(buffer); pixf.premultiply(); } if (!prj_trans.equal()) { double offset_x = ext.minx() - start_x; double offset_y = ext.miny() - start_y; reproject_and_scale_raster(target, *source, prj_trans, offset_x, offset_y, mesh_size, scaling_method); } else { if (scaling_method == SCALING_BILINEAR8) { scale_image_bilinear8(target.data_, source->data_, 0.0, 0.0); } else { double image_ratio_x = ext.width() / source->data_.width(); double image_ratio_y = ext.height() / source->data_.height(); scale_image_agg(target.data_, source->data_, scaling_method, image_ratio_x, image_ratio_y, 0.0, 0.0, source->get_filter_factor()); } } cairo_save_restore guard(context_); context_.set_operator(comp_op); context_.add_image(start_x, start_y, target.data_, opacity); } } } namespace detail { template struct markers_dispatch { markers_dispatch(Context & ctx, SvgPath & marker, Attributes const& attributes, Detector & detector, markers_symbolizer const& sym, box2d const& bbox, agg::trans_affine const& marker_trans, double scale_factor) :ctx_(ctx), marker_(marker), attributes_(attributes), detector_(detector), sym_(sym), bbox_(bbox), marker_trans_(marker_trans), scale_factor_(scale_factor) {} template void add_path(T & path) { marker_placement_enum placement_method = get(sym_, keys::markers_placement_type, MARKER_POINT_PLACEMENT); bool ignore_placement = get(sym_, keys::ignore_placement, false); bool allow_overlap = get(sym_, keys::allow_overlap, false); double opacity = get(sym_, keys::opacity, 1.0); double spacing = get(sym_, keys::spacing, 100.0); double max_error = get(sym_, keys::max_error, 0.2); if (placement_method != MARKER_LINE_PLACEMENT || path.type() == geometry_type::types::Point) { double x = 0; double y = 0; if (path.type() == geometry_type::types::LineString) { if (!label::middle_point(path, x, y)) return; } else if (placement_method == MARKER_INTERIOR_PLACEMENT) { if (!label::interior_position(path, x, y)) return; } else { if (!label::centroid(path, x, y)) return; } coord2d center = bbox_.center(); agg::trans_affine matrix = agg::trans_affine_translation(-center.x, -center.y); matrix *= marker_trans_; matrix *=agg::trans_affine_translation(x, y); box2d transformed_bbox = bbox_ * matrix; if (allow_overlap || detector_.has_placement(transformed_bbox)) { render_vector_marker(ctx_, pixel_position(x,y), marker_, attributes_, marker_trans_, opacity, true); if (!ignore_placement) { detector_.insert(transformed_bbox); } } } else { markers_placement placement(path, bbox_, marker_trans_, detector_, spacing * scale_factor_, max_error, allow_overlap); double x, y, angle; while (placement.get_point(x, y, angle, ignore_placement)) { agg::trans_affine matrix = marker_trans_; matrix.rotate(angle); render_vector_marker(ctx_, pixel_position(x,y),marker_, attributes_, matrix, opacity, true); } } } Context & ctx_; SvgPath & marker_; Attributes const& attributes_; Detector & detector_; markers_symbolizer const& sym_; box2d const& bbox_; agg::trans_affine const& marker_trans_; double scale_factor_; }; template struct markers_dispatch_2 { markers_dispatch_2(Context & ctx, ImageMarker & marker, Detector & detector, markers_symbolizer const& sym, box2d const& bbox, agg::trans_affine const& marker_trans, double scale_factor) :ctx_(ctx), marker_(marker), detector_(detector), sym_(sym), bbox_(bbox), marker_trans_(marker_trans), scale_factor_(scale_factor) {} template void add_path(T & path) { marker_placement_enum placement_method = get(sym_, keys::markers_placement_type, MARKER_POINT_PLACEMENT); double opacity = get(sym_, keys::opacity, 1.0); double spacing = get(sym_, keys::spacing, 100.0); double max_error = get(sym_, keys::max_error, 0.2); bool allow_overlap = get(sym_, keys::allow_overlap, false); bool ignore_placement = get(sym_, keys::ignore_placement, false); if (placement_method != MARKER_LINE_PLACEMENT || path.type() == geometry_type::types::Point) { double x = 0; double y = 0; if (path.type() == geometry_type::types::LineString) { if (!label::middle_point(path, x, y)) return; } else if (placement_method == MARKER_INTERIOR_PLACEMENT) { if (!label::interior_position(path, x, y)) return; } else { if (!label::centroid(path, x, y)) return; } coord2d center = bbox_.center(); agg::trans_affine matrix = agg::trans_affine_translation(-center.x, -center.y); matrix *= marker_trans_; matrix *=agg::trans_affine_translation(x, y); box2d transformed_bbox = bbox_ * matrix; if (allow_overlap || detector_.has_placement(transformed_bbox)) { ctx_.add_image(matrix, *marker_, opacity); if (!ignore_placement) { detector_.insert(transformed_bbox); } } } else { markers_placement placement(path, bbox_, marker_trans_, detector_, spacing * scale_factor_, max_error, allow_overlap); double x, y, angle; while (placement.get_point(x, y, angle, ignore_placement)) { coord2d center = bbox_.center(); agg::trans_affine matrix = agg::trans_affine_translation(-center.x, -center.y); matrix *= marker_trans_; matrix *= agg::trans_affine_rotation(angle); matrix *= agg::trans_affine_translation(x, y); ctx_.add_image(matrix, *marker_, opacity); } } } Context & ctx_; ImageMarker & marker_; Detector & detector_; markers_symbolizer const& sym_; box2d const& bbox_; agg::trans_affine const& marker_trans_; double scale_factor_; }; } void cairo_renderer_base::process(markers_symbolizer const& sym, mapnik::feature_impl & feature, proj_transform const& prj_trans) { typedef boost::mpl::vector conv_types; cairo_save_restore guard(context_); composite_mode_e comp_op = get(sym, keys::comp_op, feature, src_over); std::string filename = get(sym, keys::file, feature, "shape://ellipse"); auto geom_transform = get_optional(sym, keys::geometry_transform); auto img_transform = get_optional(sym, keys::image_transform); auto width = get_optional(sym, keys::width); auto height = get_optional(sym, keys::height); bool clip = get(sym, keys::clip, feature, false); double smooth = get(sym, keys::smooth, feature, 0.0); context_.set_operator(comp_op); agg::trans_affine tr = agg::trans_affine_scaling(scale_factor_); if (!filename.empty()) { boost::optional mark = mapnik::marker_cache::instance().find(filename, true); if (mark && *mark) { agg::trans_affine geom_tr; if (geom_transform) { evaluate_transform(geom_tr, feature, *geom_transform); } box2d const& bbox = (*mark)->bounding_box(); setup_transform_scaling(tr, bbox.width(), bbox.height(), feature, sym); if (img_transform) { evaluate_transform(tr, feature, *img_transform); } if ((*mark)->is_vector()) { using namespace mapnik::svg; typedef agg::pod_bvector svg_attributes_type; typedef detail::markers_dispatch dispatch_type; boost::optional const& stock_vector_marker = (*mark)->get_vector_data(); // special case for simple ellipse markers // to allow for full control over rx/ry dimensions if (filename == "shape://ellipse" && (width || height)) { svg_storage_type marker_ellipse; vertex_stl_adapter stl_storage(marker_ellipse.source()); svg_path_adapter svg_path(stl_storage); build_ellipse(sym, feature, marker_ellipse, svg_path); svg_attributes_type attributes; bool result = push_explicit_style( (*stock_vector_marker)->attributes(), attributes, sym); agg::trans_affine marker_tr = agg::trans_affine_scaling(scale_factor_); if (img_transform) { evaluate_transform(marker_tr, feature, *img_transform); } box2d new_bbox = marker_ellipse.bounding_box(); dispatch_type dispatch(context_, marker_ellipse, result?attributes:(*stock_vector_marker)->attributes(), *detector_, sym, new_bbox, marker_tr, scale_factor_); vertex_converter, dispatch_type, markers_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> converter(query_extent_, dispatch, sym, t_, prj_trans, marker_tr, scale_factor_); if (clip && feature.paths().size() > 0) // optional clip (default: true) { geometry_type::types type = feature.paths()[0].type(); if (type == geometry_type::types::Polygon) converter.set(); // line clipping disabled due to https://github.com/mapnik/mapnik/issues/1426 //else if (type == geometry_type::types::LineString) // converter.template set(); // don't clip if type==geometry_type::types::Point } converter.set(); //always transform if (smooth > 0.0) converter.set(); // optional smooth converter apply_markers_multi(feature, converter, sym); } else { svg_attributes_type attributes; bool result = push_explicit_style( (*stock_vector_marker)->attributes(), attributes, sym); dispatch_type dispatch(context_, **stock_vector_marker, result?attributes:(*stock_vector_marker)->attributes(), *detector_, sym, bbox, tr, scale_factor_); vertex_converter, dispatch_type, markers_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> converter(query_extent_, dispatch, sym, t_, prj_trans, tr, scale_factor_); if (clip && feature.paths().size() > 0) // optional clip (default: true) { geometry_type::types type = feature.paths()[0].type(); if (type == geometry_type::types::Polygon) converter.set(); // line clipping disabled due to https://github.com/mapnik/mapnik/issues/1426 //else if (type == geometry_type::types::LineString) // converter.template set(); // don't clip if type==geometry_type::types::Point } converter.set(); //always transform if (smooth > 0.0) converter.set(); // optional smooth converter apply_markers_multi(feature, converter, sym); } } else // raster markers { typedef detail::markers_dispatch_2 dispatch_type; boost::optional marker = (*mark)->get_bitmap_data(); if ( marker ) { dispatch_type dispatch(context_, *marker, *detector_, sym, bbox, tr, scale_factor_); vertex_converter, dispatch_type, markers_symbolizer, CoordTransform, proj_transform, agg::trans_affine, conv_types> converter(query_extent_, dispatch, sym, t_, prj_trans, tr, scale_factor_); if (clip && feature.paths().size() > 0) // optional clip (default: true) { geometry_type::types type = feature.paths()[0].type(); if (type == geometry_type::types::Polygon) converter.set(); // line clipping disabled due to https://github.com/mapnik/mapnik/issues/1426 //else if (type == geometry_type::types::LineString) // converter.template set(); // don't clip if type==geometry_type::types::Point } converter.set(); //always transform if (smooth > 0.0) converter.set(); // optional smooth converter apply_markers_multi(feature, converter, sym); } } } } } void cairo_renderer_base::process(text_symbolizer const& sym, mapnik::feature_impl & feature, proj_transform const& prj_trans) { text_symbolizer_helper helper( sym, feature, prj_trans, width_, height_, scale_factor_, t_, font_manager_, *detector_, query_extent_); cairo_save_restore guard(context_); composite_mode_e comp_op = get(sym, keys::comp_op, feature, src_over); context_.set_operator(comp_op); placements_list const &placements = helper.get(); for (glyph_positions_ptr glyphs : placements) { context_.add_text(glyphs, face_manager_, font_manager_, scale_factor_); } } template class cairo_renderer; template class cairo_renderer; } #endif // HAVE_CAIRO