BuildingSymbolizer: rewrite rendering algorithm
- cache transformed vertices for multiple passes - perform each rendering stage on the whole (Multi)Polygon: 1. stroke ground outline 2. fill wall strips 3. stroke and fill roof - don't render back walls if roof and wall fills are opaque
This commit is contained in:
parent
c617b8763a
commit
874b8f531a
9 changed files with 489 additions and 165 deletions
|
@ -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);
|
||||
|
|
|
@ -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<types>(type_ & types::Polygon);
|
||||
|
|
|
@ -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 <mapnik/color.hpp>
|
||||
#include <mapnik/util/noncopyable.hpp>
|
||||
|
||||
// agg
|
||||
#pragma GCC diagnostic push
|
||||
#include <mapnik/warning_ignore_agg.hpp>
|
||||
#include "agg_vcgen_stroke.h"
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
namespace mapnik { namespace detail {
|
||||
|
||||
template <typename Derived, typename Rasterizer>
|
||||
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<Derived&>(*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 <typename VertexSource>
|
||||
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
|
|
@ -30,26 +30,22 @@
|
|||
#include <mapnik/path.hpp>
|
||||
#include <mapnik/transform_path_adapter.hpp>
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#include <mapnik/warning_ignore_agg.hpp>
|
||||
#include "agg_conv_transform.h"
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
namespace mapnik {
|
||||
|
||||
struct render_building_symbolizer
|
||||
{
|
||||
using vertex_adapter_type = geometry::polygon_vertex_adapter<double>;
|
||||
using transform_path_type = transform_path_adapter<view_transform, vertex_adapter_type>;
|
||||
using roof_type = agg::conv_transform<transform_path_type>;
|
||||
|
||||
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 <typename F1, typename F2, typename F3>
|
||||
template <typename Context>
|
||||
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<geometry::polygon<double>>())
|
||||
{
|
||||
auto const& poly = geom.get<geometry::polygon<double>>();
|
||||
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<geometry::multi_polygon<double>>())
|
||||
{
|
||||
auto const& multi_poly = geom.get<geometry::multi_polygon<double>>();
|
||||
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 <typename F>
|
||||
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 <typename Geom, typename F1, typename F2, typename F3>
|
||||
void make_building(Geom & poly, F1 & face_func, F2 & frame_func, F3 & roof_func)
|
||||
template <typename T, typename Context>
|
||||
void render_ground(geometry::polygon<T> const& poly,
|
||||
proj_transform const& prj_trans,
|
||||
Context & painter)
|
||||
{
|
||||
using vertex_adapter_type = geometry::polygon_vertex_adapter<T>;
|
||||
using transform_path_type = transform_path_adapter<view_transform, vertex_adapter_type>;
|
||||
|
||||
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 <typename Context>
|
||||
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 <typename Context>
|
||||
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 <typename VertexSource, typename Context>
|
||||
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 <typename Context>
|
||||
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 <typename Context>
|
||||
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)
|
||||
|
|
|
@ -83,6 +83,12 @@ public:
|
|||
::operator delete(vertices_);
|
||||
}
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
pos_ = 0;
|
||||
}
|
||||
|
||||
size_type size() const
|
||||
{
|
||||
return pos_;
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
*****************************************************************************/
|
||||
|
||||
// mapnik
|
||||
#include <mapnik/make_unique.hpp>
|
||||
#include <mapnik/image_any.hpp>
|
||||
#include <mapnik/feature.hpp>
|
||||
#include <mapnik/agg_renderer.hpp>
|
||||
|
@ -30,12 +29,10 @@
|
|||
#include <mapnik/symbolizer.hpp>
|
||||
#include <mapnik/expression_evaluator.hpp>
|
||||
#include <mapnik/expression.hpp>
|
||||
#include <mapnik/renderer_common/agg_building_symbolizer_context.hpp>
|
||||
#include <mapnik/renderer_common/process_building_symbolizer.hpp>
|
||||
|
||||
// stl
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
|
||||
// agg
|
||||
#pragma GCC diagnostic push
|
||||
#include <mapnik/warning_ignore_agg.hpp>
|
||||
#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<agg_building_context, rasterizer>
|
||||
{
|
||||
using base = detail::agg_building_symbolizer_context<agg_building_context, rasterizer>;
|
||||
using pixfmt_type = agg::pixfmt_rgba32_pre;
|
||||
using renderer_base = agg::renderer_base<pixfmt_type>;
|
||||
using renderer_type = agg::renderer_scanline_aa_solid<renderer_base>;
|
||||
|
||||
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 <typename T0,typename T1>
|
||||
void agg_renderer<T0,T1>::process(building_symbolizer const& sym,
|
||||
mapnik::feature_impl & feature,
|
||||
proj_transform const& prj_trans)
|
||||
{
|
||||
using ren_base = agg::renderer_base<agg::pixfmt_rgba32_pre>;
|
||||
using renderer = agg::renderer_scanline_aa_solid<ren_base>;
|
||||
|
||||
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<value_double, keys::gamma>(sym, feature, common_.vars_);
|
||||
|
@ -76,36 +96,12 @@ void agg_renderer<T0,T1>::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<vertex_adapter> 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<image_rgba8>::process(building_symbolizer const&,
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -23,20 +23,46 @@
|
|||
#if defined(HAVE_CAIRO)
|
||||
|
||||
// mapnik
|
||||
#include <mapnik/make_unique.hpp>
|
||||
#include <mapnik/feature.hpp>
|
||||
#include <mapnik/symbolizer.hpp>
|
||||
#include <mapnik/proj_transform.hpp>
|
||||
#include <mapnik/cairo/cairo_renderer.hpp>
|
||||
#include <mapnik/util/noncopyable.hpp>
|
||||
// mapnik symbolizer generics
|
||||
#include <mapnik/renderer_common/process_building_symbolizer.hpp>
|
||||
|
||||
// stl
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
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 <typename VertexSource>
|
||||
void stroke_preserve(VertexSource & vs, unsigned path_id = 0)
|
||||
{
|
||||
cairo_.add_path(vs, path_id);
|
||||
cairo_.stroke_preserve();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mapnik::(local)
|
||||
|
||||
template <typename T>
|
||||
void cairo_renderer<T>::process(building_symbolizer const& sym,
|
||||
|
@ -44,37 +70,17 @@ void cairo_renderer<T>::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<composite_mode_e, keys::comp_op>(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<cairo_ptr>::process(building_symbolizer const&,
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#if defined(GRID_RENDERER)
|
||||
|
||||
// mapnik
|
||||
#include <mapnik/make_unique.hpp>
|
||||
#include <mapnik/feature.hpp>
|
||||
#include <mapnik/grid/grid_rasterizer.hpp>
|
||||
#include <mapnik/grid/grid_renderer.hpp>
|
||||
|
@ -32,69 +31,66 @@
|
|||
#include <mapnik/expression_evaluator.hpp>
|
||||
#include <mapnik/expression.hpp>
|
||||
#include <mapnik/symbolizer.hpp>
|
||||
#include <mapnik/renderer_common/agg_building_symbolizer_context.hpp>
|
||||
#include <mapnik/renderer_common/process_building_symbolizer.hpp>
|
||||
|
||||
// stl
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
|
||||
// agg
|
||||
#pragma GCC diagnostic push
|
||||
#include <mapnik/warning_ignore_agg.hpp>
|
||||
#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<grid_building_context, grid_rasterizer>
|
||||
{
|
||||
using base = detail::agg_building_symbolizer_context<grid_building_context, grid_rasterizer>;
|
||||
using renderer_base = grid_renderer_base_type;
|
||||
using renderer_type = agg::renderer_scanline_bin_solid<renderer_base>;
|
||||
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 <typename T>
|
||||
void grid_renderer<T>::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<grid_renderer_base_type>;
|
||||
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<vertex_adapter> 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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue