Merge commit '88360c365f2ea288740db51e65588f839fc67bba' into harfbuzz

Conflicts:
	include/mapnik/cairo_renderer.hpp
This commit is contained in:
Hermann Kraus 2013-03-16 18:21:59 +01:00
commit 346ce07117
28 changed files with 709 additions and 166 deletions

View file

@ -7,26 +7,26 @@ endif
OS:=$(shell uname -s)
ifeq ($(NPROCS),)
NPROCS:=1
ifeq ($(JOBS),)
JOBS:=1
ifeq ($(OS),Linux)
NPROCS:=$(shell grep -c ^processor /proc/cpuinfo)
JOBS:=$(shell grep -c ^processor /proc/cpuinfo)
endif
ifeq ($(OS),Darwin)
NPROCS:=$(shell sysctl -n hw.ncpu)
JOBS:=$(shell sysctl -n hw.ncpu)
endif
endif
all: mapnik
install:
@python scons/scons.py -j$(NPROCS) --config=cache --implicit-cache --max-drift=1 install
@python scons/scons.py -j$(JOBS) --config=cache --implicit-cache --max-drift=1 install
mapnik:
@python scons/scons.py -j$(NPROCS) --config=cache --implicit-cache --max-drift=1
@python scons/scons.py -j$(JOBS) --config=cache --implicit-cache --max-drift=1
clean:
@python scons/scons.py -j$(NPROCS) -c --config=cache --implicit-cache --max-drift=1
@python scons/scons.py -j$(JOBS) -c --config=cache --implicit-cache --max-drift=1
@if test -e ".sconsign.dblite"; then rm ".sconsign.dblite"; fi
@if test -e "config.log"; then rm "config.log"; fi
@if test -e ".sconf_temp/"; then rm -r ".sconf_temp/"; fi
@ -46,7 +46,7 @@ rebuild:
make uninstall && make clean && time make && make install
uninstall:
@python scons/scons.py -j$(NPROCS) --config=cache --implicit-cache --max-drift=1 uninstall
@python scons/scons.py -j$(JOBS) --config=cache --implicit-cache --max-drift=1 uninstall
test:
@ ./run_tests

View file

@ -460,7 +460,7 @@ HELP_REQUESTED = False
if ('-h' in command_line_args) or ('--help' in command_line_args):
HELP_REQUESTED = True
if ('-c' in command_line_args) or ('--clean' in command_line_args):
if ('install' not in command_line_args) and ('-c' in command_line_args) or ('--clean' in command_line_args):
HELP_REQUESTED = True
if 'configure' in command_line_args and not HELP_REQUESTED:

View file

@ -20,6 +20,8 @@
#include <QtGui>
#define BOOST_CHRONO_HEADER_ONLY
#include <boost/timer/timer.hpp>
#include <boost/bind.hpp>
#include <mapnik/agg_renderer.hpp>
@ -502,7 +504,10 @@ void render_agg(mapnik::Map const& map, double scaling_factor, QPixmap & pix)
try
{
ren.apply();
{
boost::timer::auto_cpu_timer t;
ren.apply();
}
QImage image((uchar*)buf.raw_data(),width,height,QImage::Format_ARGB32);
pix = QPixmap::fromImage(image.rgbSwapped());
}

View file

@ -3,10 +3,10 @@
######################################################################
TEMPLATE = app
QMAKE_CXX = clang++
QMAKE_CXXFLAGS += $$system(mapnik-config --cflags)
QMAKE_CXXFLAGS += $$system(mapnik-config --cxxflags)
QMAKE_LFLAGS += $$system(mapnik-config --libs)
QMAKE_LFLAGS += $$system(mapnik-config --ldflags --dep-libs)
QMAKE_LFLAGS += -lboost_timer
# Input
CONFIG += qt debug_and_release
@ -19,17 +19,17 @@ HEADERS += mainwindow.hpp \
layerwidget.hpp \
layerlistmodel.hpp \
layerdelegate.hpp \
styles_model.hpp
styles_model.hpp
HEADERS += about_dialog.hpp \
info_dialog.hpp \
layer_info_dialog.hpp
SOURCES += main.cpp \
mainwindow.cpp \
mainwindow.cpp \
mapwidget.cpp \
layerwidget.cpp \
layerlistmodel.cpp \
layerlistmodel.cpp \
layerdelegate.cpp \
styles_model.cpp

View file

@ -35,6 +35,7 @@
#include <mapnik/ctrans.hpp> // for CoordTransform
#include <mapnik/image_compositing.hpp> // for composite_mode_e
#include <mapnik/pixel_position.hpp>
#include <mapnik/request.hpp>
// boost
#include <boost/scoped_ptr.hpp>
@ -70,6 +71,8 @@ public:
// create with external placement detector, possibly non-empty
agg_renderer(Map const &m, T & pixmap, boost::shared_ptr<label_collision_detector4> detector,
double scale_factor=1.0, unsigned offset_x=0, unsigned offset_y=0);
// pass in mapnik::request object to provide the mutable things per render
agg_renderer(Map const& m, request const& req, T & pixmap, double scale_factor=1.0, unsigned offset_x=0, unsigned offset_y=0);
~agg_renderer();
void start_map_processing(Map const& map);
void end_map_processing(Map const& map);

View file

