From a492028f253eb26583cf3a7e97542ff69f5a69fd Mon Sep 17 00:00:00 2001 From: Blake Thompson Date: Fri, 7 Aug 2015 15:54:39 -0500 Subject: [PATCH 1/3] Initial commit of changes for image_filters so they can be called outside of agg renderer --- include/mapnik/image_filter.hpp | 16 +- src/save_map.cpp | 9 + test/unit/imaging/image_filter.cpp | 353 +++++++++++++++++++++++++++++ 3 files changed, 377 insertions(+), 1 deletion(-) create mode 100644 test/unit/imaging/image_filter.cpp diff --git a/include/mapnik/image_filter.hpp b/include/mapnik/image_filter.hpp index e5116953e..3f533e8ef 100644 --- a/include/mapnik/image_filter.hpp +++ b/include/mapnik/image_filter.hpp @@ -931,6 +931,20 @@ struct filter_radius_visitor } }; -}} +template +void filter_image(Src & src, std::string const& filter) +{ + std::vector filter_vector; + parse_image_filters(filter, filter_vector); + filter_visitor visitor(src); + for (filter_type const& filter_tag : filter_vector) + { + util::apply_visitor(visitor, filter_tag); + } +} + +} // End Namespace Filter + +} // End Namespace Mapnik #endif // MAPNIK_IMAGE_FILTER_HPP diff --git a/src/save_map.cpp b/src/save_map.cpp index d757f9e7f..96b618aff 100644 --- a/src/save_map.cpp +++ b/src/save_map.cpp @@ -624,6 +624,15 @@ void serialize_map(ptree & pt, Map const& map, bool explicit_defaults) set_attr(map_node, "background-image-opacity", opacity); } + if (map.image_filters().size() > 0) + { + std::string filters_str; + std::back_insert_iterator sink(filters_str); + if (generate_image_filters(sink, map.image_filters())) + { + set_attr(map_node, "image-filters", filters_str); + } + } int buffer_size = map.buffer_size(); if ( buffer_size || explicit_defaults) diff --git a/test/unit/imaging/image_filter.cpp b/test/unit/imaging/image_filter.cpp new file mode 100644 index 000000000..7a3e01a15 --- /dev/null +++ b/test/unit/imaging/image_filter.cpp @@ -0,0 +1,353 @@ +#include "catch.hpp" + +// mapnik +#include +#include +#include +#include +#include + +TEST_CASE("image filter") { + +SECTION("test bad filter input") { + + mapnik::image_rgba8 im(3,3); + mapnik::fill(im,mapnik::color("blue")); + mapnik::set_pixel(im, 1, 1, mapnik::color("red")); + + // This will not throw as it is just ignored during parsing + // as if no filter is applied + mapnik::filter::filter_image(im, "foo,asdfasdf()"); + mapnik::filter::filter_image(im, "colorize-alpha("); + mapnik::filter::filter_image(im, "color-to-alpha(blue"); + mapnik::filter::filter_image(im, "color-to-alpha(,blue)"); + mapnik::filter::filter_image(im, "colorize-alpha()"); + + CHECK(im(0,0) == 0xffff0000); + CHECK(im(0,1) == 0xffff0000); + CHECK(im(0,2) == 0xffff0000); + CHECK(im(1,0) == 0xffff0000); + CHECK(im(1,1) == 0xff0000ff); + CHECK(im(1,2) == 0xffff0000); + CHECK(im(2,0) == 0xffff0000); + CHECK(im(2,1) == 0xffff0000); + CHECK(im(2,2) == 0xffff0000); + +} // END SECTION + +SECTION("test blur") { + + mapnik::image_rgba8 im(3,3); + mapnik::fill(im,mapnik::color("blue")); + mapnik::set_pixel(im, 1, 1, mapnik::color("red")); + + mapnik::filter::filter_image(im, "blur"); + + CHECK(im(0,0) == 0xffc60038); + CHECK(im(0,1) == 0xffe2001c); + CHECK(im(0,2) == 0xffc60038); + CHECK(im(1,0) == 0xffc60038); + CHECK(im(1,1) == 0xffe2001c); + CHECK(im(1,2) == 0xffc60038); + CHECK(im(2,0) == 0xffc60038); + CHECK(im(2,1) == 0xffe2001c); + CHECK(im(2,2) == 0xffc60038); + +} // END SECTION + +SECTION("test gray") { + + mapnik::image_rgba8 im(3,3); + mapnik::fill(im,mapnik::color("blue")); + mapnik::set_pixel(im, 1, 1, mapnik::color("red")); + + mapnik::filter::filter_image(im, "gray"); + + CHECK(im(0,0) == 0xff1c1c1c); + CHECK(im(0,1) == 0xff1c1c1c); + CHECK(im(0,2) == 0xff1c1c1c); + CHECK(im(1,0) == 0xff1c1c1c); + CHECK(im(1,1) == 0xff4c4c4c); + CHECK(im(1,2) == 0xff1c1c1c); + CHECK(im(2,0) == 0xff1c1c1c); + CHECK(im(2,1) == 0xff1c1c1c); + CHECK(im(2,2) == 0xff1c1c1c); + +} // END SECTION + +SECTION("test agg stack blur") { + + mapnik::image_rgba8 im(3,3); + mapnik::fill(im,mapnik::color("blue")); + mapnik::set_pixel(im, 1, 1, mapnik::color("red")); + + mapnik::filter::filter_image(im, "agg-stack-blur(1,1)"); + + CHECK(im(0,0) == 0xffef000f); + CHECK(im(0,1) == 0xffdf001f); + CHECK(im(0,2) == 0xffef000f); + CHECK(im(1,0) == 0xffdf001f); + CHECK(im(1,1) == 0xffbf003f); + CHECK(im(1,2) == 0xffdf001f); + CHECK(im(2,0) == 0xffef000f); + CHECK(im(2,1) == 0xffdf001f); + CHECK(im(2,2) == 0xffef000f); + +} // END SECTION + +SECTION("test scale-hsla") { + + mapnik::image_rgba8 im(3,3); + mapnik::fill(im,mapnik::color("blue")); + mapnik::set_pixel(im, 1, 1, mapnik::color("red")); + + // Should throw because a value is greater then 1.0 + REQUIRE_THROWS(mapnik::filter::filter_image(im, "scale-hsla(0.0,1.5,0.0,1.0,0.0,0.5,0.0,0.5)");); + + mapnik::filter::filter_image(im, "scale-hsla(0.0,0.5,0.0,1.0,0.0,0.5,0.0,0.5)"); + + CHECK(im(0,0) == 0x80004000); + CHECK(im(0,1) == 0x80004000); + CHECK(im(0,2) == 0x80004000); + CHECK(im(1,0) == 0x80004000); + CHECK(im(1,1) == 0x80000040); + CHECK(im(1,2) == 0x80004000); + CHECK(im(2,0) == 0x80004000); + CHECK(im(2,1) == 0x80004000); + CHECK(im(2,2) == 0x80004000); + +} // END SECTION + +SECTION("test emboss") { + + mapnik::image_rgba8 im(3,3); + mapnik::fill(im,mapnik::color("white")); + mapnik::set_pixel(im, 1, 1, mapnik::color("orange")); + + mapnik::filter::filter_image(im, "emboss"); + + CHECK(im(0,0) == 0xff004bff); + CHECK(im(0,1) == 0xff00a5ff); + CHECK(im(0,2) == 0xff004bff); + CHECK(im(1,0) == 0xffffffff); + CHECK(im(1,1) == 0xff00a5ff); + CHECK(im(1,2) == 0xffffffff); + CHECK(im(2,0) == 0xffffffff); + CHECK(im(2,1) == 0xffffffff); + CHECK(im(2,2) == 0xffffffff); + +} // END SECTION + +SECTION("test sharpen") { + + mapnik::image_rgba8 im(3,3); + mapnik::fill(im,mapnik::color("blue")); + mapnik::set_pixel(im, 1, 1, mapnik::color("gray")); + + mapnik::filter::filter_image(im, "sharpen"); + + CHECK(im(0,0) == 0xffff0000); + CHECK(im(0,1) == 0xffff0000); + CHECK(im(0,2) == 0xffff0000); + CHECK(im(1,0) == 0xffff0000); + CHECK(im(1,1) == 0xff00ffff); + CHECK(im(1,2) == 0xffff0000); + CHECK(im(2,0) == 0xffff0000); + CHECK(im(2,1) == 0xffff0000); + CHECK(im(2,2) == 0xffff0000); + +} // END SECTION + +SECTION("test edge detect") { + + mapnik::image_rgba8 im(3,3); + mapnik::fill(im,mapnik::color("blue")); + mapnik::set_pixel(im, 1, 1, mapnik::color("gray")); + + mapnik::filter::filter_image(im, "edge-detect"); + + CHECK(im(0,0) == 0xff000000); + CHECK(im(0,1) == 0xff008080); + CHECK(im(0,2) == 0xff000000); + CHECK(im(1,0) == 0xff00ffff); + CHECK(im(1,1) == 0xffff0000); + CHECK(im(1,2) == 0xff00ffff); + CHECK(im(2,0) == 0xff000000); + CHECK(im(2,1) == 0xff008080); + CHECK(im(2,2) == 0xff000000); + +} // END SECTION + +SECTION("test sobel") { + + mapnik::image_rgba8 im(3,3); + mapnik::fill(im,mapnik::color("blue")); + mapnik::set_pixel(im, 1, 1, mapnik::color("gray")); + + mapnik::filter::filter_image(im, "sobel"); + + CHECK(im(0,0) == 0xfffeffff); + CHECK(im(0,1) == 0xfffeffff); + CHECK(im(0,2) == 0xfffeffff); + CHECK(im(1,0) == 0xff000000); + CHECK(im(1,1) == 0xff000000); + CHECK(im(1,2) == 0xff000000); + CHECK(im(2,0) == 0xfffeffff); + CHECK(im(2,1) == 0xfffeffff); + CHECK(im(2,2) == 0xfffeffff); + +} // END SECTION + +SECTION("test x-gradient") { + + mapnik::image_rgba8 im(3,3); + mapnik::fill(im,mapnik::color("blue")); + mapnik::set_pixel(im, 1, 1, mapnik::color("gray")); + + mapnik::filter::filter_image(im, "x-gradient"); + + CHECK(im(0,0) == 0xff808080); + CHECK(im(0,1) == 0xffbf4040); + CHECK(im(0,2) == 0xff808080); + CHECK(im(1,0) == 0xff808080); + CHECK(im(1,1) == 0xff808080); + CHECK(im(1,2) == 0xff808080); + CHECK(im(2,0) == 0xff808080); + CHECK(im(2,1) == 0xff41c0c0); + CHECK(im(2,2) == 0xff808080); + +} // END SECTION + +SECTION("test y-gradient") { + + mapnik::image_rgba8 im(3,3); + mapnik::fill(im,mapnik::color("blue")); + mapnik::set_pixel(im, 1, 1, mapnik::color("gray")); + + mapnik::filter::filter_image(im, "y-gradient"); + + CHECK(im(0,0) == 0xff808080); + CHECK(im(0,1) == 0xff808080); + CHECK(im(0,2) == 0xff808080); + CHECK(im(1,0) == 0xffbf4040); + CHECK(im(1,1) == 0xff808080); + CHECK(im(1,2) == 0xff41c0c0); + CHECK(im(2,0) == 0xff808080); + CHECK(im(2,1) == 0xff808080); + CHECK(im(2,2) == 0xff808080); + +} // END SECTION + +SECTION("test invert") { + + mapnik::image_rgba8 im(3,3); + mapnik::fill(im,mapnik::color("blue")); + mapnik::set_pixel(im, 1, 1, mapnik::color("gray")); + + mapnik::filter::filter_image(im, "invert"); + + CHECK(im(0,0) == 0xff00ffff); + CHECK(im(0,1) == 0xff00ffff); + CHECK(im(0,2) == 0xff00ffff); + CHECK(im(1,0) == 0xff00ffff); + CHECK(im(1,1) == 0xff7f7f7f); + CHECK(im(1,2) == 0xff00ffff); + CHECK(im(2,0) == 0xff00ffff); + CHECK(im(2,1) == 0xff00ffff); + CHECK(im(2,2) == 0xff00ffff); + +} // END SECTION + +SECTION("test colorize-alpha - one color") { + + mapnik::image_rgba8 im(3,3); + mapnik::fill(im,mapnik::color("blue")); + mapnik::set_pixel(im, 1, 1, mapnik::color("gray")); + + mapnik::filter::filter_image(im, "colorize-alpha(blue)"); + + CHECK(im(0,0) == 0xffff0000); + CHECK(im(0,1) == 0xffff0000); + CHECK(im(0,2) == 0xffff0000); + CHECK(im(1,0) == 0xffff0000); + CHECK(im(1,1) == 0xffff0000); + CHECK(im(1,2) == 0xffff0000); + CHECK(im(2,0) == 0xffff0000); + CHECK(im(2,1) == 0xffff0000); + CHECK(im(2,2) == 0xffff0000); + +} // END SECTION + +SECTION("test colorize-alpha - two color") { + + mapnik::image_rgba8 im(3,3); + mapnik::fill(im,mapnik::color("blue")); + mapnik::set_pixel(im, 1, 1, mapnik::color("gray")); + + mapnik::filter::filter_image(im, "colorize-alpha(green,blue)"); + + CHECK(im(0,0) == 0xfffc0000); + CHECK(im(0,1) == 0xfffc0000); + CHECK(im(0,2) == 0xfffc0000); + CHECK(im(1,0) == 0xfffc0000); + CHECK(im(1,1) == 0xfffc0000); + CHECK(im(1,2) == 0xfffc0000); + CHECK(im(2,0) == 0xfffc0000); + CHECK(im(2,1) == 0xfffc0000); + CHECK(im(2,2) == 0xfffc0000); + +} // END SECTION + +SECTION("test color-blind-protanope") { + + mapnik::image_rgba8 im(2,2); + mapnik::fill(im,mapnik::color("blue")); + mapnik::set_pixel(im, 0, 1, mapnik::color("green")); + mapnik::set_pixel(im, 1, 0, mapnik::color("yellow")); + mapnik::set_pixel(im, 1, 1, mapnik::color("red")); + + mapnik::filter::filter_image(im, "color-blind-protanope"); + + CHECK(im(0,0) == 0xff9a4a00); + CHECK(im(0,1) == 0xff006e7c); + CHECK(im(1,0) == 0xffd9f6ff); + CHECK(im(1,1) == 0xff1d7e8e); + +} // END SECTION + +SECTION("test color-blind-deuteranope") { + + mapnik::image_rgba8 im(2,2); + mapnik::fill(im,mapnik::color("blue")); + mapnik::set_pixel(im, 0, 1, mapnik::color("green")); + mapnik::set_pixel(im, 1, 0, mapnik::color("yellow")); + mapnik::set_pixel(im, 1, 1, mapnik::color("red")); + + mapnik::filter::filter_image(im, "color-blind-deuteranope"); + + CHECK(im(0,0) == 0xff824f00); + CHECK(im(0,1) == 0xff1c688b); + CHECK(im(1,0) == 0xffe9f5ff); + CHECK(im(1,1) == 0xff0077a0); + +} // END SECTION + +SECTION("test color-blind-tritanope") { + + mapnik::image_rgba8 im(2,2); + mapnik::fill(im,mapnik::color("blue")); + mapnik::set_pixel(im, 0, 1, mapnik::color("green")); + mapnik::set_pixel(im, 1, 0, mapnik::color("yellow")); + mapnik::set_pixel(im, 1, 1, mapnik::color("red")); + + mapnik::filter::filter_image(im, "color-blind-tritanope"); + + CHECK(im(0,0) == 0xff595500); + CHECK(im(0,1) == 0xff80763a); + CHECK(im(1,0) == 0xfff8f3ff); + CHECK(im(1,1) == 0xff0017fd); + +} // END SECTION + +} // END TEST CASE + From 792e94ae90b3596c8ab5cf0afb931172dc8033b1 Mon Sep 17 00:00:00 2001 From: Blake Thompson Date: Tue, 11 Aug 2015 14:31:29 -0500 Subject: [PATCH 2/3] Removed image-filter from map object, made it so that premultiplication/demultiplication only took place when correctly required in image filters. --- include/mapnik/image_filter.hpp | 36 ++++++++++++++++++++++++--------- include/mapnik/map.hpp | 12 ----------- src/agg/agg_renderer.cpp | 22 +++++++++----------- src/load_map.cpp | 9 --------- src/save_map.cpp | 10 --------- test/data-visual | 2 +- 6 files changed, 36 insertions(+), 55 deletions(-) diff --git a/include/mapnik/image_filter.hpp b/include/mapnik/image_filter.hpp index 3f533e8ef..c7c1f7d49 100644 --- a/include/mapnik/image_filter.hpp +++ b/include/mapnik/image_filter.hpp @@ -26,6 +26,7 @@ //mapnik #include +#include #include // boost GIL @@ -394,17 +395,15 @@ void apply_convolution_3x3(Src const& src_view, Dst & dst_view, Filter const& fi template void apply_filter(Src & src, Filter const& filter) { - { - demultiply_alpha(src); - double_buffer tb(src); - apply_convolution_3x3(tb.src_view, tb.dst_view, filter); - } // ensure ~double_buffer() is called before premultiplying - premultiply_alpha(src); + demultiply_alpha(src); + double_buffer tb(src); + apply_convolution_3x3(tb.src_view, tb.dst_view, filter); } template void apply_filter(Src & src, agg_stack_blur const& op) { + premultiply_alpha(src); agg::rendering_buffer buf(src.bytes(),src.width(),src.height(), src.row_size()); agg::pixfmt_rgba32_pre pixf(buf); agg::stack_blur_rgba32(pixf,op.rx,op.ry); @@ -427,6 +426,7 @@ template void apply_filter(Src & src, color_to_alpha const& op) { using namespace boost::gil; + bool premultiplied = src.get_premultiplied(); rgba8_view_t src_view = rgba8_view(src); double cr = static_cast(op.color.red())/255.0; double cg = static_cast(op.color.green())/255.0; @@ -450,7 +450,7 @@ void apply_filter(Src & src, color_to_alpha const& op) r = g = b = 0; continue; } - else + else if (premultiplied) { sr /= sa; sg /= sa; @@ -479,6 +479,8 @@ void apply_filter(Src & src, color_to_alpha const& op) } } } + // set as premultiplied + set_premultiplied_alpha(src, true); } template @@ -509,6 +511,8 @@ void apply_filter(Src & src, colorize_alpha const& op) } } } + // set as premultiplied + set_premultiplied_alpha(src, true); } else if (size > 1) { @@ -584,6 +588,8 @@ void apply_filter(Src & src, colorize_alpha const& op) } } } + // set as premultiplied + set_premultiplied_alpha(src, true); } } @@ -597,6 +603,7 @@ void apply_filter(Src & src, scale_hsla const& transform) // should be run to avoid overhead of temp buffer if (tinting || set_alpha) { + bool premultiplied = src.get_premultiplied(); rgba8_view_t src_view = rgba8_view(src); for (std::ptrdiff_t y = 0; y < src_view.height(); ++y) { @@ -617,12 +624,13 @@ void apply_filter(Src & src, scale_hsla const& transform) r = g = b = 0; continue; } - else + else if (premultiplied) { r2 /= a2; g2 /= a2; b2 /= a2; } + if (set_alpha) { a2 = transform.a0 + (a2 * (transform.a1 - transform.a0)); @@ -671,6 +679,8 @@ void apply_filter(Src & src, scale_hsla const& transform) if (b>a) b=a; } } + // set as premultiplied + set_premultiplied_alpha(src, true); } } @@ -679,6 +689,7 @@ void color_blind_filter(Src & src, ColorBlindFilter const& op) { using namespace boost::gil; rgba8_view_t src_view = rgba8_view(src); + bool premultiplied = src.get_premultiplied(); for (std::ptrdiff_t y = 0; y < src_view.height(); ++y) { @@ -700,7 +711,7 @@ void color_blind_filter(Src & src, ColorBlindFilter const& op) r = g = b = 0; continue; } - else + else if (premultiplied) { dr /= da; dg /= da; @@ -790,7 +801,8 @@ void color_blind_filter(Src & src, ColorBlindFilter const& op) b = static_cast(db * 255.0); } } - + // set as premultiplied + set_premultiplied_alpha(src, true); } template @@ -814,6 +826,7 @@ void apply_filter(Src & src, color_blind_tritanope const& op) template void apply_filter(Src & src, gray const& /*op*/) { + premultiply_alpha(src); using namespace boost::gil; rgba8_view_t src_view = rgba8_view(src); @@ -864,6 +877,7 @@ void x_gradient_impl(Src const& src_view, Dst const& dst_view) template void apply_filter(Src & src, x_gradient const& /*op*/) { + premultiply_alpha(src); double_buffer tb(src); x_gradient_impl(tb.src_view, tb.dst_view); } @@ -871,6 +885,7 @@ void apply_filter(Src & src, x_gradient const& /*op*/) template void apply_filter(Src & src, y_gradient const& /*op*/) { + premultiply_alpha(src); double_buffer tb(src); x_gradient_impl(rotated90ccw_view(tb.src_view), rotated90ccw_view(tb.dst_view)); @@ -879,6 +894,7 @@ void apply_filter(Src & src, y_gradient const& /*op*/) template void apply_filter(Src & src, invert const& /*op*/) { + premultiply_alpha(src); using namespace boost::gil; rgba8_view_t src_view = rgba8_view(src); diff --git a/include/mapnik/map.hpp b/include/mapnik/map.hpp index 686d53661..23ea96a7c 100644 --- a/include/mapnik/map.hpp +++ b/include/mapnik/map.hpp @@ -33,7 +33,6 @@ #include #include #include -#include // boost #include @@ -102,7 +101,6 @@ private: boost::optional font_directory_; freetype_engine::font_file_mapping_type font_file_mapping_; freetype_engine::font_memory_cache_type font_memory_cache_; - std::vector filters_; public: @@ -502,16 +500,6 @@ public: return font_memory_cache_; } - std::vector & image_filters() - { - return filters_; - } - - std::vector const& image_filters() const - { - return filters_; - } - private: friend void swap(Map & rhs, Map & lhs); void fixAspectRatio(); diff --git a/src/agg/agg_renderer.cpp b/src/agg/agg_renderer.cpp index 41eceee6c..74e3c935b 100644 --- a/src/agg/agg_renderer.cpp +++ b/src/agg/agg_renderer.cpp @@ -199,15 +199,6 @@ void agg_renderer::start_map_processing(Map const& map) template void agg_renderer::end_map_processing(Map const& map) { - if (map.image_filters().size() > 0) - { - mapnik::filter::filter_visitor visitor(pixmap_); - for (mapnik::filter::filter_type const& filter_tag : map.image_filters()) - { - util::apply_visitor(visitor, filter_tag); - } - } - mapnik::demultiply_alpha(pixmap_); MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: End map processing"; } @@ -318,6 +309,7 @@ void agg_renderer::end_style_processing(feature_type_style const& st) { util::apply_visitor(visitor, filter_tag); } + mapnik::premultiply_alpha(*current_buffer_); } if (st.comp_op()) { @@ -334,11 +326,15 @@ void agg_renderer::end_style_processing(feature_type_style const& st) -common_.t_.offset()); } } - // apply any 'direct' image filters - mapnik::filter::filter_visitor visitor(pixmap_); - for (mapnik::filter::filter_type const& filter_tag : st.direct_image_filters()) + if (st.direct_image_filters().size() > 0) { - util::apply_visitor(visitor, filter_tag); + // apply any 'direct' image filters + mapnik::filter::filter_visitor visitor(pixmap_); + for (mapnik::filter::filter_type const& filter_tag : st.direct_image_filters()) + { + util::apply_visitor(visitor, filter_tag); + } + mapnik::premultiply_alpha(pixmap_); } MAPNIK_LOG_DEBUG(agg_renderer) << "agg_renderer: End processing style"; } diff --git a/src/load_map.cpp b/src/load_map.cpp index 4e5cc6ab3..6e15ba2fb 100644 --- a/src/load_map.cpp +++ b/src/load_map.cpp @@ -197,15 +197,6 @@ void map_parser::parse_map(Map & map, xml_node const& node, std::string const& b map.set_background(*bgcolor); } - optional filters = map_node.get_opt_attr("image-filters"); - if (filters) - { - if (!parse_image_filters(*filters, map.image_filters())) - { - throw config_error("failed to parse image-filters: '" + *filters + "'"); - } - } - optional image_filename = map_node.get_opt_attr("background-image"); if (image_filename) { diff --git a/src/save_map.cpp b/src/save_map.cpp index 96b618aff..7ba97579d 100644 --- a/src/save_map.cpp +++ b/src/save_map.cpp @@ -624,16 +624,6 @@ void serialize_map(ptree & pt, Map const& map, bool explicit_defaults) set_attr(map_node, "background-image-opacity", opacity); } - if (map.image_filters().size() > 0) - { - std::string filters_str; - std::back_insert_iterator sink(filters_str); - if (generate_image_filters(sink, map.image_filters())) - { - set_attr(map_node, "image-filters", filters_str); - } - } - int buffer_size = map.buffer_size(); if ( buffer_size || explicit_defaults) { diff --git a/test/data-visual b/test/data-visual index 83b4d4733..d675ac955 160000 --- a/test/data-visual +++ b/test/data-visual @@ -1 +1 @@ -Subproject commit 83b4d4733e70fbea95715ae57c24937006fc593c +Subproject commit d675ac955854989535b03d39c47140ba6811a317 From 0b6b11b9cf03c1431aeab01c82e6bdf676b99e35 Mon Sep 17 00:00:00 2001 From: Blake Thompson Date: Tue, 11 Aug 2015 19:22:09 -0500 Subject: [PATCH 3/3] Added the ability for filter_image to throw properly with bad input and added the ability for a new image to be returned --- CHANGELOG.md | 1 + include/mapnik/image_filter.hpp | 22 ++++++++++++++++- test/unit/imaging/image_filter.cpp | 38 ++++++++++++++++++++++++------ 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0810100b6..2f703fe8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ Released: YYYY XX, 2015 - Added 3 new image-filters to simulate types of colorblindness (`color-blind-protanope`,`color-blind-deuteranope`,`color-blind-tritanope`) - Fix so that null text boxes have no bounding boxes when attempting placement ( 162f82cba5b0fb984c425586c6a4b354917abc47 ) - Patch to add legacy method for setting JPEG quality in images ( #3024 ) +- Added `filter_image` method which can modify an image in place or return a new image that is filtered. ## 3.0.2 diff --git a/include/mapnik/image_filter.hpp b/include/mapnik/image_filter.hpp index c7c1f7d49..d6ed0cda4 100644 --- a/include/mapnik/image_filter.hpp +++ b/include/mapnik/image_filter.hpp @@ -951,7 +951,10 @@ template void filter_image(Src & src, std::string const& filter) { std::vector filter_vector; - parse_image_filters(filter, filter_vector); + if(!parse_image_filters(filter, filter_vector)) + { + throw std::runtime_error("Failed to parse filter argument in filter_image: '" + filter + "'"); + } filter_visitor visitor(src); for (filter_type const& filter_tag : filter_vector) { @@ -959,6 +962,23 @@ void filter_image(Src & src, std::string const& filter) } } +template +Src filter_image(Src const& src, std::string const& filter) +{ + std::vector filter_vector; + if(!parse_image_filters(filter, filter_vector)) + { + throw std::runtime_error("Failed to parse filter argument in filter_image: '" + filter + "'"); + } + Src new_src(src); + filter_visitor visitor(new_src); + for (filter_type const& filter_tag : filter_vector) + { + util::apply_visitor(visitor, filter_tag); + } + return new_src; +} + } // End Namespace Filter } // End Namespace Mapnik diff --git a/test/unit/imaging/image_filter.cpp b/test/unit/imaging/image_filter.cpp index 7a3e01a15..f07836d6f 100644 --- a/test/unit/imaging/image_filter.cpp +++ b/test/unit/imaging/image_filter.cpp @@ -15,14 +15,17 @@ SECTION("test bad filter input") { mapnik::fill(im,mapnik::color("blue")); mapnik::set_pixel(im, 1, 1, mapnik::color("red")); - // This will not throw as it is just ignored during parsing - // as if no filter is applied - mapnik::filter::filter_image(im, "foo,asdfasdf()"); - mapnik::filter::filter_image(im, "colorize-alpha("); - mapnik::filter::filter_image(im, "color-to-alpha(blue"); - mapnik::filter::filter_image(im, "color-to-alpha(,blue)"); - mapnik::filter::filter_image(im, "colorize-alpha()"); + REQUIRE_THROWS( mapnik::filter::filter_image(im, "foo,asdfasdf()"); ); + REQUIRE_THROWS( mapnik::filter::filter_image(im, "colorize-alpha("); ); + REQUIRE_THROWS( mapnik::filter::filter_image(im, "color-to-alpha(blue"); ); + REQUIRE_THROWS( mapnik::filter::filter_image(im, "color-to-alpha(,blue)"); ); + REQUIRE_THROWS( mapnik::filter::filter_image(im, "colorize-alpha()"); ); + REQUIRE_THROWS( + mapnik::image_rgba8 const& im2 = im; + mapnik::image_rgba8 new_im = mapnik::filter::filter_image(im2, "foo"); + ); + CHECK(im(0,0) == 0xffff0000); CHECK(im(0,1) == 0xffff0000); CHECK(im(0,2) == 0xffff0000); @@ -55,6 +58,27 @@ SECTION("test blur") { } // END SECTION +SECTION("test blur constant") { + + mapnik::image_rgba8 im_orig(3,3); + mapnik::fill(im_orig,mapnik::color("blue")); + mapnik::set_pixel(im_orig, 1, 1, mapnik::color("red")); + + mapnik::image_rgba8 const& im_new = im_orig; + mapnik::image_rgba8 im = mapnik::filter::filter_image(im_new, "blur"); + + CHECK(im(0,0) == 0xffc60038); + CHECK(im(0,1) == 0xffe2001c); + CHECK(im(0,2) == 0xffc60038); + CHECK(im(1,0) == 0xffc60038); + CHECK(im(1,1) == 0xffe2001c); + CHECK(im(1,2) == 0xffc60038); + CHECK(im(2,0) == 0xffc60038); + CHECK(im(2,1) == 0xffe2001c); + CHECK(im(2,2) == 0xffc60038); + +} // END SECTION + SECTION("test gray") { mapnik::image_rgba8 im(3,3);