diff --git a/include/mapnik/cairo/render_polygon_pattern.hpp b/include/mapnik/cairo/render_polygon_pattern.hpp new file mode 100644 index 000000000..98bb518a3 --- /dev/null +++ b/include/mapnik/cairo/render_polygon_pattern.hpp @@ -0,0 +1,174 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2017 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_CAIRO_RENDER_POLYGON_PATTERN_HPP +#define MAPNIK_CAIRO_RENDER_POLYGON_PATTERN_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mapnik { + +struct cairo_renderer_process_visitor_p +{ + cairo_renderer_process_visitor_p(cairo_context & context, + agg::trans_affine & image_tr, + unsigned offset_x, + unsigned offset_y, + float opacity) + : context_(context), + image_tr_(image_tr), + offset_x_(offset_x), + offset_y_(offset_y), + opacity_(opacity) {} + + void operator() (marker_null const&) {} + + void operator() (marker_svg const& marker) + { + mapnik::rasterizer ras; + mapnik::box2d const& bbox_image = marker.get_data()->bounding_box() * image_tr_; + mapnik::image_rgba8 image(bbox_image.width(), bbox_image.height()); + render_pattern(ras, marker, image_tr_, 1.0, image); + cairo_pattern pattern(image, opacity_); + pattern.set_extend(CAIRO_EXTEND_REPEAT); + pattern.set_origin(offset_x_, offset_y_); + context_.set_pattern(pattern); + } + + void operator() (marker_rgba8 const& marker) + { + cairo_pattern pattern(marker.get_data(), opacity_); + pattern.set_extend(CAIRO_EXTEND_REPEAT); + pattern.set_origin(offset_x_, offset_y_); + context_.set_pattern(pattern); + } + + private: + cairo_context & context_; + agg::trans_affine & image_tr_; + unsigned offset_x_; + unsigned offset_y_; + float opacity_; +}; + +struct cairo_pattern_base +{ + mapnik::marker const& marker_; + renderer_common const& common_; + symbolizer_base const& sym_; + mapnik::feature_impl const& feature_; + proj_transform const& prj_trans_; + + agg::trans_affine geom_transform() const + { + agg::trans_affine tr; + auto transform = get_optional(sym_, keys::geometry_transform); + if (transform) + { + evaluate_transform(tr, feature_, common_.vars_, *transform, common_.scale_factor_); + } + return tr; + } +}; + +template +struct cairo_polygon_pattern : cairo_pattern_base +{ + cairo_polygon_pattern(mapnik::marker const& marker, + renderer_common const& common, + symbolizer_base const& sym, + mapnik::feature_impl const& feature, + proj_transform const& prj_trans) + : cairo_pattern_base{marker, common, sym, feature, prj_trans}, + clip_(get(sym_, feature_, common_.vars_)), + clip_box_(clipping_extent(common)), + tr_(geom_transform()), + converter_(clip_box_, sym, common.t_, prj_trans, tr_, + feature, common.vars_, common.scale_factor_) + { + value_double simplify_tolerance = get(sym, feature, common_.vars_); + value_double smooth = get(sym, feature, common_.vars_); + + converter_.template set(); + if (simplify_tolerance > 0.0) converter_.template set(); + if (smooth > 0.0) converter_.template set(); + } + + void render(cairo_fill_rule_t fill_rule, cairo_context & context) + { + unsigned offset_x=0; + unsigned offset_y=0; + pattern_alignment_enum alignment = get(sym_, feature_, common_.vars_); + if (alignment == LOCAL_ALIGNMENT) + { + double x0 = 0.0; + double y0 = 0.0; + using apply_local_alignment = detail::apply_local_alignment; + apply_local_alignment apply(common_.t_, prj_trans_, clip_box_, x0, y0); + util::apply_visitor(geometry::vertex_processor(apply), feature_.get_geometry()); + offset_x = std::abs(clip_box_.width() - x0); + offset_y = std::abs(clip_box_.height() - y0); + } + + value_double opacity = get(sym_, feature_, common_.vars_); + agg::trans_affine image_tr = agg::trans_affine_scaling(common_.scale_factor_); + auto image_transform = get_optional(sym_, keys::image_transform); + if (image_transform) + { + evaluate_transform(image_tr, feature_, common_.vars_, *image_transform, common_.scale_factor_); + } + + composite_mode_e comp_op = get(sym_, feature_, common_.vars_); + + cairo_save_restore guard(context); + context.set_operator(comp_op); + + util::apply_visitor(cairo_renderer_process_visitor_p( + context, image_tr, offset_x, offset_y, opacity), marker_); + + using apply_vertex_converter_type = detail::apply_vertex_converter; + using vertex_processor_type = geometry::vertex_processor; + apply_vertex_converter_type apply(converter_, context); + mapnik::util::apply_visitor(vertex_processor_type(apply),feature_.get_geometry()); + // fill polygon + context.set_fill_rule(fill_rule); + context.fill(); + } + + const bool clip_; + const box2d clip_box_; + const agg::trans_affine tr_; + VertexConverter converter_; +}; + +} // namespace mapnik + + +#endif // MAPNIK_CAIRO_RENDER_POLYGON_PATTERN_HPP diff --git a/src/cairo/process_line_pattern_symbolizer.cpp b/src/cairo/process_line_pattern_symbolizer.cpp index a55e22c19..ad7098b46 100644 --- a/src/cairo/process_line_pattern_symbolizer.cpp +++ b/src/cairo/process_line_pattern_symbolizer.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -40,13 +41,16 @@ namespace mapnik { -struct cairo_renderer_process_visitor_l +namespace { - cairo_renderer_process_visitor_l(renderer_common const& common, - line_pattern_symbolizer const& sym, - mapnik::feature_impl & feature, - std::size_t & width, - std::size_t & height) + +struct prepare_pattern_visitor +{ + prepare_pattern_visitor(renderer_common const& common, + symbolizer_base const& sym, + feature_impl const& feature, + std::size_t & width, + std::size_t & height) : common_(common), sym_(sym), feature_(feature), @@ -81,86 +85,156 @@ struct cairo_renderer_process_visitor_l private: renderer_common const& common_; - line_pattern_symbolizer const& sym_; - mapnik::feature_impl & feature_; + symbolizer_base const& sym_; + feature_impl const& feature_; std::size_t & width_; std::size_t & height_; }; +template +using vertex_converter_type = vertex_converter; + +struct warp_pattern : cairo_pattern_base +{ + using vc_type = vertex_converter_type<>; + + warp_pattern(mapnik::marker const& marker, + renderer_common const& common, + symbolizer_base const& sym, + mapnik::feature_impl const& feature, + proj_transform const& prj_trans) + : cairo_pattern_base{marker, common, sym, feature, prj_trans}, + clip_(get(sym, feature, common.vars_)), + offset_(get(sym, feature, common.vars_)), + clip_box_(clipping_extent(common)), + tr_(geom_transform()), + converter_(clip_box_, sym, common.t_, prj_trans, tr_, + feature, common.vars_, common.scale_factor_) + { + value_double offset = get(sym, feature, common.vars_); + value_double simplify_tolerance = get(sym, feature, common.vars_); + value_double smooth = get(sym, feature, common.vars_); + + if (std::fabs(offset) > 0.0) converter_.template set(); + converter_.template set(); + if (simplify_tolerance > 0.0) converter_.template set(); + if (smooth > 0.0) converter_.template set(); + if (clip_) converter_.template set(); + } + + box2d clip_box() const + { + box2d clipping_extent = common_.query_extent_; + if (clip_) + { + double pad_per_pixel = static_cast(common_.query_extent_.width() / common_.width_); + double pixels = std::ceil(std::max(marker_.width() / 2.0 + std::fabs(offset_), + (std::fabs(offset_) * offset_converter_default_threshold))); + double padding = pad_per_pixel * pixels * common_.scale_factor_; + + clipping_extent.pad(padding); + } + return clipping_extent; + } + + void render(cairo_context & context) + { + std::size_t width = marker_.width(); + std::size_t height = marker_.height(); + + // TODO - re-implement at renderer level like polygon_pattern symbolizer + prepare_pattern_visitor visit(common_, sym_, feature_, width, height); + std::shared_ptr pattern = util::apply_visitor(visit, marker_); + pattern->set_extend(CAIRO_EXTEND_REPEAT); + pattern->set_filter(CAIRO_FILTER_BILINEAR); + + composite_mode_e comp_op = get(sym_, feature_, common_.vars_); + + cairo_save_restore guard(context); + context.set_operator(comp_op); + context.set_line_width(height); + + using rasterizer_type = line_pattern_rasterizer; + rasterizer_type ras(context, *pattern, width, height); + + using apply_vertex_converter_type = detail::apply_vertex_converter< + vc_type, rasterizer_type>; + using vertex_processor_type = geometry::vertex_processor; + apply_vertex_converter_type apply(converter_, ras); + mapnik::util::apply_visitor(vertex_processor_type(apply), feature_.get_geometry()); + } + + const bool clip_; + const double offset_; + const box2d clip_box_; + const agg::trans_affine tr_; + vc_type converter_; +}; + +using repeat_pattern_base = cairo_polygon_pattern>; + +struct repeat_pattern : repeat_pattern_base +{ + using repeat_pattern_base::cairo_polygon_pattern; + + void render(cairo_context & context) + { + if (has_key(sym_, keys::stroke_dasharray)) + { + converter_.template set(); + } + + if (clip_) converter_.template set(); + + value_double offset = get(sym_, feature_, common_.vars_); + if (std::fabs(offset) > 0.0) converter_.template set(); + + repeat_pattern_base::render(CAIRO_FILL_RULE_WINDING, context); + } +}; + +} + template void cairo_renderer::process(line_pattern_symbolizer const& sym, - mapnik::feature_impl & feature, - proj_transform const& prj_trans) + feature_impl & feature, + proj_transform const& prj_trans) { std::string filename = get(sym, feature, common_.vars_); - composite_mode_e comp_op = get(sym, feature, common_.vars_); - value_bool clip = get(sym, feature, common_.vars_); - value_double offset = get(sym, feature, common_.vars_); - value_double simplify_tolerance = get(sym, feature, common_.vars_); - value_double smooth = get(sym, feature, common_.vars_); + std::shared_ptr marker = marker_cache::instance().find(filename, true); - if (filename.empty()) + if (marker->is()) { return; } - std::shared_ptr marker = marker_cache::instance().find(filename, true); - - if (marker->is()) return; - - std::size_t width = marker->width(); - std::size_t height = marker->height(); - - cairo_save_restore guard(context_); - context_.set_operator(comp_op); - // TODO - re-implement at renderer level like polygon_pattern symbolizer - cairo_renderer_process_visitor_l visit(common_, - sym, - feature, - width, - height); - std::shared_ptr pattern = util::apply_visitor(visit, *marker); - - context_.set_line_width(height); - - pattern->set_extend(CAIRO_EXTEND_REPEAT); - pattern->set_filter(CAIRO_FILTER_BILINEAR); - - agg::trans_affine tr; - auto geom_transform = get_optional(sym, keys::geometry_transform); - if (geom_transform) { evaluate_transform(tr, feature, common_.vars_, *geom_transform, common_.scale_factor_); } - - box2d clipping_extent = common_.query_extent_; - if (clip) + line_pattern_enum pattern = get(sym, feature, common_.vars_); + switch (pattern) { - double pad_per_pixel = static_cast(common_.query_extent_.width()/common_.width_); - double pixels = std::ceil(std::max(width / 2.0 + std::fabs(offset), - (std::fabs(offset) * offset_converter_default_threshold))); - double padding = pad_per_pixel * pixels * common_.scale_factor_; - - clipping_extent.pad(padding); + case LINE_PATTERN_WARP: + { + warp_pattern pattern(*marker, common_, sym, feature, prj_trans); + pattern.render(context_); + break; + } + case LINE_PATTERN_REPEAT: + { + repeat_pattern pattern(*marker, common_, sym, feature, prj_trans); + pattern.render(context_); + break; + } + case line_pattern_enum_MAX: + default: + MAPNIK_LOG_ERROR(process_line_pattern_symbolizer) << "Incorrect line-pattern value."; } - using rasterizer_type = line_pattern_rasterizer; - rasterizer_type ras(context_, *pattern, width, height); - using vertex_converter_type = vertex_converter; - - vertex_converter_type converter(clipping_extent,sym, common_.t_, prj_trans, tr, feature, common_.vars_, common_.scale_factor_); - - if (clip) converter.set(); - converter.set(); // always transform - if (std::fabs(offset) > 0.0) converter.set(); // parallel offset - converter.set(); // optional affine transform - if (simplify_tolerance > 0.0) converter.set(); // optional simplify converter - if (smooth > 0.0) converter.set(); // optional smooth converter - - using apply_vertex_converter_type = detail::apply_vertex_converter; - using vertex_processor_type = geometry::vertex_processor; - apply_vertex_converter_type apply(converter, ras); - mapnik::util::apply_visitor(vertex_processor_type(apply), feature.get_geometry()); } template void cairo_renderer::process(line_pattern_symbolizer const&, diff --git a/src/cairo/process_polygon_pattern_symbolizer.cpp b/src/cairo/process_polygon_pattern_symbolizer.cpp index 456359b4d..6f6f716b4 100644 --- a/src/cairo/process_polygon_pattern_symbolizer.cpp +++ b/src/cairo/process_polygon_pattern_symbolizer.cpp @@ -27,123 +27,38 @@ #include #include #include -#include +#include #include #include #include #include -#include -#include #include #include namespace mapnik { -struct cairo_renderer_process_visitor_p -{ - cairo_renderer_process_visitor_p(cairo_context & context, - agg::trans_affine & image_tr, - unsigned offset_x, - unsigned offset_y, - float opacity) - : context_(context), - image_tr_(image_tr), - offset_x_(offset_x), - offset_y_(offset_y), - opacity_(opacity) {} - - void operator() (marker_null const&) {} - - void operator() (marker_svg const& marker) - { - mapnik::rasterizer ras; - mapnik::box2d const& bbox_image = marker.get_data()->bounding_box() * image_tr_; - mapnik::image_rgba8 image(bbox_image.width(), bbox_image.height()); - render_pattern(ras, marker, image_tr_, 1.0, image); - cairo_pattern pattern(image, opacity_); - pattern.set_extend(CAIRO_EXTEND_REPEAT); - pattern.set_origin(offset_x_, offset_y_); - context_.set_pattern(pattern); - } - - void operator() (marker_rgba8 const& marker) - { - cairo_pattern pattern(marker.get_data(), opacity_); - pattern.set_extend(CAIRO_EXTEND_REPEAT); - pattern.set_origin(offset_x_, offset_y_); - context_.set_pattern(pattern); - } - - private: - cairo_context & context_; - agg::trans_affine & image_tr_; - unsigned offset_x_; - unsigned offset_y_; - float opacity_; -}; - template void cairo_renderer::process(polygon_pattern_symbolizer const& sym, mapnik::feature_impl & feature, proj_transform const& prj_trans) { - composite_mode_e comp_op = get(sym, feature, common_.vars_); std::string filename = get(sym, feature, common_.vars_); - value_bool clip = get(sym, feature, common_.vars_); - value_double simplify_tolerance = get(sym, feature, common_.vars_); - value_double smooth = get(sym, feature, common_.vars_); - value_double opacity = get(sym, feature, common_.vars_); - agg::trans_affine image_tr = agg::trans_affine_scaling(common_.scale_factor_); - auto image_transform = get_optional(sym, keys::image_transform); - if (image_transform) evaluate_transform(image_tr, feature, common_.vars_, *image_transform, common_.scale_factor_); - - cairo_save_restore guard(context_); - context_.set_operator(comp_op); - std::shared_ptr marker = mapnik::marker_cache::instance().find(filename,true); if (marker->is()) return; - unsigned offset_x=0; - unsigned offset_y=0; - box2d const& clip_box = clipping_extent(common_); - pattern_alignment_enum alignment = get(sym, feature, common_.vars_); - if (alignment == LOCAL_ALIGNMENT) - { - double x0 = 0.0; - double y0 = 0.0; - using apply_local_alignment = detail::apply_local_alignment; - apply_local_alignment apply(common_.t_, prj_trans, clip_box, x0, y0); - util::apply_visitor(geometry::vertex_processor(apply), feature.get_geometry()); - offset_x = std::abs(clip_box.width() - x0); - offset_y = std::abs(clip_box.height() - y0); - } - - util::apply_visitor(cairo_renderer_process_visitor_p(context_, image_tr, offset_x, offset_y, opacity), *marker); - - agg::trans_affine tr; - auto geom_transform = get_optional(sym, keys::geometry_transform); - if (geom_transform) { evaluate_transform(tr, feature, common_.vars_, *geom_transform, common_.scale_factor_); } using vertex_converter_type = vertex_converter; + using pattern_type = cairo_polygon_pattern; - vertex_converter_type converter(clip_box,sym,common_.t_,prj_trans,tr,feature,common_.vars_,common_.scale_factor_); - if (prj_trans.equal() && clip) converter.set(); - converter.set(); //always transform - converter.set(); - if (simplify_tolerance > 0.0) converter.set(); // optional simplify converter - if (smooth > 0.0) converter.set(); // optional smooth converter + pattern_type pattern(*marker, common_, sym, feature, prj_trans); - using apply_vertex_converter_type = detail::apply_vertex_converter; - using vertex_processor_type = geometry::vertex_processor; - apply_vertex_converter_type apply(converter, context_); - mapnik::util::apply_visitor(vertex_processor_type(apply),feature.get_geometry()); - // fill polygon - context_.set_fill_rule(CAIRO_FILL_RULE_EVEN_ODD); - context_.fill(); + if (prj_trans.equal() && pattern.clip_) pattern.converter_.set(); + + pattern.render(CAIRO_FILL_RULE_EVEN_ODD, context_); } template void cairo_renderer::process(polygon_pattern_symbolizer const&,