310 lines
10 KiB
C++
310 lines
10 KiB
C++
/*****************************************************************************
|
|
*
|
|
* This file is part of Mapnik (c++ mapping toolkit)
|
|
*
|
|
* Copyright (C) 2015 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/debug.hpp>
|
|
#include <mapnik/layer.hpp>
|
|
#include <mapnik/proj_transform.hpp>
|
|
#include <mapnik/cairo/cairo_renderer.hpp>
|
|
#include <mapnik/cairo/cairo_render_vector.hpp>
|
|
#include <mapnik/map.hpp>
|
|
#include <mapnik/svg/svg_path_adapter.hpp>
|
|
#include <mapnik/pixel_position.hpp>
|
|
#include <mapnik/attribute.hpp>
|
|
#include <mapnik/request.hpp>
|
|
#include <mapnik/label_collision_detector.hpp>
|
|
#include <mapnik/marker.hpp>
|
|
#include <mapnik/marker_cache.hpp>
|
|
#include <mapnik/feature_type_style.hpp>
|
|
|
|
// agg
|
|
#include "agg/include/agg_trans_affine.h" // for trans_affine, etc
|
|
|
|
// stl
|
|
#include <cmath>
|
|
|
|
namespace mapnik
|
|
{
|
|
|
|
class feature_type_style;
|
|
|
|
template <typename T>
|
|
cairo_renderer<T>::cairo_renderer(Map const& m,
|
|
T const& cairo,
|
|
double scale_factor,
|
|
unsigned offset_x,
|
|
unsigned offset_y)
|
|
: feature_style_processor<cairo_renderer>(m, scale_factor),
|
|
m_(m),
|
|
context_(cairo),
|
|
common_(m, attributes(), offset_x, offset_y, m.width(), m.height(), scale_factor),
|
|
face_manager_(common_.shared_font_library_),
|
|
style_level_compositing_(false)
|
|
{
|
|
setup(m);
|
|
}
|
|
|
|
template <typename T>
|
|
cairo_renderer<T>::cairo_renderer(Map const& m,
|
|
request const& req,
|
|
attributes const& vars,
|
|
T const& cairo,
|
|
double scale_factor,
|
|
unsigned offset_x,
|
|
unsigned offset_y)
|
|
: feature_style_processor<cairo_renderer>(m, scale_factor),
|
|
m_(m),
|
|
context_(cairo),
|
|
common_(m, req, vars, offset_x, offset_y, req.width(), req.height(), scale_factor),
|
|
face_manager_(common_.shared_font_library_),
|
|
style_level_compositing_(false)
|
|
|
|
{
|
|
setup(m);
|
|
}
|
|
|
|
template <typename T>
|
|
cairo_renderer<T>::cairo_renderer(Map const& m,
|
|
T 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),
|
|
m_(m),
|
|
context_(cairo),
|
|
common_(m, attributes(), offset_x, offset_y, m.width(), m.height(), scale_factor, detector),
|
|
face_manager_(common_.shared_font_library_),
|
|
style_level_compositing_(false)
|
|
|
|
{
|
|
setup(m);
|
|
}
|
|
|
|
template <typename T>
|
|
cairo_renderer<T>::~cairo_renderer() {}
|
|
|
|
struct setup_marker_visitor
|
|
{
|
|
setup_marker_visitor(cairo_context & context, renderer_common const& common)
|
|
: context_(context), common_(common) {}
|
|
|
|
void operator() (marker_null const &) const{}
|
|
void operator() (marker_svg const &) const {}
|
|
|
|
void operator() (marker_rgba8 const& marker) const
|
|
{
|
|
mapnik::image_rgba8 const& bg_image = marker.get_data();
|
|
std::size_t w = bg_image.width();
|
|
std::size_t h = bg_image.height();
|
|
if ( w > 0 && h > 0)
|
|
{
|
|
// repeat background-image both vertically and horizontally
|
|
std::size_t x_steps = std::size_t(std::ceil(common_.width_/double(w)));
|
|
std::size_t y_steps = std::size_t(std::ceil(common_.height_/double(h)));
|
|
for (std::size_t x=0;x<x_steps;++x)
|
|
{
|
|
for (std::size_t 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
cairo_context & context_;
|
|
renderer_common const& common_;
|
|
};
|
|
|
|
template <typename T>
|
|
void cairo_renderer<T>::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
|
|
std::shared_ptr<mapnik::marker const> bg_marker = mapnik::marker_cache::instance().find(*image_filename,true);
|
|
util::apply_visitor(setup_marker_visitor(context_, common_), *bg_marker);
|
|
}
|
|
MAPNIK_LOG_DEBUG(cairo_renderer) << "cairo_renderer: Scale=" << map.scale();
|
|
}
|
|
|
|
template <typename T>
|
|
void cairo_renderer<T>::start_map_processing(Map const& map)
|
|
{
|
|
MAPNIK_LOG_DEBUG(cairo_renderer) << "cairo_renderer: 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 <typename T>
|
|
void cairo_renderer<T>::end_map_processing(Map const&)
|
|
{
|
|
MAPNIK_LOG_DEBUG(cairo_renderer) << "cairo_renderer: End map processing";
|
|
}
|
|
|
|
template <typename T>
|
|
void cairo_renderer<T>::start_layer_processing(layer const& lay, box2d<double> const& query_extent)
|
|
{
|
|
MAPNIK_LOG_DEBUG(cairo_renderer) << "cairo_renderer: Start processing layer=" << lay.name() ;
|
|
MAPNIK_LOG_DEBUG(cairo_renderer) << "cairo_renderer: -- datasource=" << lay.datasource().get();
|
|
MAPNIK_LOG_DEBUG(cairo_renderer) << "cairo_renderer: -- query_extent=" << query_extent;
|
|
|
|
if (lay.clear_label_cache())
|
|
{
|
|
common_.detector_->clear();
|
|
}
|
|
common_.query_extent_ = query_extent;
|
|
}
|
|
|
|
template <typename T>
|
|
void cairo_renderer<T>::end_layer_processing(layer const&)
|
|
{
|
|
MAPNIK_LOG_DEBUG(cairo_renderer) << "cairo_renderer: End layer processing";
|
|
}
|
|
|
|
template <typename T>
|
|
void cairo_renderer<T>::start_style_processing(feature_type_style const & st)
|
|
{
|
|
MAPNIK_LOG_DEBUG(cairo_renderer) << "cairo_renderer:start style processing";
|
|
|
|
style_level_compositing_ = st.comp_op() || st.get_opacity() < 1;
|
|
|
|
if (style_level_compositing_)
|
|
{
|
|
context_.push_group();
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
void cairo_renderer<T>::end_style_processing(feature_type_style const & st)
|
|
{
|
|
MAPNIK_LOG_DEBUG(cairo_renderer) << "cairo_renderer:end style processing";
|
|
|
|
if (style_level_compositing_)
|
|
{
|
|
context_.pop_group();
|
|
composite_mode_e comp_op = st.comp_op() ? *st.comp_op() : src_over;
|
|
context_.set_operator(comp_op);
|
|
context_.paint(st.get_opacity());
|
|
}
|
|
}
|
|
|
|
struct cairo_render_marker_visitor
|
|
{
|
|
cairo_render_marker_visitor(cairo_context & context,
|
|
renderer_common const& common,
|
|
pixel_position const& pos,
|
|
agg::trans_affine const& tr,
|
|
double opacity,
|
|
bool recenter)
|
|
: context_(context),
|
|
common_(common),
|
|
pos_(pos),
|
|
tr_(tr),
|
|
opacity_(opacity),
|
|
recenter_(recenter) {}
|
|
|
|
void operator() (marker_null const&) {}
|
|
|
|
void operator() (marker_svg const& marker)
|
|
{
|
|
mapnik::svg_path_ptr vmarker = marker.get_data();
|
|
if (vmarker)
|
|
{
|
|
box2d<double> bbox = vmarker->bounding_box();
|
|
agg::trans_affine marker_tr = tr_;
|
|
if (recenter_)
|
|
{
|
|
coord<double,2> c = bbox.center();
|
|
marker_tr = agg::trans_affine_translation(-c.x,-c.y);
|
|
marker_tr *= tr_;
|
|
}
|
|
marker_tr *= agg::trans_affine_scaling(common_.scale_factor_);
|
|
agg::pod_bvector<svg::path_attributes> const & attributes = vmarker->attributes();
|
|
svg::vertex_stl_adapter<svg::svg_path_storage> stl_storage(vmarker->source());
|
|
svg::svg_path_adapter svg_path(stl_storage);
|
|
marker_tr.translate(pos_.x, pos_.y);
|
|
render_vector_marker(context_, svg_path, attributes, bbox, marker_tr, opacity_);
|
|
}
|
|
}
|
|
|
|
void operator() (marker_rgba8 const& marker)
|
|
{
|
|
double width = marker.get_data().width();
|
|
double height = marker.get_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_data(), opacity_);
|
|
}
|
|
|
|
private:
|
|
cairo_context & context_;
|
|
renderer_common const& common_;
|
|
pixel_position const& pos_;
|
|
agg::trans_affine const& tr_;
|
|
double opacity_;
|
|
bool recenter_;
|
|
};
|
|
|
|
template <typename T>
|
|
void cairo_renderer<T>::render_marker(pixel_position const& pos,
|
|
marker const& marker,
|
|
agg::trans_affine const& tr,
|
|
double opacity,
|
|
bool recenter)
|
|
|
|
{
|
|
cairo_save_restore guard(context_);
|
|
cairo_render_marker_visitor visitor(context_,
|
|
common_,
|
|
pos,
|
|
tr,
|
|
opacity,
|
|
recenter);
|
|
util::apply_visitor(visitor, marker);
|
|
}
|
|
|
|
template class cairo_renderer<cairo_ptr>;
|
|
|
|
}
|
|
|
|
#endif // HAVE_CAIRO
|