mirror of
https://github.com/mapnik/mapnik.git
synced 2025-12-08 20:13:09 +00:00
1186 lines
46 KiB
C++
1186 lines
46 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/std.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_renderer.hpp>
|
|
#include <mapnik/cairo_context.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>
|
|
#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>
|
|
|
|
// 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)
|
|
{
|
|
typedef boost::mpl::vector<clip_poly_tag,transform_tag,affine_transform_tag,simplify_tag,smooth_tag> conv_types;
|
|
typedef vertex_converter<box2d<double>, cairo_context, polygon_symbolizer,
|
|
CoordTransform, proj_transform, agg::trans_affine,
|
|
conv_types, feature_impl> vertex_converter_type;
|
|
|
|
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)
|
|
{
|
|
typedef coord_transform<CoordTransform,geometry_type> path_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)
|
|
{
|
|
typedef boost::mpl::vector<clip_line_tag, transform_tag,
|
|
affine_transform_tag,
|
|
simplify_tag, smooth_tag,
|
|
offset_transform_tag,
|
|
dash_tag, stroke_tag> conv_types;
|
|
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_, true);
|
|
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);
|
|
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, mapnik::svg_storage_type & vmarker,
|
|
agg::pod_bvector<svg::path_attributes> const & attributes,
|
|
agg::trans_affine const& tr, double opacity, bool recenter)
|
|
{
|
|
using namespace mapnik::svg;
|
|
box2d<double> bbox = vmarker.bounding_box();
|
|
|
|
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);
|
|
}
|
|
|
|
vertex_stl_adapter<svg_path_storage> 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(common_.scale_factor_);
|
|
agg::pod_bvector<svg::path_attributes> 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(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_,
|
|
[&](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);
|
|
double opacity = get<double>(sym,keys::opacity,feature, common_.vars_, 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_, common_.font_manager_, common_.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<geometry_type> clipped_geometry_type;
|
|
typedef coord_transform<CoordTransform,clipped_geometry_type> path_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);
|
|
}
|
|
else
|
|
{
|
|
marker.reset(std::make_shared<mapnik::marker>());
|
|
}
|
|
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 * common_.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(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)
|
|
{
|
|
//typedef agg::conv_clip_polygon<geometry_type> clipped_geometry_type;
|
|
//typedef coord_transform<CoordTransform,clipped_geometry_type> path_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_, true);
|
|
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);
|
|
|
|
context_.set_operator(comp_op);
|
|
|
|
boost::optional<mapnik::marker_ptr> 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;
|
|
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_); }
|
|
|
|
typedef boost::mpl::vector<clip_poly_tag,transform_tag,affine_transform_tag,simplify_tag,smooth_tag> conv_types;
|
|
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 Context, typename SvgPath, typename Attributes, typename Detector>
|
|
struct markers_dispatch
|
|
{
|
|
markers_dispatch(Context & ctx,
|
|
SvgPath & marker,
|
|
Attributes const& attributes,
|
|
Detector & detector,
|
|
markers_symbolizer const& sym,
|
|
box2d<double> const& bbox,
|
|
agg::trans_affine const& marker_trans,
|
|
feature_impl const& feature,
|
|
mapnik::attributes const& vars,
|
|
double scale_factor)
|
|
:ctx_(ctx),
|
|
marker_(marker),
|
|
attributes_(attributes),
|
|
detector_(detector),
|
|
sym_(sym),
|
|
bbox_(bbox),
|
|
marker_trans_(marker_trans),
|
|
feature_(feature),
|
|
vars_(vars),
|
|
scale_factor_(scale_factor) {}
|
|
|
|
|
|
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);
|
|
|
|
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<double> 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<T, label_collision_detector4> 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<double> const& bbox_;
|
|
agg::trans_affine const& marker_trans_;
|
|
feature_impl const& feature_;
|
|
attributes const& vars_;
|
|
double scale_factor_;
|
|
};
|
|
|
|
template <typename Context, typename ImageMarker, typename Detector>
|
|
struct markers_dispatch_2
|
|
{
|
|
markers_dispatch_2(Context & ctx,
|
|
ImageMarker & marker,
|
|
Detector & detector,
|
|
markers_symbolizer const& sym,
|
|
box2d<double> const& bbox,
|
|
agg::trans_affine const& marker_trans,
|
|
feature_impl const& feature,
|
|
mapnik::attributes const& vars,
|
|
double scale_factor)
|
|
:ctx_(ctx),
|
|
marker_(marker),
|
|
detector_(detector),
|
|
sym_(sym),
|
|
bbox_(bbox),
|
|
marker_trans_(marker_trans),
|
|
feature_(feature),
|
|
vars_(vars),
|
|
scale_factor_(scale_factor) {}
|
|
|
|
|
|
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);
|
|
|
|
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<double> 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<T, label_collision_detector4> 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<double> const& bbox_;
|
|
agg::trans_affine const& marker_trans_;
|
|
feature_impl const& feature_;
|
|
attributes const& vars_;
|
|
double scale_factor_;
|
|
};
|
|
|
|
}
|
|
void cairo_renderer_base::process(markers_symbolizer const& sym,
|
|
mapnik::feature_impl & feature,
|
|
proj_transform const& prj_trans)
|
|
{
|
|
typedef agg::pod_bvector<svg::path_attributes> svg_attribute_type;
|
|
typedef detail::markers_dispatch_2<cairo_context, mapnik::image_data_32,
|
|
label_collision_detector4> raster_dispatch_type;
|
|
typedef detail::markers_dispatch<cairo_context, mapnik::svg_storage_type, svg_attribute_type,
|
|
label_collision_detector4> vector_dispatch_type;
|
|
|
|
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_;
|
|
|
|
render_markers_symbolizer(
|
|
sym, feature, prj_trans, common_, clip_box,
|
|
[&](svg::svg_path_adapter &, svg_attribute_type const &attr, svg_storage_type &marker,
|
|
box2d<double> const &bbox, agg::trans_affine const &marker_trans,
|
|
bool) -> vector_dispatch_type {
|
|
return vector_dispatch_type(context_, marker, attr, *common_.detector_, sym, bbox,
|
|
marker_trans, feature, common_.vars_, common_.scale_factor_);
|
|
},
|
|
[&](image_data_32 &marker, agg::trans_affine const &marker_trans,
|
|
box2d<double> const &bbox) -> raster_dispatch_type {
|
|
return raster_dispatch_type(context_, marker, *common_.detector_, sym, bbox,
|
|
marker_trans, feature, common_.vars_, common_.scale_factor_);
|
|
});
|
|
}
|
|
|
|
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);
|
|
context_.set_operator(comp_op);
|
|
|
|
placements_list const &placements = helper.get();
|
|
for (glyph_positions_ptr glyphs : placements)
|
|
{
|
|
context_.add_text(glyphs, face_manager_, common_.font_manager_, 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<>
|
|
{
|
|
typedef cairo_renderer_base renderer_type;
|
|
|
|
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_, 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)
|
|
{
|
|
typedef label_collision_detector4 detector_type;
|
|
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
|