diff --git a/CMakeLists.txt b/CMakeLists.txt index ffb86d4ac..bd7b4ab6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -142,7 +142,10 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${MAPNIK_OUTPUT_DIR}") set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${MAPNIK_OUTPUT_DIR}/lib") # needs to be before the first call of find_boost. -list(APPEND MAPNIK_COMPILE_DEFS BOOST_SPIRIT_X3_HIDE_CXX17_WARNING) +if(CMAKE_CXX_STANDARD VERSION_LESS 17) + list(APPEND MAPNIK_COMPILE_DEFS BOOST_SPIRIT_X3_HIDE_CXX17_WARNING) +endif() + if(USE_MULTITHREADED) set(Boost_USE_MULTITHREADED ON) list(APPEND MAPNIK_COMPILE_DEFS MAPNIK_THREADSAFE) diff --git a/SConstruct b/SConstruct index 0c3f67ce6..25a054795 100644 --- a/SConstruct +++ b/SConstruct @@ -43,7 +43,7 @@ ICU_LIBS_DEFAULT='/usr/' DEFAULT_CC = "cc" DEFAULT_CXX = "c++" -DEFAULT_CXX_STD = "14" +DEFAULT_CXX_STD = "17" DEFAULT_CXX_CXXFLAGS = " -DU_USING_ICU_NAMESPACE=0" DEFAULT_CXX_LINKFLAGS = "" if sys.platform == 'darwin': @@ -467,9 +467,9 @@ opts.AddVariables( BoolVariable('MAPNIK_RENDER', 'Compile and install a utility to render a map to an image', 'True'), BoolVariable('COLOR_PRINT', 'Print build status information in color', 'True'), BoolVariable('BIGINT', 'Compile support for 64-bit integers in mapnik::value', 'True'), - BoolVariable('USE_BOOST_FILESYSTEM','Use boost::filesytem even if `std::filesystem` is avaible (since c++17)', 'False'), + BoolVariable('USE_BOOST_FILESYSTEM','Use boost::filesytem even if `std::filesystem` is available (since c++17)', 'False'), BoolVariable('QUIET', 'Reduce build verbosity', 'False'), - ) +) # variables to pickle after successful configure step # these include all scons core variables as well as custom @@ -1633,6 +1633,9 @@ if not preconfigured: if env['BIGINT']: env.Append(CPPDEFINES = '-DBIGINT') + if int(env['CXX_STD']) < 17: + env['USE_BOOST_FILESYSTEM'] = True + if env['USE_BOOST_FILESYSTEM']: env.Append(CPPDEFINES = '-DUSE_BOOST_FILESYSTEM') @@ -1659,7 +1662,7 @@ if not preconfigured: ['program_options', 'boost/program_options.hpp', False] ] - if int(env['CXX_STD']) < 17 or env['USE_BOOST_FILESYSTEM']: + if env['USE_BOOST_FILESYSTEM']: BOOST_LIBSHEADERS.append(['system', 'boost/system/system_error.hpp', True]) BOOST_LIBSHEADERS.append(['filesystem', 'boost/filesystem/operations.hpp', True]) # if requested, sort LIBPATH and CPPPATH before running CheckLibWithHeader tests diff --git a/demo/viewer/mainwindow.cpp b/demo/viewer/mainwindow.cpp index 753b1286b..2b211bca9 100644 --- a/demo/viewer/mainwindow.cpp +++ b/demo/viewer/mainwindow.cpp @@ -106,10 +106,7 @@ MainWindow::MainWindow() // slider connect(slider_, SIGNAL(valueChanged(int)), mapWidget_, SLOT(zoomToLevel(int))); // renderer selector - connect(renderer_selector_, - SIGNAL(currentIndexChanged(QString const&)), - mapWidget_, - SLOT(updateRenderer(QString const&))); + connect(renderer_selector_, SIGNAL(currentIndexChanged(int)), mapWidget_, SLOT(updateRenderer(int))); // scale factor connect(scale_factor_, SIGNAL(valueChanged(double)), mapWidget_, SLOT(updateScaleFactor(double))); @@ -390,7 +387,7 @@ void MainWindow::createToolBars() scale_factor_ = new QDoubleSpinBox(fileToolBar); scale_factor_->setMinimum(0.1); - scale_factor_->setMaximum(5.0); + scale_factor_->setMaximum(10.0); scale_factor_->setSingleStep(0.1); scale_factor_->setValue(1.0); diff --git a/demo/viewer/mapwidget.cpp b/demo/viewer/mapwidget.cpp index 184b928c4..942c09593 100644 --- a/demo/viewer/mapwidget.cpp +++ b/demo/viewer/mapwidget.cpp @@ -545,15 +545,15 @@ void render_cairo(mapnik::Map const& map, double scaling_factor, QPixmap& pix) #endif } -void MapWidget::updateRenderer(QString const& txt) +void MapWidget::updateRenderer(int index) { - if (txt == "AGG") + std::cerr << "updateRenderer:" << index << std::endl; + if (index == 0) cur_renderer_ = AGG; - else if (txt == "Cairo") + else if (index == 1) cur_renderer_ = Cairo; - else if (txt == "Grid") + else if (index == 2) cur_renderer_ = Grid; - std::cerr << "Update renderer called" << std::endl; updateMap(); } diff --git a/demo/viewer/mapwidget.hpp b/demo/viewer/mapwidget.hpp index 4f6748f71..292ef2e26 100644 --- a/demo/viewer/mapwidget.hpp +++ b/demo/viewer/mapwidget.hpp @@ -82,7 +82,7 @@ class MapWidget : public QWidget void zoomToLevel(int level); void updateMap(); void layerSelected(int); - void updateRenderer(QString const& txt); + void updateRenderer(int); void updateScaleFactor(double scale_factor); signals: void mapViewChanged(); diff --git a/deps/agg/include/agg_pixfmt_rgba.h b/deps/agg/include/agg_pixfmt_rgba.h index 40ddf6a07..225fdef69 100644 --- a/deps/agg/include/agg_pixfmt_rgba.h +++ b/deps/agg/include/agg_pixfmt_rgba.h @@ -24,6 +24,7 @@ #ifndef AGG_PIXFMT_RGBA_INCLUDED #define AGG_PIXFMT_RGBA_INCLUDED +#include #include #include #include "agg_basics.h" @@ -3166,6 +3167,10 @@ private: +extern template struct MAPNIK_DECL comp_op_rgba_hue; +extern template struct MAPNIK_DECL comp_op_rgba_saturation; +extern template struct MAPNIK_DECL comp_op_rgba_color; +extern template struct MAPNIK_DECL comp_op_rgba_value; //----------------------------------------------------------------------- typedef blender_rgba blender_rgba32; //----blender_rgba32 diff --git a/include/mapnik/agg_renderer.hpp b/include/mapnik/agg_renderer.hpp index d73ea3e8e..ca6d080a9 100644 --- a/include/mapnik/agg_renderer.hpp +++ b/include/mapnik/agg_renderer.hpp @@ -92,7 +92,7 @@ class buffer_stack void pop() { - // ^ ensure irator is not out-of-range + // ^ ensure iterator is not out-of-range // prior calling this method ++position_; } diff --git a/include/mapnik/cairo/cairo_render_vector.hpp b/include/mapnik/cairo/cairo_render_vector.hpp index e28f499f4..9fca4b617 100644 --- a/include/mapnik/cairo/cairo_render_vector.hpp +++ b/include/mapnik/cairo/cairo_render_vector.hpp @@ -34,7 +34,7 @@ class cairo_context; void render_vector_marker(cairo_context& context, svg_path_adapter& svg_path, - svg_attribute_type const& attributes, + svg::group const& group_attr, box2d const& bbox, agg::trans_affine const& tr, double opacity); diff --git a/include/mapnik/cairo/render_polygon_pattern.hpp b/include/mapnik/cairo/render_polygon_pattern.hpp index 89dba18e3..9f2885624 100644 --- a/include/mapnik/cairo/render_polygon_pattern.hpp +++ b/include/mapnik/cairo/render_polygon_pattern.hpp @@ -56,11 +56,11 @@ struct cairo_renderer_process_visitor_p cairo_context context(cairo); svg_storage_type& svg = *marker.get_data(); - svg_attribute_type const& svg_attributes = svg.attributes(); + auto const& svg_group = svg.svg_group(); svg::vertex_stl_adapter stl_storage(svg.source()); svg::svg_path_adapter svg_path(stl_storage); - render_vector_marker(context, svg_path, svg_attributes, bbox, tr, opacity_); + render_vector_marker(context, svg_path, svg_group, bbox, tr, opacity_); return surface; } diff --git a/include/mapnik/feature_style_processor.hpp b/include/mapnik/feature_style_processor.hpp index a8047fd0e..060ffacf8 100644 --- a/include/mapnik/feature_style_processor.hpp +++ b/include/mapnik/feature_style_processor.hpp @@ -47,7 +47,11 @@ struct layer_rendering_material; enum eAttributeCollectionPolicy { DEFAULT = 0, COLLECT_ALL = 1 }; template +#ifdef __GNUC__ +class MAPNIK_DECL feature_style_processor +#else class feature_style_processor +#endif { public: explicit feature_style_processor(Map const& m, double scale_factor = 1.0); diff --git a/include/mapnik/geometry/boost_adapters.hpp b/include/mapnik/geometry/boost_adapters.hpp index 6efc6b376..5700e8c6e 100644 --- a/include/mapnik/geometry/boost_adapters.hpp +++ b/include/mapnik/geometry/boost_adapters.hpp @@ -42,6 +42,8 @@ MAPNIK_DISABLE_WARNING_POP #include #include #include +// std +#include BOOST_GEOMETRY_REGISTER_POINT_2D(mapnik::geometry::point, double, boost::geometry::cs::cartesian, x, y) BOOST_GEOMETRY_REGISTER_POINT_2D(mapnik::geometry::point, diff --git a/include/mapnik/json/properties_generator_grammar.hpp b/include/mapnik/json/properties_generator_grammar.hpp index 29af61798..e3f58634e 100644 --- a/include/mapnik/json/properties_generator_grammar.hpp +++ b/include/mapnik/json/properties_generator_grammar.hpp @@ -30,8 +30,7 @@ MAPNIK_DISABLE_WARNING_PUSH #include #include -#include -#include +#include MAPNIK_DISABLE_WARNING_POP #include diff --git a/include/mapnik/marker_helpers.hpp b/include/mapnik/marker_helpers.hpp index deaabd72d..60885e15e 100644 --- a/include/mapnik/marker_helpers.hpp +++ b/include/mapnik/marker_helpers.hpp @@ -56,7 +56,7 @@ struct vector_markers_dispatch : util::noncopyable { vector_markers_dispatch(svg_path_ptr const& src, svg_path_adapter& path, - svg_attribute_type const& attrs, + svg::group const& group_attrs, agg::trans_affine const& marker_trans, symbolizer_base const& sym, Detector& detector, @@ -69,7 +69,7 @@ struct vector_markers_dispatch : util::noncopyable , renderer_context_(renderer_context) , src_(src) , path_(path) - , attrs_(attrs) + , group_attrs_(group_attrs) , detector_(detector) {} @@ -86,7 +86,7 @@ struct vector_markers_dispatch : util::noncopyable agg::trans_affine matrix = params_.placement_params.tr; matrix.rotate(angle); matrix.translate(x, y); - renderer_context_.render_marker(src_, path_, attrs_, params_, matrix); + renderer_context_.render_marker(src_, path_, group_attrs_, params_, matrix); } } @@ -101,7 +101,7 @@ struct vector_markers_dispatch : util::noncopyable markers_renderer_context& renderer_context_; svg_path_ptr const& src_; svg_path_adapter& path_; - svg_attribute_type const& attrs_; + svg::group const& group_attrs_; Detector& detector_; }; @@ -152,8 +152,8 @@ void build_ellipse(symbolizer_base const& sym, svg_storage_type& marker_ellipse, svg::svg_path_adapter& svg_path); -bool push_explicit_style(svg_attribute_type const& src, - svg_attribute_type& dst, +bool push_explicit_style(svg::group const& src, + svg::group& dst, symbolizer_base const& sym, feature_impl& feature, attributes const& vars); diff --git a/include/mapnik/pixel_position.hpp b/include/mapnik/pixel_position.hpp index 991719c9c..cbfb3d145 100644 --- a/include/mapnik/pixel_position.hpp +++ b/include/mapnik/pixel_position.hpp @@ -27,7 +27,38 @@ namespace mapnik { -struct rotation; +struct rotation +{ + rotation() + : sin(0) + , cos(1.) + {} + rotation(double sin_, double cos_) + : sin(sin_) + , cos(cos_) + {} + rotation(double angle) + : sin(std::sin(angle)) + , cos(std::cos(angle)) + {} + void reset() + { + sin = 0.; + cos = 1.; + } + void init(double angle) + { + sin = std::sin(angle); + cos = std::cos(angle); + } + double sin; + double cos; + rotation operator~() const { return rotation(sin, -cos); } + rotation operator!() const { return rotation(-sin, cos); } + + double angle() const { return std::atan2(sin, cos); } +}; + struct pixel_position { double x; @@ -59,11 +90,17 @@ struct pixel_position } pixel_position rotate(rotation const& rot) const; + pixel_position operator~() const { return pixel_position(x, -y); } double length() { return std::sqrt(x * x + y * y); } }; +inline pixel_position pixel_position::rotate(rotation const& rot) const +{ + return pixel_position(x * rot.cos - y * rot.sin, x * rot.sin + y * rot.cos); +} + inline pixel_position operator*(double factor, pixel_position const& pos) { return pixel_position(factor * pos.x, factor * pos.y); diff --git a/include/mapnik/projection.hpp b/include/mapnik/projection.hpp index 6dc4ad399..5b65ea8d8 100644 --- a/include/mapnik/projection.hpp +++ b/include/mapnik/projection.hpp @@ -77,7 +77,8 @@ class MAPNIK_DECL projection std::string const& params() const; void forward(double& x, double& y) const; void inverse(double& x, double& y) const; - std::string expanded() const; + std::string definition() const; + std::string description() const; void init_proj() const; private: diff --git a/include/mapnik/renderer_common/render_markers_symbolizer.hpp b/include/mapnik/renderer_common/render_markers_symbolizer.hpp index 756383527..c7bb02918 100644 --- a/include/mapnik/renderer_common/render_markers_symbolizer.hpp +++ b/include/mapnik/renderer_common/render_markers_symbolizer.hpp @@ -58,7 +58,7 @@ struct markers_renderer_context : util::noncopyable virtual void render_marker(svg_path_ptr const& src, svg_path_adapter& path, - svg_attribute_type const& attrs, + svg::group const& group_attrs, markers_dispatch_params const& params, agg::trans_affine const& marker_tr) = 0; }; diff --git a/include/mapnik/renderer_common/render_thunk.hpp b/include/mapnik/renderer_common/render_thunk.hpp index 58b773eb2..3553f282a 100644 --- a/include/mapnik/renderer_common/render_thunk.hpp +++ b/include/mapnik/renderer_common/render_thunk.hpp @@ -44,20 +44,20 @@ namespace mapnik { struct vector_marker_render_thunk : util::movable { svg_path_ptr src_; - svg_attribute_type attrs_; + svg::group group_attrs_; agg::trans_affine tr_; double opacity_; composite_mode_e comp_op_; bool snap_to_pixels_; vector_marker_render_thunk(svg_path_ptr const& src, - svg_attribute_type const& attrs, + svg::group const& group_attrs, agg::trans_affine const& marker_trans, double opacity, composite_mode_e comp_op, bool snap_to_pixels) : src_(src) - , attrs_(attrs) + , group_attrs_(group_attrs) , tr_(marker_trans) , opacity_(opacity) , comp_op_(comp_op) diff --git a/include/mapnik/svg/geometry_svg_generator.hpp b/include/mapnik/svg/geometry_svg_generator.hpp index c127b6cdb..df9ae843e 100644 --- a/include/mapnik/svg/geometry_svg_generator.hpp +++ b/include/mapnik/svg/geometry_svg_generator.hpp @@ -36,8 +36,8 @@ MAPNIK_DISABLE_WARNING_PUSH #include #include -#include -#include +#include +#include #include #include MAPNIK_DISABLE_WARNING_POP diff --git a/include/mapnik/svg/svg_bounding_box.hpp b/include/mapnik/svg/svg_bounding_box.hpp new file mode 100644 index 000000000..1085a0910 --- /dev/null +++ b/include/mapnik/svg/svg_bounding_box.hpp @@ -0,0 +1,114 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2021 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_SVG_BOUNDING_BOX_HPP +#define MAPNIK_SVG_BOUNDING_BOX_HPP + +#include +#include +MAPNIK_DISABLE_WARNING_PUSH +#include +#include "agg_basics.h" +MAPNIK_DISABLE_WARNING_POP + +namespace mapnik { +namespace svg { +namespace detail { +template +struct bounding_box +{ + bounding_box(VertexSource& vs, double& x1, double& y1, double& x2, double& y2, bool& first) + : vs_(vs) + , x1_(x1) + , y1_(y1) + , x2_(x2) + , y2_(y2) + , first_(first) + {} + + void operator()(group const& g) const + { + for (auto const& elem : g.elements) + { + mapbox::util::apply_visitor(bounding_box(vs_, x1_, y1_, x2_, y2_, first_), elem); + } + } + void operator()(path_attributes const& attr) const + { + vs_.rewind(attr.index); + vs_.transformer(const_cast(attr.transform)); + unsigned cmd; + double x; + double y; + while (!is_stop(cmd = vs_.vertex(&x, &y))) + { + if (is_vertex(cmd)) + { + if (first_) + { + x1_ = x; + y1_ = y; + x2_ = x; + y2_ = y; + first_ = false; + } + else + { + if (x < x1_) + x1_ = x; + if (y < y1_) + y1_ = y; + if (x > x2_) + x2_ = x; + if (y > y2_) + y2_ = y; + } + } + } + } + VertexSource& vs_; + double& x1_; + double& y1_; + double& x2_; + double& y2_; + bool& first_; +}; +} // namespace detail + +template +void bounding_box(VertexSource& vs, group const& g, double& x1, double& y1, double& x2, double& y2) +{ + bool first = true; + x1 = 1.0; + y1 = 1.0; + x2 = 0.0; + y2 = 0.0; // ^^ FIXME: AGG specific "invalid bbox" logic + for (auto const& elem : g.elements) + { + mapbox::util::apply_visitor(detail::bounding_box(vs, x1, y1, x2, y2, first), elem); + } +} + +} // namespace svg +} // namespace mapnik + +#endif // MAPNIK_SVG_BOUNDING_BOX_HPP diff --git a/include/mapnik/svg/svg_converter.hpp b/include/mapnik/svg/svg_converter.hpp index 3f89aa64f..103a14d6c 100644 --- a/include/mapnik/svg/svg_converter.hpp +++ b/include/mapnik/svg/svg_converter.hpp @@ -26,6 +26,7 @@ // mapnik #include #include +#include #include #include @@ -38,13 +39,16 @@ MAPNIK_DISABLE_WARNING_PUSH #include "agg_conv_contour.h" #include "agg_conv_curve.h" #include "agg_color_rgba.h" -#include "agg_bounding_rect.h" MAPNIK_DISABLE_WARNING_POP // stl +#include #include #include +#include +#include + namespace mapnik { namespace svg { @@ -53,30 +57,41 @@ class svg_converter : util::noncopyable { public: - svg_converter(VertexSource& source, AttributeSource& attributes) + svg_converter(VertexSource& source, group& svg_group) : source_(source) - , attributes_(attributes) + , svg_group_(svg_group) , attr_stack_() , svg_width_(0.0) , svg_height_(0.0) {} + void set_opacity(double opacity) { svg_group_.opacity = opacity; } + + void begin_group() + { + current_group_->elements.emplace_back(group{cur_attr().opacity, {}, current_group_}); + current_group_ = ¤t_group_->elements.back().get(); + } + + void end_group() { current_group_ = current_group_->parent; } + void begin_path() { std::size_t idx = source_.start_new_path(); - attributes_.emplace_back(cur_attr(), safe_cast(idx)); + current_group_->elements.emplace_back(path_attributes{cur_attr(), safe_cast(idx)}); } void end_path() { - if (attributes_.empty()) + if (current_group_->elements.empty()) { throw std::runtime_error("end_path : The path was not begun"); } - path_attributes& attr = attributes_.back(); - unsigned idx = attr.index; - attr = cur_attr(); - attr.index = idx; + // auto& elem = current_group_->elements.back(); + // auto& attr = elem.get(); + // unsigned index = attr.index; + // attr = cur_attr(); + // attr.index = index; } void move_to(double x, double y, bool rel = false) // M, m @@ -295,17 +310,10 @@ class svg_converter : util::noncopyable // Make all polygons CCW-oriented void arrange_orientations() { source_.arrange_orientations_all_paths(agg::path_flags_ccw); } - // FIXME!!!! - unsigned operator[](unsigned idx) - { - transform_ = attributes_[idx].transform; - return attributes_[idx].index; - } - void bounding_rect(double* x1, double* y1, double* x2, double* y2) { - agg::conv_transform trans(source_, transform_); - agg::bounding_rect(trans, *this, 0, attributes_.size(), x1, y1, x2, y2); + agg::conv_transform path(source_, transform_); + bounding_box(path, svg_group_, *x1, *y1, *x2, *y2); } void set_dimensions(double w, double h) @@ -334,7 +342,8 @@ class svg_converter : util::noncopyable private: VertexSource& source_; - AttributeSource& attributes_; + group& svg_group_; + group* current_group_ = &svg_group_; AttributeSource attr_stack_; agg::trans_affine transform_; double svg_width_; diff --git a/include/mapnik/svg/svg_group.hpp b/include/mapnik/svg/svg_group.hpp new file mode 100644 index 000000000..8c977cc7c --- /dev/null +++ b/include/mapnik/svg/svg_group.hpp @@ -0,0 +1,48 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2021 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_SVG_GROUP_HPP +#define MAPNIK_SVG_GROUP_HPP + +#include +#include +#include +#include + +namespace mapnik { +namespace svg { + +struct group; + +using group_element = mapbox::util::variant; + +struct group +{ + double opacity = 1.0; + std::vector elements; + group* parent = nullptr; +}; + +} // namespace svg +} // namespace mapnik + +#endif // MAPNIK_SVG_GROUP_HPP diff --git a/include/mapnik/svg/svg_renderer_agg.hpp b/include/mapnik/svg/svg_renderer_agg.hpp index 89701063b..8afed5c74 100644 --- a/include/mapnik/svg/svg_renderer_agg.hpp +++ b/include/mapnik/svg/svg_renderer_agg.hpp @@ -25,6 +25,7 @@ // mapnik #include +#include #include #include #include @@ -41,6 +42,7 @@ MAPNIK_DISABLE_WARNING_POP #include MAPNIK_DISABLE_WARNING_PUSH #include +#include "agg_pixfmt_rgba.h" #include "agg_path_storage.h" #include "agg_conv_transform.h" #include "agg_conv_stroke.h" @@ -62,6 +64,8 @@ MAPNIK_DISABLE_WARNING_PUSH #include "agg_span_interpolator_linear.h" MAPNIK_DISABLE_WARNING_POP +#include + namespace mapnik { namespace svg { @@ -124,122 +128,16 @@ class renderer_agg : util::noncopyable using vertex_source_type = VertexSource; using attribute_source_type = AttributeSource; - renderer_agg(VertexSource& source, AttributeSource const& attributes) + // mapnik::svg_path_adapter, mapnik::svg_attribute_type, renderer_solid, agg::pixfmt_rgba32_pre + renderer_agg(VertexSource& source, group const& svg_group) : source_(source) , curved_(source_) , curved_dashed_(curved_) , curved_stroked_(curved_) , curved_dashed_stroked_(curved_dashed_) - , attributes_(attributes) + , svg_group_(svg_group) {} - template - void render_gradient(Rasterizer& ras, - Scanline& sl, - Renderer& ren, - gradient const& grad, - agg::trans_affine const& mtx, - double opacity, - box2d const& symbol_bbox, - curved_trans_type& curved_trans, - unsigned path_id) - { - using gamma_lut_type = agg::gamma_lut; - using color_func_type = agg::gradient_lut, 1024>; - using interpolator_type = agg::span_interpolator_linear<>; - using span_allocator_type = agg::span_allocator; - - span_allocator_type m_alloc; - color_func_type m_gradient_lut; - gamma_lut_type m_gamma_lut; - - double x1, x2, y1, y2, radius; - grad.get_control_points(x1, y1, x2, y2, radius); - - m_gradient_lut.remove_all(); - for (mapnik::stop_pair const& st : grad.get_stop_array()) - { - mapnik::color const& stop_color = st.second; - unsigned r = stop_color.red(); - unsigned g = stop_color.green(); - unsigned b = stop_color.blue(); - unsigned a = stop_color.alpha(); - m_gradient_lut.add_color(st.first, agg::rgba8_pre(r, g, b, int(a * opacity))); - } - if (m_gradient_lut.build_lut()) - { - agg::trans_affine transform = mtx; - double scale = mtx.scale(); - transform.invert(); - agg::trans_affine tr; - tr = grad.get_transform(); - tr.invert(); - transform *= tr; - - if (grad.get_units() != USER_SPACE_ON_USE) - { - double bx1 = symbol_bbox.minx(); - double by1 = symbol_bbox.miny(); - double bx2 = symbol_bbox.maxx(); - double by2 = symbol_bbox.maxy(); - - if (grad.get_units() == OBJECT_BOUNDING_BOX) - { - bounding_rect_single(curved_trans, path_id, &bx1, &by1, &bx2, &by2); - } - transform.translate(-bx1 / scale, -by1 / scale); - transform.scale(scale / (bx2 - bx1), scale / (by2 - by1)); - } - - if (grad.get_gradient_type() == RADIAL) - { - using gradient_adaptor_type = agg::gradient_radial_focus; - using span_gradient_type = - agg::span_gradient; - - // the agg radial gradient assumes it is centred on 0 - transform.translate(-x2, -y2); - - // scale everything up since agg turns things into integers a bit too soon - int scaleup = 255; - radius *= scaleup; - x1 *= scaleup; - y1 *= scaleup; - x2 *= scaleup; - y2 *= scaleup; - - transform.scale(scaleup, scaleup); - interpolator_type span_interpolator(transform); - gradient_adaptor_type gradient_adaptor(radius, (x1 - x2), (y1 - y2)); - - span_gradient_type span_gradient(span_interpolator, gradient_adaptor, m_gradient_lut, 0, radius); - - render_scanlines_aa(ras, sl, ren, m_alloc, span_gradient); - } - else - { - using gradient_adaptor_type = linear_gradient_from_segment; - using span_gradient_type = - agg::span_gradient; - // scale everything up since agg turns things into integers a bit too soon - int scaleup = 255; - x1 *= scaleup; - y1 *= scaleup; - x2 *= scaleup; - y2 *= scaleup; - - transform.scale(scaleup, scaleup); - - interpolator_type span_interpolator(transform); - gradient_adaptor_type gradient_adaptor(x1, y1, x2, y2); - - span_gradient_type span_gradient(span_interpolator, gradient_adaptor, m_gradient_lut, 0, scaleup); - - render_scanlines_aa(ras, sl, ren, m_alloc, span_gradient); - } - } - } - template void render(Rasterizer& ras, Scanline& sl, @@ -249,67 +147,245 @@ class renderer_agg : util::noncopyable box2d const& symbol_bbox) { - using namespace agg; - trans_affine transform; - - curved_stroked_trans_type curved_stroked_trans(curved_stroked_, transform); - curved_dashed_stroked_trans_type curved_dashed_stroked_trans(curved_dashed_stroked_, transform); - curved_trans_type curved_trans(curved_, transform); - curved_trans_contour_type curved_trans_contour(curved_trans); - - curved_trans_contour.auto_detect_orientation(true); - - for (unsigned i = 0; i < attributes_.size(); ++i) + double adjusted_opacity = opacity * svg_group_.opacity; // adjust top level opacity + if (adjusted_opacity < 1.0) { - mapnik::svg::path_attributes const& attr = attributes_[i]; + mapnik::image_rgba8 im(ren.width(), ren.height(), true, true); + agg::rendering_buffer buf(im.bytes(), im.width(), im.height(), im.row_size()); + PixelFormat pixf(buf); + Renderer ren_g(pixf); + for (auto const& elem : svg_group_.elements) + { + mapbox::util::apply_visitor( + group_renderer(*this, ras, sl, ren_g, mtx, symbol_bbox), + elem); + } + ren.blend_from(ren_g.ren(), 0, 0, 0, unsigned(adjusted_opacity * 255)); + } + else + { + for (auto const& elem : svg_group_.elements) + { + mapbox::util::apply_visitor( + group_renderer(*this, ras, sl, ren, mtx, symbol_bbox), + elem); + } + } + } + + template + struct group_renderer + { + group_renderer(renderer_agg& renderer, + Rasterizer& ras, + Scanline& sl, + Renderer& ren, + agg::trans_affine const& mtx, + box2d const& symbol_bbox) + : renderer_(renderer) + , ras_(ras) + , sl_(sl) + , ren_(ren) + , mtx_(mtx) + , symbol_bbox_(symbol_bbox) + {} + + void render_gradient(Rasterizer& ras, + Scanline& sl, + Renderer& ren, + gradient const& grad, + agg::trans_affine const& mtx, + double opacity, + box2d const& symbol_bbox, + curved_trans_type& curved_trans, + unsigned path_id) const + { + using gamma_lut_type = agg::gamma_lut; + using color_func_type = agg::gradient_lut, 1024>; + using interpolator_type = agg::span_interpolator_linear<>; + using span_allocator_type = agg::span_allocator; + + span_allocator_type m_alloc; + color_func_type m_gradient_lut; + gamma_lut_type m_gamma_lut; + + double x1, x2, y1, y2, radius; + grad.get_control_points(x1, y1, x2, y2, radius); + + m_gradient_lut.remove_all(); + for (mapnik::stop_pair const& st : grad.get_stop_array()) + { + mapnik::color const& stop_color = st.second; + unsigned r = stop_color.red(); + unsigned g = stop_color.green(); + unsigned b = stop_color.blue(); + unsigned a = stop_color.alpha(); + m_gradient_lut.add_color(st.first, agg::rgba8_pre(r, g, b, int(a * opacity))); + } + if (m_gradient_lut.build_lut()) + { + agg::trans_affine tr = mtx; + agg::trans_affine transform = grad.get_transform(); + transform *= tr; + transform.invert(); + + if (grad.get_units() != USER_SPACE_ON_USE) + { + double scale = mtx.scale(); + double bx1 = symbol_bbox.minx(); + double by1 = symbol_bbox.miny(); + double bx2 = symbol_bbox.maxx(); + double by2 = symbol_bbox.maxy(); + + if (grad.get_units() == OBJECT_BOUNDING_BOX) + { + bounding_rect_single(curved_trans, path_id, &bx1, &by1, &bx2, &by2); + } + transform.translate(-bx1 / scale, -by1 / scale); + transform.scale(scale / (bx2 - bx1), scale / (by2 - by1)); + } + else + { + double scaledown = 255; + x1 /= scaledown; // fx + y1 /= scaledown; // fy + x2 /= scaledown; // cx + y2 /= scaledown; // cy + radius /= scaledown; + transform.translate(-symbol_bbox.minx(), -symbol_bbox.miny()); + transform.scale(1 / scaledown, 1 / scaledown); + } + if (grad.get_gradient_type() == RADIAL) + { + using gradient_adaptor_type = agg::gradient_radial_focus; + using span_gradient_type = + agg::span_gradient; + + // the agg radial gradient assumes it is centred on 0 + transform.translate(-x2, -y2); + // scale everything up since agg turns things into integers a bit too soon + int scaleup = 255; + radius *= scaleup; + x1 *= scaleup; + y1 *= scaleup; + x2 *= scaleup; + y2 *= scaleup; + transform.scale(scaleup, scaleup); + + interpolator_type span_interpolator(transform); + gradient_adaptor_type gradient_adaptor(radius, (x1 - x2), (y1 - y2)); + + span_gradient_type span_gradient(span_interpolator, gradient_adaptor, m_gradient_lut, 0, radius); + + render_scanlines_aa(ras, sl, ren, m_alloc, span_gradient); + } + else + { + using gradient_adaptor_type = linear_gradient_from_segment; + using span_gradient_type = + agg::span_gradient; + // scale everything up since agg turns things into integers a bit too soon + int scaleup = 255; + x1 *= scaleup; + y1 *= scaleup; + x2 *= scaleup; + y2 *= scaleup; + + transform.scale(scaleup, scaleup); + + interpolator_type span_interpolator(transform); + gradient_adaptor_type gradient_adaptor(x1, y1, x2, y2); + + span_gradient_type span_gradient(span_interpolator, gradient_adaptor, m_gradient_lut, 0, scaleup); + + render_scanlines_aa(ras, sl, ren, m_alloc, span_gradient); + } + } + } + + void operator()(group const& g) const + { + double opacity = g.opacity; + if (opacity < 1.0) + { + mapnik::image_rgba8 im(ren_.width(), ren_.height(), true, true); + agg::rendering_buffer buf(im.bytes(), im.width(), im.height(), im.row_size()); + PixelFormat pixf(buf); + Renderer ren(pixf); + for (auto const& elem : g.elements) + { + mapbox::util::apply_visitor(group_renderer(renderer_, ras_, sl_, ren, mtx_, symbol_bbox_), elem); + } + ren_.blend_from(ren.ren(), 0, 0, 0, unsigned(opacity * 255)); + } + else + { + for (auto const& elem : g.elements) + { + mapbox::util::apply_visitor(group_renderer(renderer_, ras_, sl_, ren_, mtx_, symbol_bbox_), elem); + } + } + } + + void operator()(path_attributes const& attr) const + { + using namespace agg; + trans_affine transform; + + curved_stroked_trans_type curved_stroked_trans(renderer_.curved_stroked_, transform); + curved_dashed_stroked_trans_type curved_dashed_stroked_trans(renderer_.curved_dashed_stroked_, transform); + curved_trans_type curved_trans(renderer_.curved_, transform); + curved_trans_contour_type curved_trans_contour(curved_trans); + curved_trans_contour.auto_detect_orientation(true); + if (!attr.visibility_flag) - continue; + return; transform = attr.transform; - transform *= mtx; + transform *= mtx_; double scl = transform.scale(); - // curved_.approximation_method(curve_inc); - curved_.approximation_scale(scl); - curved_.angle_tolerance(0.0); + // renderer_.curved_.approximation_method(curve_inc); + renderer_.curved_.approximation_scale(scl); + renderer_.curved_.angle_tolerance(0.0); typename PixelFormat::color_type color; if (attr.fill_flag || attr.fill_gradient.get_gradient_type() != NO_GRADIENT) { - ras.reset(); + ras_.reset(); // https://github.com/mapnik/mapnik/issues/1129 if (std::fabs(curved_trans_contour.width()) <= 1) { - ras.add_path(curved_trans, attr.index); + ras_.add_path(curved_trans, attr.index); } else { curved_trans_contour.miter_limit(attr.miter_limit); - ras.add_path(curved_trans_contour, attr.index); + ras_.add_path(curved_trans_contour, attr.index); } if (attr.fill_gradient.get_gradient_type() != NO_GRADIENT) { - render_gradient(ras, - sl, - ren, + render_gradient(ras_, + sl_, + ren_, attr.fill_gradient, transform, - attr.fill_opacity * attr.opacity * opacity, - symbol_bbox, + attr.fill_opacity, + symbol_bbox_, curved_trans, attr.index); } else { - ras.filling_rule(attr.even_odd_flag ? fill_even_odd : fill_non_zero); + ras_.filling_rule(attr.even_odd_flag ? fill_even_odd : fill_non_zero); color = attr.fill_color; - color.opacity(color.opacity() * attr.fill_opacity * attr.opacity * opacity); - ScanlineRenderer ren_s(ren); + color.opacity(color.opacity() * attr.fill_opacity); + ScanlineRenderer ren_s(ren_); color.premultiply(); ren_s.color(color); - render_scanlines(ras, sl, ren_s); + render_scanlines(ras_, sl_, ren_s); } } @@ -317,97 +393,207 @@ class renderer_agg : util::noncopyable { if (attr.dash.size() > 0) { - curved_dashed_stroked_.width(attr.stroke_width); - curved_dashed_stroked_.line_join(attr.line_join); - curved_dashed_stroked_.line_cap(attr.line_cap); - curved_dashed_stroked_.miter_limit(attr.miter_limit); - curved_dashed_stroked_.inner_join(inner_round); - curved_dashed_stroked_.approximation_scale(scl); + renderer_.curved_dashed_stroked_.width(attr.stroke_width); + renderer_.curved_dashed_stroked_.line_join(attr.line_join); + renderer_.curved_dashed_stroked_.line_cap(attr.line_cap); + renderer_.curved_dashed_stroked_.miter_limit(attr.miter_limit); + renderer_.curved_dashed_stroked_.inner_join(inner_round); + renderer_.curved_dashed_stroked_.approximation_scale(scl); // If the *visual* line width is considerable we // turn on processing of curve cups. //--------------------- if (attr.stroke_width * scl > 1.0) { - curved_.angle_tolerance(0.2); + renderer_.curved_.angle_tolerance(0.2); } - ras.reset(); - curved_dashed_.remove_all_dashes(); + ras_.reset(); + renderer_.curved_dashed_.remove_all_dashes(); for (auto d : attr.dash) { - curved_dashed_.add_dash(std::get<0>(d), std::get<1>(d)); + renderer_.curved_dashed_.add_dash(std::get<0>(d), std::get<1>(d)); } - curved_dashed_.dash_start(attr.dash_offset); - ras.add_path(curved_dashed_stroked_trans, attr.index); + renderer_.curved_dashed_.dash_start(attr.dash_offset); + ras_.add_path(curved_dashed_stroked_trans, attr.index); if (attr.stroke_gradient.get_gradient_type() != NO_GRADIENT) { - render_gradient(ras, - sl, - ren, + render_gradient(ras_, + sl_, + ren_, attr.stroke_gradient, transform, - attr.stroke_opacity * attr.opacity * opacity, - symbol_bbox, + attr.stroke_opacity, + symbol_bbox_, curved_trans, attr.index); } else { - ras.filling_rule(fill_non_zero); + ras_.filling_rule(fill_non_zero); color = attr.stroke_color; - color.opacity(color.opacity() * attr.stroke_opacity * attr.opacity * opacity); - ScanlineRenderer ren_s(ren); + color.opacity(color.opacity() * attr.stroke_opacity); + ScanlineRenderer ren_s(ren_); color.premultiply(); ren_s.color(color); - render_scanlines(ras, sl, ren_s); + render_scanlines(ras_, sl_, ren_s); } } else { - curved_stroked_.width(attr.stroke_width); - curved_stroked_.line_join(attr.line_join); - curved_stroked_.line_cap(attr.line_cap); - curved_stroked_.miter_limit(attr.miter_limit); - curved_stroked_.inner_join(inner_round); - curved_stroked_.approximation_scale(scl); + renderer_.curved_stroked_.width(attr.stroke_width); + renderer_.curved_stroked_.line_join(attr.line_join); + renderer_.curved_stroked_.line_cap(attr.line_cap); + renderer_.curved_stroked_.miter_limit(attr.miter_limit); + renderer_.curved_stroked_.inner_join(inner_round); + renderer_.curved_stroked_.approximation_scale(scl); // If the *visual* line width is considerable we // turn on processing of curve cups. //--------------------- if (attr.stroke_width * scl > 1.0) { - curved_.angle_tolerance(0.2); + renderer_.curved_.angle_tolerance(0.2); } - ras.reset(); - ras.add_path(curved_stroked_trans, attr.index); + ras_.reset(); + ras_.add_path(curved_stroked_trans, attr.index); if (attr.stroke_gradient.get_gradient_type() != NO_GRADIENT) { - render_gradient(ras, - sl, - ren, + render_gradient(ras_, + sl_, + ren_, attr.stroke_gradient, transform, - attr.stroke_opacity * attr.opacity * opacity, - symbol_bbox, + attr.stroke_opacity, + symbol_bbox_, curved_trans, attr.index); } else { - ras.filling_rule(fill_non_zero); + ras_.filling_rule(fill_non_zero); color = attr.stroke_color; - color.opacity(color.opacity() * attr.stroke_opacity * attr.opacity * opacity); - ScanlineRenderer ren_s(ren); + color.opacity(color.opacity() * attr.stroke_opacity); + ScanlineRenderer ren_s(ren_); color.premultiply(); ren_s.color(color); - render_scanlines(ras, sl, ren_s); + render_scanlines(ras_, sl_, ren_s); } } } } - } + renderer_agg& renderer_; + Rasterizer& ras_; + Scanline& sl_; + Renderer& ren_; + agg::trans_affine const& mtx_; + box2d const& symbol_bbox_; + }; #if defined(GRID_RENDERER) + + template + struct grid_renderer + { + grid_renderer(renderer_agg& renderer, + Rasterizer& ras, + Scanline& sl, + Renderer& ren, + agg::trans_affine const& mtx, + mapnik::value_integer const& feature_id) + : renderer_(renderer) + , ras_(ras) + , sl_(sl) + , ren_(ren) + , mtx_(mtx) + , feature_id_(feature_id) + {} + + void operator()(group const& g) const + { + for (auto const& elem : g.elements) + { + mapbox::util::apply_visitor(grid_renderer(renderer_, ras_, sl_, ren_, mtx_, feature_id_), elem); + } + } + void operator()(path_attributes const& attr) const + { + using namespace agg; + + trans_affine transform; + curved_stroked_trans_type curved_stroked_trans(renderer_.curved_stroked_, transform); + curved_trans_type curved_trans(renderer_.curved_, transform); + curved_trans_contour_type curved_trans_contour(curved_trans); + + curved_trans_contour.auto_detect_orientation(true); + + if (!attr.visibility_flag) + return; + + transform = attr.transform; + transform *= mtx_; + + double scl = transform.scale(); + // renderer_.curved_.approximation_method(curve_inc); + renderer_.curved_.approximation_scale(scl); + renderer_.curved_.angle_tolerance(0.0); + + typename PixelFormat::color_type color{feature_id_}; + + if (attr.fill_flag || attr.fill_gradient.get_gradient_type() != NO_GRADIENT) + { + ras_.reset(); + + if (std::fabs(curved_trans_contour.width()) <= 1) + { + ras_.add_path(curved_trans, attr.index); + } + else + { + curved_trans_contour.miter_limit(attr.miter_limit); + ras_.add_path(curved_trans_contour, attr.index); + } + + ras_.filling_rule(attr.even_odd_flag ? fill_even_odd : fill_non_zero); + ScanlineRenderer ren_s(ren_); + ren_s.color(color); + render_scanlines(ras_, sl_, ren_s); + } + + if (attr.stroke_flag || attr.stroke_gradient.get_gradient_type() != NO_GRADIENT) + { + renderer_.curved_stroked_.width(attr.stroke_width); + // m_curved_stroked.line_join((attr.line_join == miter_join) ? miter_join_round : attr.line_join); + renderer_.curved_stroked_.line_join(attr.line_join); + renderer_.curved_stroked_.line_cap(attr.line_cap); + renderer_.curved_stroked_.miter_limit(attr.miter_limit); + renderer_.curved_stroked_.inner_join(inner_round); + renderer_.curved_stroked_.approximation_scale(scl); + + // If the *visual* line width is considerable we + // turn on processing of curve cusps. + //--------------------- + if (attr.stroke_width * scl > 1.0) + { + renderer_.curved_.angle_tolerance(0.2); + } + ras_.reset(); + ras_.add_path(curved_stroked_trans, attr.index); + + ras_.filling_rule(fill_non_zero); + ScanlineRenderer ren_s(ren_); + ren_s.color(color); + render_scanlines(ras_, sl_, ren_s); + } + } + + renderer_agg& renderer_; + Rasterizer& ras_; + Scanline& sl_; + Renderer& ren_; + agg::trans_affine const& mtx_; + mapnik::value_integer const& feature_id_; + }; + template void render_id(Rasterizer& ras, Scanline& sl, @@ -418,76 +604,11 @@ class renderer_agg : util::noncopyable box2d const& /*symbol_bbox*/) { - using namespace agg; - - trans_affine transform; - curved_stroked_trans_type curved_stroked_trans(curved_stroked_, transform); - curved_trans_type curved_trans(curved_, transform); - curved_trans_contour_type curved_trans_contour(curved_trans); - - curved_trans_contour.auto_detect_orientation(true); - - for (unsigned i = 0; i < attributes_.size(); ++i) + for (auto const& elem : svg_group_.elements) { - mapnik::svg::path_attributes const& attr = attributes_[i]; - if (!attr.visibility_flag) - continue; - - transform = attr.transform; - - transform *= mtx; - double scl = transform.scale(); - // curved_.approximation_method(curve_inc); - curved_.approximation_scale(scl); - curved_.angle_tolerance(0.0); - - typename PixelFormat::color_type color(feature_id); - - if (attr.fill_flag || attr.fill_gradient.get_gradient_type() != NO_GRADIENT) - { - ras.reset(); - - if (std::fabs(curved_trans_contour.width()) <= 1) - { - ras.add_path(curved_trans, attr.index); - } - else - { - curved_trans_contour.miter_limit(attr.miter_limit); - ras.add_path(curved_trans_contour, attr.index); - } - - ras.filling_rule(attr.even_odd_flag ? fill_even_odd : fill_non_zero); - ScanlineRenderer ren_s(ren); - ren_s.color(color); - render_scanlines(ras, sl, ren_s); - } - - if (attr.stroke_flag || attr.stroke_gradient.get_gradient_type() != NO_GRADIENT) - { - curved_stroked_.width(attr.stroke_width); - // m_curved_stroked.line_join((attr.line_join == miter_join) ? miter_join_round : attr.line_join); - curved_stroked_.line_join(attr.line_join); - curved_stroked_.line_cap(attr.line_cap); - curved_stroked_.miter_limit(attr.miter_limit); - curved_stroked_.inner_join(inner_round); - curved_stroked_.approximation_scale(scl); - - // If the *visual* line width is considerable we - // turn on processing of curve cusps. - //--------------------- - if (attr.stroke_width * scl > 1.0) - { - curved_.angle_tolerance(0.2); - } - ras.reset(); - ras.add_path(curved_stroked_trans, attr.index); - - ras.filling_rule(fill_non_zero); - ScanlineRenderer ren_s(ren); - ren_s.color(color); - render_scanlines(ras, sl, ren_s); - } + mapbox::util::apply_visitor( + grid_renderer(*this, ras, sl, ren, mtx, feature_id), + elem); } } #endif @@ -496,9 +617,10 @@ class renderer_agg : util::noncopyable { return source_; } - inline AttributeSource const& attributes() const + + inline group const& svg_group() const { - return attributes_; + return svg_group_; } private: @@ -508,7 +630,7 @@ class renderer_agg : util::noncopyable curved_dashed_type curved_dashed_; curved_stroked_type curved_stroked_; curved_dashed_stroked_type curved_dashed_stroked_; - AttributeSource const& attributes_; + group const& svg_group_; }; } // namespace svg diff --git a/include/mapnik/svg/svg_storage.hpp b/include/mapnik/svg/svg_storage.hpp index 82ee144ef..9970e9099 100644 --- a/include/mapnik/svg/svg_storage.hpp +++ b/include/mapnik/svg/svg_storage.hpp @@ -26,6 +26,7 @@ // mapnik #include #include +#include namespace mapnik { namespace svg { @@ -39,15 +40,9 @@ class svg_storage : util::noncopyable , svg_height_(0) {} - VertexSource& source() // FIXME!! make const - { - return source_; - } + VertexSource& source() { return source_; } - AttributeSource& attributes() // FIXME!! make const - { - return attributes_; - } + svg::group& svg_group() { return svg_group_; } void set_bounding_box(box2d const& b) { bounding_box_ = b; } @@ -68,7 +63,7 @@ class svg_storage : util::noncopyable private: VertexSource source_; - AttributeSource attributes_; + svg::group svg_group_; box2d bounding_box_; double svg_width_; double svg_height_; diff --git a/include/mapnik/text/glyph_positions.hpp b/include/mapnik/text/glyph_positions.hpp index 6227391f2..80ffefb26 100644 --- a/include/mapnik/text/glyph_positions.hpp +++ b/include/mapnik/text/glyph_positions.hpp @@ -26,7 +26,6 @@ // mapnik #include #include -#include #include #include diff --git a/include/mapnik/text/placement_finder.hpp b/include/mapnik/text/placement_finder.hpp index 5c49e9012..10040b48d 100644 --- a/include/mapnik/text/placement_finder.hpp +++ b/include/mapnik/text/placement_finder.hpp @@ -27,7 +27,6 @@ #include #include #include -#include #include namespace mapnik { diff --git a/include/mapnik/text/rotation.hpp b/include/mapnik/text/rotation.hpp deleted file mode 100644 index 89cac2d45..000000000 --- a/include/mapnik/text/rotation.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef MAPNIK_ROTATION_HPP -#define MAPNIK_ROTATION_HPP - -#include - -namespace mapnik { - -struct rotation -{ - rotation() - : sin(0) - , cos(1.) - {} - rotation(double sin_, double cos_) - : sin(sin_) - , cos(cos_) - {} - rotation(double angle) - : sin(std::sin(angle)) - , cos(std::cos(angle)) - {} - void reset() - { - sin = 0.; - cos = 1.; - } - void init(double angle) - { - sin = std::sin(angle); - cos = std::cos(angle); - } - double sin; - double cos; - rotation operator~() const { return rotation(sin, -cos); } - rotation operator!() const { return rotation(-sin, cos); } - - double angle() const { return std::atan2(sin, cos); } -}; -} // namespace mapnik - -#endif // ROTATION_HPP diff --git a/include/mapnik/text/text_layout.hpp b/include/mapnik/text/text_layout.hpp index 06e01d447..b366e5454 100644 --- a/include/mapnik/text/text_layout.hpp +++ b/include/mapnik/text/text_layout.hpp @@ -33,7 +33,6 @@ #include #include #include -#include // stl #include diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f080525b9..2f344fd9c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -44,6 +44,8 @@ endif() set_target_properties(mapnik PROPERTIES POSITION_INDEPENDENT_CODE ON + CXX_VISIBILITY_PRESET hidden + VISIBILITY_INLINES_HIDDEN ON OUTPUT_NAME "mapnik" PREFIX "lib" IMPORT_PREFIX "lib" # for the archive on dll platforms diff --git a/src/agg/agg_renderer.cpp b/src/agg/agg_renderer.cpp index debd94a58..1d171713f 100644 --- a/src/agg/agg_renderer.cpp +++ b/src/agg/agg_renderer.cpp @@ -432,7 +432,7 @@ struct agg_render_marker_visitor svg_path_adapter svg_path(stl_storage); svg::renderer_agg svg_renderer( svg_path, - marker.get_data()->attributes()); + marker.get_data()->svg_group()); // https://github.com/mapnik/mapnik/issues/1316 // https://github.com/mapnik/mapnik/issues/1866 diff --git a/src/agg/process_group_symbolizer.cpp b/src/agg/process_group_symbolizer.cpp index 77d9689b4..5c936f63d 100644 --- a/src/agg/process_group_symbolizer.cpp +++ b/src/agg/process_group_symbolizer.cpp @@ -92,7 +92,7 @@ struct thunk_renderer : render_thunk_list_dispatch renderer_base renb(pixf); svg::vertex_stl_adapter stl_storage(thunk.src_->source()); svg_path_adapter svg_path(stl_storage); - svg_renderer_type svg_renderer(svg_path, thunk.attrs_); + svg_renderer_type svg_renderer(svg_path, thunk.group_attrs_); agg::trans_affine offset_tr = thunk.tr_; offset_tr.translate(offset_.x, offset_.y); diff --git a/src/agg/process_markers_symbolizer.cpp b/src/agg/process_markers_symbolizer.cpp index 055f9a764..f83ae4a06 100644 --- a/src/agg/process_markers_symbolizer.cpp +++ b/src/agg/process_markers_symbolizer.cpp @@ -75,11 +75,11 @@ struct agg_markers_renderer_context : markers_renderer_context virtual void render_marker(svg_path_ptr const& src, svg_path_adapter& path, - svg_attribute_type const& attrs, + svg::group const& group_attrs, markers_dispatch_params const& params, agg::trans_affine const& marker_tr) { - SvgRenderer svg_renderer(path, attrs); + SvgRenderer svg_renderer(path, group_attrs); render_vector_marker(svg_renderer, ras_, renb_, diff --git a/src/cairo/cairo_context.cpp b/src/cairo/cairo_context.cpp index 50a25018d..3772684bd 100644 --- a/src/cairo/cairo_context.cpp +++ b/src/cairo/cairo_context.cpp @@ -36,7 +36,7 @@ cairo_face::cairo_face(std::shared_ptr const& library, face_ptr co : face_(face) { static cairo_user_data_key_t key; - c_face_ = cairo_ft_font_face_create_for_ft_face(face->get_face(), FT_LOAD_NO_HINTING); + c_face_ = cairo_ft_font_face_create_for_ft_face(face->get_face(), FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING); cairo_font_face_set_user_data(c_face_, &key, new handle(library, face), destroy); } @@ -381,6 +381,7 @@ void cairo_context::add_image(agg::trans_affine const& tr, image_rgba8 const& da void cairo_context::set_font_face(cairo_face_manager& manager, face_ptr face) { cairo_set_font_face(cairo_.get(), manager.get_face(face)->face()); + check_object_status_and_throw_exception(*this); } void cairo_context::set_font_matrix(cairo_matrix_t const& matrix) diff --git a/src/cairo/cairo_render_vector.cpp b/src/cairo/cairo_render_vector.cpp index 49dde34ef..41c514479 100644 --- a/src/cairo/cairo_render_vector.cpp +++ b/src/cairo/cairo_render_vector.cpp @@ -30,23 +30,49 @@ #include namespace mapnik { -void render_vector_marker(cairo_context& context, - svg_path_adapter& svg_path, - svg_attribute_type const& attributes, - box2d const& bbox, - agg::trans_affine const& tr, - double opacity) +namespace { +struct group_renderer { - agg::trans_affine transform; + group_renderer(agg::trans_affine const& transform, + cairo_context& context, + svg_path_adapter& svg_path, + box2d const& bbox) + : transform_(transform) + , context_(context) + , svg_path_(svg_path) + , bbox_(bbox) + {} - for (auto const& attr : attributes) + void operator()(svg::group const& g) const + { + double opacity = g.opacity; + if (opacity < 1.0) + { + context_.push_group(); + for (auto const& elem : g.elements) + { + mapbox::util::apply_visitor(group_renderer(transform_, context_, svg_path_, bbox_), elem); + } + context_.pop_group(); + context_.paint(opacity); + } + else + { + for (auto const& elem : g.elements) + { + mapbox::util::apply_visitor(group_renderer(transform_, context_, svg_path_, bbox_), elem); + } + } + } + + void operator()(svg::path_attributes const& attr) const { if (!attr.visibility_flag) - continue; - cairo_save_restore guard(context); - transform = attr.transform; - transform *= tr; + return; + cairo_save_restore guard(context_); + agg::trans_affine transform = attr.transform; + transform *= transform_; // TODO - this 'is_valid' check is not used in the AGG renderer and also // appears to lead to bogus results with @@ -58,66 +84,99 @@ void render_vector_marker(cairo_context& context, transform.store_to(m); cairo_matrix_t matrix; cairo_matrix_init(&matrix, m[0], m[1], m[2], m[3], m[4], m[5]); - context.transform(matrix); + context_.transform(matrix); } if (attr.fill_flag || attr.fill_gradient.get_gradient_type() != NO_GRADIENT) { - context.add_agg_path(svg_path, attr.index); + context_.add_agg_path(svg_path_, attr.index); if (attr.even_odd_flag) { - context.set_fill_rule(CAIRO_FILL_RULE_EVEN_ODD); + context_.set_fill_rule(CAIRO_FILL_RULE_EVEN_ODD); } else { - context.set_fill_rule(CAIRO_FILL_RULE_WINDING); + context_.set_fill_rule(CAIRO_FILL_RULE_WINDING); } if (attr.fill_gradient.get_gradient_type() != NO_GRADIENT) { - cairo_gradient g(attr.fill_gradient, attr.fill_opacity * attr.opacity * opacity); + cairo_gradient g(attr.fill_gradient, attr.fill_opacity * attr.opacity); - context.set_gradient(g, bbox); - context.fill(); + context_.set_gradient(g, bbox_); + context_.fill(); } else if (attr.fill_flag) { - double fill_opacity = attr.fill_opacity * attr.opacity * opacity * attr.fill_color.opacity(); - context.set_color(attr.fill_color.r / 255.0, - attr.fill_color.g / 255.0, - attr.fill_color.b / 255.0, - fill_opacity); - context.fill(); + double fill_opacity = attr.fill_opacity * attr.opacity * attr.fill_color.opacity(); + context_.set_color(attr.fill_color.r / 255.0, + attr.fill_color.g / 255.0, + attr.fill_color.b / 255.0, + fill_opacity); + context_.fill(); } } if (attr.stroke_gradient.get_gradient_type() != NO_GRADIENT || attr.stroke_flag) { - context.add_agg_path(svg_path, attr.index); + context_.add_agg_path(svg_path_, attr.index); if (attr.stroke_gradient.get_gradient_type() != NO_GRADIENT) { - context.set_line_width(attr.stroke_width); - context.set_line_cap(line_cap_enum(attr.line_cap)); - context.set_line_join(line_join_enum(attr.line_join)); - context.set_miter_limit(attr.miter_limit); - cairo_gradient g(attr.stroke_gradient, attr.fill_opacity * attr.opacity * opacity); - context.set_gradient(g, bbox); - context.stroke(); + context_.set_line_width(attr.stroke_width); + context_.set_line_cap(line_cap_enum(attr.line_cap)); + context_.set_line_join(line_join_enum(attr.line_join)); + context_.set_miter_limit(attr.miter_limit); + cairo_gradient g(attr.stroke_gradient, attr.fill_opacity * attr.opacity); + context_.set_gradient(g, bbox_); + context_.stroke(); } else if (attr.stroke_flag) { - double stroke_opacity = attr.stroke_opacity * attr.opacity * opacity * attr.stroke_color.opacity(); - context.set_color(attr.stroke_color.r / 255.0, - attr.stroke_color.g / 255.0, - attr.stroke_color.b / 255.0, - stroke_opacity); - context.set_line_width(attr.stroke_width); - context.set_line_cap(line_cap_enum(attr.line_cap)); - context.set_line_join(line_join_enum(attr.line_join)); - context.set_miter_limit(attr.miter_limit); - context.stroke(); + double stroke_opacity = attr.stroke_opacity * attr.opacity * attr.stroke_color.opacity(); + context_.set_color(attr.stroke_color.r / 255.0, + attr.stroke_color.g / 255.0, + attr.stroke_color.b / 255.0, + stroke_opacity); + context_.set_line_width(attr.stroke_width); + context_.set_line_cap(line_cap_enum(attr.line_cap)); + context_.set_line_join(line_join_enum(attr.line_join)); + context_.set_miter_limit(attr.miter_limit); + context_.stroke(); } } } + agg::trans_affine const& transform_; + cairo_context& context_; + svg_path_adapter& svg_path_; + box2d const& bbox_; +}; + +} // namespace + +void render_vector_marker(cairo_context& context, + svg_path_adapter& svg_path, + svg::group const& group_attrs, + box2d const& bbox, + agg::trans_affine const& tr, + double opacity) +{ + double adjusted_opacity = opacity * group_attrs.opacity; // adjust top level opacity + if (adjusted_opacity < 1.0) + { + context.push_group(); + for (auto const& elem : group_attrs.elements) + { + mapbox::util::apply_visitor(group_renderer(tr, context, svg_path, bbox), elem); + } + context.pop_group(); + context.paint(opacity); + } + else + { + for (auto const& elem : group_attrs.elements) + { + mapbox::util::apply_visitor(group_renderer(tr, context, svg_path, bbox), elem); + } + } } } // namespace mapnik diff --git a/src/cairo/cairo_renderer.cpp b/src/cairo/cairo_renderer.cpp index 952eb3755..310c0eed3 100644 --- a/src/cairo/cairo_renderer.cpp +++ b/src/cairo/cairo_renderer.cpp @@ -269,11 +269,10 @@ struct cairo_render_marker_visitor marker_tr *= tr_; } marker_tr *= agg::trans_affine_scaling(common_.scale_factor_); - auto const& attributes = vmarker->attributes(); svg::vertex_stl_adapter 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_); + render_vector_marker(context_, svg_path, vmarker->svg_group(), bbox, marker_tr, opacity_); } } diff --git a/src/cairo/process_group_symbolizer.cpp b/src/cairo/process_group_symbolizer.cpp index 637d20d9e..eb0724dfe 100644 --- a/src/cairo/process_group_symbolizer.cpp +++ b/src/cairo/process_group_symbolizer.cpp @@ -73,7 +73,7 @@ struct thunk_renderer : render_thunk_list_dispatch offset_tr.translate(offset_.x, offset_.y); mapnik::render_vector_marker(context_, svg_path, - thunk.attrs_, + thunk.group_attrs_, thunk.src_->bounding_box(), offset_tr, thunk.opacity_); diff --git a/src/cairo/process_markers_symbolizer.cpp b/src/cairo/process_markers_symbolizer.cpp index 895884598..8f4f7da1b 100644 --- a/src/cairo/process_markers_symbolizer.cpp +++ b/src/cairo/process_markers_symbolizer.cpp @@ -40,11 +40,11 @@ struct cairo_markers_renderer_context : markers_renderer_context virtual void render_marker(svg_path_ptr const& src, svg_path_adapter& path, - svg_attribute_type const& attrs, + svg::group const& group_attrs, markers_dispatch_params const& params, agg::trans_affine const& marker_tr) { - render_vector_marker(ctx_, path, attrs, src->bounding_box(), marker_tr, params.opacity); + render_vector_marker(ctx_, path, group_attrs, src->bounding_box(), marker_tr, params.opacity); } virtual void diff --git a/src/grid/grid_renderer.cpp b/src/grid/grid_renderer.cpp index b6197201e..0c16f297e 100644 --- a/src/grid/grid_renderer.cpp +++ b/src/grid/grid_renderer.cpp @@ -184,7 +184,7 @@ struct grid_render_marker_visitor svg_path_adapter svg_path(stl_storage); svg::renderer_agg svg_renderer( svg_path, - marker.get_data()->attributes()); + marker.get_data()->svg_group()); svg_renderer.render_id(*ras_ptr_, sl, renb, feature_.id(), mtx, opacity_, bbox); } diff --git a/src/grid/process_group_symbolizer.cpp b/src/grid/process_group_symbolizer.cpp index 669d791e9..39b91d330 100644 --- a/src/grid/process_group_symbolizer.cpp +++ b/src/grid/process_group_symbolizer.cpp @@ -84,7 +84,7 @@ struct thunk_renderer : render_thunk_list_dispatch renderer_type ren(renb); svg::vertex_stl_adapter stl_storage(thunk.src_->source()); svg_path_adapter svg_path(stl_storage); - svg_renderer_type svg_renderer(svg_path, thunk.attrs_); + svg_renderer_type svg_renderer(svg_path, thunk.group_attrs_); agg::trans_affine offset_tr = thunk.tr_; offset_tr.translate(offset_.x, offset_.y); agg::scanline_bin sl; diff --git a/src/grid/process_markers_symbolizer.cpp b/src/grid/process_markers_symbolizer.cpp index b5639568a..7d11050d6 100644 --- a/src/grid/process_markers_symbolizer.cpp +++ b/src/grid/process_markers_symbolizer.cpp @@ -91,11 +91,11 @@ struct grid_markers_renderer_context : markers_renderer_context virtual void render_marker(svg_path_ptr const& src, svg_path_adapter& path, - svg_attribute_type const& attrs, + svg::group const& svg_group, markers_dispatch_params const& params, agg::trans_affine const& marker_tr) { - SvgRenderer svg_renderer_(path, attrs); + SvgRenderer svg_renderer_(path, svg_group); agg::scanline_bin sl_; svg_renderer_.render_id(ras_, sl_, renb_, feature_.id(), marker_tr, params.opacity, src->bounding_box()); place_feature(); @@ -153,6 +153,7 @@ void grid_renderer::process(markers_symbolizer const& sym, } template void grid_renderer::process(markers_symbolizer const&, mapnik::feature_impl&, proj_transform const&); + } // namespace mapnik -#endif +#endif // GRID_RENDEDER diff --git a/src/marker_cache.cpp b/src/marker_cache.cpp index 06cd5f45f..db8f3fe5f 100644 --- a/src/marker_cache.cpp +++ b/src/marker_cache.cpp @@ -170,7 +170,7 @@ std::shared_ptr marker_cache::find(std::string const& uri, svg_path_ptr marker_path(std::make_shared()); vertex_stl_adapter stl_storage(marker_path->source()); svg_path_adapter svg_path(stl_storage); - svg_converter_type svg(svg_path, marker_path->attributes()); + svg_converter_type svg(svg_path, marker_path->svg_group()); svg_parser p(svg, strict); p.parse_from_string(known_svg_string); @@ -211,7 +211,7 @@ std::shared_ptr marker_cache::find(std::string const& uri, svg_path_ptr marker_path(std::make_shared()); vertex_stl_adapter stl_storage(marker_path->source()); svg_path_adapter svg_path(stl_storage); - svg_converter_type svg(svg_path, marker_path->attributes()); + svg_converter_type svg(svg_path, marker_path->svg_group()); svg_parser p(svg, strict); p.parse(uri); diff --git a/src/marker_helpers.cpp b/src/marker_helpers.cpp index 709500e1d..ccfb08237 100644 --- a/src/marker_helpers.cpp +++ b/src/marker_helpers.cpp @@ -60,7 +60,7 @@ void build_ellipse(symbolizer_base const& sym, { half_stroke_width = get(sym, keys::stroke_width, feature, vars, 0.0) / 2.0; } - svg::svg_converter_type styled_svg(svg_path, marker_ellipse.attributes()); + svg::svg_converter_type styled_svg(svg_path, marker_ellipse.svg_group()); styled_svg.push_attr(); styled_svg.begin_path(); agg::ellipse c(0, 0, width / 2.0, height / 2.0); @@ -78,8 +78,107 @@ void build_ellipse(symbolizer_base const& sym, marker_ellipse.set_bounding_box(lox, loy, hix, hiy); } -bool push_explicit_style(svg_attribute_type const& src, - svg_attribute_type& dst, +namespace detail { + +struct push_explicit_style +{ + push_explicit_style(svg::group& dst, + boost::optional const& fill_color, + boost::optional const& fill_opacity, + boost::optional const& stroke_color, + boost::optional const& stroke_width, + boost::optional const& stroke_opacity) + : current_group_(&dst) + , fill_color_(fill_color) + , fill_opacity_(fill_opacity) + , stroke_color_(stroke_color) + , stroke_width_(stroke_width) + , stroke_opacity_(stroke_opacity) + {} + + bool operator()(svg::group const& g) const + { + current_group_->elements.emplace_back(svg::group{g.opacity, {}, current_group_}); + current_group_ = ¤t_group_->elements.back().get(); + bool success = false; + for (auto const& elem : g.elements) + { + if (mapbox::util::apply_visitor(push_explicit_style(*current_group_, + fill_color_, + fill_opacity_, + stroke_color_, + stroke_width_, + stroke_opacity_), + elem)) + { + success = true; + } + } + current_group_ = current_group_->parent; + return success; + } + + bool operator()(svg::path_attributes const& attr) const + { + svg::path_attributes new_attr{attr, attr.index}; + + if (!attr.visibility_flag) + return false; + + if (!attr.stroke_none) + { + if (stroke_width_) + { + new_attr.stroke_width = *stroke_width_; + new_attr.stroke_flag = true; + } + if (stroke_color_) + { + color const& s_color = *stroke_color_; + new_attr.stroke_color = agg::rgba(s_color.red() / 255.0, + s_color.green() / 255.0, + s_color.blue() / 255.0, + s_color.alpha() / 255.0); + new_attr.stroke_flag = true; + } + if (stroke_opacity_) + { + new_attr.stroke_opacity = *stroke_opacity_; + new_attr.stroke_flag = true; + } + } + if (!attr.fill_none) + { + if (fill_color_) + { + color const& f_color = *fill_color_; + new_attr.fill_color = agg::rgba(f_color.red() / 255.0, + f_color.green() / 255.0, + f_color.blue() / 255.0, + f_color.alpha() / 255.0); + new_attr.fill_flag = true; + } + if (fill_opacity_) + { + new_attr.fill_opacity = *fill_opacity_; + new_attr.fill_flag = true; + } + } + current_group_->elements.emplace_back(new_attr); + return true; + } + mutable svg::group* current_group_; + boost::optional const& fill_color_; + boost::optional const& fill_opacity_; + boost::optional const& stroke_color_; + boost::optional const& stroke_width_; + boost::optional const& stroke_opacity_; +}; + +} // namespace detail + +bool push_explicit_style(svg::group const& src, + svg::group& dst, symbolizer_base const& sym, feature_impl& feature, attributes const& vars) @@ -89,60 +188,23 @@ bool push_explicit_style(svg_attribute_type const& src, auto stroke_color = get_optional(sym, keys::stroke, feature, vars); auto stroke_width = get_optional(sym, keys::stroke_width, feature, vars); auto stroke_opacity = get_optional(sym, keys::stroke_opacity, feature, vars); + bool success = false; + dst.opacity = src.opacity; if (fill_color || fill_opacity || stroke_color || stroke_width || stroke_opacity) { - bool success = false; - for (unsigned i = 0; i < src.size(); ++i) + for (auto const& elem : src.elements) { - dst.push_back(src[i]); - mapnik::svg::path_attributes& attr = dst.back(); - if (!attr.visibility_flag) - continue; - success = true; - - if (!attr.stroke_none) - { - if (stroke_width) - { - attr.stroke_width = *stroke_width; - attr.stroke_flag = true; - } - if (stroke_color) - { - color const& s_color = *stroke_color; - attr.stroke_color = agg::rgba(s_color.red() / 255.0, - s_color.green() / 255.0, - s_color.blue() / 255.0, - s_color.alpha() / 255.0); - attr.stroke_flag = true; - } - if (stroke_opacity) - { - attr.stroke_opacity = *stroke_opacity; - attr.stroke_flag = true; - } - } - if (!attr.fill_none) - { - if (fill_color) - { - color const& f_color = *fill_color; - attr.fill_color = agg::rgba(f_color.red() / 255.0, - f_color.green() / 255.0, - f_color.blue() / 255.0, - f_color.alpha() / 255.0); - attr.fill_flag = true; - } - if (fill_opacity) - { - attr.fill_opacity = *fill_opacity; - attr.fill_flag = true; - } - } + if (mapbox::util::apply_visitor(detail::push_explicit_style(dst, + fill_color, + fill_opacity, + stroke_color, + stroke_width, + stroke_opacity), + elem)) + success = true; } - return success; } - return false; + return success; } void setup_transform_scaling(agg::trans_affine& tr, diff --git a/src/projection.cpp b/src/projection.cpp index ceb826541..319d19698 100644 --- a/src/projection.cpp +++ b/src/projection.cpp @@ -31,7 +31,8 @@ #ifdef MAPNIK_USE_PROJ // proj #include -#include // HUGE_VAL +#include // HUGE_VAL +#include // strlen #endif namespace mapnik { @@ -196,13 +197,27 @@ projection::~projection() #endif } -std::string projection::expanded() const +std::string projection::description() const { #ifdef MAPNIK_USE_PROJ if (proj_) { PJ_PROJ_INFO info = proj_pj_info(proj_); - return mapnik::util::trim_copy(info.definition); + if (std::strlen(info.description) > 0) + return mapnik::util::trim_copy(info.description); + } +#endif + return std::string("Undefined"); +} + +std::string projection::definition() const +{ +#ifdef MAPNIK_USE_PROJ + if (proj_) + { + PJ_PROJ_INFO info = proj_pj_info(proj_); + if (std::strlen(info.definition) > 0) + return mapnik::util::trim_copy(info.definition); } #endif return params_; diff --git a/src/renderer_common/render_markers_symbolizer.cpp b/src/renderer_common/render_markers_symbolizer.cpp index b946b8767..02eede2dd 100644 --- a/src/renderer_common/render_markers_symbolizer.cpp +++ b/src/renderer_common/render_markers_symbolizer.cpp @@ -55,14 +55,13 @@ struct render_marker_symbolizer_visitor , renderer_context_(renderer_context) {} - svg_attribute_type const& get_marker_attributes(svg_path_ptr const& stock_marker, - svg_attribute_type& custom_attr) const + svg::group const& get_marker_attributes(svg_path_ptr const& stock_marker, svg::group& custom_group_attrs) const { - auto const& stock_attr = stock_marker->attributes(); - if (push_explicit_style(stock_attr, custom_attr, sym_, feature_, common_.vars_)) - return custom_attr; + auto const& stock_group_attrs = stock_marker->svg_group(); + if (push_explicit_style(stock_group_attrs, custom_group_attrs, sym_, feature_, common_.vars_)) + return custom_group_attrs; else - return stock_attr; + return stock_group_attrs; } template @@ -130,8 +129,9 @@ struct render_marker_symbolizer_visitor svg_path_ptr marker_ptr = stock_vector_marker; bool is_ellipse = false; - svg_attribute_type s_attributes; - auto const& r_attributes = get_marker_attributes(stock_vector_marker, s_attributes); + // svg_attribute_type s_attributes; + svg::group svg_group_attrs; + auto const& svg_group_attrs_updated = get_marker_attributes(stock_vector_marker, svg_group_attrs); // special case for simple ellipse markers // to allow for full control over rx/ry dimensions @@ -161,7 +161,7 @@ struct render_marker_symbolizer_visitor vector_dispatch_type rasterizer_dispatch(marker_ptr, svg_path, - r_attributes, + svg_group_attrs_updated, image_tr, sym_, *common_.detector_, diff --git a/src/renderer_common/render_pattern.cpp b/src/renderer_common/render_pattern.cpp index 6cc49610f..02080351e 100644 --- a/src/renderer_common/render_pattern.cpp +++ b/src/renderer_common/render_pattern.cpp @@ -47,11 +47,15 @@ void render_pattern(marker_svg const& marker, double opacity, image_rgba8& image) { - using pixfmt = agg::pixfmt_rgba32_pre; + using color_type = agg::rgba8; + using order_type = agg::order_rgba; + using blender_type = agg::comp_op_adaptor_rgba_pre; // comp blender + using buf_type = agg::rendering_buffer; + using pixfmt = agg::pixfmt_custom_blend_rgba; using renderer_base = agg::renderer_base; using renderer_solid = agg::renderer_scanline_aa_solid; - agg::scanline_u8 sl; + agg::scanline_u8 sl; mapnik::box2d const& bbox = marker.bounding_box() * tr; mapnik::coord c = bbox.center(); agg::trans_affine mtx = agg::trans_affine_translation(-c.x, -c.y); @@ -66,9 +70,8 @@ void render_pattern(marker_svg const& marker, svg_path_adapter svg_path(stl_storage); svg::renderer_agg svg_renderer( svg_path, - marker.get_data()->attributes()); + marker.get_data()->svg_group()); rasterizer ras; - svg_renderer.render(ras, sl, renb, mtx, opacity, bbox); } diff --git a/src/renderer_common/render_thunk_extractor.cpp b/src/renderer_common/render_thunk_extractor.cpp index c5e626135..a6756128c 100644 --- a/src/renderer_common/render_thunk_extractor.cpp +++ b/src/renderer_common/render_thunk_extractor.cpp @@ -48,11 +48,11 @@ struct thunk_markers_renderer_context : markers_renderer_context virtual void render_marker(svg_path_ptr const& src, svg_path_adapter& path, - svg_attribute_type const& attrs, + svg::group const& group_attrs, markers_dispatch_params const& params, agg::trans_affine const& marker_tr) { - vector_marker_render_thunk thunk(src, attrs, marker_tr, params.opacity, comp_op_, params.snap_to_pixels); + vector_marker_render_thunk thunk(src, group_attrs, marker_tr, params.opacity, comp_op_, params.snap_to_pixels); thunks_.emplace_back(std::move(thunk)); } diff --git a/src/svg/svg_parser.cpp b/src/svg/svg_parser.cpp index 9e2f7c8a4..c67cf4466 100644 --- a/src/svg/svg_parser.cpp +++ b/src/svg/svg_parser.cpp @@ -539,6 +539,7 @@ void traverse_tree(svg_parser& parser, rapidxml::xml_node const* node) if (parser.css_style_) process_css(parser, node); parse_attr(parser, node); + parser.path_.begin_group(); } break; case "use"_case: @@ -547,7 +548,11 @@ void traverse_tree(svg_parser& parser, rapidxml::xml_node const* node) if (parser.css_style_) process_css(parser, node); parse_attr(parser, node); + if (parser.path_.cur_attr().opacity < 1.0) + parser.path_.begin_group(); parse_use(parser, node); + if (parser.path_.cur_attr().opacity < 1.0) + parser.path_.end_group(); parser.path_.pop_attr(); break; default: @@ -558,7 +563,11 @@ void traverse_tree(svg_parser& parser, rapidxml::xml_node const* node) parse_attr(parser, node); if (parser.path_.display()) { + if (parser.path_.cur_attr().opacity < 1.0) + parser.path_.begin_group(); parse_element(parser, node->name(), node); + if (parser.path_.cur_attr().opacity < 1.0) + parser.path_.end_group(); } parser.path_.pop_attr(); } @@ -611,6 +620,7 @@ void end_element(svg_parser& parser, rapidxml::xml_node const* node) auto name = name_to_int(node->name()); if (!parser.is_defs_ && (name == "g"_case)) { + parser.path_.end_group(); if (node->first_node() != nullptr) { parser.path_.pop_attr(); @@ -618,10 +628,10 @@ void end_element(svg_parser& parser, rapidxml::xml_node const* node) } else if (name == "svg"_case) { - if (node->first_node() != nullptr) - { - parser.path_.pop_attr(); - } + // if (node->first_node() != nullptr) + //{ + // parser.path_.pop_attr(); + //} } else if (name == "defs"_case) { @@ -670,9 +680,10 @@ void parse_element(svg_parser& parser, char const* name, rapidxml::xml_node const* node) { + parser.path_.opacity(1.0); // default path opacity = 1.0 for (rapidxml::xml_attribute const* attr = node->first_attribute(); attr; attr = attr->next_attribute()) { auto const* name = attr->name(); @@ -1521,8 +1533,8 @@ void parse_radial_gradient(svg_parser& parser, rapidxml::xml_node const* n return; double cx = 0.5; double cy = 0.5; - double fx = 0.0; - double fy = 0.0; + double fx = 0.5; + double fy = 0.5; double r = 0.5; bool has_percent = false; @@ -1531,12 +1543,20 @@ void parse_radial_gradient(svg_parser& parser, rapidxml::xml_node const* n { cx = parse_svg_value(parser, attr->value(), has_percent); } + else if (gr.get_units() == USER_SPACE_ON_USE) + { + cx = 0.5 * (parser.vbox_ ? parser.vbox_->width : parser.path_.width()); // 50% + } attr = node->first_attribute("cy"); if (attr != nullptr) { cy = parse_svg_value(parser, attr->value(), has_percent); } + else if (gr.get_units() == USER_SPACE_ON_USE) + { + cy = 0.5 * (parser.vbox_ ? parser.vbox_->height : parser.path_.height()); // 50% + } attr = node->first_attribute("fx"); if (attr != nullptr) @@ -1544,25 +1564,26 @@ void parse_radial_gradient(svg_parser& parser, rapidxml::xml_node const* n fx = parse_svg_value(parser, attr->value(), has_percent); } else + { fx = cx; - + } attr = node->first_attribute("fy"); if (attr != nullptr) { fy = parse_svg_value(parser, attr->value(), has_percent); } else + { fy = cy; - + } attr = node->first_attribute("r"); if (attr != nullptr) { r = parse_svg_value(parser, attr->value(), has_percent); } - // this logic for detecting %'s will not support mixed coordinates. - if (has_percent && gr.get_units() == USER_SPACE_ON_USE) + else if (gr.get_units() == USER_SPACE_ON_USE) { - gr.set_units(USER_SPACE_ON_USE_BOUNDING_BOX); + r = 0.5 * parser.normalized_diagonal_; // 50% } gr.set_gradient_type(RADIAL); diff --git a/src/text/glyph_positions.cpp b/src/text/glyph_positions.cpp index 8b1fbca39..b3fa96a55 100644 --- a/src/text/glyph_positions.cpp +++ b/src/text/glyph_positions.cpp @@ -22,7 +22,6 @@ // mapnik #include #include -#include #include // stl diff --git a/src/text/text_layout.cpp b/src/text/text_layout.cpp index 3be71243a..87c00a8ea 100644 --- a/src/text/text_layout.cpp +++ b/src/text/text_layout.cpp @@ -87,11 +87,6 @@ pixel_position evaluate_displacement(double dx, double dy, directions_e dir) } } -pixel_position pixel_position::rotate(rotation const& rot) const -{ - return pixel_position(x * rot.cos - y * rot.sin, x * rot.sin + y * rot.cos); -} - text_layout::text_layout(face_manager_freetype& font_manager, feature_impl const& feature, attributes const& attrs, diff --git a/test/data b/test/data index 90dc594c6..b5d6733df 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit 90dc594c693055eae69aeb66f00685969fc3113c +Subproject commit b5d6733df57557788d190a50eb6207418ae4c32a diff --git a/test/data-visual b/test/data-visual index ac363ee88..7dfd4568d 160000 --- a/test/data-visual +++ b/test/data-visual @@ -1 +1 @@ -Subproject commit ac363ee887d2a55039fd19390c186663d8f0f206 +Subproject commit 7dfd4568d6181da8be3543c8b7522b596a79b774 diff --git a/test/unit/svg/svg_parser_test.cpp b/test/unit/svg/svg_parser_test.cpp index 10f8ff406..2ff448067 100644 --- a/test/unit/svg/svg_parser_test.cpp +++ b/test/unit/svg/svg_parser_test.cpp @@ -30,12 +30,126 @@ #include #include #include +#include #include "util.hpp" #include #include +#include +#include +// boost +#include +#include +#include namespace // internal { + +std::ostream& operator<<(std::ostream& os, agg::rgba8 const& c) +{ + os << mapnik::color{c.r, c.g, c.b, c.a}; + return os; +} + +std::ostream& operator<<(std::ostream& os, mapnik::gradient const& gr) +{ + double cx, cy, fx, fy, r; + gr.get_control_points(fx, fy, cx, cy, r); + + os << "\n"; + for (auto const& stop : gr.get_stop_array()) + { + os << " (stop) << "\" color=\"" << std::get<1>(stop) << "\"/>\n"; + } + + os << "\n"; + + return os; +} + +std::ostream& operator<<(std::ostream& os, agg::trans_affine const& tr) +{ + os << "matrix(" << tr.sx << "," << tr.shy << "," << tr.shx << "," << tr.sy << "," << tr.tx << "," + << tr.ty << ")"; + return os; +} + +struct group_attribute_visitor +{ + group_attribute_visitor(std::stringstream& ss, unsigned padding) + : ss_(ss) + , padding_(padding) + {} + + void operator()(mapnik::svg::group const& g) const + { + padding_ += 2; + // ss_ << "padding=>" << padding_ << std::endl; + std::string pad(padding_, ' '); + ss_ << pad << "" << std::endl; + for (auto const& elem : g.elements) + { + mapbox::util::apply_visitor(group_attribute_visitor(ss_, padding_), elem); + } + ss_ << pad << "" << std::endl; + padding_ -= 2; + } + void operator()(mapnik::svg::path_attributes const& attr) const + { + padding_ += 2; + std::string pad(padding_, ' '); + ss_ << pad << "" << std::endl; + ss_ << pad << attr.transform << std::endl; + if (!attr.fill_gradient.get_stop_array().empty()) + ss_ << pad << attr.fill_gradient; + if (!attr.stroke_gradient.get_stop_array().empty()) + ss_ << pad << attr.stroke_gradient; + + ss_ << pad << " " << attr.opacity << "" << std::endl; + ss_ << pad << " " << attr.fill_color << "" << std::endl; + ss_ << pad << " " << attr.fill_opacity << "" << std::endl; + ss_ << pad << " " << attr.stroke_color << "" << std::endl; + ss_ << pad << " " << attr.stroke_width << "" << std::endl; + ss_ << pad << " " << attr.stroke_opacity << "" << std::endl; + ss_ << pad << "" << std::endl; + padding_ -= 2; + } + std::stringstream& ss_; + mutable unsigned padding_; +}; + +bool check_equal_attributes(std::string const& svg_file, mapnik::svg::group const& g) +{ + unsigned padding = 0; + std::stringstream ss; + ss << "" << std::endl; + for (auto const& elem : g.elements) + { + mapbox::util::apply_visitor(group_attribute_visitor(ss, padding), elem); + } + ss << "" << std::endl; + + std::string well_known_xml = svg_file + ".xml"; + if (!mapnik::util::exists(well_known_xml)) + { + std::cerr << "Well-known-xml-filename:" << well_known_xml << std::endl; + std::cerr << ss.str() << std::endl; + return false; + } + else + { + boost::property_tree::ptree t0, t1; + boost::property_tree::read_xml(well_known_xml, t0, boost::property_tree::xml_parser::trim_whitespace); + boost::property_tree::read_xml(ss, t1, boost::property_tree::xml_parser::trim_whitespace); + + if (t0 != t1) + { + std::cerr << ss.str() << std::endl; + } + return (t0 == t1); + } +} + struct test_parser { mapnik::svg_storage_type path; @@ -47,7 +161,7 @@ struct test_parser explicit test_parser(bool strict = false) : stl_storage(path.source()) , svg_path(stl_storage) - , svg(svg_path, path.attributes()) + , svg(svg_path, path.svg_group()) , p(svg, strict) {} @@ -261,10 +375,10 @@ TEST_CASE("SVG parser") mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); mapnik::svg::svg_path_adapter path(stl_storage); - auto const& attrs = storage->attributes(); + auto const& group_attrs = storage->svg_group(); agg::line_cap_e expected_cap(agg::square_cap); - REQUIRE(attrs.size() == 1); - REQUIRE(attrs[0].line_cap == expected_cap); + + REQUIRE(check_equal_attributes(svg_name, group_attrs)); double x, y; unsigned cmd; @@ -427,23 +541,22 @@ TEST_CASE("SVG parser") REQUIRE(storage); mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); - auto const& attrs = storage->attributes(); + auto const& group_attrs = storage->svg_group(); agg::line_join_e expected_join(agg::bevel_join); - REQUIRE(attrs.size() == 1); - REQUIRE(attrs[0].line_join == expected_join); + REQUIRE(check_equal_attributes(svg_name, group_attrs)); } SECTION("SVG ") { - // std::string svg_name("./test/data/svg/line.svg"); std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); REQUIRE(marker); REQUIRE(marker->is()); mapnik::marker_svg const& svg = mapnik::util::get(*marker); auto bbox = svg.bounding_box(); - // REQUIRE(bbox == mapnik::box2d(0.3543307086614174,0.3543307086614174, - // 424.8425196850394059,141.3779527559055396)); + REQUIRE( + bbox == + mapnik::box2d(0.3779527559055118, 0.3779527559055118, 453.1653543307086807, 150.8031496062992005)); auto storage = svg.get_data(); REQUIRE(storage); mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); @@ -471,14 +584,15 @@ TEST_CASE("SVG parser") SECTION("SVG ") { - // std::string svg_name("./test/data/svg/polyline.svg"); std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); REQUIRE(marker); REQUIRE(marker->is()); mapnik::marker_svg const& svg = mapnik::util::get(*marker); auto bbox = svg.bounding_box(); - // REQUIRE(bbox == mapnik::box2d(1.0,1.0,1199.0,399.0)); + REQUIRE( + bbox == + mapnik::box2d(0.3779527559055118, 0.3779527559055118, 453.1653543307086807, 150.8031496062992005)); auto storage = svg.get_data(); REQUIRE(storage); mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); @@ -510,14 +624,15 @@ TEST_CASE("SVG parser") SECTION("SVG ") { - // std::string svg_name("./test/data/svg/polygon.svg"); std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); REQUIRE(marker); REQUIRE(marker->is()); mapnik::marker_svg const& svg = mapnik::util::get(*marker); auto bbox = svg.bounding_box(); - // REQUIRE(bbox == mapnik::box2d(1.0,1.0,1199.0,399.0)); + REQUIRE( + bbox == + mapnik::box2d(0.3779527559055118, 0.3779527559055118, 453.1653543307086807, 150.8031496062992005)); auto storage = svg.get_data(); REQUIRE(storage); mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); @@ -548,14 +663,16 @@ TEST_CASE("SVG parser") SECTION("SVG ") { - // std::string svg_name("./test/data/svg/gradient.svg"); std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); REQUIRE(marker); REQUIRE(marker->is()); mapnik::marker_svg const& svg = mapnik::util::get(*marker); auto bbox = svg.bounding_box(); - // REQUIRE(bbox == mapnik::box2d(1.0,1.0,799.0,599.0)); + + REQUIRE( + bbox == + mapnik::box2d(75.7795275590551114, 0.1889763779527559, 226.5826771653543119, 113.1968503937007853)); auto storage = svg.get_data(); REQUIRE(storage); mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); @@ -651,15 +768,17 @@ TEST_CASE("SVG parser") REQUIRE(marker->is()); mapnik::marker_svg const& svg = mapnik::util::get(*marker); auto bbox = svg.bounding_box(); - // REQUIRE(bbox == mapnik::box2d(1.0,1.0,699.0,199.0)); + + REQUIRE( + bbox == + mapnik::box2d(0.3779527559055118, 0.3779527559055118, 264.1889763779527698, 75.2125984251968447)); auto storage = svg.get_data(); REQUIRE(storage); mapnik::svg::vertex_stl_adapter stl_storage(storage->source()); - auto const& attrs = storage->attributes(); - REQUIRE(attrs.size() == 3); - REQUIRE(attrs[1].fill_gradient == attrs[2].fill_gradient); + auto const& group_attrs = storage->svg_group(); + REQUIRE(check_equal_attributes(svg_name, group_attrs)); mapnik::svg::svg_path_adapter path(stl_storage); double x, y; unsigned cmd; @@ -694,24 +813,20 @@ TEST_CASE("SVG parser") SECTION("SVG with transformations") { - // std::string svg_name("./test/data/svg/gradient-transform.svg"); std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false); REQUIRE(marker); REQUIRE(marker->is()); mapnik::marker_svg const& svg = mapnik::util::get(*marker); auto bbox = svg.bounding_box(); - // REQUIRE(bbox == mapnik::box2d(1.0,1.0,799.0,599.0)); + REQUIRE( + bbox == + mapnik::box2d(75.7795275590551114, 0.1889763779527559, 226.5826771653543119, 113.1968503937007853)); auto storage = svg.get_data(); REQUIRE(storage); - auto const& attrs = storage->attributes(); - REQUIRE(attrs.size() == 3); - REQUIRE(attrs[1].fill_gradient == attrs[2].fill_gradient); - REQUIRE(attrs[1].fill_gradient.get_gradient_type() == mapnik::RADIAL); - agg::trans_affine transform; - transform *= agg::trans_affine_translation(240, 155); - REQUIRE(attrs[1].fill_gradient.get_transform() == transform); + auto const& group_attrs = storage->svg_group(); + REQUIRE(check_equal_attributes(svg_name, group_attrs)); } SECTION("SVG with xlink:href") @@ -722,15 +837,12 @@ TEST_CASE("SVG parser") REQUIRE(marker->is()); mapnik::marker_svg const& svg = mapnik::util::get(*marker); auto bbox = svg.bounding_box(); - // REQUIRE(bbox == mapnik::box2d(20,20,460,230)); + REQUIRE(bbox == mapnik::box2d(20, 20, 460, 230)); auto storage = svg.get_data(); REQUIRE(storage); - auto const& attrs = storage->attributes(); - REQUIRE(attrs.size() == 2); - REQUIRE(attrs[0].fill_gradient.get_gradient_type() == mapnik::LINEAR); - REQUIRE(attrs[1].fill_gradient.get_gradient_type() == mapnik::LINEAR); - REQUIRE(attrs[1].fill_gradient.has_stop()); + auto const& group_attrs = storage->svg_group(); + REQUIRE(check_equal_attributes(svg_name, group_attrs)); } SECTION("SVG with radial percents") @@ -741,21 +853,10 @@ TEST_CASE("SVG parser") REQUIRE(marker->is()); mapnik::marker_svg const& svg = mapnik::util::get(*marker); auto bbox = svg.bounding_box(); - // REQUIRE(bbox == mapnik::box2d(0,0,200,200)); + REQUIRE(bbox == mapnik::box2d(0, 0, 200, 200)); auto storage = svg.get_data(); REQUIRE(storage); - - double x1, x2, y1, y2, r; - auto const& attrs = storage->attributes(); - REQUIRE(attrs.size() == 1); - REQUIRE(attrs[0].fill_gradient.get_gradient_type() == mapnik::RADIAL); - REQUIRE(attrs[0].fill_gradient.has_stop()); - attrs[0].fill_gradient.get_control_points(x1, y1, x2, y2, r); - REQUIRE(x1 == 0); - REQUIRE(y1 == 0.25); - REQUIRE(x2 == 0.10); - REQUIRE(y2 == 0.10); - REQUIRE(r == 0.75); + REQUIRE(check_equal_attributes(svg_name, storage->svg_group())); } SECTION("SVG ") diff --git a/test/unit/svg/svg_path_parser_test.cpp b/test/unit/svg/svg_path_parser_test.cpp index 25ffdb172..fdef688ca 100644 --- a/test/unit/svg/svg_path_parser_test.cpp +++ b/test/unit/svg/svg_path_parser_test.cpp @@ -38,7 +38,7 @@ void test_path_parser(std::string const& str, Expected const& expected) mapnik::svg_path_ptr marker_path(std::make_shared()); vertex_stl_adapter stl_storage(marker_path->source()); svg_path_adapter svg_path(stl_storage); - svg_converter_type svg(svg_path, marker_path->attributes()); + svg_converter_type svg(svg_path, marker_path->svg_group()); CHECK(mapnik::svg::parse_path(str.c_str(), svg)); double x, y; diff --git a/test/unit/svg/svg_renderer_test.cpp b/test/unit/svg/svg_renderer_test.cpp index b028db1a2..a657b345e 100644 --- a/test/unit/svg/svg_renderer_test.cpp +++ b/test/unit/svg/svg_renderer_test.cpp @@ -48,12 +48,17 @@ namespace { mapnik::image_rgba8 render_svg(std::string const& filename, double scale_factor) { - using pixfmt = agg::pixfmt_rgba32_pre; + using color_type = agg::rgba8; + using order_type = agg::order_rgba; + using blender_type = agg::comp_op_adaptor_rgba_pre; // comp blender + using buf_type = agg::rendering_buffer; + using pixfmt = agg::pixfmt_custom_blend_rgba; using renderer_base = agg::renderer_base; using renderer_solid = agg::renderer_scanline_aa_solid; agg::rasterizer_scanline_aa<> ras_ptr; agg::scanline_u8 sl; + std::shared_ptr marker = mapnik::marker_cache::instance().find(filename, false); mapnik::marker_svg const& svg = mapnik::util::get(*marker); double svg_width, svg_height; @@ -72,9 +77,9 @@ mapnik::image_rgba8 render_svg(std::string const& filename, double scale_factor) mapnik::svg::vertex_stl_adapter stl_storage(svg.get_data()->source()); mapnik::svg::svg_path_adapter svg_path(stl_storage); - mapnik::svg:: - renderer_agg - renderer(svg_path, svg.get_data()->attributes()); + mapnik::svg::renderer_agg renderer( + svg_path, + svg.get_data()->svg_group()); double opacity = 1.0; renderer.render(ras_ptr, sl, renb, mtx, opacity, {0, 0, svg_width, svg_height}); return im; @@ -101,8 +106,8 @@ TEST_CASE("SVG renderer") double scale_factor = 1.0; std::string octocat_inline("./test/data/svg/octocat.svg"); std::string octocat_css("./test/data/svg/octocat-css.svg"); - auto image1 = render_svg(octocat_inline, 1.0); - auto image2 = render_svg(octocat_css, 1.0); + auto image1 = render_svg(octocat_inline, scale_factor); + auto image2 = render_svg(octocat_css, scale_factor); REQUIRE(equal(image1, image2)); } } diff --git a/utils/mapnik-render/mapnik-render.cpp b/utils/mapnik-render/mapnik-render.cpp index 1a05a7795..716cdf1fd 100644 --- a/utils/mapnik-render/mapnik-render.cpp +++ b/utils/mapnik-render/mapnik-render.cpp @@ -8,16 +8,21 @@ #include #include #include - +#include +#include #include MAPNIK_DISABLE_WARNING_PUSH #include #include #include +#include +#include MAPNIK_DISABLE_WARNING_POP #include +BOOST_FUSION_ADAPT_STRUCT(mapnik::box2d, (double, minx_)(double, miny_)(double, maxx_)(double, maxy_)) + int main(int argc, char** argv) { namespace po = boost::program_options; @@ -35,7 +40,6 @@ int main(int argc, char** argv) logger.set_severity(mapnik::logger::error); int map_width = 600; int map_height = 400; - try { po::options_description desc("mapnik-render utility"); @@ -51,7 +55,10 @@ int main(int argc, char** argv) ("map-width",po::value(),"map width in pixels") ("map-height",po::value(),"map height in pixels") ("variables","make map parameters available as render-time variables") - ; + ("bbox", po::value(), "bounding box e.g in Map's SRS") + ("geographic,g","bounding box is in WGS 84 lon/lat") + ("plugins-dir", po::value(), "directory containing input plug-ins (default: ./plugins/input)") + ("fonts-dir", po::value(), "directory containing fonts (default: relative to or ./fonts if no specified)"); // clang-format on po::positional_options_description p; p.add("xml", 1); @@ -65,7 +72,6 @@ int main(int argc, char** argv) std::clog << "version " << MAPNIK_VERSION_STRING << std::endl; return 1; } - if (vm.count("help")) { std::clog << desc << std::endl; @@ -88,7 +94,8 @@ int main(int argc, char** argv) } else { - std::clog << "please provide an xml map as first argument!" << std::endl; + std::clog << "mapnik-render: no XML map specified" << std::endl; + std::clog << "Try \"mapnik-render --help\" for more information" << std::endl; return -1; } @@ -122,11 +129,94 @@ int main(int argc, char** argv) map_height = vm["map-height"].as(); } - mapnik::datasource_cache::instance().register_datasources("./plugins/input/"); - mapnik::freetype_engine::register_fonts("./fonts", true); + if (vm.count("plugins-dir")) + { + mapnik::datasource_cache::instance().register_datasources(vm["plugins-dir"].as()); + if (vm.count("fonts-dir")) + { + mapnik::freetype_engine::register_fonts(vm["fonts-dir"].as(), true); + } + else + { + // relative to plugins-dir + try + { + mapnik::fs::path p(vm["plugins-dir"].as()); + p = p.parent_path() / "fonts"; + mapnik::freetype_engine::register_fonts(p.string(), true); + } + catch (...) + {} + } + } + else + { + mapnik::datasource_cache::instance().register_datasources("./plugins/input"); + mapnik::freetype_engine::register_fonts("./fonts", true); + } + if (verbose) + { + auto plugin_names = mapnik::datasource_cache::instance().plugin_names(); + if (plugin_names.empty()) + { + std::cerr << "*WARNING*: no datasource plug-ings registered" << std::endl; + } + else + { + std::cerr << "Registered datasource plug-ins:"; + for (auto const& name : plugin_names) + { + std::cerr << name << " "; + } + std::cerr << std::endl; + } + } + mapnik::Map map(map_width, map_height); mapnik::load_map(map, xml_file, true); - map.zoom_all(); + + if (vm.count("bbox")) + { + namespace x3 = boost::spirit::x3; + + mapnik::box2d bbox; + std::string str = vm["bbox"].as(); + + auto start = str.begin(); + auto end = str.end(); + if (!x3::phrase_parse(start, + end, + x3::double_ >> -x3::lit(',') >> x3::double_ >> -x3::lit(',') >> x3::double_ >> + -x3::lit(',') >> x3::double_, + x3::space, + bbox)) + { + std::cerr << "Failed to parse BBOX: " << str << std::endl; + return -1; + } + if (!bbox.valid()) + { + std::cerr << "Invalid BBOX: " << str << std::endl; + return -1; + } + if (vm.count("geographic")) + { + mapnik::projection source("epsg:4326"); + mapnik::projection destination(map.srs()); + mapnik::proj_transform tr(source, destination); + if (!tr.forward(bbox)) + { + std::cerr << "Failed to project input BBOX into " << map.srs() << std::endl; + return -1; + } + } + std::cerr << "zoom to:" << bbox << std::endl; + map.zoom_to_box(bbox); + } + else + { + map.zoom_all(); + } mapnik::image_rgba8 im(map.width(), map.height()); mapnik::request req(map.width(), map.height(), map.get_current_extent()); req.set_buffer_size(map.buffer_size()); diff --git a/utils/svg2png/svg2png.cpp b/utils/svg2png/svg2png.cpp index 250e38ba4..7a9d378a1 100644 --- a/utils/svg2png/svg2png.cpp +++ b/utils/svg2png/svg2png.cpp @@ -56,22 +56,32 @@ MAPNIK_DISABLE_WARNING_POP struct main_marker_visitor { - main_marker_visitor(std::string const& svg_name, double scale_factor, bool verbose, bool auto_open) + main_marker_visitor(std::string const& svg_name, double scale_factor, double opacity, bool verbose, bool auto_open) : svg_name_(svg_name) , scale_factor_(scale_factor) + , opacity_(opacity) , verbose_(verbose) , auto_open_(auto_open) {} int operator()(mapnik::marker_svg const& marker) const { +#if 1 + using color_type = agg::rgba8; + using order_type = agg::order_rgba; + using blender_type = agg::comp_op_adaptor_rgba_pre; // comp blender + using buf_type = agg::rendering_buffer; + using pixfmt = agg::pixfmt_custom_blend_rgba; + using renderer_base = agg::renderer_base; + using renderer_solid = agg::renderer_scanline_aa_solid; +#else using pixfmt = agg::pixfmt_rgba32_pre; using renderer_base = agg::renderer_base; using renderer_solid = agg::renderer_scanline_aa_solid; +#endif agg::rasterizer_scanline_aa<> ras_ptr; agg::scanline_u8 sl; - double opacity = 1; double w, h; std::tie(w, h) = marker.dimensions(); if (w == 0 || h == 0) @@ -110,11 +120,10 @@ struct main_marker_visitor mapnik::svg::vertex_stl_adapter stl_storage(marker.get_data()->source()); mapnik::svg::svg_path_adapter svg_path(stl_storage); - mapnik::svg:: - renderer_agg - svg_renderer_this(svg_path, marker.get_data()->attributes()); + mapnik::svg::renderer_agg + svg_renderer_this(svg_path, marker.get_data()->svg_group()); - svg_renderer_this.render(ras_ptr, sl, renb, mtx, opacity, bbox); + svg_renderer_this.render(ras_ptr, sl, renb, mtx, opacity_, bbox); std::string png_name(svg_name_); boost::algorithm::ireplace_last(png_name, ".svg", ".png"); @@ -147,11 +156,21 @@ struct main_marker_visitor private: std::string svg_name_; - double scale_factor_ = 1.0; + double scale_factor_; + double opacity_; bool verbose_; bool auto_open_; }; +void check_opacity_range(double opacity) +{ + using namespace boost::program_options; + if (opacity < 0.0 || opacity > 1.0) + { + throw invalid_option_value(std::to_string(opacity)); + } +} + int main(int argc, char** argv) { namespace po = boost::program_options; @@ -163,7 +182,8 @@ int main(int argc, char** argv) std::vector svg_files; mapnik::setup(); mapnik::logger::instance().set_severity(mapnik::logger::error); - double scale_factor = 1.0; + double scale_factor; + double opacity; std::string usage = "Usage: svg2png [options] "; try { @@ -175,7 +195,8 @@ int main(int argc, char** argv) ("verbose,v","verbose output") ("open,o","automatically open the file after rendering (os x only)") ("strict,s","enables strict SVG parsing") - ("scale-factor", po::value(), "provide scaling factor (default: 1.0)") + ("scale-factor", po::value()->default_value(1.0), "provide scaling factor (default: 1.0)") + ("opacity", po::value()->default_value(1.0)->notifier(&check_opacity_range), "top level opacity (default: 1.0)") ("svg",po::value >(),"svg file to read") ; // clang-format on @@ -216,6 +237,10 @@ int main(int argc, char** argv) { scale_factor = vm["scale-factor"].as(); } + if (vm.count("opacity")) + { + opacity = vm["opacity"].as(); + } if (vm.count("svg")) { svg_files = vm["svg"].as>(); @@ -241,7 +266,7 @@ int main(int argc, char** argv) } std::shared_ptr marker = mapnik::marker_cache::instance().find(svg_name, false, strict); - main_marker_visitor visitor(svg_name, scale_factor, verbose, auto_open); + main_marker_visitor visitor(svg_name, scale_factor, opacity, verbose, auto_open); status = mapnik::util::apply_visitor(visitor, *marker); } }