mapnik/src/cairo_renderer.cpp

1814 lines
61 KiB
C++
Raw Normal View History

/*****************************************************************************
*
* 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
2012-07-25 03:35:41 +02:00
#include <mapnik/layer.hpp>
#include <mapnik/feature_type_style.hpp>
2012-04-08 02:20:56 +02:00
#include <mapnik/debug.hpp>
#include <mapnik/cairo_renderer.hpp>
#include <mapnik/image_util.hpp>
#include <mapnik/unicode.hpp>
#include <mapnik/markers_placement.hpp>
2010-06-18 17:39:32 +02:00
#include <mapnik/parse_path.hpp>
#include <mapnik/marker.hpp>
#include <mapnik/marker_cache.hpp>
#include <mapnik/svg/svg_path_adapter.hpp>
#include <mapnik/svg/svg_path_attributes.hpp>
#include <mapnik/segment.hpp>
#include <mapnik/symbolizer_helpers.hpp>
2011-09-01 03:18:10 +02:00
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/warp.hpp>
#include <mapnik/config.hpp>
2012-03-04 23:25:13 +01:00
#include <mapnik/text_path.hpp>
#include <mapnik/vertex_converters.hpp>
#include <mapnik/marker_helpers.hpp>
// cairo
#include <cairomm/context.h>
#include <cairomm/surface.h>
#include <cairo-ft.h>
#include <cairo-version.h>
// boost
#include <boost/utility.hpp>
#include <boost/make_shared.hpp>
// agg
#include "agg_conv_clip_polyline.h"
#include "agg_conv_clip_polygon.h"
#include "agg_conv_smooth_poly1.h"
// markers
#include "agg_path_storage.h"
#include "agg_ellipse.h"
namespace mapnik
{
2009-12-16 21:02:06 +01:00
class cairo_pattern : private boost::noncopyable
{
public:
cairo_pattern(image_data_32 const& data)
{
2010-06-02 13:03:30 +02:00
int pixels = data.width() * data.height();
const unsigned int *in_ptr = data.getData();
const unsigned int *in_end = in_ptr + pixels;
unsigned int *out_ptr;
surface_ = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, data.width(), data.height());
out_ptr = reinterpret_cast<unsigned int *>(surface_->get_data());
2010-06-02 13:03:30 +02:00
while (in_ptr < in_end)
{
unsigned int in = *in_ptr++;
unsigned int r = (in >> 0) & 0xff;
unsigned int g = (in >> 8) & 0xff;
unsigned int b = (in >> 16) & 0xff;
unsigned int a = (in >> 24) & 0xff;
//r = r * a / 255;
//g = g * a / 255;
//b = b * a / 255;
2010-06-02 13:03:30 +02:00
*out_ptr++ = (a << 24) | (r << 16) | (g << 8) | b;
}
// mark the surface as dirty as we've modified it behind cairo's back
surface_->mark_dirty();
2010-06-02 13:03:30 +02:00
pattern_ = Cairo::SurfacePattern::create(surface_);
2009-12-16 21:02:06 +01:00
}
~cairo_pattern()
2009-12-16 21:02:06 +01:00
{
}
2012-02-02 02:53:35 +01:00
2009-12-16 21:02:06 +01:00
void set_matrix(Cairo::Matrix const& matrix)
{
2010-06-02 13:03:30 +02:00
pattern_->set_matrix(matrix);
2009-12-16 21:02:06 +01:00
}
void set_origin(double x, double y)
{
2010-06-02 13:03:30 +02:00
Cairo::Matrix matrix;
2012-02-02 02:53:35 +01:00
2010-06-02 13:03:30 +02:00
pattern_->get_matrix(matrix);
2012-02-02 02:53:35 +01:00
2010-06-02 13:03:30 +02:00
matrix.x0 = -x;
matrix.y0 = -y;
2012-02-02 02:53:35 +01:00
2010-06-02 13:03:30 +02:00
pattern_->set_matrix(matrix);
2009-12-16 21:02:06 +01:00
}
2012-02-02 02:53:35 +01:00
2009-12-16 21:02:06 +01:00
void set_extend(Cairo::Extend extend)
{
2010-06-02 13:03:30 +02:00
pattern_->set_extend(extend);
2009-12-16 21:02:06 +01:00
}
2009-12-16 21:02:06 +01:00
void set_filter(Cairo::Filter filter)
{
2010-06-02 13:03:30 +02:00
pattern_->set_filter(filter);
2009-12-16 21:02:06 +01:00
}
Cairo::RefPtr<Cairo::SurfacePattern> const& pattern() const
2009-12-16 21:02:06 +01:00
{
2010-06-02 13:03:30 +02:00
return pattern_;
2009-12-16 21:02:06 +01:00
}
2009-12-16 21:02:06 +01:00
private:
Cairo::RefPtr<Cairo::ImageSurface> surface_;
Cairo::RefPtr<Cairo::SurfacePattern> pattern_;
};
class cairo_gradient : private boost::noncopyable
{
public:
cairo_gradient(const mapnik::gradient &grad, double opacity=1.0)
{
double x1,x2,y1,y2,r;
grad.get_control_points(x1,y1,x2,y2,r);
if (grad.get_gradient_type() == LINEAR)
{
pattern_ = Cairo::LinearGradient::create(x1, y1, x2, y2);
}
else if (grad.get_gradient_type() == RADIAL)
{
pattern_ = Cairo::RadialGradient::create(x1, y1, 0, x2, y2, r);
}
units_ = grad.get_units();
BOOST_FOREACH ( mapnik::stop_pair const& st, grad.get_stop_array() )
{
mapnik::color const& stop_color = st.second;
double r= static_cast<double> (stop_color.red())/255.0;
double g= static_cast<double> (stop_color.green())/255.0;
double b= static_cast<double> (stop_color.blue())/255.0;
double a= static_cast<double> (stop_color.alpha())/255.0;
pattern_->add_color_stop_rgba(st.first, r, g, b, a*opacity);
}
double m[6];
agg::trans_affine tr = grad.get_transform();
tr.invert();
tr.store_to(m);
pattern_->set_matrix(Cairo::Matrix(m[0],m[1],m[2],m[3],m[4],m[5]));
}
~cairo_gradient()
{
}
Cairo::RefPtr<Cairo::Gradient> const& gradient() const
{
return pattern_;
}
gradient_unit_e units() const
{
return units_;
}
private:
Cairo::RefPtr<Cairo::Gradient> pattern_;
gradient_unit_e units_;
};
2009-12-16 21:02:06 +01:00
class cairo_face : private boost::noncopyable
{
public:
cairo_face(boost::shared_ptr<freetype_engine> const& engine, face_ptr const& face)
2010-06-02 13:03:30 +02:00
: face_(face)
2009-12-16 21:02:06 +01:00
{
2010-06-02 13:03:30 +02:00
static cairo_user_data_key_t key;
cairo_font_face_t *c_face;
2010-06-02 13:03:30 +02:00
c_face = cairo_ft_font_face_create_for_ft_face(face->get_face(), FT_LOAD_NO_HINTING);
cairo_font_face_set_user_data(c_face, &key, new handle(engine, face), destroy);
2009-12-16 21:02:06 +01:00
2010-06-02 13:03:30 +02:00
cairo_face_ = Cairo::RefPtr<Cairo::FontFace>(new Cairo::FontFace(c_face));
2009-12-16 21:02:06 +01:00
}
Cairo::RefPtr<Cairo::FontFace> const& face() const
2009-12-16 21:02:06 +01:00
{
2010-06-02 13:03:30 +02:00
return cairo_face_;
2009-12-16 21:02:06 +01:00
}
private:
class handle
{
public:
2010-06-02 13:03:30 +02:00
handle(boost::shared_ptr<freetype_engine> const& engine, face_ptr const& face)
: engine_(engine), face_(face) {}
2009-12-16 21:02:06 +01:00
private:
2010-06-02 13:03:30 +02:00
boost::shared_ptr<freetype_engine> engine_;
face_ptr face_;
2009-12-16 21:02:06 +01:00
};
static void destroy(void *data)
{
2010-06-02 13:03:30 +02:00
handle *h = static_cast<handle *>(data);
2009-12-16 21:02:06 +01:00
2010-06-02 13:03:30 +02:00
delete h;
2009-12-16 21:02:06 +01:00
}
private:
face_ptr face_;
Cairo::RefPtr<Cairo::FontFace> cairo_face_;
};
2012-07-02 14:28:40 +02:00
cairo_face_manager::cairo_face_manager(boost::shared_ptr<freetype_engine> engine)
: font_engine_(engine)
2009-12-16 21:02:06 +01:00
{
}
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())
{
2010-06-02 13:03:30 +02:00
entry = itr->second;
2009-12-16 21:02:06 +01:00
}
else
{
2012-03-12 14:07:36 +01:00
entry = boost::make_shared<cairo_face>(font_engine_, face);
2010-06-02 13:03:30 +02:00
cache_.insert(std::make_pair(face, entry));
2009-12-16 21:02:06 +01:00
}
2012-03-13 15:56:11 +01:00
2009-12-16 21:02:06 +01:00
return entry;
}
class cairo_context : private boost::noncopyable
{
public:
cairo_context(Cairo::RefPtr<Cairo::Context> const& context)
2010-06-02 13:03:30 +02:00
: context_(context)
2009-12-16 21:02:06 +01:00
{
2010-06-02 13:03:30 +02:00
context_->save();
2009-12-16 21:02:06 +01:00
}
~cairo_context()
2009-12-16 21:02:06 +01:00
{
2010-06-02 13:03:30 +02:00
context_->restore();
2009-12-16 21:02:06 +01:00
}
void set_color(color const &color, double opacity = 1.0)
{
2010-06-02 13:03:30 +02:00
set_color(color.red(), color.green(), color.blue(), color.alpha() * opacity / 255.0);
2009-12-16 21:02:06 +01:00
}
void set_color(int r, int g, int b, double opacity = 1.0)
{
2010-06-02 13:03:30 +02:00
context_->set_source_rgba(r / 255.0, g / 255.0, b / 255.0, opacity);
2009-12-16 21:02:06 +01:00
}
2012-05-10 13:10:37 +02:00
2012-05-03 15:34:17 +02:00
void set_operator(composite_mode_e comp_op)
{
switch (comp_op)
{
case clear:
context_->set_operator(Cairo::OPERATOR_CLEAR);
break;
case src:
context_->set_operator(Cairo::OPERATOR_SOURCE);
break;
case dst:
context_->set_operator(Cairo::OPERATOR_DEST);
break;
case src_over:
context_->set_operator(Cairo::OPERATOR_OVER);
break;
case dst_over:
context_->set_operator(Cairo::OPERATOR_DEST_OVER);
2012-05-10 13:10:37 +02:00
break;
2012-05-03 15:34:17 +02:00
case src_in:
context_->set_operator(Cairo::OPERATOR_IN);
break;
case dst_in:
context_->set_operator(Cairo::OPERATOR_DEST_IN);
2012-05-10 13:10:37 +02:00
break;
2012-05-03 15:34:17 +02:00
case src_out:
context_->set_operator(Cairo::OPERATOR_OUT);
break;
case dst_out:
context_->set_operator(Cairo::OPERATOR_DEST_OUT);
2012-05-10 13:10:37 +02:00
break;
2012-05-03 15:34:17 +02:00
case src_atop:
context_->set_operator(Cairo::OPERATOR_ATOP);
break;
case dst_atop:
context_->set_operator(Cairo::OPERATOR_DEST_ATOP);
2012-05-10 13:10:37 +02:00
break;
2012-05-03 15:34:17 +02:00
case _xor:
context_->set_operator(Cairo::OPERATOR_XOR);
break;
case plus:
context_->set_operator(Cairo::OPERATOR_ADD);
2012-05-10 13:10:37 +02:00
break;
#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 10, 0)
2012-05-03 15:34:17 +02:00
case multiply:
context_->set_operator(static_cast<Cairo::Operator>(CAIRO_OPERATOR_MULTIPLY));
2012-05-10 13:10:37 +02:00
break;
2012-05-03 15:34:17 +02:00
case screen:
context_->set_operator(static_cast<Cairo::Operator>(CAIRO_OPERATOR_SCREEN));
2012-05-10 13:10:37 +02:00
break;
2012-05-03 15:34:17 +02:00
case overlay:
context_->set_operator(static_cast<Cairo::Operator>(CAIRO_OPERATOR_OVERLAY));
break;
case darken:
context_->set_operator(static_cast<Cairo::Operator>(CAIRO_OPERATOR_DARKEN));
break;
case lighten:
context_->set_operator(static_cast<Cairo::Operator>(CAIRO_OPERATOR_LIGHTEN));
break;
case color_dodge:
context_->set_operator(static_cast<Cairo::Operator>(CAIRO_OPERATOR_COLOR_DODGE));
break;
case color_burn:
context_->set_operator(static_cast<Cairo::Operator>(CAIRO_OPERATOR_COLOR_BURN));
break;
case hard_light:
context_->set_operator(static_cast<Cairo::Operator>(CAIRO_OPERATOR_HARD_LIGHT));
break;
case soft_light:
context_->set_operator(static_cast<Cairo::Operator>(CAIRO_OPERATOR_SOFT_LIGHT));
break;
case difference:
context_->set_operator(static_cast<Cairo::Operator>(CAIRO_OPERATOR_DIFFERENCE));
break;
case exclusion:
context_->set_operator(static_cast<Cairo::Operator>(CAIRO_OPERATOR_EXCLUSION));
break;
#else
#warning building against cairo older that 1.10.0, some compositing options are disabled
case multiply:
case screen:
case overlay:
case darken:
case lighten:
case color_dodge:
case color_burn:
case hard_light:
case soft_light:
case difference:
case exclusion:
break;
#endif
2012-05-03 15:34:17 +02:00
case contrast:
case minus:
case invert:
case invert_rgb:
case grain_merge:
case grain_extract:
case hue:
case saturation:
case _color:
case _value:
//case colorize_alpha:
2012-05-10 13:10:37 +02:00
break;
}
2012-05-03 15:34:17 +02:00
}
2009-12-16 21:02:06 +01:00
void set_line_join(line_join_e join)
{
2010-06-02 13:03:30 +02:00
if (join == MITER_JOIN)
context_->set_line_join(Cairo::LINE_JOIN_MITER);
else if (join == MITER_REVERT_JOIN)
context_->set_line_join(Cairo::LINE_JOIN_MITER);
else if (join == ROUND_JOIN)
context_->set_line_join(Cairo::LINE_JOIN_ROUND);
else
context_->set_line_join(Cairo::LINE_JOIN_BEVEL);
2009-12-16 21:02:06 +01:00
}
void set_line_cap(line_cap_e cap)
{
2010-06-02 13:03:30 +02:00
if (cap == BUTT_CAP)
context_->set_line_cap(Cairo::LINE_CAP_BUTT);
else if (cap == SQUARE_CAP)
context_->set_line_cap(Cairo::LINE_CAP_SQUARE);
else
context_->set_line_cap(Cairo::LINE_CAP_ROUND);
2009-12-16 21:02:06 +01:00
}
void set_miter_limit(double limit)
{
2010-06-02 13:03:30 +02:00
context_->set_miter_limit(limit);
2009-12-16 21:02:06 +01:00
}
void set_line_width(double width)
{
2010-06-02 13:03:30 +02:00
context_->set_line_width(width);
2009-12-16 21:02:06 +01:00
}
void set_dash(dash_array const &dashes, double scale_factor)
2009-12-16 21:02:06 +01:00
{
2010-06-02 13:03:30 +02:00
std::valarray<double> d(dashes.size() * 2);
dash_array::const_iterator itr = dashes.begin();
dash_array::const_iterator end = dashes.end();
int index = 0;
for (; itr != end; ++itr)
{
d[index++] = itr->first * scale_factor;
d[index++] = itr->second * scale_factor;
2010-06-02 13:03:30 +02:00
}
context_->set_dash(d, 0.0);
2009-12-16 21:02:06 +01:00
}
void set_fill_rule(Cairo::FillRule fill_rule)
{
context_->set_fill_rule(fill_rule);
}
2009-12-16 21:02:06 +01:00
void move_to(double x, double y)
{
#if CAIRO_VERSION < CAIRO_VERSION_ENCODE(1, 6, 0)
2010-06-02 13:03:30 +02:00
if (x < -32767.0) x = -32767.0;
else if (x > 32767.0) x = 32767.0;
if (y < -32767.0) y = -32767.0;
else if (y > 32767.0) y = 32767.0;
#endif
2010-06-02 13:03:30 +02:00
context_->move_to(x, y);
2009-12-16 21:02:06 +01:00
}
void curve_to(double ct1_x, double ct1_y, double ct2_x, double ct2_y, double end_x, double end_y)
{
context_->curve_to(ct1_x,ct1_y,ct2_x,ct2_y,end_x,end_y);
}
void close_path()
{
context_->close_path();
}
2009-12-16 21:02:06 +01:00
void line_to(double x, double y)
{
#if CAIRO_VERSION < CAIRO_VERSION_ENCODE(1, 6, 0)
2010-06-02 13:03:30 +02:00
if (x < -32767.0) x = -32767.0;
else if (x > 32767.0) x = 32767.0;
if (y < -32767.0) y = -32767.0;
else if (y > 32767.0) y = 32767.0;
2009-12-16 21:02:06 +01:00
#endif
2010-06-02 13:03:30 +02:00
context_->line_to(x, y);
2009-12-16 21:02:06 +01:00
}
2009-12-16 21:02:06 +01:00
template <typename T>
void add_path(T& path, unsigned start_index = 0)
2009-12-16 21:02:06 +01:00
{
2010-06-02 13:03:30 +02:00
double x, y;
path.rewind(start_index);
2010-06-02 13:03:30 +02:00
for (unsigned cm = path.vertex(&x, &y); cm != SEG_END; cm = path.vertex(&x, &y))
{
if (cm == SEG_MOVETO)
{
move_to(x, y);
}
else if (cm == SEG_LINETO)
{
line_to(x, y);
}
else if (cm == SEG_CLOSE)
{
close_path();
}
2010-06-02 13:03:30 +02:00
}
2009-12-16 21:02:06 +01:00
}
template <typename T>
void add_agg_path(T& path, unsigned start_index = 0)
{
double x=0;
double y=0;
path.rewind(start_index);
for (unsigned cm = path.vertex(&x, &y); !agg::is_stop(cm); cm = path.vertex(&x, &y))
{
if (agg::is_move_to(cm))
{
move_to(x, y);
}
else if (agg::is_drawing(cm))
{
if (agg::is_curve3(cm))
{
double end_x=0;
double end_y=0;
MAPNIK_LOG_WARN(cairo_renderer) << "Curve 3 not implemented";
path.vertex(&end_x, &end_y);
curve_to(x,y,x,y,end_x,end_y);
}
else if (agg::is_curve4(cm))
{
double ct2_x=0;
double ct2_y=0;
double end_x=0;
double end_y=0;
path.vertex(&ct2_x, &ct2_y);
path.vertex(&end_x, &end_y);
curve_to(x,y,ct2_x,ct2_y,end_x,end_y);
}
else if (agg::is_line_to(cm))
{
line_to(x, y);
}
else
{
MAPNIK_LOG_WARN(cairo_renderer) << "Unimplemented drawing command: " << cm;
move_to(x, y);
}
}
else if (agg::is_close(cm))
{
close_path();
}
else
{
MAPNIK_LOG_WARN(cairo_renderer) << "Unimplemented path command: " << cm;
}
}
}
2009-12-16 21:02:06 +01:00
void rectangle(double x, double y, double w, double h)
{
2010-06-02 13:03:30 +02:00
context_->rectangle(x, y, w, h);
2009-12-16 21:02:06 +01:00
}
void stroke()
2009-12-16 21:02:06 +01:00
{
2010-06-02 13:03:30 +02:00
context_->stroke();
2009-12-16 21:02:06 +01:00
}
void fill()
2009-12-16 21:02:06 +01:00
{
2010-06-02 13:03:30 +02:00
context_->fill();
2009-12-16 21:02:06 +01:00
}
void paint()
2009-12-16 21:02:06 +01:00
{
2010-06-02 13:03:30 +02:00
context_->paint();
2009-12-16 21:02:06 +01:00
}
2009-12-16 21:02:06 +01:00
void set_pattern(cairo_pattern const& pattern)
{
2010-06-02 13:03:30 +02:00
context_->set_source(pattern.pattern());
2009-12-16 21:02:06 +01:00
}
void set_gradient(cairo_gradient const& pattern, const box2d<double> &bbox)
{
Cairo::RefPtr<Cairo::Gradient> p = pattern.gradient();
double bx1=bbox.minx();
double by1=bbox.miny();
double bx2=bbox.maxx();
double by2=bbox.maxy();
if (pattern.units() != USER_SPACE_ON_USE)
{
if (pattern.units() == OBJECT_BOUNDING_BOX)
{
context_->get_path_extents (bx1, by1, bx2, by2);
}
Cairo::Matrix m = p->get_matrix();
m.scale(1.0/(bx2-bx1),1.0/(by2-by1));
m.translate(-bx1,-by1);
p->set_matrix(m);
}
context_->set_source(p);
}
2009-12-16 21:02:06 +01:00
void add_image(double x, double y, image_data_32 & data, double opacity = 1.0)
{
2010-06-02 13:03:30 +02:00
cairo_pattern pattern(data);
2010-06-02 13:03:30 +02:00
pattern.set_origin(x, y);
2010-06-02 13:03:30 +02:00
context_->save();
context_->set_source(pattern.pattern());
context_->paint_with_alpha(opacity);
context_->restore();
2009-12-16 21:02:06 +01:00
}
2012-08-14 13:58:00 +02:00
void add_image(agg::trans_affine const& tr, image_data_32 & data, double opacity = 1.0)
{
cairo_pattern pattern(data);
if (!tr.is_identity())
{
double m[6];
tr.store_to(m);
Cairo::Matrix cairo_matrix(m[0],m[1],m[2],m[3],m[4],m[5]);
cairo_matrix.invert();
pattern.set_matrix(cairo_matrix);
}
context_->save();
context_->set_source(pattern.pattern());
context_->paint_with_alpha(opacity);
context_->restore();
}
2009-12-16 21:02:06 +01:00
void set_font_face(cairo_face_manager & manager, face_ptr face)
{
2010-06-02 13:03:30 +02:00
context_->set_font_face(manager.get_face(face)->face());
2009-12-16 21:02:06 +01:00
}
2009-12-16 21:02:06 +01:00
void set_font_matrix(Cairo::Matrix const& matrix)
{
2010-06-02 13:03:30 +02:00
context_->set_font_matrix(matrix);
2009-12-16 21:02:06 +01:00
}
void set_matrix(Cairo::Matrix const& matrix)
{
2010-06-02 13:03:30 +02:00
context_->set_matrix(matrix);
}
void transform(Cairo::Matrix const& matrix)
{
context_->transform(matrix);
}
void translate(double x, double y)
{
context_->translate(x,y);
}
void save()
{
context_->save();
}
void restore()
{
context_->restore();
}
2009-12-16 21:02:06 +01:00
void show_glyph(unsigned long index, double x, double y)
{
2010-06-02 13:03:30 +02:00
Cairo::Glyph glyph;
2010-06-02 13:03:30 +02:00
glyph.index = index;
glyph.x = x;
glyph.y = y;
cairo_show_glyphs(context_->cobj(), &glyph, 1);
if (context_->get_status() != CAIRO_STATUS_SUCCESS)
{
throw std::runtime_error("cairo: show_glyph");
}
2009-12-16 21:02:06 +01:00
}
2009-12-16 21:02:06 +01:00
void glyph_path(unsigned long index, double x, double y)
{
2010-06-02 13:03:30 +02:00
Cairo::Glyph glyph;
2010-06-02 13:03:30 +02:00
glyph.index = index;
glyph.x = x;
glyph.y = y;
cairo_glyph_path(context_->cobj(), &glyph, 1);
if (context_->get_status() != CAIRO_STATUS_SUCCESS)
{
throw std::runtime_error("cairo: glyph_path");
}
2009-12-16 21:02:06 +01:00
}
void add_text(text_path const& path,
2010-06-02 13:03:30 +02:00
cairo_face_manager & manager,
2012-07-13 12:17:35 +02:00
face_manager<freetype_engine> &font_manager,
double scale_factor = 1.0)
2009-12-16 21:02:06 +01:00
{
double sx = path.center.x;
double sy = path.center.y;
2010-06-02 13:03:30 +02:00
path.rewind();
2010-06-02 13:03:30 +02:00
for (int iii = 0; iii < path.num_nodes(); iii++)
{
char_info_ptr c;
2010-06-02 13:03:30 +02:00
double x, y, angle;
path.vertex(&c, &x, &y, &angle);
face_set_ptr faces = font_manager.get_face_set(c->format->face_name, c->format->fontset);
double text_size = c->format->text_size * scale_factor;
faces->set_character_sizes(text_size);
glyph_ptr glyph = faces->get_glyph(c->c);
2012-02-02 02:53:35 +01:00
2010-06-02 13:03:30 +02:00
if (glyph)
{
Cairo::Matrix matrix;
2010-06-02 13:03:30 +02:00
matrix.xx = text_size * cos(angle);
matrix.xy = text_size * sin(angle);
matrix.yx = text_size * -sin(angle);
matrix.yy = text_size * cos(angle);
matrix.x0 = 0;
matrix.y0 = 0;
2010-06-02 13:03:30 +02:00
set_font_matrix(matrix);
2010-06-02 13:03:30 +02:00
set_font_face(manager, glyph->get_face());
glyph_path(glyph->get_index(), sx + x, sy - y);
2012-08-07 13:00:31 +02:00
set_line_width(2.0 * c->format->halo_radius * scale_factor);
set_line_join(ROUND_JOIN);
set_color(c->format->halo_fill);
stroke();
set_color(c->format->fill);
2010-06-02 13:03:30 +02:00
show_glyph(glyph->get_index(), sx + x, sy - y);
}
}
2009-12-16 21:02:06 +01:00
}
private:
Cairo::RefPtr<Cairo::Context> context_;
};
cairo_renderer_base::cairo_renderer_base(Map const& m,
Cairo::RefPtr<Cairo::Context> const& context,
double scale_factor,
unsigned offset_x,
unsigned offset_y)
2009-12-16 21:02:06 +01:00
: m_(m),
context_(context),
width_(m.width()),
height_(m.height()),
scale_factor_(scale_factor),
2010-06-25 17:23:35 +02:00
t_(m.width(),m.height(),m.get_current_extent(),offset_x,offset_y),
2012-03-10 01:20:50 +01:00
font_engine_(boost::make_shared<freetype_engine>()),
2009-12-16 21:02:06 +01:00
font_manager_(*font_engine_),
2012-07-02 14:28:40 +02:00
face_manager_(font_engine_),
2010-06-25 17:23:35 +02:00
detector_(box2d<double>(-m.buffer_size() ,-m.buffer_size() , m.width() + m.buffer_size() ,m.height() + m.buffer_size()))
2009-12-16 21:02:06 +01:00
{
MAPNIK_LOG_DEBUG(cairo_renderer) << "cairo_renderer_base: Scale=" << m.scale();
2009-12-16 21:02:06 +01:00
}
template <>
cairo_renderer<Cairo::Context>::cairo_renderer(Map const& m, Cairo::RefPtr<Cairo::Context> const& context, double scale_factor, unsigned offset_x, unsigned offset_y)
2012-07-13 12:17:35 +02:00
: feature_style_processor<cairo_renderer>(m,scale_factor),
cairo_renderer_base(m,context,scale_factor,offset_x,offset_y) {}
2009-12-16 21:02:06 +01:00
template <>
cairo_renderer<Cairo::Surface>::cairo_renderer(Map const& m, Cairo::RefPtr<Cairo::Surface> const& surface, double scale_factor, unsigned offset_x, unsigned offset_y)
2012-07-13 12:17:35 +02:00
: feature_style_processor<cairo_renderer>(m,scale_factor),
cairo_renderer_base(m,Cairo::Context::create(surface),scale_factor,offset_x,offset_y) {}
2009-12-16 21:02:06 +01:00
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<double> 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<color> bg = m_.background();
if (bg)
2012-02-02 02:53:35 +01:00
{
cairo_context context(context_);
context.set_color(*bg);
context.paint();
2012-02-02 02:53:35 +01:00
}
}
template <>
void cairo_renderer<Cairo::Context>::end_map_processing(Map const& )
{
MAPNIK_LOG_DEBUG(cairo_renderer) << "cairo_renderer_base: End map processing";
}
template <>
void cairo_renderer<Cairo::Surface>::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;
2012-03-13 15:56:11 +01:00
if (lay.clear_label_cache())
2012-02-02 02:53:35 +01:00
{
detector_.clear();
2012-02-02 02:53:35 +01:00
}
query_extent_ = query_extent;
}
void cairo_renderer_base::end_layer_processing(layer const&)
{
MAPNIK_LOG_DEBUG(cairo_renderer) << "cairo_renderer_base: End layer processing";
}
2012-05-03 13:17:37 +02:00
void cairo_renderer_base::start_style_processing(feature_type_style const& st)
{
MAPNIK_LOG_DEBUG(cairo_renderer) << "cairo_renderer:start style processing";
}
2012-04-18 16:09:12 +02:00
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_context context(context_);
context.set_operator(sym.comp_op());
context.set_color(sym.get_fill(), sym.get_opacity());
2012-05-10 13:10:37 +02:00
agg::trans_affine tr;
evaluate_transform(tr, feature, sym.get_transform());
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_symbolizer,
CoordTransform, proj_transform, agg::trans_affine, conv_types>
converter(query_extent_,context,sym,t_,prj_trans,tr,1.0);
2012-05-10 13:10:37 +02:00
if (prj_trans.equal() && sym.clip()) converter.set<clip_poly_tag>(); //optional clip (default: true)
converter.set<transform_tag>(); //always transform
converter.set<affine_transform_tag>();
if (sym.simplify_tolerance() > 0.0) converter.set<simplify_tag>(); // optional simplify converter
if (sym.smooth() > 0.0) converter.set<smooth_tag>(); // optional smooth converter
2012-05-10 13:10:37 +02:00
BOOST_FOREACH( geometry_type & geom, feature.paths())
{
if (geom.size() > 2)
2010-06-02 13:03:30 +02:00
{
converter.apply(geom);
2010-06-02 13:03:30 +02:00
}
2009-12-16 21:02:06 +01:00
}
// fill polygon
context.fill();
}
2012-03-13 15:56:11 +01:00
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;
2012-03-13 15:56:11 +01:00
cairo_context context(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,value_type>(feature), *height_expr);
2012-07-13 12:17:35 +02:00
height = result.to_double() * scale_factor_;
}
2009-12-16 21:02:06 +01:00
for (unsigned i = 0; i < feature.num_geometries(); ++i)
{
geometry_type const& geom = feature.get_geometry(i);
if (geom.size() > 2)
2012-02-02 02:53:35 +01:00
{
boost::scoped_ptr<geometry_type> frame(new geometry_type(LineString));
boost::scoped_ptr<geometry_type> roof(new geometry_type(Polygon));
std::deque<segment_t> face_segments;
2012-07-31 14:31:22 +02:00
double x0 = 0;
double y0 = 0;
double x, y;
geom.rewind(0);
2012-07-31 14:31:22 +02:00
for (unsigned cm = geom.vertex(&x, &y); cm != SEG_END;
cm = geom.vertex(&x, &y))
{
if (cm == SEG_MOVETO)
{
frame->move_to(x,y);
}
2012-07-31 14:31:22 +02:00
else if (cm == SEG_LINETO || cm == SEG_CLOSE)
2010-06-02 13:03:30 +02:00
{
frame->line_to(x,y);
2012-07-31 14:31:22 +02:00
face_segments.push_back(segment_t(x0,y0,x,y));
}
x0 = x;
y0 = y;
}
2012-02-02 02:53:35 +01:00
std::sort(face_segments.begin(), face_segments.end(), y_order);
std::deque<segment_t>::const_iterator itr = face_segments.begin();
2012-07-31 14:31:22 +02:00
std::deque<segment_t>::const_iterator end=face_segments.end();
for (; itr != end; ++itr)
{
boost::scoped_ptr<geometry_type> 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);
2010-06-02 13:03:30 +02:00
path_type faces_path(t_, *faces, prj_trans);
context.set_color(int(fill.red() * 0.8), int(fill.green() * 0.8),
int(fill.blue() * 0.8), 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);
2012-07-31 14:31:22 +02:00
for (unsigned cm = geom.vertex(&x, &y); cm != SEG_END;
cm = geom.vertex(&x, &y))
{
if (cm == SEG_MOVETO)
{
2012-07-31 14:31:22 +02:00
frame->move_to(x,y+height);
roof->move_to(x,y+height);
2010-06-02 13:03:30 +02:00
}
2012-07-31 14:31:22 +02:00
else if (cm == SEG_LINETO || cm == SEG_CLOSE)
2010-06-02 13:03:30 +02:00
{
2012-07-31 14:31:22 +02:00
frame->line_to(x,y+height);
roof->line_to(x,y+height);
2010-06-02 13:03:30 +02:00
}
}
path_type path(t_, *frame, prj_trans);
context.set_color(fill.red()*0.8, fill.green()*0.8, fill.blue()*0.8,sym.get_opacity());
context.set_line_width(scale_factor_);
context.add_path(path);
context.stroke();
path_type roof_path(t_, *roof, prj_trans);
context.set_color(fill, fill.alpha() * sym.get_opacity() / 255.0 );
context.add_path(roof_path);
context.fill();
2010-06-02 13:03:30 +02:00
}
}
}
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,
offset_transform_tag, affine_transform_tag,
simplify_tag, smooth_tag, dash_tag, stroke_tag> conv_types;
cairo_context context(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());
2012-07-13 12:17:35 +02:00
context.set_line_width(stroke_.get_width() * scale_factor_);
if (stroke_.has_dash())
2012-02-02 02:53:35 +01:00
{
context.set_dash(stroke_.get_dash_array(), scale_factor_);
}
2012-03-23 00:37:24 +01:00
agg::trans_affine tr;
evaluate_transform(tr, feature, sym.get_transform());
box2d<double> clipping_extent = query_extent_;
if (sym.clip())
{
double padding = (double)(query_extent_.width()/width_);
float half_stroke = stroke_.get_width()/2.0;
if (half_stroke > 1)
padding *= half_stroke;
if (fabs(sym.offset()) > 0)
padding *= fabs(sym.offset()) * 1.2;
double x0 = query_extent_.minx();
double y0 = query_extent_.miny();
double x1 = query_extent_.maxx();
double y1 = query_extent_.maxy();
clipping_extent.init(x0 - padding, y0 - padding, x1 + padding , y1 + padding);
}
vertex_converter<box2d<double>, cairo_context, line_symbolizer,
CoordTransform, proj_transform, agg::trans_affine, conv_types>
converter(clipping_extent,context,sym,t_,prj_trans,tr,scale_factor_);
2012-05-10 13:10:37 +02:00
if (sym.clip()) converter.set<clip_line_tag>(); // optional clip (default: true)
converter.set<transform_tag>(); // always transform
if (fabs(sym.offset()) > 0.0) converter.set<offset_transform_tag>(); // parallel offset
converter.set<affine_transform_tag>(); // optional affine transform
if (sym.simplify_tolerance() > 0.0) converter.set<simplify_tag>(); // optional simplify converter
if (sym.smooth() > 0.0) converter.set<smooth_tag>(); // optional smooth converter
2012-05-10 13:10:37 +02:00
BOOST_FOREACH( geometry_type & geom, feature.paths())
{
if (geom.size() > 1)
{
converter.apply(geom);
2010-06-02 13:03:30 +02:00
}
2009-12-16 21:02:06 +01:00
}
// stroke
context.stroke();
}
2009-12-16 21:02:06 +01:00
void cairo_renderer_base::render_box(box2d<double> const& b)
{
cairo_context context(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);
}
2012-05-10 13:10:37 +02:00
agg::trans_affine transform;
2012-05-10 13:10:37 +02:00
for(unsigned i = 0; i < attributes.size(); ++i)
{
mapnik::svg::path_attributes const& attr = attributes[i];
if (!attr.visibility_flag)
continue;
2012-02-02 02:53:35 +01:00
context.save();
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);
context.transform(Cairo::Matrix(m[0],m[1],m[2],m[3],m[4],m[5]));
}
2012-02-02 02:53:35 +01:00
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*opacity);
2012-02-02 02:53:35 +01:00
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,attr.fill_color.g,attr.fill_color.b, 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,attr.stroke_color.g,attr.stroke_color.b, 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();
2012-02-02 02:53:35 +01:00
}
}
context.restore();
}
}
2012-08-14 13:58:00 +02:00
void cairo_renderer_base::render_marker(pixel_position const& pos, marker const& marker, const agg::trans_affine & tr, double opacity, bool recenter)
{
cairo_context context(context_);
if (marker.is_vector())
{
mapnik::svg_path_ptr vmarker = *marker.get_vector_data();
if (vmarker)
{
agg::pod_bvector<svg::path_attributes> const & attributes = vmarker->attributes();
render_vector_marker(context, pos, *vmarker, attributes, tr, opacity, recenter);
}
}
else if (marker.is_bitmap())
{
2012-08-14 18:14:30 +02:00
agg::trans_affine matrix = tr;
matrix *= agg::trans_affine_translation(pos.x, pos.y);
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_ptr> marker;
if ( !filename.empty() )
{
marker = marker_cache::instance().find(filename, true);
}
else
{
marker.reset(boost::make_shared<mapnik::marker>());
}
if (marker)
{
for (unsigned i = 0; i < feature.num_geometries(); ++i)
2012-02-02 02:53:35 +01:00
{
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<double> 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());
2012-02-02 02:53:35 +01:00
if (!sym.get_ignore_placement())
detector_.insert(label_ext);
}
2010-06-02 13:03:30 +02:00
}
2009-12-16 21:02:06 +01:00
}
}
void cairo_renderer_base::process(shield_symbolizer const& sym,
mapnik::feature_impl & feature,
proj_transform const& prj_trans)
{
shield_symbolizer_helper<face_manager<freetype_engine>,
2012-07-06 00:40:16 +02:00
label_collision_detector4> helper(
sym, feature, prj_trans,
width_, height_,
2012-07-13 12:17:35 +02:00
scale_factor_,
2012-07-06 00:40:16 +02:00
t_, font_manager_, detector_, query_extent_);
cairo_context context(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 marker_pos = helper.get_marker_position(placements[ii]);
2012-08-14 18:14:30 +02:00
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(marker_pos,
2012-08-14 18:14:30 +02:00
helper.get_marker(),
marker_tr,
sym.get_opacity());
2012-08-14 18:14:30 +02:00
2012-07-13 12:17:35 +02:00
context.add_text(placements[ii], face_manager_, font_manager_, scale_factor_);
2010-06-02 13:03:30 +02:00
}
2009-12-16 21:02:06 +01:00
}
}
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;
2012-05-10 13:10:37 +02:00
std::string filename = path_processor_type::evaluate( *sym.get_filename(), feature);
boost::optional<mapnik::marker_ptr> marker = mapnik::marker_cache::instance().find(filename,true);
if (!marker && !(*marker)->is_bitmap()) return;
2009-12-16 21:02:06 +01:00
unsigned width((*marker)->width());
unsigned height((*marker)->height());
2009-12-16 21:02:06 +01:00
cairo_context context(context_);
context.set_operator(sym.comp_op());
cairo_pattern pattern(**((*marker)->get_bitmap_data()));
2009-12-16 21:02:06 +01:00
pattern.set_extend(Cairo::EXTEND_REPEAT);
pattern.set_filter(Cairo::FILTER_BILINEAR);
2012-07-13 12:17:35 +02:00
context.set_line_width(height * scale_factor_);
2012-05-10 13:10:37 +02:00
for (unsigned i = 0; i < feature.num_geometries(); ++i)
{
geometry_type & geom = feature.get_geometry(i);
if (geom.size() > 1)
2010-06-02 13:03:30 +02:00
{
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);
2012-03-13 15:56:11 +01:00
double length(0);
double x0(0), y0(0);
double x, y;
2012-02-02 02:53:35 +01:00
for (unsigned cm = path.vertex(&x, &y); cm != SEG_END; cm = path.vertex(&x, &y))
{
if (cm == SEG_MOVETO)
2010-06-02 13:03:30 +02:00
{
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);
2012-02-02 02:53:35 +01:00
Cairo::Matrix 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);
2012-02-02 02:53:35 +01:00
pattern.set_matrix(matrix);
2012-02-02 02:53:35 +01:00
context.set_pattern(pattern);
2012-02-02 02:53:35 +01:00
context.move_to(x0, y0);
context.line_to(x, y);
context.stroke();
2012-02-02 02:53:35 +01:00
length = length + hypot(x - x0, y - y0);
2010-06-02 13:03:30 +02:00
}
x0 = x;
y0 = y;
}
2010-06-02 13:03:30 +02:00
}
2009-12-16 21:02:06 +01:00
}
}
2009-12-16 21:02:06 +01:00
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_context context(context_);
context.set_operator(sym.comp_op());
2012-05-10 13:10:37 +02:00
std::string filename = path_processor_type::evaluate( *sym.get_filename(), feature);
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);
2009-12-16 21:02:06 +01:00
//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<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>
converter(query_extent_,context,sym,t_,prj_trans,tr, scale_factor_);
2012-05-10 13:10:37 +02:00
if (prj_trans.equal() && sym.clip()) converter.set<clip_poly_tag>(); //optional clip (default: true)
converter.set<transform_tag>(); //always transform
converter.set<affine_transform_tag>();
if (sym.simplify_tolerance() > 0.0) converter.set<simplify_tag>(); // optional simplify converter
if (sym.smooth() > 0.0) converter.set<smooth_tag>(); // optional smooth converter
2012-05-10 13:10:37 +02:00
BOOST_FOREACH( geometry_type & geom, feature.paths())
{
if (geom.size() > 2)
2010-06-02 13:03:30 +02:00
{
converter.apply(geom);
2010-06-02 13:03:30 +02:00
}
2009-12-16 21:02:06 +01:00
}
// 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<double> target_ext = box2d<double>(source->ext_);
prj_trans.backward(target_ext, PROJ_ENVELOPE_POINTS);
box2d<double> ext = t_.forward(target_ext);
int start_x = static_cast<int>(ext.minx());
int start_y = static_cast<int>(ext.miny());
int end_x = static_cast<int>(ceil(ext.maxx()));
int end_y = static_cast<int>(ceil(ext.maxy()));
int raster_width = end_x - start_x;
int raster_height = end_y - start_y;
if (raster_width > 0 && raster_height > 0)
{
image_data_32 target_data(raster_width,raster_height);
raster target(target_ext, target_data);
scaling_method_e scaling_method = sym.get_scaling_method();
double filter_radius = sym.calculate_filter_factor();
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<image_data_32>(target.data_,source->data_, 0.0, 0.0);
} else
{
double scaling_ratio = ext.width() / source->data_.width();
scale_image_agg<image_data_32>(target.data_,
source->data_,
scaling_method,
scaling_ratio,
0.0,
0.0,
filter_radius);
}
}
cairo_context context(context_);
context.set_operator(sym.comp_op());
context.add_image(start_x, start_y, target.data_, sym.get_opacity());
}
2009-12-16 21:02:06 +01:00
}
}
2009-12-16 21:02:06 +01:00
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,
double scale_factor)
:ctx_(ctx),
marker_(marker),
attributes_(attributes),
detector_(detector),
sym_(sym),
bbox_(bbox),
marker_trans_(marker_trans),
scale_factor_(scale_factor) {}
template <typename T>
void add_path(T & path)
{
marker_placement_e placement_method = sym_.get_marker_placement();
if (placement_method != MARKER_LINE_PLACEMENT)
{
double x = 0;
double y = 0;
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 (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<T, label_collision_detector4> 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<double> const& bbox_;
agg::trans_affine const& marker_trans_;
double scale_factor_;
};
2012-08-14 13:58:00 +02:00
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,
double scale_factor)
:ctx_(ctx),
marker_(marker),
detector_(detector),
sym_(sym),
bbox_(bbox),
marker_trans_(marker_trans),
scale_factor_(scale_factor) {}
template <typename T>
void add_path(T & path)
{
marker_placement_e placement_method = sym_.get_marker_placement();
if (placement_method != MARKER_LINE_PLACEMENT)
{
double x = 0;
double y = 0;
2012-08-14 13:58:00 +02:00
if (placement_method == MARKER_INTERIOR_PLACEMENT)
{
if (!label::interior_position(path, x, y))
return;
2012-08-14 13:58:00 +02:00
}
else
{
if (!label::centroid(path, x, y))
return;
2012-08-14 13:58:00 +02:00
}
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 (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<T, label_collision_detector4> 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<double> 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<clip_line_tag,clip_poly_tag,transform_tag,smooth_tag> conv_types;
cairo_context context(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<marker_ptr> 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<double> const& bbox = (*mark)->bounding_box();
setup_transform_scaling(tr, bbox, feature, sym);
evaluate_transform(tr, feature, sym.get_image_transform());
2012-08-14 13:58:00 +02:00
if ((*mark)->is_vector())
{
using namespace mapnik::svg;
typedef agg::pod_bvector<path_attributes> svg_attributes_type;
typedef detail::markers_dispatch<cairo_context, mapnik::svg_storage_type,
svg_attributes_type,label_collision_detector4> dispatch_type;
2012-08-14 13:58:00 +02:00
boost::optional<svg_path_ptr> const& stock_vector_marker = (*mark)->get_vector_data();
2012-02-02 02:53:35 +01:00
2012-08-14 13:58:00 +02:00
expression_ptr const& width_expr = sym.get_width();
expression_ptr const& height_expr = sym.get_height();
2012-08-14 13:58:00 +02:00
// special case for simple ellipse markers
// to allow for full control over rx/ry dimensions
if (filename == "shape://ellipse"
&& (width_expr || height_expr))
{
2012-08-14 13:58:00 +02:00
svg_storage_type marker_ellipse;
vertex_stl_adapter<svg_path_storage> 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<double> bbox = marker_ellipse.bounding_box();
dispatch_type dispatch(context, marker_ellipse, result?attributes:(*stock_vector_marker)->attributes(),
detector_, sym, bbox, marker_tr, scale_factor_);
vertex_converter<box2d<double>, 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<clip_poly_tag>();
// line clipping disabled due to https://github.com/mapnik/mapnik/issues/1426
//else if (type == LineString)
// converter.template set<clip_line_tag>();
2012-08-14 13:58:00 +02:00
// don't clip if type==Point
}
converter.set<transform_tag>(); //always transform
if (sym.smooth() > 0.0) converter.set<smooth_tag>(); // optional smooth converter
BOOST_FOREACH(geometry_type & geom, feature.paths())
{
converter.apply(geom);
}
}
2012-08-14 13:58:00 +02:00
else
{
2012-08-14 13:58:00 +02:00
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<box2d<double>, 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<clip_poly_tag>();
// line clipping disabled due to https://github.com/mapnik/mapnik/issues/1426
//else if (type == LineString)
// converter.template set<clip_line_tag>();
2012-08-14 13:58:00 +02:00
// don't clip if type==Point
}
converter.set<transform_tag>(); //always transform
if (sym.smooth() > 0.0) converter.set<smooth_tag>(); // optional smooth converter
BOOST_FOREACH(geometry_type & geom, feature.paths())
{
converter.apply(geom);
}
}
}
2012-08-14 13:58:00 +02:00
else // raster markers
{
2012-08-14 13:58:00 +02:00
typedef detail::markers_dispatch_2<cairo_context,
mapnik::image_ptr,
label_collision_detector4> dispatch_type;
boost::optional<mapnik::image_ptr> marker = (*mark)->get_bitmap_data();
if ( marker )
{
2012-08-14 13:58:00 +02:00
dispatch_type dispatch(context, *marker,
detector_, sym, bbox, tr, scale_factor_);
vertex_converter<box2d<double>, 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<clip_poly_tag>();
// line clipping disabled due to https://github.com/mapnik/mapnik/issues/1426
//else if (type == LineString)
// converter.template set<clip_line_tag>();
2012-08-14 13:58:00 +02:00
// don't clip if type==Point
}
converter.set<transform_tag>(); //always transform
if (sym.smooth() > 0.0) converter.set<smooth_tag>(); // optional smooth converter
BOOST_FOREACH(geometry_type & geom, feature.paths())
{
converter.apply(geom);
}
2012-02-02 02:53:35 +01:00
}
}
}
}
}
2009-12-16 21:02:06 +01:00
void cairo_renderer_base::process(text_symbolizer const& sym,
mapnik::feature_impl & feature,
proj_transform const& prj_trans)
{
2012-07-06 00:40:16 +02:00
text_symbolizer_helper<face_manager<freetype_engine>,
label_collision_detector4> helper(
sym, feature, prj_trans,
width_, height_,
2012-07-13 12:17:35 +02:00
scale_factor_,
2012-07-06 00:40:16 +02:00
t_, font_manager_, detector_, query_extent_);
2012-07-13 12:17:35 +02:00
cairo_context context(context_);
context.set_operator(sym.comp_op());
2012-05-10 13:10:37 +02:00
while (helper.next())
{
placements_type const& placements = helper.placements();
for (unsigned int ii = 0; ii < placements.size(); ++ii)
{
2012-07-13 12:17:35 +02:00
context.add_text(placements[ii], face_manager_, font_manager_, scale_factor_);
}
2009-12-16 21:02:06 +01:00
}
}
template class cairo_renderer<Cairo::Surface>;
template class cairo_renderer<Cairo::Context>;
}
2012-07-13 12:17:35 +02:00
#endif // HAVE_CAIRO