diff --git a/bindings/python/mapnik_image.cpp b/bindings/python/mapnik_image.cpp index a7142e699..4a4fcae6f 100644 --- a/bindings/python/mapnik_image.cpp +++ b/bindings/python/mapnik_image.cpp @@ -116,11 +116,6 @@ mapnik::image_view_any get_view(mapnik::image_any const& data,unsigned x,unsigne return mapnik::create_view(data,x,y,w,h); } -bool painted(mapnik::image_any const& im) -{ - return im.painted(); -} - bool is_solid(mapnik::image_any const& im) { return mapnik::is_solid(im); @@ -131,6 +126,11 @@ void background(mapnik::image_any & im, mapnik::color const& c) mapnik::fill(im, c); } +void compare(mapnik::image_any const& im1, mapnik::image_any const& im2, double threshold, bool alpha) +{ + mapnik::compare(im1, im2, threshold, alpha); +} + uint32_t get_pixel(mapnik::image_any const& im, unsigned x, unsigned y) { if (x < static_cast(im.width()) && y < static_cast(im.height())) @@ -209,6 +209,11 @@ void set_grayscale_to_alpha(image_any & im) mapnik::set_grayscale_to_alpha(im); } +void set_grayscale_to_alpha_c(image_any & im, mapnik::color const& c) +{ + mapnik::set_grayscale_to_alpha(im, c); +} + void set_color_to_alpha(image_any & im, mapnik::color const& c) { mapnik::set_color_to_alpha(im, c); @@ -322,10 +327,11 @@ void export_image() .def("width",&image_any::width) .def("height",&image_any::height) .def("view",&get_view) - .def("painted",&painted) + .def("painted",&image_any::painted) .def("is_solid",&is_solid) .def("background",&background, "Set the background color of the image.") .def("set_grayscale_to_alpha",&set_grayscale_to_alpha, "Set the grayscale values to the alpha channel of the Image") + .def("set_grayscale_to_alpha",&set_grayscale_to_alpha_c, "Set the grayscale values to the alpha channel of the Image") .def("set_color_to_alpha",&set_color_to_alpha, "Set a given color to the alpha channel of the Image") .def("set_alpha",&set_alpha, "Set the overall alpha channel of the Image") .def("composite",&composite, @@ -336,6 +342,12 @@ void export_image() arg("dx")=0, arg("dy")=0 )) + .def("compare",&compare, + ( arg("self"), + arg("image"), + arg("threshold")=0.0, + arg("alpha")=true + )) .def("premultiplied",&premultiplied) .def("premultiply",&premultiply) .def("demultiply",&demultiply) diff --git a/include/mapnik/image_util.hpp b/include/mapnik/image_util.hpp index 58f976e26..e42c8b1fe 100644 --- a/include/mapnik/image_util.hpp +++ b/include/mapnik/image_util.hpp @@ -128,6 +128,9 @@ MAPNIK_DECL void set_alpha (T & image, float opacity); template MAPNIK_DECL void set_grayscale_to_alpha (T & image); +template +MAPNIK_DECL void set_grayscale_to_alpha (T & image, color const& c); + template MAPNIK_DECL void set_color_to_alpha (T & image, color const& c); @@ -156,6 +159,9 @@ MAPNIK_DECL void view_to_string (image_view_any const& view, std::ostringstream MAPNIK_DECL image_view_any create_view (image_any const& data, unsigned x, unsigned y, unsigned w, unsigned h); +template +MAPNIK_DECL unsigned compare(T const& im1, T const& im2, double threshold = 0, bool alpha = true); + inline bool is_png(std::string const& filename) { return boost::algorithm::iends_with(filename,std::string(".png")); diff --git a/src/image_util.cpp b/src/image_util.cpp index 02f5fdd32..33d62022c 100644 --- a/src/image_util.cpp +++ b/src/image_util.cpp @@ -468,9 +468,9 @@ namespace detail { struct premultiply_visitor { template - bool operator() (T & data) + bool operator() (T &) { - throw std::runtime_error("Error: Premultiply with " + std::string(typeid(data).name()) + " is not supported"); + return false; } }; @@ -492,9 +492,9 @@ bool premultiply_visitor::operator() (image_rgba8 & data) struct demultiply_visitor { template - bool operator() (T & data) + bool operator() (T &) { - throw std::runtime_error("Error: Premultiply with " + std::string(typeid(data).name()) + " is not supported"); + return false; } }; @@ -654,7 +654,7 @@ struct visitor_set_grayscale_to_alpha template void operator() (T & data) { - throw std::runtime_error("Error: set_grayscale_to_alpha with " + std::string(typeid(data).name()) + " is not supported"); + std::clog << "Warning: set_grayscale_to_alpha with " + std::string(typeid(data).name()) + " is not supported, image was not modified" << std::endl; } }; @@ -680,6 +680,43 @@ void visitor_set_grayscale_to_alpha::operator() (image_rgba8 & data } } +struct visitor_set_grayscale_to_alpha_c +{ + visitor_set_grayscale_to_alpha_c(color const& c) + : c_(c) {} + + template + void operator() (T & data) + { + std::clog << "Warning: set_grayscale_to_alpha with " + std::string(typeid(data).name()) + " is not supported, image was not modified" << std::endl; + } + + private: + color const& c_; +}; + +template <> +void visitor_set_grayscale_to_alpha_c::operator() (image_rgba8 & data) +{ + using pixel_type = typename image_rgba8::pixel_type; + for (unsigned int y = 0; y < data.height(); ++y) + { + pixel_type* row_from = data.getRow(y); + for (unsigned int x = 0; x < data.width(); ++x) + { + pixel_type rgba = row_from[x]; + pixel_type r = rgba & 0xff; + pixel_type g = (rgba >> 8 ) & 0xff; + pixel_type b = (rgba >> 16) & 0xff; + + // magic numbers for grayscale + pixel_type a = static_cast(std::ceil((r * .3) + (g * .59) + (b * .11))); + + row_from[x] = (a << 24)| (c_.blue() << 16) | (c_.green() << 8) | (c_.red()) ; + } + } +} + } // end detail ns template<> @@ -694,7 +731,6 @@ MAPNIK_DECL void set_grayscale_to_alpha (image_any & data) } } -// TEMPORARY can be removed once image_any is only way it is being passed. template<> MAPNIK_DECL void set_grayscale_to_alpha (image_rgba8 & data) { @@ -708,6 +744,31 @@ MAPNIK_DECL void set_grayscale_to_alpha (image_rgba8 & data) } } +template<> +MAPNIK_DECL void set_grayscale_to_alpha (image_any & data, color const& c) +{ + // Prior to calling the data must not be premultiplied + bool remultiply = mapnik::demultiply_alpha(data); + util::apply_visitor(detail::visitor_set_grayscale_to_alpha_c(c), data); + if (remultiply) + { + mapnik::premultiply_alpha(data); + } +} + +template<> +MAPNIK_DECL void set_grayscale_to_alpha (image_rgba8 & data, color const& c) +{ + // Prior to calling the data must not be premultiplied + bool remultiply = mapnik::demultiply_alpha(data); + detail::visitor_set_grayscale_to_alpha_c visit(c); + visit(data); + if (remultiply) + { + mapnik::premultiply_alpha(data); + } +} + namespace detail { struct visitor_set_color_to_alpha @@ -1282,4 +1343,116 @@ MAPNIK_DECL image_view_any create_view(image_any const& data,unsigned x,unsigned return util::apply_visitor(detail::visitor_create_view(x,y,w,h), data); } +template +MAPNIK_DECL unsigned compare(T const& im1, T const& im2, double threshold, bool) +{ + using pixel_type = typename T::pixel_type; + if (im1.width() != im2.width() || im1.height() != im2.height()) + { + std::clog << "Warning the two images compared are not the same sizes." << std::endl; + return im1.width() * im1.height(); + } + unsigned difference = 0; + for (unsigned int y = 0; y < im1.height(); ++y) + { + const pixel_type * row_from = im1.getRow(y); + const pixel_type * row_from2 = im2.getRow(y); + for (unsigned int x = 0; x < im1.width(); ++x) + { + double d = std::abs(static_cast(row_from[x]) - static_cast(row_from2[x])); + if (d > threshold) + { + ++difference; + } + } + } + return difference; +} + +template MAPNIK_DECL unsigned compare(image_gray8 const&, image_gray8 const&, double, bool); +template MAPNIK_DECL unsigned compare(image_gray16 const&, image_gray16 const&, double, bool); +template MAPNIK_DECL unsigned compare(image_gray32f const&, image_gray32f const&, double, bool); + +template <> +MAPNIK_DECL unsigned compare(image_null const&, image_null const&, double, bool) +{ + return 0; +} + +template <> +MAPNIK_DECL unsigned compare(image_rgba8 const& im1, image_rgba8 const& im2, double threshold, bool alpha) +{ + using pixel_type = image_rgba8::pixel_type; + if (im1.width() != im2.width() || im1.height() != im2.height()) + { + std::clog << "Warning: The two images compared are not the same sizes." << std::endl; + return im1.width() * im1.height(); + } + unsigned difference = 0; + for (unsigned int y = 0; y < im1.height(); ++y) + { + const pixel_type * row_from = im1.getRow(y); + const pixel_type * row_from2 = im2.getRow(y); + for (unsigned int x = 0; x < im1.width(); ++x) + { + unsigned rgba = row_from[x]; + unsigned rgba2 = row_from2[x]; + unsigned r = rgba & 0xff; + unsigned g = (rgba >> 8 ) & 0xff; + unsigned b = (rgba >> 16) & 0xff; + unsigned r2 = rgba2 & 0xff; + unsigned g2 = (rgba2 >> 8 ) & 0xff; + unsigned b2 = (rgba2 >> 16) & 0xff; + if (std::abs(static_cast(r - r2)) > static_cast(threshold) || + std::abs(static_cast(g - g2)) > static_cast(threshold) || + std::abs(static_cast(b - b2)) > static_cast(threshold)) { + ++difference; + continue; + } + if (alpha) { + unsigned a = (rgba >> 24) & 0xff; + unsigned a2 = (rgba2 >> 24) & 0xff; + if (std::abs(static_cast(a - a2)) > static_cast(threshold)) { + ++difference; + continue; + } + } + } + } + return difference; +} + +namespace detail +{ + +struct visitor_compare +{ + visitor_compare(image_any const& im2, double threshold, bool alpha) + : im2_(im2), threshold_(threshold), alpha_(alpha) {} + + template + unsigned operator() (T const & im1) + { + if (!im2_.is()) + { + std::clog << "Warning: Comparing different image types." << std::endl; + return im1.width() * im1.height(); + } + return mapnik::compare(im1, util::get(im2_), threshold_, alpha_); + } + + private: + image_any const& im2_; + double threshold_; + bool alpha_; +}; + +} // end detail ns + +template <> +MAPNIK_DECL unsigned compare(image_any const& im1, image_any const& im2, double threshold, bool alpha) +{ + util::apply_visitor(detail::visitor_compare(im2, threshold, alpha), im1); +} + } // end ns