/***************************************************************************** * * 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 #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 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(boost::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 = boost::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_(boost::make_shared()), font_manager_(*font_engine_), face_manager_(font_engine_), detector_(boost::make_shared( box2d(-m.buffer_size(), -m.buffer_size(), m.width() + m.buffer_size(), m.height() + m.buffer_size()))) { MAPNIK_LOG_DEBUG(cairo_renderer) << "cairo_renderer_base: Scale=" << m.scale(); } cairo_renderer_base::cairo_renderer_base(Map const& m, cairo_ptr const& cairo, boost::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_(boost::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, cairo_ptr const& cairo, boost::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, boost::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::start_map_processing(Map const& map) { MAPNIK_LOG_DEBUG(cairo_renderer) << "cairo_renderer_base: Start map processing bbox=" << map.get_current_extent(); #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 6, 0) box2d bounds = t_.forward(t_.extent()); context_.rectangle(bounds.minx(), bounds.miny(), bounds.maxx(), bounds.maxy()); context_.clip(); #else #warning building against cairo older that 1.6.0, map clipping is disabled #endif boost::optional bg = m_.background(); if (bg) { cairo_save_restore guard(context_); context_.set_color(*bg); context_.paint(); } } template <> void cairo_renderer::end_map_processing(Map const& ) { MAPNIK_LOG_DEBUG(cairo_renderer) << "cairo_renderer_base: End map processing"; } template <> 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_); context_.set_operator(sym.comp_op()); context_.set_color(sym.get_fill(), sym.get_opacity()); agg::trans_affine tr; evaluate_transform(tr, feature, sym.get_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() && sym.clip()) converter.set(); //optional clip (default: true) converter.set(); //always transform converter.set(); if (sym.simplify_tolerance() > 0.0) converter.set(); // optional simplify converter if (sym.smooth() > 0.0) converter.set(); // optional smooth converter BOOST_FOREACH( geometry_type & geom, feature.paths()) { if (geom.size() > 2) { converter.apply(geom); } } // fill polygon 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_); context_.set_operator(sym.comp_op()); color const& fill = sym.get_fill(); double height = 0.0; expression_ptr height_expr = sym.height(); if (height_expr) { value_type result = boost::apply_visitor(evaluate(feature), *height_expr); height = result.to_double() * scale_factor_; } for (unsigned i = 0; i < feature.num_geometries(); ++i) { geometry_type const& geom = feature.get_geometry(i); if (geom.size() > 2) { boost::scoped_ptr frame(new geometry_type(LineString)); boost::scoped_ptr roof(new geometry_type(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 || cm == SEG_CLOSE) { frame->line_to(x,y); face_segments.push_back(segment_t(x0,y0,x,y)); } x0 = x; y0 = y; } std::sort(face_segments.begin(), face_segments.end(), y_order); std::deque::const_iterator itr = face_segments.begin(); std::deque::const_iterator end=face_segments.end(); for (; itr != end; ++itr) { boost::scoped_ptr faces(new geometry_type(Polygon)); faces->move_to(itr->get<0>(), itr->get<1>()); faces->line_to(itr->get<2>(), itr->get<3>()); faces->line_to(itr->get<2>(), itr->get<3>() + height); faces->line_to(itr->get<0>(), itr->get<1>() + 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() * sym.get_opacity() / 255.0); context_.add_path(faces_path); context_.fill(); frame->move_to(itr->get<0>(), itr->get<1>()); frame->line_to(itr->get<0>(), itr->get<1>() + 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 || cm == SEG_CLOSE) { frame->line_to(x,y+height); roof->line_to(x,y+height); } } 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() * sym.get_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, sym.get_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_); mapnik::stroke const& stroke_ = sym.get_stroke(); context_.set_operator(sym.comp_op()); context_.set_color(stroke_.get_color(), stroke_.get_opacity()); context_.set_line_join(stroke_.get_line_join()); context_.set_line_cap(stroke_.get_line_cap()); context_.set_miter_limit(stroke_.get_miterlimit()); context_.set_line_width(stroke_.get_width() * scale_factor_); if (stroke_.has_dash()) { context_.set_dash(stroke_.get_dash_array(), scale_factor_); } agg::trans_affine tr; evaluate_transform(tr, feature, sym.get_transform()); box2d clipping_extent = query_extent_; if (sym.clip()) { double padding = (double)(query_extent_.width()/width_); double half_stroke = stroke_.get_width()/2.0; if (half_stroke > 1) padding *= half_stroke; if (std::fabs(sym.offset()) > 0) padding *= std::fabs(sym.offset()) * 1.2; 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 (sym.clip()) converter.set(); // optional clip (default: true) converter.set(); // always transform if (std::fabs(sym.offset()) > 0.0) converter.set(); // parallel offset converter.set(); // optional affine transform if (sym.simplify_tolerance() > 0.0) converter.set(); // optional simplify converter if (sym.smooth() > 0.0) converter.set(); // optional smooth converter BOOST_FOREACH( geometry_type & geom, feature.paths()) { if (geom.size() > 1) { converter.apply(geom); } } // stroke 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*opacity); context.set_gradient(g,bbox); context.fill(); } else if(attr.fill_flag) { double fill_opacity = attr.fill_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*opacity); context.set_gradient(g,bbox); context.stroke(); } else if (attr.stroke_flag) { double stroke_opacity = attr.stroke_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, const agg::trans_affine & 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::pod_bvector const & attributes = vmarker->attributes(); render_vector_marker(context_, pos, *vmarker, attributes, tr, opacity, recenter); } } else if (marker.is_bitmap()) { agg::trans_affine matrix = tr; double width = (*marker.get_bitmap_data())->width(); double height = (*marker.get_bitmap_data())->height(); double cx = 0.5 * width; double cy = 0.5 * height; matrix *= agg::trans_affine_translation( boost::math::iround(pos.x - cx), boost::math::iround(pos.y - cy)); context_.add_image(matrix, **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 = path_processor_type::evaluate( *sym.get_filename(), feature); boost::optional marker; if ( !filename.empty() ) { marker = marker_cache::instance().find(filename, true); } else { marker.reset(boost::make_shared()); } if (marker) { for (unsigned i = 0; i < feature.num_geometries(); ++i) { geometry_type const& geom = feature.get_geometry(i); double x; double y; double z = 0; if (sym.get_point_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); double dx = 0.5 * (*marker)->width(); double dy = 0.5 * (*marker)->height(); agg::trans_affine tr = agg::trans_affine_scaling(scale_factor_); evaluate_transform(tr, feature, sym.get_image_transform()); box2d label_ext (-dx, -dy, dx, dy); label_ext *= tr; label_ext *= agg::trans_affine_translation(x,y); if (sym.get_allow_overlap() || detector_->has_placement(label_ext)) { render_marker(pixel_position(x,y),**marker, tr, sym.get_opacity()); if (!sym.get_ignore_placement()) detector_->insert(label_ext); } } } } void cairo_renderer_base::process(shield_symbolizer const& sym, mapnik::feature_impl & feature, proj_transform const& prj_trans) { shield_symbolizer_helper, label_collision_detector4> helper( sym, feature, prj_trans, width_, height_, scale_factor_, t_, font_manager_, *detector_, query_extent_); cairo_save_restore guard(context_); context_.set_operator(sym.comp_op()); while (helper.next()) { placements_type const& placements = helper.placements(); for (unsigned int ii = 0; ii < placements.size(); ++ii) { pixel_position pos = helper.get_marker_position(placements[ii]); pos.x += 0.5 * helper.get_marker_width(); pos.y += 0.5 * helper.get_marker_height(); double dx = 0.5 * helper.get_marker_width(); double dy = 0.5 * helper.get_marker_height(); agg::trans_affine marker_tr = agg::trans_affine_translation(-dx,-dy); marker_tr *= agg::trans_affine_scaling(scale_factor_); marker_tr *= agg::trans_affine_translation(dx,dy); marker_tr *= helper.get_image_transform(); render_marker(pos, helper.get_marker(), marker_tr, sym.get_opacity()); context_.add_text(placements[ii], 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 = path_processor_type::evaluate( *sym.get_filename(), feature); boost::optional marker = mapnik::marker_cache::instance().find(filename,true); if (!marker && !(*marker)->is_bitmap()) return; unsigned width((*marker)->width()); unsigned height((*marker)->height()); cairo_save_restore guard(context_); context_.set_operator(sym.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 (unsigned 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 = atan2(dy, dx); double offset = 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_); context_.set_operator(sym.comp_op()); std::string filename = path_processor_type::evaluate( *sym.get_filename(), feature); 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; evaluate_transform(tr, feature, sym.get_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() && sym.clip()) converter.set(); //optional clip (default: true) converter.set(); //always transform converter.set(); if (sym.simplify_tolerance() > 0.0) converter.set(); // optional simplify converter if (sym.smooth() > 0.0) converter.set(); // optional smooth converter BOOST_FOREACH( geometry_type & geom, feature.paths()) { if (geom.size() > 2) { converter.apply(geom); } } // fill polygon 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 = sym.get_colorizer(); 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(ext.minx()); int start_y = static_cast(ext.miny()); int end_x = static_cast(std::ceil(ext.maxx())); int end_y = static_cast(std::ceil(ext.maxy())); 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); scaling_method_e scaling_method = sym.get_scaling_method(); double filter_radius = sym.calculate_filter_factor(); bool premultiply_source = !source->premultiplied_alpha_; boost::optional is_premultiplied = sym.premultiplied(); if (is_premultiplied) { if (*is_premultiplied) premultiply_source = false; else premultiply_source = true; } 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, sym.get_mesh_size(), filter_radius, scaling_method); } else { if (scaling_method == SCALING_BILINEAR8) { scale_image_bilinear8(target.data_,source->data_, 0.0, 0.0); } else { double scaling_ratio = ext.width() / source->data_.width(); scale_image_agg(target.data_, source->data_, scaling_method, scaling_ratio, 0.0, 0.0, filter_radius); } } cairo_save_restore guard(context_); context_.set_operator(sym.comp_op()); context_.add_image(start_x, start_y, target.data_, sym.get_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_e placement_method = sym_.get_marker_placement(); if (placement_method != MARKER_LINE_PLACEMENT || path.type() == Point) { double x = 0; double y = 0; if (path.type() == 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 (sym_.get_allow_overlap() || detector_.has_placement(transformed_bbox)) { render_vector_marker(ctx_, pixel_position(x,y), marker_, attributes_, marker_trans_, sym_.get_opacity(), true); if (!sym_.get_ignore_placement()) { detector_.insert(transformed_bbox); } } } else { markers_placement placement(path, bbox_, marker_trans_, detector_, sym_.get_spacing() * scale_factor_, sym_.get_max_error(), sym_.get_allow_overlap()); double x, y, angle; while (placement.get_point(x, y, angle)) { agg::trans_affine matrix = marker_trans_; matrix.rotate(angle); render_vector_marker(ctx_, pixel_position(x,y),marker_, attributes_, matrix, sym_.get_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_e placement_method = sym_.get_marker_placement(); if (placement_method != MARKER_LINE_PLACEMENT || path.type() == Point) { double x = 0; double y = 0; if (path.type() == 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 (sym_.get_allow_overlap() || detector_.has_placement(transformed_bbox)) { ctx_.add_image(matrix, *marker_, sym_.get_opacity()); if (!sym_.get_ignore_placement()) { detector_.insert(transformed_bbox); } } } else { markers_placement placement(path, bbox_, marker_trans_, detector_, sym_.get_spacing() * scale_factor_, sym_.get_max_error(), sym_.get_allow_overlap()); double x, y, angle; while (placement.get_point(x, y, angle)) { 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_, sym_.get_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_); context_.set_operator(sym.comp_op()); agg::trans_affine tr = agg::trans_affine_scaling(scale_factor_); std::string filename = path_processor_type::evaluate(*sym.get_filename(), feature); if (!filename.empty()) { boost::optional mark = mapnik::marker_cache::instance().find(filename, true); if (mark && *mark) { agg::trans_affine geom_tr; evaluate_transform(geom_tr, feature, sym.get_transform()); box2d const& bbox = (*mark)->bounding_box(); setup_transform_scaling(tr, bbox, feature, sym); evaluate_transform(tr, feature, sym.get_image_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(); expression_ptr const& width_expr = sym.get_width(); expression_ptr const& height_expr = sym.get_height(); // special case for simple ellipse markers // to allow for full control over rx/ry dimensions if (filename == "shape://ellipse" && (width_expr || height_expr)) { 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_); evaluate_transform(marker_tr, feature, sym.get_image_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 (sym.clip() && feature.paths().size() > 0) // optional clip (default: true) { eGeomType type = feature.paths()[0].type(); if (type == Polygon) converter.set(); // line clipping disabled due to https://github.com/mapnik/mapnik/issues/1426 //else if (type == LineString) // converter.template set(); // don't clip if type==Point } converter.set(); //always transform if (sym.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 (sym.clip() && feature.paths().size() > 0) // optional clip (default: true) { eGeomType type = feature.paths()[0].type(); if (type == Polygon) converter.set(); // line clipping disabled due to https://github.com/mapnik/mapnik/issues/1426 //else if (type == LineString) // converter.template set(); // don't clip if type==Point } converter.set(); //always transform if (sym.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 (sym.clip() && feature.paths().size() > 0) // optional clip (default: true) { eGeomType type = feature.paths()[0].type(); if (type == Polygon) converter.set(); // line clipping disabled due to https://github.com/mapnik/mapnik/issues/1426 //else if (type == LineString) // converter.template set(); // don't clip if type==Point } converter.set(); //always transform if (sym.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, label_collision_detector4> helper( sym, feature, prj_trans, width_, height_, scale_factor_, t_, font_manager_, *detector_, query_extent_); cairo_save_restore guard(context_); context_.set_operator(sym.comp_op()); while (helper.next()) { placements_type const& placements = helper.placements(); for (unsigned int ii = 0; ii < placements.size(); ++ii) { context_.add_text(placements[ii], face_manager_, font_manager_, scale_factor_); } } } template class cairo_renderer; template class cairo_renderer; } #endif // HAVE_CAIRO