mirror of
https://github.com/mapnik/mapnik.git
synced 2025-12-08 20:13:09 +00:00
1144 lines
45 KiB
C++
1144 lines
45 KiB
C++
/*****************************************************************************
|
|
*
|
|
* 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 <mapnik/make_unique.hpp>
|
|
#include <mapnik/rule.hpp>
|
|
#include <mapnik/debug.hpp>
|
|
#include <mapnik/layer.hpp>
|
|
#include <mapnik/raster.hpp>
|
|
#include <mapnik/feature.hpp>
|
|
#include <mapnik/proj_transform.hpp>
|
|
#include <mapnik/feature_type_style.hpp>
|
|
#include <mapnik/cairo/cairo_renderer.hpp>
|
|
#include <mapnik/image_util.hpp>
|
|
#include <mapnik/unicode.hpp>
|
|
#include <mapnik/markers_placement.hpp>
|
|
#include <mapnik/parse_path.hpp>
|
|
#include <mapnik/marker.hpp>
|
|
#include <mapnik/marker_cache.hpp>
|
|
#include <mapnik/font_set.hpp>
|
|
#include <mapnik/parse_path.hpp>
|
|
#include <mapnik/map.hpp>
|
|
#include <mapnik/svg/svg_path_adapter.hpp>
|
|
#include <mapnik/svg/svg_path_attributes.hpp>
|
|
#include <mapnik/segment.hpp>
|
|
#include <mapnik/text/symbolizer_helpers.hpp>
|
|
#include <mapnik/raster_colorizer.hpp>
|
|
#include <mapnik/expression_evaluator.hpp>
|
|
#include <mapnik/warp.hpp>
|
|
#include <mapnik/config.hpp>
|
|
#include <mapnik/vertex_converters.hpp>
|
|
#include <mapnik/marker_helpers.hpp> // todo - only need some of this expensive header
|
|
#include <mapnik/noncopyable.hpp>
|
|
#include <mapnik/pixel_position.hpp>
|
|
#include <mapnik/feature_factory.hpp>
|
|
#include <mapnik/attribute_collector.hpp>
|
|
#include <mapnik/group/group_layout_manager.hpp>
|
|
#include <mapnik/group/group_symbolizer_helper.hpp>
|
|
#include <mapnik/attribute.hpp>
|
|
#include <mapnik/agg_rasterizer.hpp>
|
|
#include <mapnik/agg_renderer.hpp>
|
|
|
|
// mapnik symbolizer generics
|
|
#include <mapnik/renderer_common/process_building_symbolizer.hpp>
|
|
#include <mapnik/renderer_common/process_point_symbolizer.hpp>
|
|
#include <mapnik/renderer_common/process_raster_symbolizer.hpp>
|
|
#include <mapnik/renderer_common/process_markers_symbolizer.hpp>
|
|
#include <mapnik/renderer_common/process_polygon_symbolizer.hpp>
|
|
#include <mapnik/renderer_common/process_group_symbolizer.hpp>
|
|
// cairo
|
|
#include <cairo.h>
|
|
#include <cairo-ft.h>
|
|
#include <cairo-version.h>
|
|
|
|
// boost
|
|
#include <boost/math/special_functions/round.hpp>
|
|
|
|
// 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 <deque>
|
|
#include <cmath>
|
|
|
|
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_renderer_base::cairo_renderer_base(Map const& m,
|
|
cairo_ptr const& cairo,
|
|
attributes const& vars,
|
|
double scale_factor,
|
|
unsigned offset_x,
|
|
unsigned offset_y)
|
|
: m_(m),
|
|
context_(cairo),
|
|
common_(m, vars, offset_x, offset_y, m.width(), m.height(), scale_factor),
|
|
face_manager_(common_.shared_font_engine_)
|
|
{
|
|
setup(m);
|
|
}
|
|
|
|
cairo_renderer_base::cairo_renderer_base(Map const& m,
|
|
request const& req,
|
|
cairo_ptr const& cairo,
|
|
attributes const& vars,
|
|
double scale_factor,
|
|
unsigned offset_x,
|
|
unsigned offset_y)
|
|
: m_(m),
|
|
context_(cairo),
|
|
common_(req, vars, offset_x, offset_y, req.width(), req.height(), scale_factor),
|
|
face_manager_(common_.shared_font_engine_)
|
|
{
|
|
setup(m);
|
|
}
|
|
|
|
cairo_renderer_base::cairo_renderer_base(Map const& m,
|
|
cairo_ptr const& cairo,
|
|
attributes const& vars,
|
|
std::shared_ptr<label_collision_detector4> detector,
|
|
double scale_factor,
|
|
unsigned offset_x,
|
|
unsigned offset_y)
|
|
: m_(m),
|
|
context_(cairo),
|
|
common_(m, vars, offset_x, offset_y, m.width(), m.height(), scale_factor, detector),
|
|
face_manager_(common_.shared_font_engine_)
|
|
{
|
|
MAPNIK_LOG_DEBUG(cairo_renderer) << "cairo_renderer_base: Scale=" << m.scale();
|
|
}
|
|
|
|
template <>
|
|
cairo_renderer<cairo_ptr>::cairo_renderer(Map const& m, cairo_ptr const& cairo, double scale_factor, unsigned offset_x, unsigned offset_y)
|
|
: feature_style_processor<cairo_renderer>(m,scale_factor),
|
|
cairo_renderer_base(m,cairo,attributes(),scale_factor,offset_x,offset_y) {}
|
|
|
|
template <>
|
|
cairo_renderer<cairo_surface_ptr>::cairo_renderer(Map const& m, cairo_surface_ptr const& surface, double scale_factor, unsigned offset_x, unsigned offset_y)
|
|
: feature_style_processor<cairo_renderer>(m,scale_factor),
|
|
cairo_renderer_base(m,create_context(surface),attributes(),scale_factor,offset_x,offset_y) {}
|
|
|
|
template <>
|
|
cairo_renderer<cairo_ptr>::cairo_renderer(Map const& m, request const& req, attributes const& vars, cairo_ptr const& cairo, double scale_factor, unsigned offset_x, unsigned offset_y)
|
|
: feature_style_processor<cairo_renderer>(m,scale_factor),
|
|
cairo_renderer_base(m,req,cairo,vars,scale_factor,offset_x,offset_y) {}
|
|
|
|
template <>
|
|
cairo_renderer<cairo_surface_ptr>::cairo_renderer(Map const& m, request const& req, attributes const& vars, cairo_surface_ptr const& surface, double scale_factor, unsigned offset_x, unsigned offset_y)
|
|
: feature_style_processor<cairo_renderer>(m,scale_factor),
|
|
cairo_renderer_base(m,req,create_context(surface),attributes(),scale_factor,offset_x,offset_y) {}
|
|
|
|
template <>
|
|
cairo_renderer<cairo_ptr>::cairo_renderer(Map const& m, cairo_ptr const& cairo, std::shared_ptr<label_collision_detector4> detector, double scale_factor, unsigned offset_x, unsigned offset_y)
|
|
: feature_style_processor<cairo_renderer>(m,scale_factor),
|
|
cairo_renderer_base(m,cairo,attributes(),detector,scale_factor,offset_x,offset_y) {}
|
|
|
|
template <>
|
|
cairo_renderer<cairo_surface_ptr>::cairo_renderer(Map const& m, cairo_surface_ptr const& surface, std::shared_ptr<label_collision_detector4> detector, double scale_factor, unsigned offset_x, unsigned offset_y)
|
|
: feature_style_processor<cairo_renderer>(m,scale_factor),
|
|
cairo_renderer_base(m,create_context(surface),attributes(),detector,scale_factor,offset_x,offset_y) {}
|
|
|
|
cairo_renderer_base::~cairo_renderer_base() {}
|
|
|
|
void cairo_renderer_base::setup(Map const& map)
|
|
{
|
|
boost::optional<color> bg = m_.background();
|
|
if (bg)
|
|
{
|
|
cairo_save_restore guard(context_);
|
|
context_.set_color(*bg);
|
|
context_.paint();
|
|
}
|
|
boost::optional<std::string> const& image_filename = map.background_image();
|
|
if (image_filename)
|
|
{
|
|
// NOTE: marker_cache returns premultiplied image, if needed
|
|
boost::optional<mapnik::marker_ptr> 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(common_.width_/double(w)));
|
|
unsigned y_steps = unsigned(std::ceil(common_.height_/double(h)));
|
|
for (unsigned x=0;x<x_steps;++x)
|
|
{
|
|
for (unsigned y=0;y<y_steps;++y)
|
|
{
|
|
agg::trans_affine matrix = agg::trans_affine_translation(
|
|
x*w,
|
|
y*h);
|
|
context_.add_image(matrix, *bg_image, 1.0f);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
MAPNIK_LOG_DEBUG(cairo_renderer) << "cairo_renderer_base: Scale=" << map.scale();
|
|
}
|
|
|
|
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();
|
|
box2d<double> bounds = common_.t_.forward(common_.t_.extent());
|
|
context_.rectangle(bounds.minx(), bounds.miny(), bounds.maxx(), bounds.maxy());
|
|
context_.clip();
|
|
}
|
|
|
|
template <>
|
|
void cairo_renderer<cairo_ptr>::end_map_processing(Map const& )
|
|
{
|
|
MAPNIK_LOG_DEBUG(cairo_renderer) << "cairo_renderer_base: End map processing";
|
|
}
|
|
|
|
template <>
|
|
void cairo_renderer<cairo_surface_ptr>::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<double> 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())
|
|
{
|
|
common_.detector_->clear();
|
|
}
|
|
common_.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)
|
|
{
|
|
using conv_types = boost::mpl::vector<clip_poly_tag,transform_tag,affine_transform_tag,simplify_tag,smooth_tag>;
|
|
using vertex_converter_type = vertex_converter<box2d<double>, cairo_context, polygon_symbolizer,
|
|
CoordTransform, proj_transform, agg::trans_affine,
|
|
conv_types, feature_impl>;
|
|
|
|
cairo_save_restore guard(context_);
|
|
composite_mode_e comp_op = get<composite_mode_e>(sym, keys::comp_op, feature, common_.vars_, src_over);
|
|
context_.set_operator(comp_op);
|
|
|
|
render_polygon_symbolizer<vertex_converter_type>(
|
|
sym, feature, prj_trans, common_, common_.query_extent_, context_,
|
|
[&](color const &fill, double opacity) {
|
|
context_.set_color(fill, opacity);
|
|
// 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)
|
|
{
|
|
using path_type = coord_transform<CoordTransform,geometry_type>;
|
|
cairo_save_restore guard(context_);
|
|
composite_mode_e comp_op = get<composite_mode_e>(sym, keys::comp_op, feature, common_.vars_, src_over);
|
|
mapnik::color fill = get<mapnik::color>(sym, keys::fill, feature, common_.vars_, mapnik::color(128,128,128));
|
|
double opacity = get<double>(sym, keys::fill_opacity, feature, common_.vars_, 1.0);
|
|
double height = get<double>(sym, keys::height, feature, common_.vars_, 0.0);
|
|
|
|
context_.set_operator(comp_op);
|
|
|
|
render_building_symbolizer(
|
|
feature, height,
|
|
[&](geometry_type &faces) {
|
|
path_type faces_path(common_.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();
|
|
},
|
|
[&](geometry_type &frame) {
|
|
path_type path(common_.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(common_.scale_factor_);
|
|
context_.add_path(path);
|
|
context_.stroke();
|
|
},
|
|
[&](geometry_type &roof) {
|
|
path_type roof_path(common_.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)
|
|
{
|
|
using conv_types = boost::mpl::vector<clip_line_tag, transform_tag,
|
|
affine_transform_tag,
|
|
simplify_tag, smooth_tag,
|
|
offset_transform_tag,
|
|
dash_tag, stroke_tag>;
|
|
cairo_save_restore guard(context_);
|
|
composite_mode_e comp_op = get<composite_mode_e>(sym, keys::comp_op, feature, common_.vars_, src_over);
|
|
bool clip = get<bool>(sym, keys::clip, feature, common_.vars_, false);
|
|
double offset = get<double>(sym, keys::offset, feature, common_.vars_, 0.0);
|
|
double simplify_tolerance = get<double>(sym, keys::simplify_tolerance, feature, common_.vars_, 0.0);
|
|
double smooth = get<double>(sym, keys::smooth, feature, common_.vars_, 0.0);
|
|
|
|
mapnik::color stroke = get<mapnik::color>(sym, keys::stroke, feature, common_.vars_, mapnik::color(0,0,0));
|
|
double stroke_opacity = get<double>(sym, keys::stroke_opacity, feature, common_.vars_, 1.0);
|
|
line_join_enum stroke_join = get<line_join_enum>(sym, keys::stroke_linejoin, feature, common_.vars_, MITER_JOIN);
|
|
line_cap_enum stroke_cap = get<line_cap_enum>(sym, keys::stroke_linecap, feature, common_.vars_, BUTT_CAP);
|
|
auto dash = get_optional<dash_array>(sym, keys::stroke_dasharray, feature, common_.vars_);
|
|
double miterlimit = get<double>(sym, keys::stroke_miterlimit, feature, common_.vars_, 4.0);
|
|
double width = get<double>(sym, keys::stroke_width, feature, common_.vars_, 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 * common_.scale_factor_);
|
|
if (dash)
|
|
{
|
|
context_.set_dash(*dash, common_.scale_factor_);
|
|
}
|
|
|
|
agg::trans_affine tr;
|
|
auto geom_transform = get_optional<transform_type>(sym, keys::geometry_transform);
|
|
if (geom_transform) { evaluate_transform(tr, feature, common_.vars_, *geom_transform, common_.scale_factor_); }
|
|
|
|
box2d<double> clipping_extent = common_.query_extent_;
|
|
if (clip)
|
|
{
|
|
double padding = (double)(common_.query_extent_.width()/common_.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 *= common_.scale_factor_;
|
|
clipping_extent.pad(padding);
|
|
}
|
|
vertex_converter<box2d<double>, cairo_context, line_symbolizer,
|
|
CoordTransform, proj_transform, agg::trans_affine, conv_types, feature_impl>
|
|
converter(clipping_extent,context_,sym,common_.t_,prj_trans,tr,feature,common_.vars_,common_.scale_factor_);
|
|
|
|
if (clip) converter.set<clip_line_tag>(); // optional clip (default: true)
|
|
converter.set<transform_tag>(); // always transform
|
|
if (std::fabs(offset) > 0.0) converter.set<offset_transform_tag>(); // parallel offset
|
|
converter.set<affine_transform_tag>(); // optional affine transform
|
|
if (simplify_tolerance > 0.0) converter.set<simplify_tag>(); // optional simplify converter
|
|
if (smooth > 0.0) converter.set<smooth_tag>(); // 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<double> 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,
|
|
svg::svg_path_adapter & svg_path, box2d<double> const& bbox,
|
|
agg::pod_bvector<svg::path_attributes> const & attributes,
|
|
agg::trans_affine const& tr, double opacity, bool recenter)
|
|
{
|
|
using namespace mapnik::svg;
|
|
agg::trans_affine mtx = tr;
|
|
if (recenter)
|
|
{
|
|
coord<double,2> 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);
|
|
}
|
|
|
|
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(common_.scale_factor_);
|
|
box2d<double> bbox = vmarker->bounding_box();
|
|
agg::pod_bvector<svg::path_attributes> const & attributes = vmarker->attributes();
|
|
svg::vertex_stl_adapter<svg::svg_path_storage> stl_storage(vmarker->source());
|
|
svg::svg_path_adapter svg_path(stl_storage);
|
|
render_vector_marker(context_, pos, svg_path, bbox, 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(common_.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)
|
|
{
|
|
composite_mode_e comp_op = get<composite_mode_e>(sym, keys::comp_op, feature, common_.vars_, src_over);
|
|
|
|
cairo_save_restore guard(context_);
|
|
context_.set_operator(comp_op);
|
|
|
|
render_point_symbolizer(
|
|
sym, feature, prj_trans, common_,
|
|
[this](pixel_position const& pos, marker const& marker,
|
|
agg::trans_affine const& tr, double opacity) {
|
|
render_marker(pos, marker, tr, opacity);
|
|
});
|
|
}
|
|
|
|
void cairo_renderer_base::process(shield_symbolizer const& sym,
|
|
mapnik::feature_impl & feature,
|
|
proj_transform const& prj_trans)
|
|
{
|
|
text_symbolizer_helper helper(
|
|
sym, feature, common_.vars_, prj_trans,
|
|
common_.width_, common_.height_,
|
|
common_.scale_factor_,
|
|
common_.t_, common_.font_manager_, *common_.detector_,
|
|
common_.query_extent_);
|
|
|
|
cairo_save_restore guard(context_);
|
|
composite_mode_e comp_op = get<composite_mode_e>(sym, keys::comp_op, feature, common_.vars_, src_over);
|
|
composite_mode_e halo_comp_op = get<composite_mode_e>(sym, keys::halo_comp_op, feature, common_.vars_, src_over);
|
|
double opacity = get<double>(sym,keys::opacity,feature, common_.vars_, 1.0);
|
|
|
|
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_, common_.font_manager_, comp_op, halo_comp_op, common_.scale_factor_);
|
|
}
|
|
}
|
|
|
|
void cairo_renderer_base::process(line_pattern_symbolizer const& sym,
|
|
mapnik::feature_impl & feature,
|
|
proj_transform const& prj_trans)
|
|
{
|
|
using clipped_geometry_type = agg::conv_clip_polyline<geometry_type>;
|
|
using path_type = coord_transform<CoordTransform,clipped_geometry_type>;
|
|
|
|
std::string filename = get<std::string>(sym, keys::file, feature, common_.vars_);
|
|
composite_mode_e comp_op = get<composite_mode_e>(sym, keys::comp_op, feature, common_.vars_, src_over);
|
|
|
|
boost::optional<marker_ptr> marker;
|
|
if ( !filename.empty() )
|
|
{
|
|
marker = marker_cache::instance().find(filename, true);
|
|
}
|
|
if (!marker || !(*marker)) return;
|
|
|
|
unsigned width = (*marker)->width();
|
|
unsigned height = (*marker)->height();
|
|
|
|
cairo_save_restore guard(context_);
|
|
context_.set_operator(comp_op);
|
|
|
|
std::shared_ptr<cairo_pattern> pattern;
|
|
image_ptr image = nullptr;
|
|
if ((*marker)->is_bitmap())
|
|
{
|
|
pattern = std::make_unique<cairo_pattern>(**((*marker)->get_bitmap_data()));
|
|
context_.set_line_width(height* common_.scale_factor_); // FIXME: do we need to scale height here?
|
|
}
|
|
else
|
|
{
|
|
mapnik::rasterizer ras;
|
|
agg::trans_affine image_tr = agg::trans_affine_scaling(common_.scale_factor_);
|
|
auto image_transform = get_optional<transform_type>(sym, keys::image_transform);
|
|
if (image_transform) evaluate_transform(image_tr, feature, common_.vars_, *image_transform);
|
|
image = render_pattern(ras, **marker, image_tr);
|
|
pattern = std::make_unique<cairo_pattern>(*image);
|
|
width = image->width();
|
|
height = image->height();
|
|
context_.set_line_width(height);
|
|
}
|
|
|
|
pattern->set_extend(CAIRO_EXTEND_REPEAT);
|
|
pattern->set_filter(CAIRO_FILTER_BILINEAR);
|
|
|
|
|
|
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(common_.query_extent_.minx(),common_.query_extent_.miny(),common_.query_extent_.maxx(),common_.query_extent_.maxy());
|
|
path_type path(common_.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)
|
|
{
|
|
//using clipped_geometry_type = agg::conv_clip_polygon<geometry_type>;
|
|
//using path_type = coord_transform<CoordTransform,clipped_geometry_type>;
|
|
|
|
cairo_save_restore guard(context_);
|
|
composite_mode_e comp_op = get<composite_mode_e>(sym, keys::comp_op, feature, common_.vars_, src_over);
|
|
std::string filename = get<std::string>(sym, keys::file, feature, common_.vars_);
|
|
bool clip = get<bool>(sym, keys::clip, feature, common_.vars_, false);
|
|
double simplify_tolerance = get<double>(sym, keys::simplify_tolerance, feature, common_.vars_, 0.0);
|
|
double smooth = get<double>(sym, keys::smooth, feature, common_.vars_, 0.0);
|
|
|
|
agg::trans_affine image_tr = agg::trans_affine_scaling(common_.scale_factor_);
|
|
auto image_transform = get_optional<transform_type>(sym, keys::image_transform);
|
|
if (image_transform) evaluate_transform(image_tr, feature, common_.vars_, *image_transform);
|
|
|
|
context_.set_operator(comp_op);
|
|
|
|
boost::optional<mapnik::marker_ptr> marker = mapnik::marker_cache::instance().find(filename,true);
|
|
if (!marker || !(*marker)) return;
|
|
|
|
if ((*marker)->is_bitmap())
|
|
{
|
|
cairo_pattern pattern(**((*marker)->get_bitmap_data()));
|
|
pattern.set_extend(CAIRO_EXTEND_REPEAT);
|
|
context_.set_pattern(pattern);
|
|
}
|
|
else
|
|
{
|
|
mapnik::rasterizer ras;
|
|
image_ptr image = render_pattern(ras, **marker, image_tr);
|
|
cairo_pattern pattern(*image);
|
|
pattern.set_extend(CAIRO_EXTEND_REPEAT);
|
|
context_.set_pattern(pattern);
|
|
}
|
|
|
|
|
|
pattern_alignment_enum alignment = get<pattern_alignment_enum>(sym, keys::alignment, feature, common_.vars_, GLOBAL_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;
|
|
auto geom_transform = get_optional<transform_type>(sym, keys::geometry_transform);
|
|
if (geom_transform) { evaluate_transform(tr, feature, common_.vars_, *geom_transform, common_.scale_factor_); }
|
|
|
|
using conv_types = boost::mpl::vector<clip_poly_tag,transform_tag,affine_transform_tag,simplify_tag,smooth_tag>;
|
|
vertex_converter<box2d<double>, cairo_context, polygon_pattern_symbolizer,
|
|
CoordTransform, proj_transform, agg::trans_affine, conv_types, feature_impl>
|
|
converter(common_.query_extent_,context_,sym,common_.t_,prj_trans,tr,feature,common_.vars_,common_.scale_factor_);
|
|
|
|
if (prj_trans.equal() && clip) converter.set<clip_poly_tag>(); //optional clip (default: true)
|
|
converter.set<transform_tag>(); //always transform
|
|
converter.set<affine_transform_tag>();
|
|
if (simplify_tolerance > 0.0) converter.set<simplify_tag>(); // optional simplify converter
|
|
if (smooth > 0.0) converter.set<smooth_tag>(); // 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)
|
|
{
|
|
cairo_save_restore guard(context_);
|
|
render_raster_symbolizer(
|
|
sym, feature, prj_trans, common_,
|
|
[&](image_data_32 &target, composite_mode_e comp_op, double opacity,
|
|
int start_x, int start_y) {
|
|
context_.set_operator(comp_op);
|
|
context_.add_image(start_x, start_y, target, opacity);
|
|
}
|
|
);
|
|
}
|
|
|
|
namespace detail {
|
|
|
|
template <typename RendererContext, typename SvgPath, typename Attributes, typename Detector>
|
|
struct markers_dispatch : mapnik::noncopyable
|
|
{
|
|
markers_dispatch(SvgPath & marker,
|
|
Attributes const& attributes,
|
|
box2d<double> const& bbox,
|
|
agg::trans_affine const& marker_trans,
|
|
markers_symbolizer const& sym,
|
|
Detector & detector,
|
|
double scale_factor,
|
|
feature_impl const& feature,
|
|
mapnik::attributes const& vars,
|
|
bool snap_to_pixels,
|
|
RendererContext const& renderer_context)
|
|
:marker_(marker),
|
|
attributes_(attributes),
|
|
bbox_(bbox),
|
|
marker_trans_(marker_trans),
|
|
sym_(sym),
|
|
detector_(detector),
|
|
scale_factor_(scale_factor),
|
|
feature_(feature),
|
|
vars_(vars),
|
|
ctx_(std::get<0>(renderer_context))
|
|
{}
|
|
|
|
|
|
template <typename T>
|
|
void add_path(T & path)
|
|
{
|
|
marker_placement_enum placement_method = get<marker_placement_enum>(
|
|
sym_, keys::markers_placement_type, feature_, vars_, MARKER_POINT_PLACEMENT);
|
|
bool ignore_placement = get<bool>(sym_, keys::ignore_placement, feature_, vars_, false);
|
|
bool allow_overlap = get<bool>(sym_, keys::allow_overlap, feature_, vars_, false);
|
|
double opacity = get<double>(sym_, keys::opacity, feature_, vars_, 1.0);
|
|
double spacing = get<double>(sym_, keys::spacing, feature_, vars_, 100.0);
|
|
double max_error = get<double>(sym_, keys::max_error, feature_, vars_, 0.2);
|
|
coord2d center = bbox_.center();
|
|
agg::trans_affine_translation recenter(-center.x, -center.y);
|
|
agg::trans_affine tr = recenter * marker_trans_;
|
|
markers_placement_finder<T, label_collision_detector4> placement_finder(
|
|
placement_method,
|
|
path,
|
|
bbox_,
|
|
tr,
|
|
detector_,
|
|
spacing * scale_factor_,
|
|
max_error,
|
|
allow_overlap);
|
|
double x, y, angle = .0;
|
|
while (placement_finder.get_point(x, y, angle, ignore_placement))
|
|
{
|
|
agg::trans_affine matrix = tr;
|
|
matrix.rotate(angle);
|
|
matrix.translate(x, y);
|
|
render_vector_marker(
|
|
ctx_,
|
|
pixel_position(x, y),
|
|
marker_,
|
|
bbox_,
|
|
attributes_,
|
|
matrix,
|
|
opacity,
|
|
false);
|
|
}
|
|
}
|
|
|
|
SvgPath & marker_;
|
|
Attributes const& attributes_;
|
|
box2d<double> const& bbox_;
|
|
agg::trans_affine const& marker_trans_;
|
|
markers_symbolizer const& sym_;
|
|
Detector & detector_;
|
|
double scale_factor_;
|
|
feature_impl const& feature_;
|
|
attributes const& vars_;
|
|
cairo_context & ctx_;
|
|
};
|
|
|
|
template <typename RendererContext, typename Detector>
|
|
struct raster_markers_dispatch : mapnik::noncopyable
|
|
{
|
|
raster_markers_dispatch(mapnik::image_data_32 & src,
|
|
agg::trans_affine const& marker_trans,
|
|
markers_symbolizer const& sym,
|
|
Detector & detector,
|
|
double scale_factor,
|
|
feature_impl const& feature,
|
|
mapnik::attributes const& vars,
|
|
RendererContext const& renderer_context)
|
|
: src_(src),
|
|
detector_(detector),
|
|
sym_(sym),
|
|
marker_trans_(marker_trans),
|
|
scale_factor_(scale_factor),
|
|
feature_(feature),
|
|
vars_(vars),
|
|
ctx_(std::get<0>(renderer_context)) {}
|
|
|
|
template <typename T>
|
|
void add_path(T & path)
|
|
{
|
|
marker_placement_enum placement_method = get<marker_placement_enum>(sym_, keys::markers_placement_type, feature_, vars_, MARKER_POINT_PLACEMENT);
|
|
double opacity = get<double>(sym_, keys::opacity, feature_, vars_, 1.0);
|
|
double spacing = get<double>(sym_, keys::spacing, feature_, vars_, 100.0);
|
|
double max_error = get<double>(sym_, keys::max_error, feature_, vars_, 0.2);
|
|
bool allow_overlap = get<bool>(sym_, keys::allow_overlap, feature_, vars_, false);
|
|
bool ignore_placement = get<bool>(sym_, keys::ignore_placement, feature_, vars_, false);
|
|
box2d<double> bbox_(0,0, src_.width(),src_.height());
|
|
markers_placement_finder<T, label_collision_detector4> placement_finder(
|
|
placement_method,
|
|
path,
|
|
bbox_,
|
|
marker_trans_,
|
|
detector_,
|
|
spacing * scale_factor_,
|
|
max_error,
|
|
allow_overlap);
|
|
double x, y, angle = .0;
|
|
while (placement_finder.get_point(x, y, angle, ignore_placement))
|
|
{
|
|
agg::trans_affine matrix = marker_trans_;
|
|
matrix.rotate(angle);
|
|
matrix.translate(x, y);
|
|
ctx_.add_image(matrix, src_, opacity);
|
|
}
|
|
}
|
|
|
|
image_data_32 & src_;
|
|
Detector & detector_;
|
|
markers_symbolizer const& sym_;
|
|
agg::trans_affine const& marker_trans_;
|
|
double scale_factor_;
|
|
feature_impl const& feature_;
|
|
attributes const& vars_;
|
|
cairo_context & ctx_;
|
|
};
|
|
|
|
}
|
|
void cairo_renderer_base::process(markers_symbolizer const& sym,
|
|
mapnik::feature_impl & feature,
|
|
proj_transform const& prj_trans)
|
|
{
|
|
using svg_attribute_type = agg::pod_bvector<svg::path_attributes>;
|
|
|
|
cairo_save_restore guard(context_);
|
|
composite_mode_e comp_op = get<composite_mode_e>(sym, keys::comp_op, feature, common_.vars_, src_over);
|
|
context_.set_operator(comp_op);
|
|
box2d<double> clip_box = common_.query_extent_;
|
|
|
|
auto renderer_context = std::tie(context_);
|
|
|
|
using RendererContextType = decltype(renderer_context);
|
|
using vector_dispatch_type = detail::markers_dispatch<RendererContextType,
|
|
svg::path_adapter<svg::vertex_stl_adapter<svg::svg_path_storage> >,
|
|
svg_attribute_type,label_collision_detector4>;
|
|
|
|
using raster_dispatch_type = detail::raster_markers_dispatch<RendererContextType,
|
|
label_collision_detector4>;
|
|
|
|
|
|
render_markers_symbolizer<vector_dispatch_type, raster_dispatch_type>(
|
|
sym, feature, prj_trans, common_, clip_box,
|
|
renderer_context);
|
|
}
|
|
|
|
void cairo_renderer_base::process(text_symbolizer const& sym,
|
|
mapnik::feature_impl & feature,
|
|
proj_transform const& prj_trans)
|
|
{
|
|
text_symbolizer_helper helper(
|
|
sym, feature, common_.vars_, prj_trans,
|
|
common_.width_, common_.height_,
|
|
common_.scale_factor_,
|
|
common_.t_, common_.font_manager_, *common_.detector_,
|
|
common_.query_extent_);
|
|
|
|
cairo_save_restore guard(context_);
|
|
composite_mode_e comp_op = get<composite_mode_e>(sym, keys::comp_op, feature, common_.vars_, src_over);
|
|
composite_mode_e halo_comp_op = get<composite_mode_e>(sym, keys::halo_comp_op, feature, common_.vars_, src_over);
|
|
|
|
placements_list const& placements = helper.get();
|
|
for (glyph_positions_ptr glyphs : placements)
|
|
{
|
|
context_.add_text(glyphs, face_manager_, common_.font_manager_, comp_op, halo_comp_op, common_.scale_factor_);
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
/**
|
|
* Render a thunk which was frozen from a previous call to
|
|
* extract_bboxes. We should now have a new offset at which
|
|
* to render it, and the boxes themselves should already be
|
|
* in the detector from the placement_finder.
|
|
*/
|
|
struct thunk_renderer : public boost::static_visitor<>
|
|
{
|
|
using renderer_type = cairo_renderer_base;
|
|
|
|
thunk_renderer(renderer_type &ren,
|
|
cairo_context &context,
|
|
cairo_face_manager &face_manager,
|
|
renderer_common &common,
|
|
pixel_position const &offset)
|
|
: ren_(ren), context_(context), face_manager_(face_manager),
|
|
common_(common), offset_(offset)
|
|
{}
|
|
|
|
void operator()(point_render_thunk const &thunk) const
|
|
{
|
|
pixel_position new_pos(thunk.pos_.x + offset_.x, thunk.pos_.y + offset_.y);
|
|
ren_.render_marker(new_pos, *thunk.marker_, thunk.tr_, thunk.opacity_,
|
|
thunk.comp_op_);
|
|
}
|
|
|
|
void operator()(text_render_thunk const &thunk) const
|
|
{
|
|
cairo_save_restore guard(context_);
|
|
context_.set_operator(thunk.comp_op_);
|
|
|
|
render_offset_placements(
|
|
thunk.placements_,
|
|
offset_,
|
|
[&] (glyph_positions_ptr glyphs)
|
|
{
|
|
if (glyphs->marker())
|
|
{
|
|
ren_.render_marker(glyphs->marker_pos(),
|
|
*(glyphs->marker()->marker),
|
|
glyphs->marker()->transform,
|
|
thunk.opacity_, thunk.comp_op_);
|
|
}
|
|
context_.add_text(glyphs, face_manager_, common_.font_manager_, src_over, src_over, common_.scale_factor_);
|
|
});
|
|
}
|
|
|
|
template <typename T>
|
|
void operator()(T const &) const
|
|
{
|
|
// TODO: warning if unimplemented?
|
|
}
|
|
|
|
private:
|
|
renderer_type &ren_;
|
|
cairo_context &context_;
|
|
cairo_face_manager &face_manager_;
|
|
renderer_common &common_;
|
|
pixel_position offset_;
|
|
};
|
|
|
|
} // anonymous namespace
|
|
|
|
void cairo_renderer_base::process(group_symbolizer const& sym,
|
|
mapnik::feature_impl & feature,
|
|
proj_transform const& prj_trans)
|
|
{
|
|
render_group_symbolizer(
|
|
sym, feature, common_.vars_, prj_trans, common_.query_extent_, common_,
|
|
[&](render_thunk_list const& thunks, pixel_position const& render_offset)
|
|
{
|
|
thunk_renderer ren(*this, context_, face_manager_, common_, render_offset);
|
|
for (render_thunk_ptr const& thunk : thunks)
|
|
{
|
|
boost::apply_visitor(ren, *thunk);
|
|
}
|
|
});
|
|
}
|
|
|
|
namespace {
|
|
|
|
// special implementation of the box drawing so that it's pixel-aligned
|
|
void render_debug_box(cairo_context &context, box2d<double> const& b)
|
|
{
|
|
cairo_save_restore guard(context);
|
|
double minx = std::floor(b.minx()) + 0.5;
|
|
double miny = std::floor(b.miny()) + 0.5;
|
|
double maxx = std::floor(b.maxx()) + 0.5;
|
|
double maxy = std::floor(b.maxy()) + 0.5;
|
|
context.move_to(minx, miny);
|
|
context.line_to(minx, maxy);
|
|
context.line_to(maxx, maxy);
|
|
context.line_to(maxx, miny);
|
|
context.close_path();
|
|
context.stroke();
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
void cairo_renderer_base::process(debug_symbolizer const& sym,
|
|
mapnik::feature_impl & feature,
|
|
proj_transform const& prj_trans)
|
|
{
|
|
using detector_type = label_collision_detector4;
|
|
cairo_save_restore guard(context_);
|
|
|
|
debug_symbolizer_mode_enum mode = get<debug_symbolizer_mode_enum>(sym, keys::mode, feature, common_.vars_, DEBUG_SYM_MODE_COLLISION);
|
|
|
|
context_.set_operator(src_over);
|
|
context_.set_color(mapnik::color(255, 0, 0), 1.0);
|
|
context_.set_line_join(MITER_JOIN);
|
|
context_.set_line_cap(BUTT_CAP);
|
|
context_.set_miter_limit(4.0);
|
|
context_.set_line_width(1.0);
|
|
|
|
if (mode == DEBUG_SYM_MODE_COLLISION)
|
|
{
|
|
typename detector_type::query_iterator itr = common_.detector_->begin();
|
|
typename detector_type::query_iterator end = common_.detector_->end();
|
|
for ( ;itr!=end; ++itr)
|
|
{
|
|
render_debug_box(context_, itr->box);
|
|
}
|
|
}
|
|
else if (mode == DEBUG_SYM_MODE_VERTEX)
|
|
{
|
|
for (auto const& geom : feature.paths())
|
|
{
|
|
double x;
|
|
double y;
|
|
double z = 0;
|
|
geom.rewind(0);
|
|
unsigned cmd = 1;
|
|
while ((cmd = geom.vertex(&x, &y)) != mapnik::SEG_END)
|
|
{
|
|
if (cmd == SEG_CLOSE) continue;
|
|
prj_trans.backward(x,y,z);
|
|
common_.t_.forward(&x,&y);
|
|
context_.move_to(std::floor(x) - 0.5, std::floor(y) + 0.5);
|
|
context_.line_to(std::floor(x) + 1.5, std::floor(y) + 0.5);
|
|
context_.move_to(std::floor(x) + 0.5, std::floor(y) - 0.5);
|
|
context_.line_to(std::floor(x) + 0.5, std::floor(y) + 1.5);
|
|
context_.stroke();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template class cairo_renderer<cairo_surface_ptr>;
|
|
template class cairo_renderer<cairo_ptr>;
|
|
}
|
|
|
|
#endif // HAVE_CAIRO
|