@ -32,6 +32,7 @@
#include <mapnik/label_collision_detector.hpp>
#include <mapnik/map.hpp>
#include <mapnik/pixel_position.hpp>
#include <mapnik/request.hpp>
#include <mapnik/rule.hpp> // for all symbolizers
#include <mapnik/noncopyable.hpp>
#include <mapnik/cairo_context.hpp>
@ -58,6 +59,7 @@ class MAPNIK_DECL cairo_renderer_base : private mapnik::noncopyable
{
protected:
cairo_renderer_base(Map const& m, cairo_ptr const& cairo, double scale_factor=1.0, unsigned offset_x=0, unsigned offset_y=0);
cairo_renderer_base(Map const& m, request const& req, cairo_ptr const& cairo, double scale_factor=1.0, unsigned offset_x=0, unsigned offset_y=0);
cairo_renderer_base(Map const& m, cairo_ptr const& cairo, boost::shared_ptr<label_collision_detector4> detector, double scale_factor=1.0, unsigned offset_x=0, unsigned offset_y=0);
public:
~cairo_renderer_base();
@ -138,6 +140,7 @@ class MAPNIK_DECL cairo_renderer : public feature_style_processor<cairo_renderer
public:
typedef cairo_renderer_base processor_impl_type;
cairo_renderer(Map const& m, T const& obj, double scale_factor=1.0, unsigned offset_x=0, unsigned offset_y=0);
cairo_renderer(Map const& m, request const& req, T const& obj, double scale_factor=1.0, unsigned offset_x=0, unsigned offset_y=0);
cairo_renderer(Map const& m, T const& obj, boost::shared_ptr<label_collision_detector4> detector, double scale_factor=1.0, unsigned offset_x=0, unsigned offset_y=0);
void end_map_processing(Map const& map);
};

View file

