/***************************************************************************** * * This file is part of Mapnik (c++ mapping toolkit) * * Copyright (C) 2011 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 * *****************************************************************************/ // mapnik #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // agg #define AGG_RENDERING_BUFFER row_ptr_cache #include "agg_rendering_buffer.h" #include "agg_pixfmt_rgba.h" #include "agg_scanline_u.h" #include "agg_image_filters.h" #include "agg_trans_bilinear.h" #include "agg_span_allocator.h" #include "agg_image_accessors.h" #include "agg_span_image_filter_rgba.h" // boost #include #include #include // stl #include namespace mapnik { class pattern_source : private boost::noncopyable { public: pattern_source(image_data_32 const& pattern) : pattern_(pattern) {} unsigned int width() const { return pattern_.width(); } unsigned int height() const { return pattern_.height(); } agg::rgba8 pixel(int x, int y) const { unsigned c = pattern_(x,y); return agg::rgba8(c & 0xff, (c >> 8) & 0xff, (c >> 16) & 0xff, (c >> 24) & 0xff); } private: image_data_32 const& pattern_; }; template agg_renderer::agg_renderer(Map const& m, T & pixmap, double scale_factor, unsigned offset_x, unsigned offset_y) : feature_style_processor(m, scale_factor), pixmap_(pixmap), internal_buffer_(), current_buffer_(&pixmap), style_level_compositing_(false), width_(pixmap_.width()), height_(pixmap_.height()), scale_factor_(scale_factor), t_(m.width(),m.height(),m.get_current_extent(),offset_x,offset_y), font_engine_(), font_manager_(font_engine_), detector_(boost::make_shared(box2d(-m.buffer_size(), -m.buffer_size(), m.width() + m.buffer_size() ,m.height() + m.buffer_size()))), ras_ptr(new rasterizer) { setup(m); } template agg_renderer::agg_renderer(Map const& m, T & pixmap, boost::shared_ptr detector, double scale_factor, unsigned offset_x, unsigned offset_y) : feature_style_processor(m, scale_factor), pixmap_(pixmap), internal_buffer_(), current_buffer_(&pixmap), style_level_compositing_(false), width_(pixmap_.width()), height_(pixmap_.height()), scale_factor_(scale_factor), t_(m.width(),m.height(),m.get_current_extent(),offset_x,offset_y), font_engine_(), font_manager_(font_engine_), detector_(detector), ras_ptr(new rasterizer) { setup(m); } template void agg_renderer::setup(Map const &m) { boost::optional const& bg = m.background(); if (bg) pixmap_.set_background(*bg); boost::optional const& image_filename = m.background_image(); if (image_filename) { boost::optional bg_marker = mapnik::marker_cache::instance()->find(*image_filename,true); if (bg_marker && (*bg_marker)->is_bitmap()) { mapnik::image_ptr bg_image = *(*bg_marker)->get_bitmap_data(); int w = bg_image->width(); int h = bg_image->height(); if ( w > 0 && h > 0) { // repeat background-image both vertically and horizontally unsigned x_steps = unsigned(std::ceil(width_/double(w))); unsigned y_steps = unsigned(std::ceil(height_/double(h))); for (unsigned x=0;x void agg_renderer::start_layer_processing(layer const& lay, box2d const& query_extent) { MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: Start processing layer=" << lay.name(); MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: -- datasource=" << lay.datasource().get(); MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: -- query_extent=" << query_extent; if (lay.clear_label_cache()) { detector_->clear(); } query_extent_ = query_extent; int buffer_size = lay.buffer_size(); if (buffer_size != 0 ) { double padding = buffer_size * (double)(query_extent.width()/pixmap_.width()); double x0 = query_extent_.minx(); double y0 = query_extent_.miny(); double x1 = query_extent_.maxx(); double y1 = query_extent_.maxy(); query_extent_.init(x0 - padding, y0 - padding, x1 + padding , y1 + padding); } boost::optional > const& maximum_extent = lay.maximum_extent(); if (maximum_extent) { query_extent_.clip(*maximum_extent); } } template void agg_renderer::end_layer_processing(layer const&) { MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: End layer processing"; } template void agg_renderer::start_style_processing(feature_type_style const& st) { MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: Start processing style"; if (st.comp_op() || st.image_filters().size() > 0 || st.get_opacity() < 1) { style_level_compositing_ = true; } else { style_level_compositing_ = false; } if (style_level_compositing_) { if (!internal_buffer_) { internal_buffer_ = boost::make_shared(pixmap_.width(),pixmap_.height()); } else { internal_buffer_->set_background(color(0,0,0,0)); // fill with transparent colour } current_buffer_ = internal_buffer_.get(); } else { current_buffer_ = &pixmap_; } } template void agg_renderer::end_style_processing(feature_type_style const& st) { if (style_level_compositing_) { bool blend_from = false; if (st.image_filters().size() > 0) { blend_from = true; mapnik::filter::filter_visitor visitor(*current_buffer_); BOOST_FOREACH(mapnik::filter::filter_type const& filter_tag, st.image_filters()) { boost::apply_visitor(visitor, filter_tag); } } if (st.comp_op()) { composite(pixmap_.data(),current_buffer_->data(), *st.comp_op(), st.get_opacity(), 0, 0, false); } else if (blend_from || st.get_opacity() < 1) { composite(pixmap_.data(),current_buffer_->data(), src_over, st.get_opacity(), 0, 0, false); } // apply any 'direct' image filters mapnik::filter::filter_visitor visitor(pixmap_); BOOST_FOREACH(mapnik::filter::filter_type const& filter_tag, st.direct_image_filters()) { boost::apply_visitor(visitor, filter_tag); } } MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: End processing style"; } template void agg_renderer::render_marker(pixel_position const& pos, marker const& marker, agg::trans_affine const& tr, double opacity, composite_mode_e comp_op) { typedef agg::rgba8 color_type; typedef agg::order_rgba order_type; typedef agg::pixel32_type pixel_type; typedef agg::comp_op_adaptor_rgba blender_type; // comp blender typedef agg::pixfmt_custom_blend_rgba pixfmt_comp_type; typedef agg::renderer_base renderer_base; typedef agg::renderer_scanline_aa_solid renderer_type; ras_ptr->reset(); ras_ptr->gamma(agg::gamma_power()); agg::scanline_u8 sl; agg::rendering_buffer buf(current_buffer_->raw_data(), width_, height_, width_ * 4); pixfmt_comp_type pixf(buf); pixf.comp_op(static_cast(comp_op)); renderer_base renb(pixf); if (marker.is_vector()) { box2d const& bbox = (*marker.get_vector_data())->bounding_box(); coord c = bbox.center(); // center the svg marker on '0,0' agg::trans_affine mtx = agg::trans_affine_translation(-c.x,-c.y); // apply symbol transformation to get to map space mtx *= tr; mtx *= agg::trans_affine_scaling(scale_factor_); // render the marker at the center of the marker box mtx.translate(pos.x, pos.y); using namespace mapnik::svg; vertex_stl_adapter stl_storage((*marker.get_vector_data())->source()); svg_path_adapter svg_path(stl_storage); svg_renderer_agg, renderer_type, agg::pixfmt_rgba32> svg_renderer(svg_path, (*marker.get_vector_data())->attributes()); svg_renderer.render(*ras_ptr, sl, renb, mtx, opacity, bbox); } else { double width = (*marker.get_bitmap_data())->width(); double height = (*marker.get_bitmap_data())->height(); double cx = 0.5 * width; double cy = 0.5 * height; if (std::fabs(1.0 - scale_factor_) < 0.001 && tr.is_identity()) { composite(current_buffer_->data(), **marker.get_bitmap_data(), comp_op, opacity, boost::math::iround(pos.x - cx), boost::math::iround(pos.y - cy), false); } else { double p[8]; double x0 = pos.x - 0.5 * width; double y0 = pos.y - 0.5 * height; p[0] = x0; p[1] = y0; p[2] = x0 + width; p[3] = y0; p[4] = x0 + width; p[5] = y0 + height; p[6] = x0; p[7] = y0 + height; agg::trans_affine marker_tr; marker_tr *= agg::trans_affine_translation(-pos.x,-pos.y); marker_tr *= tr; marker_tr *= agg::trans_affine_scaling(scale_factor_); marker_tr *= agg::trans_affine_translation(pos.x,pos.y); marker_tr.transform(&p[0], &p[1]); marker_tr.transform(&p[2], &p[3]); marker_tr.transform(&p[4], &p[5]); marker_tr.transform(&p[6], &p[7]); ras_ptr->move_to_d(p[0],p[1]); ras_ptr->line_to_d(p[2],p[3]); ras_ptr->line_to_d(p[4],p[5]); ras_ptr->line_to_d(p[6],p[7]); agg::span_allocator sa; agg::image_filter_bilinear filter_kernel; agg::image_filter_lut filter(filter_kernel, false); image_data_32 const& src = **marker.get_bitmap_data(); agg::rendering_buffer marker_buf((unsigned char *)src.getBytes(), src.width(), src.height(), src.width()*4); agg::pixfmt_rgba32_pre pixf(marker_buf); typedef agg::image_accessor_clone img_accessor_type; typedef agg::span_interpolator_linear interpolator_type; typedef agg::span_image_filter_rgba_2x2 span_gen_type; typedef agg::renderer_scanline_aa_alpha, span_gen_type> renderer_type; img_accessor_type ia(pixf); interpolator_type interpolator(agg::trans_affine(p, 0, 0, width, height) ); span_gen_type sg(ia, interpolator, filter); renderer_type rp(renb,sa, sg, unsigned(opacity*255)); agg::render_scanlines(*ras_ptr, sl, rp); } } } template void agg_renderer::painted(bool painted) { pixmap_.painted(painted); } template void agg_renderer::debug_draw_box(box2d const& box, double x, double y, double angle) { agg::rendering_buffer buf(pixmap_.raw_data(), width_, height_, width_ * 4); debug_draw_box(buf, box, x, y, angle); } template template void agg_renderer::debug_draw_box(R& buf, box2d const& box, double x, double y, double angle) { typedef agg::pixfmt_rgba32 pixfmt; typedef agg::renderer_base renderer_base; typedef agg::renderer_scanline_aa_solid renderer_type; agg::scanline_p8 sl_line; pixfmt pixf(buf); renderer_base renb(pixf); renderer_type ren(renb); // compute tranformation matrix agg::trans_affine tr = agg::trans_affine_rotation(angle).translate(x, y); // prepare path agg::path_storage pbox; pbox.start_new_path(); pbox.move_to(box.minx(), box.miny()); pbox.line_to(box.maxx(), box.miny()); pbox.line_to(box.maxx(), box.maxy()); pbox.line_to(box.minx(), box.maxy()); pbox.line_to(box.minx(), box.miny()); // prepare stroke with applied transformation typedef agg::conv_transform conv_transform; typedef agg::conv_stroke conv_stroke; conv_transform tbox(pbox, tr); conv_stroke sbox(tbox); sbox.generator().width(1.0 * scale_factor_); // render the outline ras_ptr->reset(); ras_ptr->add_path(sbox); ren.color(agg::rgba8(0x33, 0x33, 0xff, 0xcc)); // blue is fine agg::render_scanlines(*ras_ptr, sl_line, ren); } template void agg_renderer::draw_geo_extent(box2d const& extent, mapnik::color const& color) { box2d box = t_.forward(extent); double x0 = box.minx(); double x1 = box.maxx(); double y0 = box.miny(); double y1 = box.maxy(); unsigned rgba = color.rgba(); for (double x=x0; x; template void agg_renderer::debug_draw_box( agg::rendering_buffer& buf, box2d const& box, double x, double y, double angle); }