Vector patterns with Cairo

This commit is contained in:
Jiri Drbalek 2018-11-02 16:27:26 +00:00
parent 38fa2a9792
commit 378b19b53d
2 changed files with 121 additions and 49 deletions

View file

@ -121,6 +121,30 @@ private:
cairo_face_cache cache_; 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 class cairo_pattern : private util::noncopyable
{ {
public: public:
@ -131,9 +155,15 @@ public:
const unsigned int *in_end = in_ptr + pixels; const unsigned int *in_end = in_ptr + pixels;
unsigned int *out_ptr; 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) while (in_ptr < in_end)
{ {
@ -150,13 +180,18 @@ public:
*out_ptr++ = (a << 24) | (r << 16) | (g << 8) | b; *out_ptr++ = (a << 24) | (r << 16) | (g << 8) | b;
} }
// mark the surface as dirty as we've modified it behind cairo's back // mark the surface as dirty as we've modified it behind cairo's back
cairo_surface_mark_dirty(surface_); cairo_surface_mark_dirty(surface_.get());
pattern_ = cairo_pattern_create_for_surface(surface_); 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() ~cairo_pattern()
{ {
if (surface_) cairo_surface_destroy(surface_);
if (pattern_) cairo_pattern_destroy(pattern_); if (pattern_) cairo_pattern_destroy(pattern_);
} }
@ -190,7 +225,7 @@ public:
} }
private: private:
cairo_surface_t * surface_; cairo_surface_ptr surface_;
cairo_pattern_t * pattern_; 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 class cairo_context : private util::noncopyable
{ {
public: public:

View file

@ -27,40 +27,92 @@
#include <mapnik/vertex_processor.hpp> #include <mapnik/vertex_processor.hpp>
#include <mapnik/vertex_converters.hpp> #include <mapnik/vertex_converters.hpp>
#include <mapnik/renderer_common/pattern_alignment.hpp> #include <mapnik/renderer_common/pattern_alignment.hpp>
#include <mapnik/renderer_common/render_pattern.hpp>
#include <mapnik/renderer_common/apply_vertex_converter.hpp> #include <mapnik/renderer_common/apply_vertex_converter.hpp>
#include <mapnik/renderer_common/clipping_extent.hpp> #include <mapnik/renderer_common/clipping_extent.hpp>
#include <mapnik/agg_rasterizer.hpp> #include <mapnik/cairo/cairo_render_vector.hpp>
#include <mapnik/marker.hpp> #include <mapnik/marker.hpp>
namespace mapnik { namespace mapnik {
struct cairo_renderer_process_visitor_p struct cairo_renderer_process_visitor_p
{ {
cairo_renderer_process_visitor_p(agg::trans_affine & image_tr) cairo_renderer_process_visitor_p(agg::trans_affine const& image_tr,
: image_tr_(image_tr) double opacity)
: image_tr_(image_tr),
opacity_(opacity)
{} {}
image_rgba8 operator() (marker_null const&) cairo_surface_ptr operator()(marker_svg const & marker) const
{ {
return image_rgba8(); 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;
} }
image_rgba8 operator() (marker_svg const& marker) cairo_surface_ptr operator()(marker_rgba8 const& marker) const
{ {
mapnik::box2d<double> const& bbox_image = marker.get_data()->bounding_box() * image_tr_; box2d<double> bbox(marker.bounding_box());
mapnik::image_rgba8 image(bbox_image.width(), bbox_image.height()); agg::trans_affine tr(transform(bbox));
render_pattern<image_rgba8>(marker, image_tr_, 1.0, image);
return image; 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;
} }
image_rgba8 operator() (marker_rgba8 const& marker) cairo_surface_ptr operator() (marker_null const&) const
{ {
return marker.get_data(); 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: private:
agg::trans_affine & image_tr_; 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 struct cairo_pattern_base
@ -121,10 +173,19 @@ struct cairo_polygon_pattern : cairo_pattern_base
cairo_save_restore guard(context); cairo_save_restore guard(context);
context.set_operator(comp_op); context.set_operator(comp_op);
image_rgba8 pattern_img(util::apply_visitor(cairo_renderer_process_visitor_p(image_tr), marker_)); cairo_renderer_process_visitor_p visitor(image_tr, opacity);
coord<double, 2> offset(pattern_offset(sym_, feature_, prj_trans_, common_, cairo_surface_ptr surface(util::apply_visitor(visitor, this->marker_));
pattern_img.width(), pattern_img.height()));
cairo_pattern pattern(pattern_img, opacity); 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_extend(CAIRO_EXTEND_REPEAT);
pattern.set_origin(-offset.x, -offset.y); pattern.set_origin(-offset.x, -offset.y);
context.set_pattern(pattern); context.set_pattern(pattern);