Skip to content
Commits on Source (4)
......@@ -6,6 +6,16 @@ Developers: Please commit along with changes.
For a complete change history, see the git log.
## 3.0.22
Released: January 22, 2019
(Packaged from c338cacd7)
- Improved padding calculation for clipping polygons and lines ([#4001](https://github.com/mapnik/mapnik/pull/4001))
- New option `line-pattern="repeat"` of LinePatternSymbolizer to repeat the pattern in the area given by `stroke-` options ([#4004](https://github.com/mapnik/mapnik/pull/4004))
- Fixed global and local pattern alignment ([#4005](https://github.com/mapnik/mapnik/pull/4005))
- SVG patterns are rendered as vectors into PDF or SVG with Cairo backend ([#4012](https://github.com/mapnik/mapnik/pull/4012))
## 3.0.21
......
mapnik (3.0.21+ds-3) UNRELEASED; urgency=medium
mapnik (3.0.22+ds-1~exp1) experimental; urgency=medium
* New upstream release.
* Bump Standards-Version to 4.3.0, no changes.
-- Bas Couwenberg <sebastic@debian.org> Tue, 25 Dec 2018 22:35:40 +0100
-- Bas Couwenberg <sebastic@debian.org> Tue, 22 Jan 2019 12:29:52 +0100
mapnik (3.0.21+ds-2) unstable; urgency=medium
......
#include "agg_pixfmt_rgba.h"
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#include <boost/version.hpp>
#if BOOST_VERSION >= 106900
#include <boost/gil.hpp>
#else
#include <boost/gil/gil_all.hpp>
#endif
#include <boost/gil/extension/toolbox/hsv.hpp>
#include <boost/gil/extension/toolbox/hsl.hpp>
#pragma GCC diagnostic pop
......
......@@ -16,7 +16,12 @@
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#if BOOST_VERSION >= 106900
#include <boost/gil.hpp>
#else
#include <boost/gil/gil_all.hpp>
#endif
#pragma GCC diagnostic pop
namespace boost { namespace gil {
......@@ -43,8 +48,12 @@ typedef mpl::vector3< hsl_color_space::hue_t
/// \ingroup LayoutModel
typedef layout<hsl_t> hsl_layout_t;
#if BOOST_VERSION >= 106800
using bits32f = boost::gil::float32_t;
GIL_DEFINE_ALL_TYPEDEFS( 32f, float32_t, hsl );
#else
GIL_DEFINE_ALL_TYPEDEFS( 32f, hsl );
#endif
/// \ingroup ColorConvert
/// \brief RGB to HSL
......
......@@ -16,7 +16,12 @@
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#if BOOST_VERSION >= 106900
#include <boost/gil.hpp>
#else
#include <boost/gil/gil_all.hpp>
#endif
#pragma GCC diagnostic pop
namespace boost { namespace gil {
......@@ -44,7 +49,13 @@ typedef mpl::vector3< hsv_color_space::hue_t
typedef layout<hsv_t> hsv_layout_t;
GIL_DEFINE_ALL_TYPEDEFS( 32f, hsv )
#if BOOST_VERSION >= 106800
using bits32 = uint32_t;
using bits32f = float32_t;
GIL_DEFINE_ALL_TYPEDEFS( 32f, float32_t, hsv )
#else
GIL_DEFINE_ALL_TYPEDEFS( 32f, hsv );
#endif
/// \ingroup ColorConvert
/// \brief RGB to HSV
......
/*****************************************************************************
*
* 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_RENDER_POLYGON_PATTERN_HPP
#define MAPNIK_RENDER_POLYGON_PATTERN_HPP
#include <mapnik/symbolizer.hpp>
#include <mapnik/vertex_processor.hpp>
#include <mapnik/renderer_common/pattern_alignment.hpp>
#include <mapnik/renderer_common/apply_vertex_converter.hpp>
#include <mapnik/renderer_common/clipping_extent.hpp>
#include <mapnik/vertex_converters.hpp>
#include <mapnik/safe_cast.hpp>
#pragma GCC diagnostic push
#include <mapnik/warning_ignore_agg.hpp>
#include "agg_basics.h"
#include "agg_rendering_buffer.h"
#include "agg_pixfmt_rgba.h"
#include "agg_color_rgba.h"
#include "agg_rasterizer_scanline_aa.h"
#include "agg_scanline_u.h"
#include "agg_renderer_scanline.h"
#include "agg_span_allocator.h"
#include "agg_span_pattern_rgba.h"
#include "agg_image_accessors.h"
#include "agg_conv_clip_polygon.h"
#pragma GCC diagnostic pop
namespace mapnik {
struct agg_pattern_base
{
image_rgba8 const& pattern_img_;
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<transform_type>(sym_, keys::geometry_transform);
if (transform)
{
evaluate_transform(tr, feature_, common_.vars_, *transform, common_.scale_factor_);
}
return tr;
}
};
template <typename VertexConverter>
struct agg_polygon_pattern : agg_pattern_base
{
using color_type = agg::rgba8;
using order_type = agg::order_rgba;
using blender_type = agg::comp_op_adaptor_rgba_pre<color_type, order_type>;
using pixfmt_type = agg::pixfmt_custom_blend_rgba<blender_type, agg::rendering_buffer>;
using wrap_x_type = agg::wrap_mode_repeat;
using wrap_y_type = agg::wrap_mode_repeat;
using img_source_type = agg::image_accessor_wrap<agg::pixfmt_rgba32_pre,
wrap_x_type,
wrap_y_type>;
using span_gen_type = agg::span_pattern_rgba<img_source_type>;
using renderer_base = agg::renderer_base<pixfmt_type>;
using renderer_type = agg::renderer_scanline_aa_alpha<renderer_base,
agg::span_allocator<agg::rgba8>,
span_gen_type>;
agg_polygon_pattern(image_rgba8 const& pattern_img,
renderer_common const& common,
symbolizer_base const& sym,
mapnik::feature_impl const& feature,
proj_transform const& prj_trans)
: agg_pattern_base{pattern_img, common, sym, feature, prj_trans},
clip_(get<value_bool, keys::clip>(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<value_double, keys::simplify_tolerance>(sym_, feature_, common_.vars_);
value_double smooth = get<value_double, keys::smooth>(sym_, feature_, common_.vars_);
if (simplify_tolerance > 0.0) converter_.template set<simplify_tag>();
converter_.template set<affine_transform_tag>();
if (smooth > 0.0) converter_.template set<smooth_tag>();
}
void render(renderer_base & ren_base, rasterizer & ras)
{
coord<double, 2> offset(pattern_offset(sym_, feature_, prj_trans_, common_,
pattern_img_.width(), pattern_img_.height()));
agg::rendering_buffer pattern_rbuf((agg::int8u*)pattern_img_.bytes(),
pattern_img_.width(), pattern_img_.height(),
pattern_img_.width() * 4);
agg::pixfmt_rgba32_pre pixf_pattern(pattern_rbuf);
img_source_type img_src(pixf_pattern);
span_gen_type sg(img_src, safe_cast<unsigned>(offset.x), safe_cast<unsigned>(offset.y));
agg::span_allocator<agg::rgba8> sa;
value_double opacity = get<double, keys::opacity>(sym_, feature_, common_.vars_);
renderer_type rp(ren_base, sa, sg, unsigned(opacity * 255));
using apply_vertex_converter_type = detail::apply_vertex_converter<
VertexConverter, rasterizer>;
using vertex_processor_type = geometry::vertex_processor<apply_vertex_converter_type>;
apply_vertex_converter_type apply(converter_, ras);
mapnik::util::apply_visitor(vertex_processor_type(apply), feature_.get_geometry());
agg::scanline_u8 sl;
agg::render_scanlines(ras, sl, rp);
}
const bool clip_;
const box2d<double> clip_box_;
const agg::trans_affine tr_;
VertexConverter converter_;
};
} // namespace mapnik
#endif // MAPNIK_RENDER_POLYGON_PATTERN_HPP
......@@ -121,6 +121,30 @@ private:
cairo_face_cache cache_;
};
struct cairo_closer
{
void operator() (cairo_t * obj)
{
if (obj) cairo_destroy(obj);
}
};
struct cairo_surface_closer
{
void operator() (cairo_surface_t * surface)
{
if (surface) cairo_surface_destroy(surface);
}
};
using cairo_ptr = std::shared_ptr<cairo_t>;
using cairo_surface_ptr = std::shared_ptr<cairo_surface_t>;
inline cairo_ptr create_context(cairo_surface_ptr const& surface)
{
return cairo_ptr(cairo_create(&*surface),cairo_closer());
}
class cairo_pattern : private util::noncopyable
{
public:
......@@ -131,9 +155,15 @@ public:
const unsigned int *in_end = in_ptr + pixels;
unsigned int *out_ptr;
surface_ = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, static_cast<int>(data.width()), static_cast<int>(data.height()));
surface_ = cairo_surface_ptr(
cairo_image_surface_create(
CAIRO_FORMAT_ARGB32,
static_cast<int>(data.width()),
static_cast<int>(data.height())),
cairo_surface_closer());
out_ptr = reinterpret_cast<unsigned int *>(cairo_image_surface_get_data(surface_));
out_ptr = reinterpret_cast<unsigned int *>(
cairo_image_surface_get_data(surface_.get()));
while (in_ptr < in_end)
{
......@@ -150,13 +180,18 @@ public:
*out_ptr++ = (a << 24) | (r << 16) | (g << 8) | b;
}
// mark the surface as dirty as we've modified it behind cairo's back
cairo_surface_mark_dirty(surface_);
pattern_ = cairo_pattern_create_for_surface(surface_);
cairo_surface_mark_dirty(surface_.get());
pattern_ = cairo_pattern_create_for_surface(surface_.get());
}
cairo_pattern(cairo_surface_ptr const& surface) :
surface_(surface),
pattern_(cairo_pattern_create_for_surface(surface_.get()))
{
}
~cairo_pattern()
{
if (surface_) cairo_surface_destroy(surface_);
if (pattern_) cairo_pattern_destroy(pattern_);
}
......@@ -190,7 +225,7 @@ public:
}
private:
cairo_surface_t * surface_;
cairo_surface_ptr surface_;
cairo_pattern_t * pattern_;
};
......@@ -255,30 +290,6 @@ private:
};
struct cairo_closer
{
void operator() (cairo_t * obj)
{
if (obj) cairo_destroy(obj);
}
};
struct cairo_surface_closer
{
void operator() (cairo_surface_t * surface)
{
if (surface) cairo_surface_destroy(surface);
}
};
using cairo_ptr = std::shared_ptr<cairo_t>;
using cairo_surface_ptr = std::shared_ptr<cairo_surface_t>;
inline cairo_ptr create_context(cairo_surface_ptr const& surface)
{
return cairo_ptr(cairo_create(&*surface),cairo_closer());
}
class cairo_context : private util::noncopyable
{
public:
......
/*****************************************************************************
*
* 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 <mapnik/symbolizer.hpp>
#include <mapnik/vertex_processor.hpp>
#include <mapnik/vertex_converters.hpp>
#include <mapnik/renderer_common/pattern_alignment.hpp>
#include <mapnik/renderer_common/apply_vertex_converter.hpp>
#include <mapnik/renderer_common/clipping_extent.hpp>
#include <mapnik/cairo/cairo_render_vector.hpp>
#include <mapnik/marker.hpp>
namespace mapnik {
struct cairo_renderer_process_visitor_p
{
cairo_renderer_process_visitor_p(agg::trans_affine const& image_tr,
double opacity)
: image_tr_(image_tr),
opacity_(opacity)
{}
cairo_surface_ptr operator()(marker_svg const & marker) const
{
box2d<double> bbox(marker.bounding_box());
agg::trans_affine tr(transform(bbox));
double width = std::max(1.0, std::round(bbox.width()));
double height = std::max(1.0, std::round(bbox.height()));
cairo_rectangle_t extent { 0, 0, width, height };
cairo_surface_ptr surface(
cairo_recording_surface_create(
CAIRO_CONTENT_COLOR_ALPHA, &extent),
cairo_surface_closer());
cairo_ptr cairo = create_context(surface);
cairo_context context(cairo);
svg_storage_type & svg = *marker.get_data();
svg_attribute_type const& svg_attributes = svg.attributes();
svg::vertex_stl_adapter<svg::svg_path_storage> stl_storage(
svg.source());
svg::svg_path_adapter svg_path(stl_storage);
render_vector_marker(context, svg_path, svg_attributes,
bbox, tr, opacity_);
return surface;
}
cairo_surface_ptr operator()(marker_rgba8 const& marker) const
{
box2d<double> bbox(marker.bounding_box());
agg::trans_affine tr(transform(bbox));
cairo_rectangle_t extent { 0, 0, bbox.width(), bbox.height() };
cairo_surface_ptr surface(
cairo_recording_surface_create(
CAIRO_CONTENT_COLOR_ALPHA, &extent),
cairo_surface_closer());
cairo_ptr cairo = create_context(surface);
cairo_context context(cairo);
context.add_image(tr, marker.get_data(), opacity_);
return surface;
}
cairo_surface_ptr operator() (marker_null const&) const
{
cairo_surface_ptr surface(
cairo_recording_surface_create(
CAIRO_CONTENT_COLOR_ALPHA, nullptr),
cairo_surface_closer());
cairo_ptr cairo = create_context(surface);
cairo_context context(cairo);
return surface;
}
private:
agg::trans_affine transform(box2d<double> & bbox) const
{
bbox *= image_tr_;
coord<double, 2> c = bbox.center();
agg::trans_affine mtx = agg::trans_affine_translation(
0.5 * bbox.width() - c.x,
0.5 * bbox.height() - c.y);
return image_tr_ * mtx;
}
agg::trans_affine const& image_tr_;
const double 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<transform_type>(sym_, keys::geometry_transform);
if (transform)
{
evaluate_transform(tr, feature_, common_.vars_, *transform, common_.scale_factor_);
}
return tr;
}
};
template <typename VertexConverter>
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<value_bool, keys::clip>(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<value_double, keys::simplify_tolerance>(sym, feature, common_.vars_);
value_double smooth = get<value_double, keys::smooth>(sym, feature, common_.vars_);
converter_.template set<affine_transform_tag>();
if (simplify_tolerance > 0.0) converter_.template set<simplify_tag>();
if (smooth > 0.0) converter_.template set<smooth_tag>();
}
void render(cairo_fill_rule_t fill_rule, cairo_context & context)
{
value_double opacity = get<value_double, keys::opacity>(sym_, feature_, common_.vars_);
agg::trans_affine image_tr = agg::trans_affine_scaling(common_.scale_factor_);
auto image_transform = get_optional<transform_type>(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<composite_mode_e, keys::comp_op>(sym_, feature_, common_.vars_);
cairo_save_restore guard(context);
context.set_operator(comp_op);
cairo_renderer_process_visitor_p visitor(image_tr, opacity);
cairo_surface_ptr surface(util::apply_visitor(visitor, this->marker_));
coord<double, 2> offset(0, 0);
cairo_rectangle_t pattern_surface_extent;
if (cairo_recording_surface_get_extents(surface.get(), &pattern_surface_extent))
{
offset = pattern_offset(sym_, feature_, prj_trans_, common_,
pattern_surface_extent.width, pattern_surface_extent.height);
}
cairo_pattern pattern(surface);
pattern.set_extend(CAIRO_EXTEND_REPEAT);
pattern.set_origin(-offset.x, -offset.y);
context.set_pattern(pattern);
using apply_vertex_converter_type = detail::apply_vertex_converter<VertexConverter, cairo_context>;
using vertex_processor_type = geometry::vertex_processor<apply_vertex_converter_type>;
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<double> clip_box_;
const agg::trans_affine tr_;
VertexConverter converter_;
};
} // namespace mapnik
#endif // MAPNIK_CAIRO_RENDER_POLYGON_PATTERN_HPP
......@@ -31,7 +31,11 @@
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#if BOOST_VERSION >= 106900
#include <boost/gil.hpp>
#else
#include <boost/gil/gil_all.hpp>
#endif
#pragma GCC diagnostic pop
#pragma GCC diagnostic push
......@@ -48,6 +52,14 @@
// stl
#include <cmath>
#if BOOST_VERSION >= 106800
namespace boost {
namespace gil {
using bits32f = boost::gil::float32_t;
}
}
#endif
// 8-bit YUV
//Y = ( ( 66 * R + 129 * G + 25 * B + 128) >> 8) + 16
//U = ( ( -38 * R - 74 * G + 112 * B + 128) >> 8) + 128
......
......@@ -43,7 +43,7 @@ public:
markers_placement_params const& params)
: placement_type_(placement_type)
{
switch (placement_type)
switch (marker_placement_enum(placement_type))
{
default:
case MARKER_POINT_PLACEMENT:
......@@ -66,7 +66,7 @@ public:
~markers_placement_finder()
{
switch (placement_type_)
switch (marker_placement_enum(placement_type_))
{
default:
case MARKER_POINT_PLACEMENT:
......@@ -90,7 +90,7 @@ public:
// Get next point where the marker should be placed. Returns true if a place is found, false if none is found.
bool get_point(double &x, double &y, double &angle, bool ignore_placement)
{
switch (placement_type_)
switch (marker_placement_enum(placement_type_))
{
default:
case MARKER_POINT_PLACEMENT:
......
......@@ -40,6 +40,8 @@
namespace mapnik
{
static constexpr double offset_converter_default_threshold = 5.0;
template <typename Geometry>
struct offset_converter
{
......@@ -48,7 +50,7 @@ struct offset_converter
offset_converter(Geometry & geom)
: geom_(geom)
, offset_(0.0)
, threshold_(5.0)
, threshold_(offset_converter_default_threshold)
, half_turn_segments_(16)
, status_(initial)
, pre_first_(vertex2d::no_init)
......
......@@ -20,49 +20,26 @@
*
*****************************************************************************/
#ifndef MAPNIK_PATTERN_ALIGNMENT_HPP
#define MAPNIK_PATTERN_ALIGNMENT_HPP
#include <mapnik/geometry.hpp>
#include <mapnik/coord.hpp>
namespace mapnik { namespace detail {
namespace mapnik {
struct apply_local_alignment
{
apply_local_alignment(view_transform const& t,
proj_transform const& prj_trans,
box2d<double> const& clip_box,
double & x, double & y)
: t_(t),
prj_trans_(prj_trans),
clip_box_(clip_box),
x_(x),
y_(y) {}
class symbolizer_base;
class feature_impl;
class proj_transform;
class renderer_common;
void operator() (geometry::polygon_vertex_adapter<double> & va)
{
using clipped_geometry_type = agg::conv_clip_polygon<geometry::polygon_vertex_adapter<double> >;
using path_type = transform_path_adapter<view_transform,clipped_geometry_type>;
clipped_geometry_type clipped(va);
clipped.clip_box(clip_box_.minx(),clip_box_.miny(),clip_box_.maxx(),clip_box_.maxy());
path_type path(t_, clipped, prj_trans_);
path.vertex(&x_,&y_);
}
coord<double, 2> pattern_offset(
symbolizer_base const & sym,
feature_impl const & feature,
proj_transform const & prj_trans,
renderer_common const & common,
unsigned pattern_width,
unsigned pattern_height);
template <typename Adapter>
void operator() (Adapter &)
{
// no-op
}
view_transform const& t_;
proj_transform const& prj_trans_;
box2d<double> const& clip_box_;
double & x_;
double & y_;
};
}}
#endif // MAPNIK_PATTERN_ALIGNMENT_HPP
......@@ -38,8 +38,7 @@ struct rasterizer;
struct marker_svg;
template <typename T>
void render_pattern(rasterizer & ras,
marker_svg const& marker,
void render_pattern(marker_svg const& marker,
agg::trans_affine const& tr,
double opacity,
T & image);
......
......@@ -94,7 +94,8 @@ enum class property_types : std::uint8_t
target_vertical_alignment,
target_upright,
target_direction,
target_font_feature_settings
target_font_feature_settings,
target_line_pattern,
};
template <typename T>
......@@ -189,6 +190,7 @@ ENUM_FROM_STRING( text_transform_enum )
ENUM_FROM_STRING( text_upright_enum )
ENUM_FROM_STRING( direction_enum )
ENUM_FROM_STRING( gamma_method_enum )
ENUM_FROM_STRING( line_pattern_enum )
// enum
template <typename T, bool is_enum = true>
......
......@@ -369,6 +369,13 @@ struct symbolizer_default<value_bool, keys::avoid_edges>
// font-feature-settings
// line-pattern
template <>
struct symbolizer_default<line_pattern_enum, keys::line_pattern>
{
static line_pattern_enum value() { return LINE_PATTERN_WARP; }
};
} // namespace mapnik
#endif // MAPNIK_SYMBOLIZER_DEFAULT_VALUES_HPP
......@@ -219,6 +219,14 @@ enum gamma_method_enum : std::uint8_t
DEFINE_ENUM (gamma_method_e, gamma_method_enum );
enum line_pattern_enum : std::uint8_t
{
LINE_PATTERN_WARP,
LINE_PATTERN_REPEAT,
line_pattern_enum_MAX
};
DEFINE_ENUM (line_pattern_e, line_pattern_enum );
}
......
......@@ -92,6 +92,7 @@ enum class keys : std::uint8_t
direction,
avoid_edges,
ff_settings,
line_pattern,
MAX_SYMBOLIZER_KEY
};
......
......@@ -27,7 +27,7 @@
#define MAPNIK_MAJOR_VERSION 3
#define MAPNIK_MINOR_VERSION 0
#define MAPNIK_PATCH_VERSION 21
#define MAPNIK_PATCH_VERSION 22
#define MAPNIK_VERSION (MAPNIK_MAJOR_VERSION*100000) + (MAPNIK_MINOR_VERSION*100) + (MAPNIK_PATCH_VERSION)
......
......@@ -27,15 +27,15 @@
#include <mapnik/agg_renderer.hpp>
#include <mapnik/agg_rasterizer.hpp>
#include <mapnik/agg_pattern_source.hpp>
#include <mapnik/agg/render_polygon_pattern.hpp>
#include <mapnik/marker.hpp>
#include <mapnik/marker_cache.hpp>
#include <mapnik/symbolizer.hpp>
#include <mapnik/vertex_converters.hpp>
#include <mapnik/vertex_processor.hpp>
#include <mapnik/util/noncopyable.hpp>
#include <mapnik/parse_path.hpp>
#include <mapnik/renderer_common/clipping_extent.hpp>
#include <mapnik/renderer_common/render_pattern.hpp>
#include <mapnik/renderer_common/pattern_alignment.hpp>
#include <mapnik/renderer_common/apply_vertex_converter.hpp>
......@@ -53,24 +53,139 @@
#include "agg_span_allocator.h"
#include "agg_span_pattern_rgba.h"
#include "agg_renderer_outline_image.h"
#include "agg_image_accessors.h"
#pragma GCC diagnostic pop
namespace mapnik {
namespace {
template <typename... Converters>
using vertex_converter_type = vertex_converter<clip_line_tag,
transform_tag,
affine_transform_tag,
simplify_tag,
smooth_tag,
offset_transform_tag,
Converters...>;
struct warp_pattern : agg_pattern_base
{
using vc_type = vertex_converter_type<>;
using color_type = agg::rgba8;
using order_type = agg::order_rgba;
using blender_type = agg::comp_op_adaptor_rgba_pre<color_type, order_type>;
using pattern_filter_type = agg::pattern_filter_bilinear_rgba8;
using pattern_type = agg::line_image_pattern<pattern_filter_type>;
using pixfmt_type = agg::pixfmt_custom_blend_rgba<blender_type, agg::rendering_buffer>;
using renderer_base = agg::renderer_base<pixfmt_type>;
using renderer_type = agg::renderer_outline_image<renderer_base, pattern_type>;
using rasterizer_type = agg::rasterizer_outline_aa<renderer_type>;
warp_pattern(image_rgba8 const& pattern_img,
renderer_common const& common,
symbolizer_base const& sym,
mapnik::feature_impl const& feature,
proj_transform const& prj_trans)
: agg_pattern_base{pattern_img, common, sym, feature, prj_trans},
clip_(get<value_bool, keys::clip>(sym, feature, common.vars_)),
offset_(get<value_double, keys::offset>(sym, feature, common.vars_)),
clip_box_(clip_box()),
tr_(geom_transform()),
converter_(clip_box_, sym, common.t_, prj_trans, tr_,
feature, common.vars_, common.scale_factor_)
{
value_double simplify_tolerance = get<value_double, keys::simplify_tolerance>(sym_, feature_, common_.vars_);
value_double smooth = get<value_double, keys::smooth>(sym_, feature_, common_.vars_);
if (std::fabs(offset_) > 0.0) converter_.template set<offset_transform_tag>();
if (simplify_tolerance > 0.0) converter_.template set<simplify_tag>();
converter_.template set<affine_transform_tag>();
if (smooth > 0.0) converter_.template set<smooth_tag>();
}
box2d<double> clip_box() const
{
box2d<double> clip_box = clipping_extent(common_);
if (clip_)
{
double pad_per_pixel = static_cast<double>(common_.query_extent_.width() / common_.width_);
double pixels = std::ceil(std::max(pattern_img_.width() / 2.0 + std::fabs(offset_),
(std::fabs(offset_) * offset_converter_default_threshold)));
double padding = pad_per_pixel * pixels * common_.scale_factor_;
clip_box.pad(padding);
}
return clip_box;
}
void render(renderer_base & ren_base, rasterizer &)
{
value_double opacity = get<double, keys::opacity>(sym_, feature_, common_.vars_);
agg::pattern_filter_bilinear_rgba8 filter;
pattern_source source(pattern_img_, opacity);
pattern_type pattern (filter, source);
renderer_type ren(ren_base, pattern);
double half_stroke = std::max(pattern_img_.width() / 2.0, pattern_img_.height() / 2.0);
int rast_clip_padding = static_cast<int>(std::round(half_stroke));
ren.clip_box(-rast_clip_padding, -rast_clip_padding,
common_.width_ + rast_clip_padding,
common_.height_ + rast_clip_padding);
rasterizer_type ras(ren);
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_vertex_converter_type apply(converter_, ras);
util::apply_visitor(vertex_processor_type(apply), feature_.get_geometry());
}
const bool clip_;
const double offset_;
const box2d<double> clip_box_;
const agg::trans_affine tr_;
vc_type converter_;
};
using repeat_pattern_base = agg_polygon_pattern<vertex_converter_type<dash_tag,
stroke_tag>>;
struct repeat_pattern : repeat_pattern_base
{
using repeat_pattern_base::agg_polygon_pattern;
void render(renderer_base & ren_base, rasterizer & ras)
{
if (has_key(sym_, keys::stroke_dasharray))
{
converter_.set<dash_tag>();
}
if (clip_) converter_.template set<clip_line_tag>();
value_double offset = get<value_double, keys::offset>(sym_, feature_, common_.vars_);
if (std::fabs(offset) > 0.0) converter_.template set<offset_transform_tag>();
// To allow lines cross themselves.
ras.filling_rule(agg::fill_non_zero);
repeat_pattern_base::render(ren_base, ras);
}
};
}
template <typename buffer_type>
struct agg_renderer_process_visitor_l
{
agg_renderer_process_visitor_l(renderer_common & common,
buffer_type & pixmap,
buffer_type * current_buffer,
std::unique_ptr<rasterizer> const& ras_ptr,
agg_renderer_process_visitor_l(renderer_common const& common,
buffer_type & current_buffer,
rasterizer & ras,
line_pattern_symbolizer const& sym,
mapnik::feature_impl & feature,
mapnik::feature_impl const& feature,
proj_transform const& prj_trans)
: common_(common),
pixmap_(pixmap),
current_buffer_(current_buffer),
ras_ptr_(ras_ptr),
ras_(ras),
sym_(sym),
feature_(feature),
prj_trans_(prj_trans) {}
......@@ -84,90 +199,57 @@ struct agg_renderer_process_visitor_l
if (image_transform) evaluate_transform(image_tr, feature_, common_.vars_, *image_transform, common_.scale_factor_);
mapnik::box2d<double> const& bbox_image = marker.get_data()->bounding_box() * image_tr;
image_rgba8 image(bbox_image.width(), bbox_image.height());
render_pattern<buffer_type>(*ras_ptr_, marker, image_tr, 1.0, image);
render(image, marker.width(), marker.height());
render_pattern<buffer_type>(marker, image_tr, 1.0, image);
render_by_pattern_type(image);
}
void operator() (marker_rgba8 const& marker) const
{
render(marker.get_data(), marker.width(), marker.height());
render_by_pattern_type(marker.get_data());
}
private:
void render(mapnik::image_rgba8 const& marker, double width, double height) const
void render_by_pattern_type(image_rgba8 const& pattern_image) const
{
using color = agg::rgba8;
using order = agg::order_rgba;
using blender_type = agg::comp_op_adaptor_rgba_pre<color, order>;
using pattern_filter_type = agg::pattern_filter_bilinear_rgba8;
using pattern_type = agg::line_image_pattern<pattern_filter_type>;
using pixfmt_type = agg::pixfmt_custom_blend_rgba<blender_type, agg::rendering_buffer>;
using renderer_base = agg::renderer_base<pixfmt_type>;
using renderer_type = agg::renderer_outline_image<renderer_base, pattern_type>;
using rasterizer_type = agg::rasterizer_outline_aa<renderer_type>;
value_double opacity = get<value_double, keys::opacity>(sym_, feature_, common_.vars_);
value_bool clip = get<value_bool, keys::clip>(sym_, feature_, common_.vars_);
value_double offset = get<value_double, keys::offset>(sym_, feature_, common_.vars_);
value_double simplify_tolerance = get<value_double, keys::simplify_tolerance>(sym_, feature_, common_.vars_);
value_double smooth = get<value_double, keys::smooth>(sym_, feature_, common_.vars_);
agg::rendering_buffer buf(current_buffer_->bytes(),current_buffer_->width(),
current_buffer_->height(), current_buffer_->row_size());
pixfmt_type pixf(buf);
pixf.comp_op(static_cast<agg::comp_op_e>(get<composite_mode_e, keys::comp_op>(sym_, feature_, common_.vars_)));
renderer_base ren_base(pixf);
agg::pattern_filter_bilinear_rgba8 filter;
pattern_source source(marker, opacity);
pattern_type pattern (filter,source);
renderer_type ren(ren_base, pattern);
double half_stroke = std::max(width / 2.0, height / 2.0);
int rast_clip_padding = static_cast<int>(std::round(half_stroke));
ren.clip_box(-rast_clip_padding,-rast_clip_padding,common_.width_+rast_clip_padding,common_.height_+rast_clip_padding);
rasterizer_type ras(ren);
agg::trans_affine tr;
auto transform = get_optional<transform_type>(sym_, keys::geometry_transform);
if (transform) evaluate_transform(tr, feature_, common_.vars_, *transform, common_.scale_factor_);
box2d<double> clip_box = clipping_extent(common_);
if (clip)
{
double padding = (double)(common_.query_extent_.width()/pixmap_.width());
if (half_stroke > 1)
padding *= half_stroke;
if (std::fabs(offset) > 0)
padding *= std::fabs(offset) * 1.2;
padding *= common_.scale_factor_;
clip_box.pad(padding);
line_pattern_enum pattern = get<line_pattern_enum, keys::line_pattern>(sym_, feature_, common_.vars_);
switch (pattern)
{
case LINE_PATTERN_WARP:
{
warp_pattern pattern(pattern_image, common_, sym_, feature_, prj_trans_);
render(pattern);
break;
}
case LINE_PATTERN_REPEAT:
{
repeat_pattern pattern(pattern_image, common_, sym_, feature_, prj_trans_);
render(pattern);
break;
}
case line_pattern_enum_MAX:
default:
MAPNIK_LOG_ERROR(process_line_pattern_symbolizer) << "Incorrect line-pattern value.";
}
}
using vertex_converter_type = vertex_converter<clip_line_tag, transform_tag,
affine_transform_tag,
simplify_tag,smooth_tag,
offset_transform_tag>;
vertex_converter_type converter(clip_box,sym_,common_.t_,prj_trans_,tr,feature_,common_.vars_,common_.scale_factor_);
if (clip) converter.set<clip_line_tag>();
converter.set<transform_tag>(); //always transform
if (simplify_tolerance > 0.0) converter.set<simplify_tag>(); // optional simplify converter
if (std::fabs(offset) > 0.0) converter.set<offset_transform_tag>(); // parallel offset
converter.set<affine_transform_tag>(); // optional affine transform
if (smooth > 0.0) converter.set<smooth_tag>(); // optional smooth converter
template <typename Pattern>
void render(Pattern & pattern) const
{
agg::rendering_buffer buf(current_buffer_.bytes(), current_buffer_.width(),
current_buffer_.height(), current_buffer_.row_size());
typename Pattern::pixfmt_type pixf(buf);
pixf.comp_op(static_cast<agg::comp_op_e>(get<composite_mode_e, keys::comp_op>(sym_, feature_, common_.vars_)));
typename Pattern::renderer_base ren_base(pixf);
using apply_vertex_converter_type = detail::apply_vertex_converter<vertex_converter_type, rasterizer_type>;
using vertex_processor_type = geometry::vertex_processor<apply_vertex_converter_type>;
apply_vertex_converter_type apply(converter, ras);
mapnik::util::apply_visitor(vertex_processor_type(apply), feature_.get_geometry());
if (pattern.clip_) pattern.converter_.template set<clip_line_tag>();
pattern.render(ren_base, ras_);
}
renderer_common & common_;
buffer_type & pixmap_;
buffer_type * current_buffer_;
std::unique_ptr<rasterizer> const& ras_ptr_;
renderer_common const& common_;
buffer_type & current_buffer_;
rasterizer & ras_;
line_pattern_symbolizer const& sym_;
mapnik::feature_impl & feature_;
mapnik::feature_impl const& feature_;
proj_transform const& prj_trans_;
};
......@@ -176,8 +258,6 @@ void agg_renderer<T0,T1>::process(line_pattern_symbolizer const& sym,
mapnik::feature_impl & feature,
proj_transform const& prj_trans)
{
std::string filename = get<std::string, keys::file>(sym, feature, common_.vars_);
if (filename.empty()) return;
ras_ptr->reset();
......@@ -187,11 +267,11 @@ void agg_renderer<T0,T1>::process(line_pattern_symbolizer const& sym,
gamma_method_ = GAMMA_POWER;
gamma_ = 1.0;
}
std::shared_ptr<mapnik::marker const> marker = marker_cache::instance().find(filename, true);
agg_renderer_process_visitor_l<buffer_type> visitor(common_,
pixmap_,
current_buffer_,
ras_ptr,
*current_buffer_,
*ras_ptr,
sym,
feature,
prj_trans);
......
......@@ -139,23 +139,11 @@ void agg_renderer<T0,T1>::process(line_symbolizer const& sym,
line_rasterizer_enum rasterizer_e = get<line_rasterizer_enum, keys::line_rasterizer>(sym, feature, common_.vars_);
if (clip)
{
double padding = static_cast<double>(common_.query_extent_.width()/pixmap_.width());
double half_stroke = 0.5 * width;
if (half_stroke > 1)
{
padding *= half_stroke;
}
if (std::fabs(offset) > 0)
{
padding *= std::fabs(offset) * 1.2;
}
padding *= common_.scale_factor_;
double pad_per_pixel = static_cast<double>(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_;
clip_box.pad(padding);
// debugging
//box2d<double> inverse = query_extent_;
//inverse.pad(-padding);
//draw_geo_extent(inverse,mapnik::color("red"));
}
if (rasterizer_e == RASTERIZER_FAST)
......