@ -52,24 +52,32 @@ class feature_style_processor
{
struct symbol_dispatch;
public:
explicit feature_style_processor(Map const& m, double scale_factor = 1.0);
explicit feature_style_processor(Map const& m,
double scale_factor = 1.0);
/*!
* \brief apply renderer to all map layers.
*/
void apply();
void apply(double scale_denom_override=0.0);
/*!
* \brief apply renderer to a single layer, providing pre-populated set of query attribute names.
*/
void apply(mapnik::layer const& lyr, std::set<std::string>& names);
void apply(mapnik::layer const& lyr,
std::set<std::string>& names,
double scale_denom_override=0.0);
/*!
* \brief render a layer given a projection and scale.
*/
void apply_to_layer(layer const& lay,
Processor & p,
projection const& proj0,
double scale,
double scale_denom,
unsigned width,
unsigned height,
box2d<double> const& extent,
int buffer_size,
std::set<std::string>& names);
private:

View file

@ -150,7 +150,7 @@ feature_style_processor<Processor>::feature_style_processor(Map const& m, double
}
template <typename Processor>
void feature_style_processor<Processor>::apply()
void feature_style_processor<Processor>::apply(double scale_denom)
{
#if defined(RENDERING_STATS)
std::clog << "\n//-- starting rendering timer...\n";
@ -163,7 +163,8 @@ void feature_style_processor<Processor>::apply()
try
{
projection proj(m_.srs(),true);
double scale_denom = mapnik::scale_denominator(m_.scale(),proj.is_geographic());
if (scale_denom <= 0.0)
scale_denom = mapnik::scale_denominator(m_.scale(),proj.is_geographic());
scale_denom *= scale_factor_;
BOOST_FOREACH ( layer const& lyr, m_.layers() )
@ -171,7 +172,17 @@ void feature_style_processor<Processor>::apply()
if (lyr.visible(scale_denom))
{
std::set<std::string> names;
apply_to_layer(lyr, p, proj, scale_denom, names);
apply_to_layer(lyr,
p,
proj,
m_.scale(),
scale_denom,
m_.width(),
m_.height(),
m_.get_current_extent(),
m_.buffer_size(),
names);
}
}
}
@ -190,19 +201,31 @@ void feature_style_processor<Processor>::apply()
}
template <typename Processor>
void feature_style_processor<Processor>::apply(mapnik::layer const& lyr, std::set<std::string>& names)
void feature_style_processor<Processor>::apply(mapnik::layer const& lyr,
std::set<std::string>& names,
double scale_denom)
{
Processor & p = static_cast<Processor&>(*this);
p.start_map_processing(m_);
try
{
projection proj(m_.srs(),true);
double scale_denom = mapnik::scale_denominator(m_.scale(),proj.is_geographic());
if (scale_denom <= 0.0)
scale_denom = mapnik::scale_denominator(m_.scale(),proj.is_geographic());
scale_denom *= scale_factor_;
if (lyr.visible(scale_denom))
{
apply_to_layer(lyr, p, proj, scale_denom, names);
apply_to_layer(lyr,
p,
proj,
m_.scale(),
scale_denom,
m_.width(),
m_.height(),
m_.get_current_extent(),
m_.buffer_size(),
names);
}
}
catch (proj_init_error& ex)
@ -215,7 +238,12 @@ void feature_style_processor<Processor>::apply(mapnik::layer const& lyr, std::se
template <typename Processor>
void feature_style_processor<Processor>::apply_to_layer(layer const& lay, Processor & p,
projection const& proj0,
double scale,
double scale_denom,
unsigned width,
unsigned height,
box2d<double> const& extent,
int buffer_size,
std::set<std::string>& names)
{
std::vector<std::string> const& style_names = lay.styles();
@ -253,20 +281,21 @@ void feature_style_processor<Processor>::apply_to_layer(layer const& lay, Proces
#endif
box2d<double> query_ext = m_.get_current_extent(); // unbuffered
box2d<double> query_ext = extent; // unbuffered
box2d<double> buffered_query_ext(query_ext); // buffered
double buffer_padding = 2.0 * scale;
boost::optional<int> layer_buffer_size = lay.buffer_size();
if (layer_buffer_size) // if layer overrides buffer size, use this value to compute buffered extent
{
double extra = 2.0 * m_.scale() * *layer_buffer_size;
buffered_query_ext.width(query_ext.width() + extra);
buffered_query_ext.height(query_ext.height() + extra);
buffer_padding *= *layer_buffer_size;
}
else
{
buffered_query_ext = m_.get_buffered_extent();
buffer_padding *= buffer_size;
}
buffered_query_ext.width(query_ext.width() + buffer_padding);
buffered_query_ext.height(query_ext.height() + buffer_padding);
// clip buffered extent by maximum extent, if supplied
boost::optional<box2d<double> > const& maximum_extent = m_.maximum_extent();
@ -363,10 +392,10 @@ void feature_style_processor<Processor>::apply_to_layer(layer const& lay, Proces
double qw = query_ext.width()>0 ? query_ext.width() : 1;
double qh = query_ext.height()>0 ? query_ext.height() : 1;
query::resolution_type res(m_.width()/qw,
m_.height()/qh);
query::resolution_type res(width/qw,
height/qh);
query q(layer_ext,res,scale_denom,m_.get_current_extent());
query q(layer_ext,res,scale_denom,extent);
std::vector<feature_type_style const*> active_styles;
attribute_collector collector(names);
double filt_factor = 1.0;

View file

@ -54,6 +54,7 @@ namespace mapnik {
class marker;
class proj_transform;
struct grid_rasterizer;
class request;
}
namespace mapnik {
@ -67,6 +68,7 @@ public:
typedef T buffer_type;
typedef grid_renderer<T> processor_impl_type;
grid_renderer(Map const& m, T & pixmap, double scale_factor=1.0, unsigned offset_x=0, unsigned offset_y=0);
grid_renderer(Map const& m, request const& req, T & pixmap, double scale_factor=1.0, unsigned offset_x=0, unsigned offset_y=0);
~grid_renderer();
void start_map_processing(Map const& map);
void end_map_processing(Map const& map);

View file

@ -26,6 +26,7 @@
//mapnik
#include <mapnik/image_filter_types.hpp>
#include <mapnik/util/hsl.hpp>
// boost
#include <boost/variant/static_visitor.hpp>
@ -403,6 +404,69 @@ void apply_filter(Src & src, agg_stack_blur const& op)
agg::stack_blur_rgba32(pixf,op.rx,op.ry);
}
template <typename Src>
void apply_filter(Src & src, hsla const& op)
{
using namespace boost::gil;
Tinter tint;
tint.h0 = .1;
tint.s0 = .3;
tint.l1 = .9;
bool tinting = !tint.is_identity();
bool set_alpha = !tint.is_alpha_identity();
// todo - should filters be able to report if they should be run?
if (tinting || set_alpha)
{
rgba8_view_t src_view = rgba8_view(src);
for (int y=0; y<src_view.height(); ++y)
{
rgba8_view_t::x_iterator src_it = src_view.row_begin(y);
for (int x=0; x<src_view.width(); ++x)
{
uint8_t & a = get_color(src_it[x], alpha_t());
uint8_t a_original = a;
if (set_alpha)
{
double a2 = tint.a0 + (a/255.0 * (tint.a1 - tint.a0));
if (a2 > 1) a2 = 1;
if (a2 < 0) a2 = 0;
a = static_cast<unsigned>(std::floor(a2 * 255.0));
}
if (a > 1 && tinting)
{
double h;
double s;
double l;
uint8_t & r = get_color(src_it[x], red_t());
uint8_t & g = get_color(src_it[x], green_t());
uint8_t & b = get_color(src_it[x], blue_t());
// demultiply
r /= a_original;
g /= a_original;
b /= a_original;
rgb2hsl(r,g,b,h,s,l);
double h2 = tint.h0 + (h * (tint.h1 - tint.h0));
double s2 = tint.s0 + (s * (tint.s1 - tint.s0));
double l2 = tint.l0 + (l * (tint.l1 - tint.l0));
if (h2 > 1) h2 = 1;
else if (h2 < 0) h2 = 0;
if (s2 > 1) s2 = 1;
else if (s2 < 0) s2 = 0;
if (l2 > 1) l2 = 1;
else if (l2 < 0) l2 = 0;
hsl2rgb(h2,s2,l2,r,g,b);
// premultiply
// we only work with premultiplied source,
// thus all color values must be <= alpha
r *= a;
g *= a;
b *= a;
}
}
}
}
}
template <typename Src>
void apply_filter(Src & src, gray const& op)
{

View file

@ -39,7 +39,10 @@ struct image_filter_grammar :
{
image_filter_grammar();
qi::rule<Iterator, ContType(), qi::ascii::space_type> start;
qi::rule<Iterator, ContType(), qi::locals<int,int>, qi::ascii::space_type> filter;
qi::rule<Iterator, ContType(), qi::ascii::space_type> filter;
qi::rule<Iterator, qi::locals<int,int>, void(ContType&), qi::ascii::space_type> agg_blur_filter;
qi::rule<Iterator, qi::locals<std::string>, void(ContType&), qi::ascii::space_type> hsla_filter;
qi::rule<Iterator, std::string(), qi::ascii::space_type> string_arg;
qi::uint_parser< unsigned, 10, 1, 3 > radius_;
};

View file

@ -30,7 +30,6 @@
#include <boost/variant/variant_fwd.hpp>
// stl
#include <iostream>
#include <vector>
#include <ostream>
#include <iterator> // for std::back_insert_iterator
@ -55,6 +54,13 @@ struct agg_stack_blur
unsigned ry;
};
struct hsla
{
hsla(std::string const& tint_string)
: tinter(tint_string) {}
std::string tinter;
};
typedef boost::variant<filter::blur,
filter::gray,
filter::agg_stack_blur,
@ -64,7 +70,8 @@ typedef boost::variant<filter::blur,
filter::sobel,
filter::x_gradient,
filter::y_gradient,
filter::invert> filter_type;
filter::invert,
filter::hsla> filter_type;
inline std::ostream& operator<< (std::ostream& os, blur)
{
@ -80,7 +87,13 @@ inline std::ostream& operator<< (std::ostream& os, gray)
inline std::ostream& operator<< (std::ostream& os, agg_stack_blur const& filter)
{
os << "agg-stack-blur:" << filter.rx << ',' << filter.ry;
os << "agg-stack-blur(" << filter.rx << ',' << filter.ry << ')';
return os;
}
inline std::ostream& operator<< (std::ostream& os, hsla const& filter)
{
os << "hsla(" << filter.tinter << ')';
return os;
}

View file

@ -0,0 +1,60 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2013 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
*
*****************************************************************************/
#ifndef MAPNIK_REQUEST_HPP
#define MAPNIK_REQUEST_HPP
// mapnik
#include <mapnik/config.hpp>
#include <mapnik/box2d.hpp>
// boost
#include <boost/optional/optional.hpp>
namespace mapnik
{
class MAPNIK_DECL request
{
public:
request(unsigned width,
unsigned height,
box2d<double> const& extent);
unsigned width() const;
unsigned height() const;
void set_buffer_size(int buffer_size);
int buffer_size() const;
box2d<double> const& extent() const;
void set_extent(box2d<double> const& box);
box2d<double> get_buffered_extent() const;
double scale() const;
~request();
private:
unsigned width_;
unsigned height_;
box2d<double> extent_;
int buffer_size_;
};
}
#endif // MAPNIK_REQUEST_HPP

108
include/mapnik/util/hsl.hpp Normal file
View file

@ -0,0 +1,108 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2013 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
*
*****************************************************************************/
#ifndef MAPNIK_HSL_HPP
#define MAPNIK_HSL_HPP
#include <cmath>
namespace mapnik {
struct Tinter {
double h0;
double h1;
double s0;
double s1;
double l0;
double l1;
double a0;
double a1;
Tinter() :
h0(0),
h1(1),
s0(0),
s1(1),
l0(0),
l1(1),
a0(0),
a1(1) { }
bool is_identity() {
return (h0 == 0 &&
h1 == 1 &&
s0 == 0 &&
s1 == 1 &&
l0 == 0 &&
l1 == 1);
}
bool is_alpha_identity() {
return (a0 == 0 &&
a1 == 1);
}
};
static inline void rgb2hsl(unsigned char red, unsigned char green, unsigned char blue,
double & h, double & s, double & l) {
double r = red/255.0;
double g = green/255.0;
double b = blue/255.0;
double max = std::max(r,std::max(g,b));
double min = std::min(r,std::min(g,b));
double delta = max - min;
double gamma = max + min;
h = s = l = gamma / 2.0;
if (delta > 0.0) {
s = l > 0.5 ? delta / (2.0 - gamma) : delta / gamma;
if (max == r && max != g) h = (g - b) / delta + (g < b ? 6.0 : 0.0);
else if (max == g && max != b) h = (b - r) / delta + 2.0;
else if (max == b && max != r) h = (r - g) / delta + 4.0;
h /= 6.0;
}
}
static inline double hueToRGB(double m1, double m2, double h) {
// poor mans fmod
if(h < 0) h += 1;
if(h > 1) h -= 1;
if (h * 6 < 1) return m1 + (m2 - m1) * h * 6;
if (h * 2 < 1) return m2;
if (h * 3 < 2) return m1 + (m2 - m1) * (0.66666 - h) * 6;
return m1;
}
static inline void hsl2rgb(double h, double s, double l,
unsigned char & r, unsigned char & g, unsigned char & b) {
if (!s) {
r = g = b = static_cast<unsigned char>(l * 255);
}
double m2 = (l <= 0.5) ? l * (s + 1) : l + s - l * s;
double m1 = l * 2 - m2;
r = static_cast<unsigned char>(hueToRGB(m1, m2, h + 0.33333) * 255);
g = static_cast<unsigned char>(hueToRGB(m1, m2, h) * 255);
b = static_cast<unsigned char>(hueToRGB(m1, m2, h - 0.33333) * 255);
}
}
#endif // end MAPNIK_HSL_HPP

View file

@ -84,6 +84,25 @@ agg_renderer<T>::agg_renderer(Map const& m, T & pixmap, double scale_factor, uns
setup(m);
}
template <typename T>
agg_renderer<T>::agg_renderer(Map const& m, request const& req, T & pixmap, double scale_factor, unsigned offset_x, unsigned offset_y)
: feature_style_processor<agg_renderer>(m, scale_factor),
pixmap_(pixmap),
internal_buffer_(),
current_buffer_(&pixmap),
style_level_compositing_(false),
width_(pixmap_.width()),
height_(pixmap_.height()),
scale_factor_(scale_factor),
t_(req.width(),req.height(),req.extent(),offset_x,offset_y),
font_engine_(),
font_manager_(font_engine_),
detector_(boost::make_shared<label_collision_detector4>(box2d<double>(-req.buffer_size(), -req.buffer_size(), req.width() + req.buffer_size() ,req.height() + req.buffer_size()))),
ras_ptr(new rasterizer)
{
setup(m);
}
template <typename T>
agg_renderer<T>::agg_renderer(Map const& m, T & pixmap, boost::shared_ptr<label_collision_detector4> detector,
double scale_factor, unsigned offset_x, unsigned offset_y)

View file

@ -109,6 +109,7 @@ else: # unix, non-macos
source = Split(
"""
request.cpp
well_known_srs.cpp
params.cpp
image_filter_types.cpp

View file

@ -210,7 +210,7 @@ void cairo_context::set_dash(dash_array const &dashes, double scale_factor)
d[index++] = itr->second * scale_factor;
}
cairo_set_dash(cairo_.get() , &d[0], dashes.size(), 0/*offset*/);
cairo_set_dash(cairo_.get() , &d[0], d.size(), 0/*offset*/);
check_object_status_and_throw_exception(*this);
}

View file

@ -139,6 +139,28 @@ cairo_renderer_base::cairo_renderer_base(Map const& m,
setup(m);
}
cairo_renderer_base::cairo_renderer_base(Map const& m,
request const& req,
cairo_ptr const& cairo,
double scale_factor,
unsigned offset_x,
unsigned offset_y)
: m_(m),
context_(cairo),
width_(req.width()),
height_(req.height()),
scale_factor_(scale_factor),
t_(req.width(),req.height(),req.extent(),offset_x,offset_y),
font_engine_(boost::make_shared<freetype_engine>()),
font_manager_(*font_engine_),
face_manager_(font_engine_),
detector_(boost::make_shared<label_collision_detector4>(
box2d<double>(-req.buffer_size(), -req.buffer_size(),
req.width() + req.buffer_size(), req.height() + req.buffer_size())))
{
setup(m);
}
cairo_renderer_base::cairo_renderer_base(Map const& m,
cairo_ptr const& cairo,
boost::shared_ptr<label_collision_detector4> detector,
@ -169,6 +191,16 @@ cairo_renderer<cairo_surface_ptr>::cairo_renderer(Map const& m, cairo_surface_pt
: feature_style_processor<cairo_renderer>(m,scale_factor),
cairo_renderer_base(m,create_context(surface),scale_factor,offset_x,offset_y) {}
template <>
cairo_renderer<cairo_ptr>::cairo_renderer(Map const& m, request const& req, 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,scale_factor,offset_x,offset_y) {}
template <>
cairo_renderer<cairo_surface_ptr>::cairo_renderer(Map const& m, request const& req, 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),scale_factor,offset_x,offset_y) {}
template <>
cairo_renderer<cairo_ptr>::cairo_renderer(Map const& m, cairo_ptr const& cairo, boost::shared_ptr<label_collision_detector4> detector, double scale_factor, unsigned offset_x, unsigned offset_y)
: feature_style_processor<cairo_renderer>(m,scale_factor),

View file

@ -152,6 +152,24 @@ bool string2float(const char * iter, const char * end, float & result)
return r && (iter == end);
}
// double conversion - here we use sprintf over karma to work
// around https://github.com/mapnik/mapnik/issues/1741
bool to_string(std::string & s, double val)
{
s.resize(s.capacity());
while (true)
{
size_t n2 = static_cast<size_t>(snprintf(&s[0], s.size()+1, "%g", val));
if (n2 <= s.size())
{
s.resize(n2);
break;
}
s.resize(n2);
}
return true;
}
#ifdef MAPNIK_KARMA_TO_STRING
bool to_string(std::string & str, int value)
@ -184,95 +202,6 @@ bool to_string(std::string & str, bool value)
return karma::generate(sink, value);
}
namespace detail {
template <typename T>
struct double_policy : boost::spirit::karma::real_policies<T>
{
typedef boost::spirit::karma::real_policies<T> base_type;
static int floatfield(T n) {
using namespace boost::spirit; // for traits
if (traits::test_zero(n))
return base_type::fmtflags::fixed;
T abs_n = traits::get_absolute_value(n);
return (abs_n >= 1e16 || abs_n < 1e-4)
? base_type::fmtflags::scientific : base_type::fmtflags::fixed;
}
static unsigned precision(T n) {
if ( n == 0.0 ) return 0;
using namespace boost::spirit; // for traits
return static_cast<unsigned>(15 - boost::math::trunc(log10(traits::get_absolute_value(n))));
}
template <typename OutputIterator>
static bool dot(OutputIterator& sink, T n, unsigned precision) {
if (n == 0.0) return true; // avoid trailing zeroes
return base_type::dot(sink, n, precision);
}
template <typename OutputIterator>
static bool fraction_part (OutputIterator& sink, T n
, unsigned precision_, unsigned precision)
{
// NOTE: copied from karma only to avoid trailing zeroes
// (maybe a bug ?)
// allow for ADL to find the correct overload for floor and log10
using namespace std;
using namespace boost::spirit; // for traits
using namespace boost::spirit::karma; // for char_inserter
if ( traits::test_zero(n) ) return true; // this part added to karma
// The following is equivalent to:
// generate(sink, right_align(precision, '0')[ulong], n);
// but it's spelled out to avoid inter-modular dependencies.
typename boost::remove_const<T>::type digits =
(traits::test_zero(n) ? 0 : floor(log10(n))) + 1;
bool r = true;
for (/**/; r && digits < precision_; digits = digits + 1)
r = char_inserter<>::call(sink, '0');
if (precision && r)
r = int_inserter<10>::call(sink, n);
return r;
}
template <typename CharEncoding, typename Tag, typename OutputIterator>
static bool exponent (OutputIterator& sink, long n)
{
// NOTE: copied from karma to force sign in exponent
const bool force_sign = true;
using namespace boost::spirit; // for traits
using namespace boost::spirit::karma; // for char_inserter, sign_inserter
unsigned long abs_n = traits::get_absolute_value(n);
bool r = char_inserter<CharEncoding, Tag>::call(sink, 'e') &&
sign_inserter::call(sink, traits::test_zero(n)
, traits::test_negative(n), force_sign);
// the C99 Standard requires at least two digits in the exponent
if (r && abs_n < 10)
r = char_inserter<CharEncoding, Tag>::call(sink, '0');
return r && int_inserter<10>::call(sink, abs_n);
}
};
}
bool to_string(std::string & str, double value)
{
namespace karma = boost::spirit::karma;
typedef karma::real_generator<double, detail::double_policy<double> > double_type;
std::back_insert_iterator<std::string> sink(str);
return karma::generate(sink, double_type(), value);
}
#else
bool to_string(std::string & s, int val)
@ -332,22 +261,6 @@ bool to_string(std::string & s, bool val)
return true;
}
bool to_string(std::string & s, double val)
{
s.resize(s.capacity());
while (true)
{
size_t n2 = static_cast<size_t>(snprintf(&s[0], s.size()+1, "%g", val));
if (n2 <= s.size())
{
s.resize(n2);
break;
}
s.resize(n2);
}
return true;
}
#endif
} // end namespace util

View file

@ -39,6 +39,7 @@
#include <mapnik/font_set.hpp>
#include <mapnik/parse_path.hpp>
#include <mapnik/map.hpp>
#include <mapnik/request.hpp>
#include <mapnik/svg/svg_converter.hpp>
#include <mapnik/svg/svg_renderer_agg.hpp>
#include <mapnik/svg/svg_path_adapter.hpp>
@ -71,6 +72,24 @@ grid_renderer<T>::grid_renderer(Map const& m, T & pixmap, double scale_factor, u
setup(m);
}
template <typename T>
grid_renderer<T>::grid_renderer(Map const& m, request const& req, T & pixmap, double scale_factor, unsigned offset_x, unsigned offset_y)
: feature_style_processor<grid_renderer>(m, scale_factor),
pixmap_(pixmap),
width_(pixmap_.width()),
height_(pixmap_.height()),
scale_factor_(scale_factor),
// NOTE: can change this to m dims instead of pixmap_ if render-time
// resolution support is dropped from grid_renderer python interface
t_(pixmap_.width(),pixmap_.height(),req.extent(),offset_x,offset_y),
font_engine_(),
font_manager_(font_engine_),
detector_(boost::make_shared<label_collision_detector4>(box2d<double>(-req.buffer_size(), -req.buffer_size(), req.width() + req.buffer_size() ,req.height() + req.buffer_size()))),
ras_ptr(new grid_rasterizer)
{
setup(m);
}
template <typename T>
void grid_renderer<T>::setup(Map const& m)
{

View file

@ -44,8 +44,11 @@ image_filter_grammar<Iterator,ContType>::image_filter_grammar()
using qi::_1;
using qi::_a;
using qi::_b;
using qi::_r1;
using qi::eps;
using qi::char_;
using qi::lexeme;
using boost::spirit::ascii::string;
using phoenix::push_back;
using phoenix::construct;
@ -75,15 +78,26 @@ image_filter_grammar<Iterator,ContType>::image_filter_grammar()
|
lit("y-gradient")[push_back(_val,construct<mapnik::filter::y_gradient>())]
|
(lit("agg-stack-blur")[_a = 1, _b = 1]
>> -( lit('(') >> radius_[_a = _1]
>> lit(',')
>> radius_[_b = _1]
>> lit(')'))
[push_back(_val,construct<mapnik::filter::agg_stack_blur>(_a,_b))])
|
lit("invert")[push_back(_val,construct<mapnik::filter::invert>())]
|
agg_blur_filter(_val)
|
hsla_filter(_val)
;
agg_blur_filter = (lit("agg-stack-blur")[_a = 1, _b = 1]
>> -( lit('(') >> radius_[_a = _1]
>> lit(',')
>> radius_[_b = _1]
>> lit(')')))
[push_back(_r1,construct<mapnik::filter::agg_stack_blur>(_a,_b))]
;
hsla_filter = (lit("hsla") >> lit('(') >> string_arg[_a = _1] >> lit(')'))
[push_back(_r1,construct<mapnik::filter::hsla>(_a))]
;
string_arg = +~char_(')');
}
template struct mapnik::image_filter_grammar<std::string::const_iterator,std::vector<mapnik::filter::filter_type> >;

85
src/request.cpp Normal file
View file

@ -0,0 +1,85 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2013 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
*
*****************************************************************************/
// mapnik
#include <mapnik/request.hpp>
namespace mapnik
{
request::request(unsigned width,
unsigned height,
box2d<double> const& extent)
: width_(width),
height_(height),
extent_(extent),
buffer_size_(0) {}
request::~request() {}
unsigned request::width() const
{
return width_;
}
unsigned request::height() const
{
return height_;
}
void request::set_buffer_size(int buffer_size)
{
buffer_size_ = buffer_size;
}
int request::buffer_size() const
{
return buffer_size_;
}
void request::set_extent(box2d<double> const& box)
{
extent_ = box;
}
box2d<double> const& request::extent() const
{
return extent_;
}
box2d<double> request::get_buffered_extent() const
{
double extra = 2.0 * scale() * buffer_size_;
box2d<double> ext(extent_);
ext.width(extent_.width() + extra);
ext.height(extent_.height() + extra);
return ext;
}
double request::scale() const
{
if (width_>0)
return extent_.width()/width_;
return extent_.width();
}
}

View file

@ -11,6 +11,12 @@ test_env.AppendUnique(LIBS='mapnik')
test_env.AppendUnique(LIBS='sqlite3')
test_env.AppendUnique(CXXFLAGS='-g')
test_env['CXXFLAGS'] = copy(test_env['LIBMAPNIK_CXXFLAGS'])
if test_env['HAS_CAIRO']:
test_env.PrependUnique(CPPPATH=test_env['CAIRO_CPPPATHS'])
test_env.Append(CXXFLAGS = '-DHAVE_CAIRO')
for cpp_test in glob.glob('*_test.cpp'):
name = cpp_test.replace('.cpp','-bin')
source_files = [cpp_test]

View file

@ -65,11 +65,6 @@ int main( int, char*[] )
BOOST_TEST_EQ( out, "-0.0001" );
out.clear();
to_string(out, double(0.0001234567890123456));
// TODO: https://github.com/mapnik/mapnik/issues/1676
BOOST_TEST_EQ( out, "0.000123457" );
out.clear();
to_string(out, double(0.0001));
BOOST_TEST_EQ( out, "0.0001" );
out.clear();
@ -98,6 +93,23 @@ int main( int, char*[] )
BOOST_TEST_EQ( out, "1e-10" );
out.clear();
to_string(out, double(-1.234e+16));
BOOST_TEST_EQ( out, "-1.234e+16" );
out.clear();
// critical failure when karam is used
// https://github.com/mapnik/mapnik/issues/1741
// https://github.com/mapbox/tilemill/issues/1456
to_string(out, double(8.3));
BOOST_TEST_EQ( out, "8.3" );
out.clear();
// non-critical failures if karma is used
to_string(out, double(0.0001234567890123456));
// TODO: https://github.com/mapnik/mapnik/issues/1676
BOOST_TEST_EQ( out, "0.000123457" );
out.clear();
to_string(out, double(0.00000000001));
BOOST_TEST_EQ( out, "1e-11" );
out.clear();
@ -185,15 +197,7 @@ int main( int, char*[] )
to_string(out, double(1.234e+16));
BOOST_TEST_EQ( out, "1.234e+16" );
out.clear();
to_string(out, double(-1.234e+16));
BOOST_TEST_EQ( out, "-1.234e+16" );
out.clear();
// https://github.com/mapbox/tilemill/issues/1456
to_string(out, double(8.3));
BOOST_TEST_EQ( out, "8.3" );
out.clear();
// int
to_string(out, int(2));
BOOST_TEST_EQ( out, "2" );

View file

@ -0,0 +1,152 @@
#include <boost/version.hpp>
#include <boost/detail/lightweight_test.hpp>
#include <iostream>
#include <mapnik/map.hpp>
#include <mapnik/load_map.hpp>
#include <mapnik/agg_renderer.hpp>
#if defined(HAVE_CAIRO)
#include <mapnik/cairo_renderer.hpp>
#include <mapnik/cairo_context.hpp>
#endif
#include <mapnik/graphics.hpp>
#include <mapnik/image_util.hpp>
#include <mapnik/datasource_cache.hpp>
#include <mapnik/font_engine_freetype.hpp>
#include <mapnik/image_data.hpp>
#include <mapnik/image_reader.hpp>
#include <mapnik/scale_denominator.hpp>
#include <mapnik/feature_style_processor.hpp>
#include <boost/foreach.hpp>
bool compare_images(std::string const& src_fn,std::string const& dest_fn)
{
using namespace mapnik;
std::auto_ptr<mapnik::image_reader> reader1(mapnik::get_image_reader(dest_fn,"png"));
if (!reader1.get())
{
throw mapnik::image_reader_exception("Failed to load: " + dest_fn);
}
boost::shared_ptr<image_32> image_ptr1 = boost::make_shared<image_32>(reader1->width(),reader1->height());
reader1->read(0,0,image_ptr1->data());
std::auto_ptr<mapnik::image_reader> reader2(mapnik::get_image_reader(src_fn,"png"));
if (!reader2.get())
{
throw mapnik::image_reader_exception("Failed to load: " + src_fn);
}
boost::shared_ptr<image_32> image_ptr2 = boost::make_shared<image_32>(reader2->width(),reader2->height());
reader2->read(0,0,image_ptr2->data());
image_data_32 const& dest = image_ptr1->data();
image_data_32 const& src = image_ptr2->data();
unsigned int width = src.width();
unsigned int height = src.height();
if ((width != dest.width()) || height != dest.height()) return false;
for (unsigned int y = 0; y < height; ++y)
{
const unsigned int* row_from = src.getRow(y);
const unsigned int* row_to = dest.getRow(y);
for (unsigned int x = 0; x < width; ++x)
{
if (row_from[x] != row_to[x]) return false;
}
}
return true;
}
int main( int, char*[] )
{
std::string expected("./tests/cpp_tests/support/map-request-marker-text-line-expected.png");
std::string expected_cairo("./tests/cpp_tests/support/map-request-marker-text-line-expected-cairo.png");
try {
mapnik::datasource_cache::instance().register_datasources("./plugins/input/");
mapnik::freetype_engine::register_fonts("./fonts", true );
mapnik::Map m(256,256);
mapnik::load_map(m,"./tests/data/good_maps/marker-text-line.xml",false);
m.zoom_all();
mapnik::image_32 im(m.width(),m.height());
double scale_factor = 1.2;
// render normally with apply() and just map and image
mapnik::agg_renderer<mapnik::image_32> renderer1(m,im,scale_factor);
renderer1.apply();
std::string actual1("/tmp/map-request-marker-text-line-actual1.png");
//mapnik::save_to_file(im,expected);
mapnik::save_to_file(im,actual1);
BOOST_TEST(compare_images(actual1,expected));
// reset image
im.clear();
// set up a mapnik::request object
mapnik::request req(m.width(),m.height(),m.get_current_extent());
req.set_buffer_size(m.buffer_size());
// render using apply() and mapnik::request
mapnik::agg_renderer<mapnik::image_32> renderer2(m,req,im,scale_factor);
renderer2.apply();
std::string actual2("/tmp/map-request-marker-text-line-actual2.png");
mapnik::save_to_file(im,actual2);
BOOST_TEST(compare_images(actual2,expected));
// reset image
im.clear();
// render with apply_to_layer api and mapnik::request params passed to apply_to_layer
mapnik::agg_renderer<mapnik::image_32> renderer3(m,req,im,scale_factor);
renderer3.start_map_processing(m);
mapnik::projection map_proj(m.srs(),true);
double scale_denom = mapnik::scale_denominator(req.scale(),map_proj.is_geographic());
scale_denom *= scale_factor;
BOOST_FOREACH ( mapnik::layer const& lyr, m.layers() )
{
if (lyr.visible(scale_denom))
{
std::set<std::string> names;
renderer3.apply_to_layer(lyr,
renderer3,
map_proj,
req.scale(),
scale_denom,
req.width(),
req.height(),
req.extent(),
req.buffer_size(),
names);
}
}
renderer3.end_map_processing(m);
std::string actual3("/tmp/map-request-marker-text-line-actual3.png");
mapnik::save_to_file(im,actual3);
BOOST_TEST(compare_images(actual3,expected));
// also test cairo
#if defined(HAVE_CAIRO)
mapnik::cairo_surface_ptr image_surface(
cairo_image_surface_create(CAIRO_FORMAT_ARGB32,req.width(),req.height()),
mapnik::cairo_surface_closer());
mapnik::cairo_ptr image_context = (mapnik::create_context(image_surface));
mapnik::cairo_renderer<mapnik::cairo_ptr> png_render(m,req,image_context,scale_factor);
png_render.apply();
//cairo_surface_write_to_png(&*image_surface, expected_cairo.c_str());
std::string actual_cairo("/tmp/map-request-marker-text-line-actual4.png");
cairo_surface_write_to_png(&*image_surface, actual_cairo.c_str());
BOOST_TEST(compare_images(actual_cairo,expected_cairo));
#endif
// TODO - test grid_renderer
} catch (std::exception const& ex) {
std::clog << ex.what() << "\n";
}
if (!::boost::detail::test_errors()) {
std::clog << "C++ Map Request rendering hook: \x1b[1;32m✓ \x1b[0m\n";
#if BOOST_VERSION >= 104600
::boost::detail::report_errors_remind().called_report_errors_function = true;
#endif
} else {
return ::boost::report_errors();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -1,4 +1,4 @@
<Map>
<Map srs="+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs">
<Style name="ellipse">
<Rule>
<MarkersSymbolizer

View file

@ -27,7 +27,7 @@ Known values for OPTION are:
--libs print library linking information
--dep-libs print library linking information for Mapnik dependencies
--ldflags print library paths (-L) information
--cflags print pre-processor and compiler flags
--cxxflags print pre-processor and compiler flags
--fonts print default fonts directory
--input-plugins print default input plugins directory
--json print all config options as json object
@ -89,7 +89,7 @@ while test $# -gt 0; do
echo ${CONFIG_INPUT_PLUGINS}
;;
--cflags)
--cxxflags)
echo -I${CONFIG_MAPNIK_INCLUDE} -I${CONFIG_MAPNIK_AGG_INCLUDE} ${CONFIG_OTHER_INCLUDES}
;;