diff --git a/include/mapnik/cairo/cairo_context.hpp b/include/mapnik/cairo/cairo_context.hpp index c44d11bf5..c38d7d10e 100644 --- a/include/mapnik/cairo/cairo_context.hpp +++ b/include/mapnik/cairo/cairo_context.hpp @@ -306,6 +306,7 @@ public: void line_to(double x, double y); void rectangle(double x, double y, double w, double h); void stroke(); + void stroke_preserve(); void fill(); void paint(); void paint(double opacity); diff --git a/include/mapnik/path.hpp b/include/mapnik/path.hpp index ad1f3aa7f..cf90f2f8f 100644 --- a/include/mapnik/path.hpp +++ b/include/mapnik/path.hpp @@ -60,6 +60,10 @@ public: : type_(type) {} + // underlying container access + container_type const* operator->() const { return &cont_; } + container_type* operator->() { return &cont_; } + types type() const { return static_cast(type_ & types::Polygon); diff --git a/include/mapnik/renderer_common/agg_building_symbolizer_context.hpp b/include/mapnik/renderer_common/agg_building_symbolizer_context.hpp new file mode 100644 index 000000000..fcf2df091 --- /dev/null +++ b/include/mapnik/renderer_common/agg_building_symbolizer_context.hpp @@ -0,0 +1,121 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2018 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_RENDERER_COMMON_AGG_BUILDING_SYMBOLIZER_CONTEXT_HPP +#define MAPNIK_RENDERER_COMMON_AGG_BUILDING_SYMBOLIZER_CONTEXT_HPP + +// mapnik +#include +#include + +// agg +#pragma GCC diagnostic push +#include +#include "agg_vcgen_stroke.h" +#pragma GCC diagnostic pop + +namespace mapnik { namespace detail { + +template +struct agg_building_symbolizer_context : util::noncopyable +{ + agg::vcgen_stroke stroke_gen; + Rasterizer & ras; + + explicit agg_building_symbolizer_context(Rasterizer & r) + : ras(r) {} + + Derived & derived() + { + return static_cast(*this); + } + + void set_color(color const& c) + { + // to be overridden in Derived + } + + void fill() { derived().render_scanlines(ras); } + void fill_from(double x, double y) { ras.move_to_d(x, y); } + void fill_to(double x, double y) { ras.line_to_d(x, y); } + void fill_close() { ras.close_polygon(); } + + void stroke() + { + stroke_gen.rewind(0); + ras.reset(); + ras.add_path(stroke_gen); + derived().render_scanlines(ras); + } + + void stroke_from(double x, double y) + { + stroke_gen.remove_all(); + stroke_gen.add_vertex(x, y, agg::path_cmd_move_to); + } + + void stroke_to(double x, double y) + { + stroke_gen.add_vertex(x, y, agg::path_cmd_line_to); + } + + void stroke_close() + { + stroke_gen.add_vertex(0, 0, agg::path_cmd_end_poly | + agg::path_flags_close); + } + + template + void stroke_preserve(VertexSource & vs, unsigned path_id = 0) + { + vs.rewind(path_id); + for (;;) + { + double x, y; + unsigned cmd = vs.vertex(&x, &y); + if (agg::is_move_to(cmd)) + { + stroke_from(x, y); + } + else if (agg::is_vertex(cmd)) + { + stroke_to(x, y); + } + else if (agg::is_close(cmd)) + { + stroke_close(); + stroke(); + } + else if (agg::is_stop(cmd)) + { + break; + } + } + // simulate the "preserve" part + ras.reset(); + ras.add_path(vs, path_id); + } +}; + +}} // namespace mapnik::detail + +#endif // MAPNIK_RENDERER_COMMON_AGG_BUILDING_SYMBOLIZER_CONTEXT_HPP diff --git a/include/mapnik/renderer_common/process_building_symbolizer.hpp b/include/mapnik/renderer_common/process_building_symbolizer.hpp index 392925136..c324a0e8b 100644 --- a/include/mapnik/renderer_common/process_building_symbolizer.hpp +++ b/include/mapnik/renderer_common/process_building_symbolizer.hpp @@ -30,26 +30,22 @@ #include #include -#pragma GCC diagnostic push -#include -#include "agg_conv_transform.h" -#pragma GCC diagnostic pop - namespace mapnik { struct render_building_symbolizer { - using vertex_adapter_type = geometry::polygon_vertex_adapter; - using transform_path_type = transform_path_adapter; - using roof_type = agg::conv_transform; - private: using size_t = std::size_t; using uint8_t = std::uint8_t; + static constexpr uint8_t SEG_MOVETO_LHR = SEG_MOVETO | 0x10; + static constexpr uint8_t SEG_MOVETO_RHR = SEG_MOVETO | 0x20; + renderer_common const& rencom_; double const height_; + bool render_back_side_ = false; + path_type roof_vertices_{path_type::types::Polygon}; public: @@ -71,6 +67,21 @@ public: // colors are not always needed so they're not extracted here } + bool has_transparent_fill() const + { + return (fill_color.alpha_ & wall_fill_color.alpha_) != 255; + } + + bool render_back_side() const + { + return render_back_side_; + } + + void render_back_side(bool value) + { + render_back_side_ = value; + } + void setup_colors(building_symbolizer const& sym, feature_impl const& feature) { @@ -124,81 +135,258 @@ public: safe_mul(base_stroke_color.alpha_, stroke_opacity); } - template + template void apply(feature_impl const& feature, proj_transform const& prj_trans, - F1 face_func, F2 frame_func, F3 roof_func) + Context & painter) { auto const& geom = feature.get_geometry(); + + roof_vertices_->clear(); + if (geom.is>()) { auto const& poly = geom.get>(); - vertex_adapter_type va(poly); - transform_path_type transformed(rencom_.t_, va, prj_trans); - make_building(transformed, face_func, frame_func, roof_func); + render_ground(poly, prj_trans, painter); } else if (geom.is>()) { auto const& multi_poly = geom.get>(); for (auto const& poly : multi_poly) { - vertex_adapter_type va(poly); - transform_path_type transformed(rencom_.t_, va, prj_trans); - make_building(transformed, face_func, frame_func, roof_func); + render_ground(poly, prj_trans, painter); } } + if (roof_vertices_.size() > 0) + { + render_walls(painter); + render_roof(painter); + } } private: - template - void render_face(double x0, double y0, double x, double y, F & face_func, path_type & frame) - { - path_type faces(path_type::types::Polygon); - faces.move_to(x0, y0); - faces.line_to(x, y); - faces.line_to(x, y - height_); - faces.line_to(x0, y0 - height_); - face_func(faces, wall_fill_color); - frame.move_to(x0, y0); - frame.line_to(x, y); - frame.line_to(x, y - height_); - frame.line_to(x0, y0 - height_); + bool is_visible(int orientation) const + { + return orientation > 0 || (orientation < 0 && render_back_side_); } - template - void make_building(Geom & poly, F1 & face_func, F2 & frame_func, F3 & roof_func) + template + void render_ground(geometry::polygon const& poly, + proj_transform const& prj_trans, + Context & painter) + { + using vertex_adapter_type = geometry::polygon_vertex_adapter; + using transform_path_type = transform_path_adapter; + + vertex_adapter_type va(poly); + transform_path_type transformed(rencom_.t_, va, prj_trans); + painter.set_color(base_stroke_color); + paint_outline(transformed, painter); + } + + template + void render_roof(Context & painter) const + { + vertex_adapter va(roof_vertices_); + painter.set_color(stroke_color); + painter.stroke_preserve(va); + painter.set_color(fill_color); + painter.fill(); + } + + template + void render_walls(Context & painter) + { + size_t const size = roof_vertices_.size(); + size_t strip_begin = size; + size_t wrap_begin = size; + size_t wrap_end = size; + double x = 0; + double y = 0; + int poly_orientation = 0; + int strip_orientation = 0; + int wrap_orientation = 0; + + painter.set_color(wall_fill_color); + + for (size_t i = 0; i < size; ++i) + { + double x0 = x; + unsigned cmd = roof_vertices_->get_vertex(i, &x, &y); + + // SEG_LINETO is the most likely command + // SEG_CLOSE contains coordinates from previous MOVETO + if (cmd == SEG_LINETO || cmd == SEG_CLOSE) + { + double dx = (x - x0) * poly_orientation; + int wall_orientation = (dx < 0 ? -1 : dx > 0 ? 1 : 0); + + if (wall_orientation && wall_orientation != strip_orientation) + { + if (wrap_orientation == 0) + { + wrap_orientation = strip_orientation; + wrap_end = i; + } + else + { + paint_wall(strip_orientation, + strip_begin, i, i, i, painter); + } + strip_orientation = wall_orientation; + strip_begin = i - 1; + } + + if (cmd == SEG_CLOSE) + { + if (wrap_orientation != strip_orientation) + { + paint_wall(wrap_orientation, + wrap_begin, wrap_end, i, i, painter); + wrap_end = wrap_begin; + } + paint_wall(strip_orientation, strip_begin, i + 1, + wrap_begin, wrap_end, painter); + } + } + else if (cmd == SEG_MOVETO_LHR || cmd == SEG_MOVETO_RHR) + { + poly_orientation = (cmd == SEG_MOVETO_RHR ? -1 : 1); + strip_orientation = 0; + strip_begin = size; + wrap_orientation = 0; + wrap_begin = i; + wrap_end = i; + // restore plain SEG_MOVETO command for `render_roof` + roof_vertices_->set_command(i, SEG_MOVETO); + } + } + } + + template + void paint_outline(VertexSource & poly, Context & painter) { - path_type frame(path_type::types::LineString); double ring_begin_x, ring_begin_y; double x0 = 0; double y0 = 0; double x, y; + double area = 0; + size_t ring_begin = roof_vertices_.size(); + uint8_t ring_begin_cmd = SEG_MOVETO; + + // stroke ground outline, collect transformed points in roof_vertices_ + // and figure out exterior ring orientation along the way poly.rewind(0); - for (unsigned cm = poly.vertex(&x, &y); cm != SEG_END; cm = poly.vertex(&x, &y)) + while (unsigned cmd = poly.vertex(&x, &y)) { - if (cm == SEG_MOVETO) + if (cmd == SEG_LINETO) // the most likely command { - ring_begin_x = x; - ring_begin_y = y; + if (ring_begin_cmd == SEG_MOVETO) // exterior ring + { + double ax = x0 - ring_begin_x; + double ay = y0 - ring_begin_y; + double bx = x - ring_begin_x; + double by = y - ring_begin_y; + area += ax * by - bx * ay; + } + roof_vertices_.line_to(x, y - height_); + painter.stroke_to(x, y); + x0 = x; + y0 = y; } - else if (cm == SEG_LINETO) + else if (cmd == SEG_MOVETO) { - render_face(x0, y0, x, y, face_func, frame); + ring_begin = roof_vertices_.size(); + ring_begin_x = x0 = x; + ring_begin_y = y0 = y; + roof_vertices_.move_to(x, y - height_); + painter.stroke_from(x, y); } - else if (cm == SEG_CLOSE) + else if (cmd == SEG_CLOSE) { - render_face(x0, y0, ring_begin_x, ring_begin_y, face_func, frame); + if (ring_begin_cmd == SEG_MOVETO) // exterior ring + { + // because in screen coordinates `y` grows downward, + // negative area means counter-clockwise orientation + // (left-hand-rule, inside is on the left) + ring_begin_cmd = (area < 0 ? SEG_MOVETO_LHR : SEG_MOVETO_RHR); + } + if (ring_begin < roof_vertices_.size()) + { + // modify SEG_MOVETO command so that `render_walls` knows + // how front-side and back-side walls are oriented + roof_vertices_->set_command(ring_begin, ring_begin_cmd); + } + painter.stroke_close(); + painter.stroke(); + x0 = ring_begin_x; + y0 = ring_begin_y; + // add close path command including coordinates; + // we utilize these when rendering walls + roof_vertices_.push_vertex(x0, y0 - height_, SEG_CLOSE); } - x0 = x; - y0 = y; + } + } + + template + void paint_wall(int orientation, + size_t begin1, size_t end1, + size_t begin2, size_t end2, + Context & painter) const + { + if (is_visible(orientation)) + { + paint_wall_faces(begin1, end1, begin2, end2, painter); + } + } + + template + void paint_wall_faces(size_t begin1, size_t end1, + size_t begin2, size_t end2, + Context & painter) const + { + if (begin1 >= end1) + return; + + double x, y; + roof_vertices_->get_vertex(begin1, &x, &y); + painter.fill_from(x, y); + + double x0 = x; + + for (size_t i = begin1; ++i < end1; ) + { + roof_vertices_->get_vertex(i, &x, &y); + painter.fill_to(x, y); + } + for (size_t i = begin2; i < end2; ++i) + { + roof_vertices_->get_vertex(i, &x, &y); + painter.fill_to(x, y); } - frame_func(frame, wall_fill_color); + double sx = stroke_width * (x < x0 ? -0.5 : 0.5); + painter.fill_to(x + sx, y); + painter.fill_to(x + sx, y + height_); + painter.fill_to(x, y + height_); - agg::trans_affine_translation tr(0, -height_); - roof_type roof(poly, tr); - roof_func(roof, fill_color); + for (size_t i = end2; i-- > begin2; ) + { + roof_vertices_->get_vertex(i, &x, &y); + painter.fill_to(x, y + height_); + } + for (size_t i = end1; i-- > begin1; ) + { + roof_vertices_->get_vertex(i, &x, &y); + painter.fill_to(x, y + height_); + } + + painter.fill_to(x - sx, y + height_); + painter.fill_to(x - sx, y); + painter.fill_to(x, y); + painter.fill_close(); + painter.fill(); } static void safe_mul(uint8_t & v, double opacity) diff --git a/include/mapnik/vertex_vector.hpp b/include/mapnik/vertex_vector.hpp index 33054a91b..e64294542 100644 --- a/include/mapnik/vertex_vector.hpp +++ b/include/mapnik/vertex_vector.hpp @@ -83,6 +83,12 @@ public: ::operator delete(vertices_); } } + + void clear() + { + pos_ = 0; + } + size_type size() const { return pos_; diff --git a/src/agg/process_building_symbolizer.cpp b/src/agg/process_building_symbolizer.cpp index d2c143d47..597071fee 100644 --- a/src/agg/process_building_symbolizer.cpp +++ b/src/agg/process_building_symbolizer.cpp @@ -21,7 +21,6 @@ *****************************************************************************/ // mapnik -#include #include #include #include @@ -30,12 +29,10 @@ #include #include #include +#include #include -// stl -#include -#include - +// agg #pragma GCC diagnostic push #include #include "agg_basics.h" @@ -45,26 +42,49 @@ #include "agg_rasterizer_scanline_aa.h" #include "agg_scanline_u.h" #include "agg_renderer_scanline.h" -#include "agg_conv_stroke.h" #pragma GCC diagnostic pop -namespace mapnik +namespace mapnik { + +namespace { // (local) + +struct agg_building_context + : detail::agg_building_symbolizer_context { + using base = detail::agg_building_symbolizer_context; + using pixfmt_type = agg::pixfmt_rgba32_pre; + using renderer_base = agg::renderer_base; + using renderer_type = agg::renderer_scanline_aa_solid; + + pixfmt_type pixf_; + renderer_base renb_; + renderer_type ren_; + agg::scanline_u8 sl_; + + agg_building_context(agg::rendering_buffer & buf, rasterizer & ras) + : base(ras), pixf_(buf), renb_(pixf_), ren_(renb_) {} + + void render_scanlines(rasterizer & ras) + { + agg::render_scanlines(ras, sl_, ren_); + } + + void set_color(color const& c) + { + ren_.color(agg::rgba8_pre(c.red(), c.green(), c.blue(), c.alpha())); + } +}; + +} // namespace mapnik::(local) template void agg_renderer::process(building_symbolizer const& sym, mapnik::feature_impl & feature, proj_transform const& prj_trans) { - using ren_base = agg::renderer_base; - using renderer = agg::renderer_scanline_aa_solid; - buffer_type & current_buffer = buffers_.top().get(); agg::rendering_buffer buf(current_buffer.bytes(), current_buffer.width(), current_buffer.height(), current_buffer.row_size()); - agg::pixfmt_rgba32_pre pixf(buf); - ren_base renb(pixf); - renderer ren(renb); - agg::scanline_u8 sl; + agg_building_context ctx{buf, *ras_ptr}; render_building_symbolizer rebus{sym, feature, common_}; double gamma = get(sym, feature, common_.vars_); @@ -76,36 +96,12 @@ void agg_renderer::process(building_symbolizer const& sym, gamma_ = gamma; } - rebus.setup_colors(sym, feature); + ctx.stroke_gen.width(rebus.stroke_width); + ctx.stroke_gen.line_join(agg::round_join); - rebus.apply( - feature, prj_trans, - [&](path_type const& faces, color const& c) - { - vertex_adapter va(faces); - ras_ptr->reset(); - ras_ptr->add_path(va); - ren.color(agg::rgba8_pre(c.red(), c.green(), c.blue(), c.alpha())); - agg::render_scanlines(*ras_ptr, sl, ren); - }, - [&](path_type const& frame, color const& c) - { - vertex_adapter va(frame); - agg::conv_stroke stroke(va); - stroke.width(rebus.stroke_width); - stroke.miter_limit(1.0); - ras_ptr->reset(); - ras_ptr->add_path(stroke); - ren.color(agg::rgba8_pre(c.red(), c.green(), c.blue(), c.alpha())); - agg::render_scanlines(*ras_ptr, sl, ren); - }, - [&](render_building_symbolizer::roof_type & roof, color const& c) - { - ras_ptr->reset(); - ras_ptr->add_path(roof); - ren.color(agg::rgba8_pre(c.red(), c.green(), c.blue(), c.alpha())); - agg::render_scanlines(*ras_ptr, sl, ren); - }); + rebus.setup_colors(sym, feature); + rebus.render_back_side(rebus.has_transparent_fill()); + rebus.apply(feature, prj_trans, ctx); } template void agg_renderer::process(building_symbolizer const&, diff --git a/src/cairo/cairo_context.cpp b/src/cairo/cairo_context.cpp index 6bcdf7ab1..411160a45 100644 --- a/src/cairo/cairo_context.cpp +++ b/src/cairo/cairo_context.cpp @@ -292,6 +292,12 @@ void cairo_context::stroke() check_object_status_and_throw_exception(*this); } +void cairo_context::stroke_preserve() +{ + cairo_stroke_preserve(cairo_.get()); + check_object_status_and_throw_exception(*this); +} + void cairo_context::fill() { cairo_fill(cairo_.get()); diff --git a/src/cairo/process_building_symbolizer.cpp b/src/cairo/process_building_symbolizer.cpp index 542bb6b20..d1e466865 100644 --- a/src/cairo/process_building_symbolizer.cpp +++ b/src/cairo/process_building_symbolizer.cpp @@ -23,20 +23,46 @@ #if defined(HAVE_CAIRO) // mapnik -#include #include #include #include #include +#include // mapnik symbolizer generics #include -// stl -#include -#include +namespace mapnik { -namespace mapnik +namespace { // (local) + +struct cairo_building_context : util::noncopyable { + cairo_context & cairo_; + + explicit cairo_building_context(cairo_context & context) + : cairo_(context) {} + + void set_color(color const& c) { cairo_.set_color(c); } + + void fill() { cairo_.fill(); } + void fill_from(double x, double y) { cairo_.move_to(x, y); } + void fill_to(double x, double y) { cairo_.line_to(x, y); } + void fill_close() { cairo_.close_path(); } + + void stroke() { cairo_.stroke(); } + void stroke_from(double x, double y) { cairo_.move_to(x, y); } + void stroke_to(double x, double y) { cairo_.line_to(x, y); } + void stroke_close() { cairo_.close_path(); } + + template + void stroke_preserve(VertexSource & vs, unsigned path_id = 0) + { + cairo_.add_path(vs, path_id); + cairo_.stroke_preserve(); + } +}; + +} // namespace mapnik::(local) template void cairo_renderer::process(building_symbolizer const& sym, @@ -44,37 +70,17 @@ void cairo_renderer::process(building_symbolizer const& sym, proj_transform const& prj_trans) { cairo_save_restore guard(context_); + cairo_building_context ctx{context_}; render_building_symbolizer rebus{sym, feature, common_}; composite_mode_e comp_op = get(sym, feature, common_.vars_); context_.set_operator(comp_op); + context_.set_line_width(rebus.stroke_width); + context_.set_line_join(ROUND_JOIN); rebus.setup_colors(sym, feature); - - rebus.apply( - feature, prj_trans, - [&](path_type const& faces, color const& c) - { - vertex_adapter va(faces); - context_.set_color(c); - context_.add_path(va); - context_.fill(); - }, - [&](path_type const& frame, color const& c) - { - vertex_adapter va(frame); - context_.set_color(c); - context_.set_line_width(rebus.stroke_width); - context_.set_miter_limit(1.0); - context_.add_path(va); - context_.stroke(); - }, - [&](render_building_symbolizer::roof_type & roof, color const& c) - { - context_.set_color(c); - context_.add_path(roof); - context_.fill(); - }); + rebus.render_back_side(rebus.has_transparent_fill()); + rebus.apply(feature, prj_trans, ctx); } template void cairo_renderer::process(building_symbolizer const&, diff --git a/src/grid/process_building_symbolizer.cpp b/src/grid/process_building_symbolizer.cpp index 0e727b029..713234675 100644 --- a/src/grid/process_building_symbolizer.cpp +++ b/src/grid/process_building_symbolizer.cpp @@ -23,7 +23,6 @@ #if defined(GRID_RENDERER) // mapnik -#include #include #include #include @@ -32,69 +31,66 @@ #include #include #include +#include #include -// stl -#include -#include - +// agg #pragma GCC diagnostic push #include #include "agg_rasterizer_scanline_aa.h" #include "agg_renderer_scanline.h" #include "agg_scanline_bin.h" -#include "agg_conv_stroke.h" #pragma GCC diagnostic pop -namespace mapnik +namespace mapnik { + +namespace { // (local) + +struct grid_building_context + : detail::agg_building_symbolizer_context { + using base = detail::agg_building_symbolizer_context; + using renderer_base = grid_renderer_base_type; + using renderer_type = agg::renderer_scanline_bin_solid; + using pixfmt_type = typename renderer_base::pixfmt_type; + using color_type = typename renderer_base::pixfmt_type::color_type; + + pixfmt_type pixf_; + renderer_base renb_; + renderer_type ren_; + agg::scanline_bin sl_; + + grid_building_context(grid_rendering_buffer & buf, grid_rasterizer & ras) + : base(ras), pixf_(buf), renb_(pixf_), ren_(renb_) {} + + void render_scanlines(grid_rasterizer & ras) + { + agg::render_scanlines(ras, sl_, ren_); + } + + void set_feature(value_integer id) + { + ren_.color(color_type(id)); + } +}; + +} // namespace mapnik::(local) template void grid_renderer::process(building_symbolizer const& sym, mapnik::feature_impl & feature, proj_transform const& prj_trans) { - using pixfmt_type = typename grid_renderer_base_type::pixfmt_type; - using color_type = typename grid_renderer_base_type::pixfmt_type::color_type; - using renderer_type = agg::renderer_scanline_bin_solid; - agg::scanline_bin sl; - grid_rendering_buffer buf(pixmap_.raw_data(), common_.width_, common_.height_, common_.width_); - pixfmt_type pixf(buf); - - grid_renderer_base_type renb(pixf); - renderer_type ren(renb); - + grid_building_context ctx{buf, *ras_ptr}; render_building_symbolizer rebus{sym, feature, common_}; - rebus.apply( - feature, prj_trans, - [&](path_type const& faces, color const& ) - { - vertex_adapter va(faces); - ras_ptr->reset(); - ras_ptr->add_path(va); - ren.color(color_type(feature.id())); - agg::render_scanlines(*ras_ptr, sl, ren); - }, - [&](path_type const& frame, color const& ) - { - vertex_adapter va(frame); - agg::conv_stroke stroke(va); - stroke.width(rebus.stroke_width); - stroke.miter_limit(1.0); - ras_ptr->reset(); - ras_ptr->add_path(stroke); - ren.color(color_type(feature.id())); - agg::render_scanlines(*ras_ptr, sl, ren); - }, - [&](render_building_symbolizer::roof_type & roof, color const& ) - { - ras_ptr->reset(); - ras_ptr->add_path(roof); - ren.color(color_type(feature.id())); - agg::render_scanlines(*ras_ptr, sl, ren); - }); + ctx.stroke_gen.width(rebus.stroke_width); + ctx.stroke_gen.line_join(agg::round_join); + ctx.set_feature(feature.id()); + + rebus.render_back_side(false); + rebus.apply(feature, prj_trans, ctx); pixmap_.add_feature(feature); }