From d053bddb4301b3a45bf0805d44b73e5485e4093c Mon Sep 17 00:00:00 2001 From: artemp Date: Thu, 26 Jan 2017 15:45:08 +0100 Subject: [PATCH] add support for PNG filters (http://www.libpng.org/pub/png/libpng-manual.txt) ref #3479 --- include/mapnik/image_options.hpp | 4 +++ include/mapnik/png_io.hpp | 7 ++-- src/image_options.cpp | 57 +++++++++++++++++++++++++++++--- src/image_util_png.cpp | 13 ++++++++ 4 files changed, 74 insertions(+), 7 deletions(-) diff --git a/include/mapnik/image_options.hpp b/include/mapnik/image_options.hpp index b860ee684..17b279ce1 100644 --- a/include/mapnik/image_options.hpp +++ b/include/mapnik/image_options.hpp @@ -38,6 +38,10 @@ using image_options_map = std::map >; inline std::string to_string(boost::optional const& val) { return val ? *val : "";} image_options_map parse_image_options(std::string const& options); +#if defined(HAVE_PNG) +int parse_png_filters(std::string const& str); +#endif + } #endif // MAPNIK_IMAGE_OPTIONS_HPP diff --git a/include/mapnik/png_io.hpp b/include/mapnik/png_io.hpp index a9e8bafac..827df4cdb 100644 --- a/include/mapnik/png_io.hpp +++ b/include/mapnik/png_io.hpp @@ -48,14 +48,17 @@ namespace mapnik { struct png_options { int colors; + int filters; int compression; int strategy; int trans_mode; double gamma; bool paletted; bool use_hextree; + png_options() : colors(256), + filters(PNG_FILTER_NONE), compression(Z_DEFAULT_COMPRESSION), strategy(Z_DEFAULT_STRATEGY), trans_mode(-1), @@ -97,7 +100,7 @@ void save_as_png(T1 & file, mask = png_get_asm_flagmask(PNG_SELECT_READ | PNG_SELECT_WRITE); png_set_asm_flags(png_ptr, flags | mask); #endif - png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_FILTER_NONE); + png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, opts.filters); png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { @@ -271,7 +274,7 @@ void save_as_png(T & file, std::vector const& palette, mask = png_get_asm_flagmask(PNG_SELECT_READ | PNG_SELECT_WRITE); png_set_asm_flags(png_ptr, flags | mask); #endif - png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_FILTER_NONE); + png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, opts.filters); png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { diff --git a/src/image_options.cpp b/src/image_options.cpp index f3b76dd45..441a6f0e4 100644 --- a/src/image_options.cpp +++ b/src/image_options.cpp @@ -42,7 +42,7 @@ x3::rule const key("key"); x3::rule const value("value"); auto const key_def = char_("a-zA-Z_") > *char_("a-zA-Z_0-9\\.\\-"); -auto const value_def = +char_("a-zA-Z_0-9\\.\\-"); +auto const value_def = +char_("a-zA-Z_0-9\\.\\-|"); auto const key_value_def = key > -('=' > value); auto const image_options_def = key_value % lit(':'); @@ -55,17 +55,64 @@ BOOST_SPIRIT_DEFINE(image_options); image_options_map parse_image_options(std::string const& str) { - auto const begin = str.begin(); + auto begin = str.begin(); auto const end = str.end(); using boost::spirit::x3::space; using mapnik::grammar::image_options; image_options_map options; - bool success = boost::spirit::x3::phrase_parse(begin, end, image_options, space, options); - if (!success) + try { - throw std::runtime_error("Can't parse image options: " + str); + bool success = boost::spirit::x3::phrase_parse(begin, end, image_options, space, options); + if (!success || begin != end) + { + throw std::runtime_error("Can't parse image options: " + str); + } + } + catch (boost::spirit::x3::expectation_failure const& ex) + { + throw std::runtime_error("Can't parse image options: " + str + " " + ex.what()); } return options; // RVO } +#if defined(HAVE_PNG) +extern "C" +{ +#include +} + +int parse_png_filters(std::string const& str) +{ + auto begin = str.begin(); + auto const end = str.end(); + using boost::spirit::x3::space; + using boost::spirit::x3::symbols; + symbols filter; + filter.add + ("none", PNG_FILTER_NONE) + ("sub", PNG_FILTER_SUB) + ("up", PNG_FILTER_UP) + ("avg", PNG_FILTER_AVG) + ("paeth", PNG_FILTER_PAETH) + ; + + std::vector opts; + try + { + bool success = boost::spirit::x3::phrase_parse(begin, end, filter % "|" , space , opts); + if (!success || begin != end) + { + throw std::runtime_error("Can't parse PNG filters: " + str); + } + } + catch (boost::spirit::x3::expectation_failure const& ex) + { + throw std::runtime_error("Can't parse PNG filters: " + str + " " + ex.what()); + } + int filters = 0; + std::for_each(opts.begin(), opts.end(), [&filters] (int f) { filters |= f;}); + return filters; +} +#endif + } // ns mapnik diff --git a/src/image_util_png.cpp b/src/image_util_png.cpp index 412060f13..77882b3f3 100644 --- a/src/image_util_png.cpp +++ b/src/image_util_png.cpp @@ -154,6 +154,19 @@ void handle_png_options(std::string const& type, throw image_writer_exception("invalid compression strategy parameter: " + *val); } } + else if (key == "f") + { + // filters = PNG_NO_FILTERS; + // filters = PNG_ALL_FILTERS; + // filters = PNG_FAST_FILTERS; + // filters = PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP | PNG_FILTER_AVG | PNG_FILTER_PAETH; + + if (!val) throw image_writer_exception("invalid filters parameter: "); + if (*val == "no") opts.filters = PNG_NO_FILTERS; + else if (*val == "all") opts.filters = PNG_ALL_FILTERS; + else if (*val == "fast") opts.filters = PNG_FAST_FILTERS; + else opts.filters = parse_png_filters(*val); // none | sub | up | avg | paeth + } else { throw image_writer_exception("unhandled png option: " + key);