diff --git a/bindings/python/mapnik_image.cpp b/bindings/python/mapnik_image.cpp index 4a4fcae6f..4f930f879 100644 --- a/bindings/python/mapnik_image.cpp +++ b/bindings/python/mapnik_image.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -126,16 +127,56 @@ 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) +std::shared_ptr cast(mapnik::image_any const& im, mapnik::image_dtype type, double offset, double scaling) { - mapnik::compare(im1, im2, threshold, alpha); + return std::make_shared(std::move(mapnik::image_cast(im, type, offset, scaling))); } -uint32_t get_pixel(mapnik::image_any const& im, unsigned x, unsigned y) +unsigned compare(mapnik::image_any const& im1, mapnik::image_any const& im2, double threshold, bool alpha) +{ + return mapnik::compare(im1, im2, threshold, alpha); +} + +struct get_pixel_visitor +{ + get_pixel_visitor(unsigned x, unsigned y) + : x_(x), y_(y) {} + + PyObject* operator() (mapnik::image_null const&) + { + throw std::runtime_error("Can not return a null image from a pixel (shouldn't have reached here)"); + } + + PyObject* operator() (mapnik::image_rgba8 const& im) + { + return PyInt_FromLong(mapnik::get_pixel(im, x_, y_)); + } + + PyObject* operator() (mapnik::image_gray8 const& im) + { + return PyInt_FromLong(mapnik::get_pixel(im, x_, y_)); + } + + PyObject* operator() (mapnik::image_gray16 const& im) + { + return PyInt_FromLong(mapnik::get_pixel(im, x_, y_)); + } + + PyObject* operator() (mapnik::image_gray32f const& im) + { + return PyFloat_FromDouble(mapnik::get_pixel(im, x_, y_)); + } + + private: + unsigned x_; + unsigned y_; +}; + +PyObject* get_pixel(mapnik::image_any const& im, unsigned x, unsigned y) { if (x < static_cast(im.width()) && y < static_cast(im.height())) { - return mapnik::get_pixel(im, x, y); + return mapnik::util::apply_visitor(get_pixel_visitor(x, y), im); } PyErr_SetString(PyExc_IndexError, "invalid x,y for image dimensions"); boost::python::throw_error_already_set(); @@ -153,7 +194,7 @@ mapnik::color get_pixel_color(mapnik::image_any const& im, unsigned x, unsigned return 0; } -void set_pixel(mapnik::image_any & im, unsigned x, unsigned y, mapnik::color const& c) +void set_pixel_color(mapnik::image_any & im, unsigned x, unsigned y, mapnik::color const& c) { if (x >= static_cast(im.width()) && y >= static_cast(im.height())) { @@ -164,6 +205,28 @@ void set_pixel(mapnik::image_any & im, unsigned x, unsigned y, mapnik::color con mapnik::set_pixel(im, x, y, c); } +void set_pixel_double(mapnik::image_any & im, unsigned x, unsigned y, double val) +{ + if (x >= static_cast(im.width()) && y >= static_cast(im.height())) + { + PyErr_SetString(PyExc_IndexError, "invalid x,y for image dimensions"); + boost::python::throw_error_already_set(); + return; + } + mapnik::set_pixel(im, x, y, val); +} + +void set_pixel_int(mapnik::image_any & im, unsigned x, unsigned y, int val) +{ + if (x >= static_cast(im.width()) && y >= static_cast(im.height())) + { + PyErr_SetString(PyExc_IndexError, "invalid x,y for image dimensions"); + boost::python::throw_error_already_set(); + return; + } + mapnik::set_pixel(im, x, y, val); +} + std::shared_ptr open_from_file(std::string const& filename) { boost::optional type = type_from_filename(filename); @@ -348,10 +411,26 @@ void export_image() arg("threshold")=0.0, arg("alpha")=true )) + .def("cast",&cast, + ( arg("self"), + arg("type"), + arg("offset")=0.0, + arg("scaling")=1.0 + )) + .add_property("offset", + &image_any::get_offset, + &image_any::set_offset, + "Gets or sets the offset component.\n") + .add_property("scaling", + &image_any::get_scaling, + &image_any::set_scaling, + "Gets or sets the offset component.\n") .def("premultiplied",&premultiplied) .def("premultiply",&premultiply) .def("demultiply",&demultiply) - .def("set_pixel",&set_pixel) + .def("set_pixel",&set_pixel_color) + .def("set_pixel",&set_pixel_double) + .def("set_pixel",&set_pixel_int) .def("get_pixel",&get_pixel) .def("get_pixel_color",&get_pixel_color) .def("clear",&clear) diff --git a/include/mapnik/image.hpp b/include/mapnik/image.hpp index fe2b2e8c0..850f6e192 100644 --- a/include/mapnik/image.hpp +++ b/include/mapnik/image.hpp @@ -29,6 +29,7 @@ #include #include #include +#include namespace mapnik { @@ -121,11 +122,21 @@ class image public: using pixel_type = T; static constexpr std::size_t pixel_size = sizeof(pixel_type); - +private: + detail::image_dimensions dimensions_; + detail::buffer buffer_; + pixel_type *pData_; + double offset_; + double scaling_; + bool premultiplied_alpha_; + bool painted_; +public: image(int width, int height, bool initialize = true, bool premultiplied = false, bool painted = false) : dimensions_(width, height), buffer_(dimensions_.width() * dimensions_.height() * pixel_size), pData_(reinterpret_cast(buffer_.data())), + offset_(0.0), + scaling_(1.0), premultiplied_alpha_(premultiplied), painted_(painted) { @@ -136,6 +147,8 @@ public: : dimensions_(rhs.dimensions_), buffer_(rhs.buffer_), pData_(reinterpret_cast(buffer_.data())), + offset_(rhs.offset_), + scaling_(rhs.scaling_), premultiplied_alpha_(rhs.premultiplied_alpha_), painted_(rhs.painted_) {} @@ -144,6 +157,8 @@ public: : dimensions_(std::move(rhs.dimensions_)), buffer_(std::move(rhs.buffer_)), pData_(reinterpret_cast(buffer_.data())), + offset_(rhs.offset_), + scaling_(rhs.scaling_), premultiplied_alpha_(rhs.premultiplied_alpha_), painted_(rhs.painted_) { @@ -161,6 +176,8 @@ public: { std::swap(dimensions_, rhs.dimensions_); std::swap(buffer_, rhs.buffer_); + std::swap(offset_, rhs.offset_); + std::swap(scaling_, rhs.scaling_); std::swap(premultiplied_alpha_, rhs.premultiplied_alpha_); std::swap(painted_, rhs.painted_); } @@ -249,6 +266,31 @@ public: std::copy(buf, buf + (x1 - x0), pData_ + row * dimensions_.width() + x0); } + inline double get_offset() const + { + return offset_; + } + + inline void set_offset(double set) + { + offset_ = set; + } + + inline double get_scaling() const + { + return scaling_; + } + + inline void set_scaling(double set) + { + if (set != 0.0) + { + scaling_ = set; + return; + } + std::clog << "Can not set scaling to 0.0, offset not set." << std::endl; + } + inline bool get_premultiplied() const { return premultiplied_alpha_; @@ -268,13 +310,6 @@ public: { return painted_; } - -private: - detail::image_dimensions dimensions_; - detail::buffer buffer_; - pixel_type *pData_; - bool premultiplied_alpha_; - bool painted_; }; using image_rgba8 = image; diff --git a/include/mapnik/image_any.hpp b/include/mapnik/image_any.hpp index fc764f6ed..db7e6bd69 100644 --- a/include/mapnik/image_any.hpp +++ b/include/mapnik/image_any.hpp @@ -38,8 +38,12 @@ struct image_null std::size_t width() const { return 0; } std::size_t height() const { return 0; } bool painted() const { return false; } + double get_offset() const { return 0.0; } + void set_offset(double) {} + double get_scaling() const { return 1.0; } + void set_scaling(double) {} bool get_premultiplied() const { return false; } - void set_premultiplied(bool) const {} + void set_premultiplied(bool) {} void set(pixel_type const&) { throw std::runtime_error("Can not set values for null image"); } pixel_type& operator() (std::size_t, std::size_t) { @@ -139,6 +143,51 @@ struct get_any_row_size_visitor return data.getRowSize(); } }; + +struct get_offset_visitor +{ + template + double operator() (T const& data) const + { + return data.get_offset(); + } +}; + +struct get_scaling_visitor +{ + template + double operator() (T const& data) const + { + return data.get_scaling(); + } +}; + +struct set_offset_visitor +{ + set_offset_visitor(double val) + : val_(val) {} + template + void operator() (T & data) + { + data.set_offset(val_); + } + private: + double val_; +}; + +struct set_scaling_visitor +{ + set_scaling_visitor(double val) + : val_(val) {} + template + void operator() (T & data) + { + data.set_scaling(val_); + } + private: + double val_; +}; + } // namespace detail struct image_any : image_base @@ -196,6 +245,26 @@ struct image_any : image_base { return util::apply_visitor(detail::get_any_row_size_visitor(),*this); } + + double get_offset() const + { + return util::apply_visitor(detail::get_offset_visitor(),*this); + } + + double get_scaling() const + { + return util::apply_visitor(detail::get_scaling_visitor(),*this); + } + + void set_offset(double val) + { + util::apply_visitor(detail::set_offset_visitor(val),*this); + } + + void set_scaling(double val) + { + util::apply_visitor(detail::set_scaling_visitor(val),*this); + } }; inline image_any create_image_any(int width, diff --git a/src/image_convert.cpp b/include/mapnik/image_cast.hpp similarity index 57% rename from src/image_convert.cpp rename to include/mapnik/image_cast.hpp index 40a9dbad9..45098a819 100644 --- a/src/image_convert.cpp +++ b/include/mapnik/image_cast.hpp @@ -20,43 +20,32 @@ * *****************************************************************************/ -// mapnik -#include -#include +#ifndef MAPNIK_IMAGE_CAST_HPP +#define MAPNIK_IMAGE_CAST_HPP + #include - +#include + namespace mapnik { -namespace detail -{ +template +MAPNIK_DECL T image_cast(image_any const&, double offset = 0.0, double scaling = 1.0); -template -struct visitor_convert -{ - using dst_type = typename T0::pixel_type; - template - T0 operator() (T1 const& src) - { - T0 dst(src.width(), src.height()); - for (unsigned y = 0; y < dst.height(); ++y) - { - for (unsigned x = 0; x < dst.width(); ++x) - { - dst(x,y) = static_cast(src(x,y)); - } - } - return T0(std::move(dst)); - } -}; +template +MAPNIK_DECL T image_cast(image_rgba8 const&, double offset = 0.0, double scaling = 1.0); -} // end detail ns +template +MAPNIK_DECL T image_cast(image_gray8 const&, double offset = 0.0, double scaling = 1.0); -template -MAPNIK_DECL T2 convert_image(T1 const& data) -{ - detail::visitor_convert visit; - return visit(data); -} +template +MAPNIK_DECL T image_cast(image_gray16 const&, double offset = 0.0, double scaling = 1.0); + +template +MAPNIK_DECL T image_cast(image_gray32f const&, double offset = 0.0, double scaling = 1.0); + +MAPNIK_DECL image_any image_cast(image_any const&, image_dtype type, double offset = 0.0, double scaling = 1.0); } // end mapnik ns + +#endif // MAPNIK_IMAGE_CAST_HPP diff --git a/include/mapnik/image_convert.hpp b/include/mapnik/image_convert.hpp deleted file mode 100644 index e57bff08d..000000000 --- a/include/mapnik/image_convert.hpp +++ /dev/null @@ -1,36 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2014 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 - * - *****************************************************************************/ - -#ifndef MAPNIK_IMAGE_CONVERT_HPP -#define MAPNIK_IMAGE_CONVERT_HPP - -#include - -namespace mapnik -{ - -template -MAPNIK_DECL T2 convert_image(T1 const& data); - -} // end mapnik ns - -#endif // MAPNIK_IMAGE_CONVERT_HPP diff --git a/include/mapnik/image_util.hpp b/include/mapnik/image_util.hpp index e42c8b1fe..09e522798 100644 --- a/include/mapnik/image_util.hpp +++ b/include/mapnik/image_util.hpp @@ -149,9 +149,33 @@ MAPNIK_DECL bool check_bounds (T const& data, std::size_t x, std::size_t y) template MAPNIK_DECL void composite_pixel(T & data, unsigned op, int x, int y, unsigned c, unsigned cover, double opacity ); +template +MAPNIK_DECL void set_pixel(image_rgba8 & data, std::size_t x, std::size_t y, T const& val); + +template +MAPNIK_DECL void set_pixel(image_gray8 & data, std::size_t x, std::size_t y, T const& val); + +template +MAPNIK_DECL void set_pixel(image_gray16 & data, std::size_t x, std::size_t y, T const& val); + +template +MAPNIK_DECL void set_pixel(image_gray32f & data, std::size_t x, std::size_t y, T const& val); + template MAPNIK_DECL void set_pixel(T1 & data, std::size_t x, std::size_t y, T2 const& val); +template +MAPNIK_DECL T get_pixel(image_rgba8 const& data, std::size_t x, std::size_t y); + +template +MAPNIK_DECL T get_pixel(image_gray8 const& data, std::size_t x, std::size_t y); + +template +MAPNIK_DECL T get_pixel(image_gray16 const& data, std::size_t x, std::size_t y); + +template +MAPNIK_DECL T get_pixel(image_gray32f const& data, std::size_t x, std::size_t y); + template MAPNIK_DECL T2 get_pixel(T1 const& data, std::size_t x, std::size_t y); diff --git a/include/mapnik/image_view.hpp b/include/mapnik/image_view.hpp index 850b4726a..96cda875d 100644 --- a/include/mapnik/image_view.hpp +++ b/include/mapnik/image_view.hpp @@ -121,6 +121,16 @@ public: return data_.get_premultiplied(); } + inline double get_offset() const + { + return data_.get_offset(); + } + + inline double get_scaling() const + { + return data_.get_scaling(); + } + private: unsigned x_; unsigned y_; diff --git a/include/mapnik/image_view_any.hpp b/include/mapnik/image_view_any.hpp index 0e4dbd1e9..425981612 100644 --- a/include/mapnik/image_view_any.hpp +++ b/include/mapnik/image_view_any.hpp @@ -70,6 +70,33 @@ struct get_view_row_size_visitor return data.getRowSize(); } }; + +struct get_view_premultiplied_visitor +{ + template + bool operator()(T const& data) const + { + return data.get_premultiplied(); + } +}; + +struct get_view_offset_visitor +{ + template + double operator()(T const& data) const + { + return data.get_offset(); + } +}; + +struct get_view_scaling_visitor +{ + template + double operator()(T const& data) const + { + return data.get_scaling(); + } +}; } // namespace detail struct image_view_any : image_view_base @@ -99,6 +126,21 @@ struct image_view_any : image_view_base { return util::apply_visitor(detail::get_view_row_size_visitor(),*this); } + + bool get_premultiplied() const + { + return util::apply_visitor(detail::get_view_premultiplied_visitor(),*this); + } + + double get_offset() const + { + return util::apply_visitor(detail::get_view_offset_visitor(),*this); + } + + double get_scaling() const + { + return util::apply_visitor(detail::get_view_scaling_visitor(),*this); + } }; } diff --git a/src/build.py b/src/build.py index 953ce6c2c..c6326906b 100644 --- a/src/build.py +++ b/src/build.py @@ -152,7 +152,7 @@ source = Split( miniz_png.cpp color.cpp conversions.cpp - image_convert.cpp + image_cast.cpp image_compositing.cpp image_scaling.cpp box2d.cpp diff --git a/src/image_cast.cpp b/src/image_cast.cpp new file mode 100644 index 000000000..56e820d50 --- /dev/null +++ b/src/image_cast.cpp @@ -0,0 +1,228 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2014 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 + +// boost +#include + +using boost::numeric_cast; +using boost::numeric::positive_overflow; +using boost::numeric::negative_overflow; + +namespace mapnik +{ + +namespace detail +{ + +template +struct visitor_image_cast +{ + using dst_type = typename T0::pixel_type; + + T0 operator() (image_null const&) + { + throw std::runtime_error("Can not cast a null image"); + } + + T0 operator() (T0 const& src) + { + return T0(src); + } + + template + T0 operator() (T1 const& src) + { + T0 dst(src.width(), src.height(), false); + for (unsigned y = 0; y < dst.height(); ++y) + { + for (unsigned x = 0; x < dst.width(); ++x) + { + try + { + dst(x,y) = numeric_cast(src(x,y)); + } + catch(negative_overflow&) + { + dst(x,y) = std::numeric_limits::min(); + } + catch(positive_overflow&) + { + dst(x,y) = std::numeric_limits::max(); + } + } + } + return T0(std::move(dst)); + } +}; + +template +struct visitor_image_cast_so +{ + using dst_type = typename T0::pixel_type; + + visitor_image_cast_so(double offset, double scaling) + : offset_(offset), scaling_(scaling) {} + + T0 operator() (image_null const&) + { + throw std::runtime_error("Can not cast a null image"); + } + + T0 operator() (T0 const& src) + { + return T0(src); + } + + template + T0 operator() (T1 const& src) + { + double src_offset = src.get_offset(); + double src_scaling = src.get_scaling(); + T0 dst(src.width(), src.height(), false); + dst.set_scaling(scaling_); + dst.set_offset(offset_); + for (unsigned y = 0; y < dst.height(); ++y) + { + for (unsigned x = 0; x < dst.width(); ++x) + { + double scaled_src_val = (numeric_cast(src(x,y)) * src_scaling) + src_offset; + double dst_val = (scaled_src_val - offset_) / scaling_; + try + { + dst(x,y) = numeric_cast(dst_val); + } + catch(negative_overflow&) + { + dst(x,y) = std::numeric_limits::min(); + } + catch(positive_overflow&) + { + dst(x,y) = std::numeric_limits::max(); + } + } + } + return T0(std::move(dst)); + } + private: + double offset_; + double scaling_; +}; + +} // end detail ns + +template +MAPNIK_DECL T image_cast(image_any const& data, double offset, double scaling) +{ + if (offset == 0.0 && scaling == 1.0 && data.get_offset() == 0.0 && data.get_scaling() == 1.0) + { + return util::apply_visitor(detail::visitor_image_cast(), data); + } + else + { + return util::apply_visitor(detail::visitor_image_cast_so(offset, scaling), data); + } +} + +template +MAPNIK_DECL T image_cast(image_rgba8 const& data, double offset, double scaling) +{ + if (offset == 0.0 && scaling == 1.0 && data.get_offset() == 0.0 && data.get_scaling() == 1.0) + { + detail::visitor_image_cast visit; + return visit(data); + } + else + { + detail::visitor_image_cast_so visit(offset, scaling); + return visit(data); + } +} + +template +MAPNIK_DECL T image_cast(image_gray8 const& data, double offset, double scaling) +{ + if (offset == 0.0 && scaling == 1.0 && data.get_offset() == 0.0 && data.get_scaling() == 1.0) + { + detail::visitor_image_cast visit; + return visit(data); + } + else + { + detail::visitor_image_cast_so visit(offset, scaling); + return visit(data); + } +} + +template +MAPNIK_DECL T image_cast(image_gray16 const& data, double offset, double scaling) +{ + if (offset == 0.0 && scaling == 1.0 && data.get_offset() == 0.0 && data.get_scaling() == 1.0) + { + detail::visitor_image_cast visit; + return visit(data); + } + else + { + detail::visitor_image_cast_so visit(offset, scaling); + return visit(data); + } +} + +template +MAPNIK_DECL T image_cast(image_gray32f const& data, double offset, double scaling) +{ + if (offset == 0.0 && scaling == 1.0 && data.get_offset() == 0.0 && data.get_scaling() == 1.0) + { + detail::visitor_image_cast visit; + return visit(data); + } + else + { + detail::visitor_image_cast_so visit(offset, scaling); + return visit(data); + } +} + +MAPNIK_DECL image_any image_cast(image_any const& data, image_dtype type, double offset, double scaling) +{ + switch (type) + { + case image_dtype_rgba8: + return image_any(std::move(image_cast(data, offset, scaling))); + case image_dtype_gray8: + return image_any(std::move(image_cast(data, offset, scaling))); + case image_dtype_gray16: + return image_any(std::move(image_cast(data, offset, scaling))); + case image_dtype_gray32f: + return image_any(std::move(image_cast(data, offset, scaling))); + case image_dtype_null: + throw std::runtime_error("Can not cast a null image"); + } + throw std::runtime_error("Unknown image type passed"); +} + +} // end mapnik ns diff --git a/src/image_util.cpp b/src/image_util.cpp index efce27448..ede5f7bf7 100644 --- a/src/image_util.cpp +++ b/src/image_util.cpp @@ -46,6 +46,13 @@ #include #include +// boost +#include + +using boost::numeric_cast; +using boost::numeric::positive_overflow; +using boost::numeric::negative_overflow; + namespace mapnik { @@ -1089,7 +1096,19 @@ struct visitor_set_pixel void operator() (T2 & data) { using pixel_type = typename T2::pixel_type; - pixel_type val = static_cast(val_); + pixel_type val; + try + { + val = numeric_cast(val_); + } + catch(negative_overflow&) + { + val = std::numeric_limits::min(); + } + catch(positive_overflow&) + { + val = std::numeric_limits::max(); + } if (check_bounds(data, x_, y_)) { data(x_, y_) = val; @@ -1160,31 +1179,74 @@ template MAPNIK_DECL void set_pixel(image_any &, std::size_t, std::size_t, int8_ template MAPNIK_DECL void set_pixel(image_any &, std::size_t, std::size_t, float const&); template MAPNIK_DECL void set_pixel(image_any &, std::size_t, std::size_t, double const&); - -// Temporary remove these later! -template <> -MAPNIK_DECL void set_pixel (image_rgba8 & data, std::size_t x, std::size_t y, color const& val) +template +MAPNIK_DECL void set_pixel (image_rgba8 & data, std::size_t x, std::size_t y, T const& val) { - detail::visitor_set_pixel visitor(x, y, val); + detail::visitor_set_pixel visitor(x, y, val); visitor(data); } -// Temporary remove these later! -template <> -MAPNIK_DECL void set_pixel (image_rgba8 & data, std::size_t x, std::size_t y, uint32_t const& val) +template MAPNIK_DECL void set_pixel(image_rgba8 &, std::size_t, std::size_t, color const&); +template MAPNIK_DECL void set_pixel(image_rgba8 &, std::size_t, std::size_t, uint32_t const&); +template MAPNIK_DECL void set_pixel(image_rgba8 &, std::size_t, std::size_t, int32_t const&); +template MAPNIK_DECL void set_pixel(image_rgba8 &, std::size_t, std::size_t, uint16_t const&); +template MAPNIK_DECL void set_pixel(image_rgba8 &, std::size_t, std::size_t, int16_t const&); +template MAPNIK_DECL void set_pixel(image_rgba8 &, std::size_t, std::size_t, uint8_t const&); +template MAPNIK_DECL void set_pixel(image_rgba8 &, std::size_t, std::size_t, int8_t const&); +template MAPNIK_DECL void set_pixel(image_rgba8 &, std::size_t, std::size_t, float const&); +template MAPNIK_DECL void set_pixel(image_rgba8 &, std::size_t, std::size_t, double const&); + +template +MAPNIK_DECL void set_pixel (image_gray8 & data, std::size_t x, std::size_t y, T const& val) { - detail::visitor_set_pixel visitor(x, y, val); + detail::visitor_set_pixel visitor(x, y, val); visitor(data); } -// Temporary remove these later! -template <> -MAPNIK_DECL void set_pixel (image_rgba8 & data, std::size_t x, std::size_t y, int32_t const& val) +template MAPNIK_DECL void set_pixel(image_gray8 &, std::size_t, std::size_t, color const&); +template MAPNIK_DECL void set_pixel(image_gray8 &, std::size_t, std::size_t, uint32_t const&); +template MAPNIK_DECL void set_pixel(image_gray8 &, std::size_t, std::size_t, int32_t const&); +template MAPNIK_DECL void set_pixel(image_gray8 &, std::size_t, std::size_t, uint16_t const&); +template MAPNIK_DECL void set_pixel(image_gray8 &, std::size_t, std::size_t, int16_t const&); +template MAPNIK_DECL void set_pixel(image_gray8 &, std::size_t, std::size_t, uint8_t const&); +template MAPNIK_DECL void set_pixel(image_gray8 &, std::size_t, std::size_t, int8_t const&); +template MAPNIK_DECL void set_pixel(image_gray8 &, std::size_t, std::size_t, float const&); +template MAPNIK_DECL void set_pixel(image_gray8 &, std::size_t, std::size_t, double const&); + +template +MAPNIK_DECL void set_pixel (image_gray16 & data, std::size_t x, std::size_t y, T const& val) { - detail::visitor_set_pixel visitor(x, y, val); + detail::visitor_set_pixel visitor(x, y, val); visitor(data); } +template MAPNIK_DECL void set_pixel(image_gray16 &, std::size_t, std::size_t, color const&); +template MAPNIK_DECL void set_pixel(image_gray16 &, std::size_t, std::size_t, uint32_t const&); +template MAPNIK_DECL void set_pixel(image_gray16 &, std::size_t, std::size_t, int32_t const&); +template MAPNIK_DECL void set_pixel(image_gray16 &, std::size_t, std::size_t, uint16_t const&); +template MAPNIK_DECL void set_pixel(image_gray16 &, std::size_t, std::size_t, int16_t const&); +template MAPNIK_DECL void set_pixel(image_gray16 &, std::size_t, std::size_t, uint8_t const&); +template MAPNIK_DECL void set_pixel(image_gray16 &, std::size_t, std::size_t, int8_t const&); +template MAPNIK_DECL void set_pixel(image_gray16 &, std::size_t, std::size_t, float const&); +template MAPNIK_DECL void set_pixel(image_gray16 &, std::size_t, std::size_t, double const&); + +template +MAPNIK_DECL void set_pixel (image_gray32f & data, std::size_t x, std::size_t y, T const& val) +{ + detail::visitor_set_pixel visitor(x, y, val); + visitor(data); +} + +template MAPNIK_DECL void set_pixel(image_gray32f &, std::size_t, std::size_t, color const&); +template MAPNIK_DECL void set_pixel(image_gray32f &, std::size_t, std::size_t, uint32_t const&); +template MAPNIK_DECL void set_pixel(image_gray32f &, std::size_t, std::size_t, int32_t const&); +template MAPNIK_DECL void set_pixel(image_gray32f &, std::size_t, std::size_t, uint16_t const&); +template MAPNIK_DECL void set_pixel(image_gray32f &, std::size_t, std::size_t, int16_t const&); +template MAPNIK_DECL void set_pixel(image_gray32f &, std::size_t, std::size_t, uint8_t const&); +template MAPNIK_DECL void set_pixel(image_gray32f &, std::size_t, std::size_t, int8_t const&); +template MAPNIK_DECL void set_pixel(image_gray32f &, std::size_t, std::size_t, float const&); +template MAPNIK_DECL void set_pixel(image_gray32f &, std::size_t, std::size_t, double const&); + namespace detail { template @@ -1263,31 +1325,74 @@ template MAPNIK_DECL int8_t get_pixel(image_view_any const&, std::size_t, std::s template MAPNIK_DECL float get_pixel(image_view_any const&, std::size_t, std::size_t); template MAPNIK_DECL double get_pixel(image_view_any const&, std::size_t, std::size_t); - -// Temporary remove these later! -template <> -MAPNIK_DECL color get_pixel (image_rgba8 const& data, std::size_t x, std::size_t y) +template +MAPNIK_DECL T get_pixel (image_rgba8 const& data, std::size_t x, std::size_t y) { - detail::visitor_get_pixel visitor(x, y); + detail::visitor_get_pixel visitor(x, y); return visitor(data); } -// Temporary remove these later! -template <> -MAPNIK_DECL uint32_t get_pixel (image_rgba8 const& data, std::size_t x, std::size_t y) +template MAPNIK_DECL color get_pixel(image_rgba8 const&, std::size_t, std::size_t); +template MAPNIK_DECL uint32_t get_pixel(image_rgba8 const&, std::size_t, std::size_t); +template MAPNIK_DECL int32_t get_pixel(image_rgba8 const&, std::size_t, std::size_t); +template MAPNIK_DECL uint16_t get_pixel(image_rgba8 const&, std::size_t, std::size_t); +template MAPNIK_DECL int16_t get_pixel(image_rgba8 const&, std::size_t, std::size_t); +template MAPNIK_DECL uint8_t get_pixel(image_rgba8 const&, std::size_t, std::size_t); +template MAPNIK_DECL int8_t get_pixel(image_rgba8 const&, std::size_t, std::size_t); +template MAPNIK_DECL float get_pixel(image_rgba8 const&, std::size_t, std::size_t); +template MAPNIK_DECL double get_pixel(image_rgba8 const&, std::size_t, std::size_t); + +template +MAPNIK_DECL T get_pixel (image_gray8 const& data, std::size_t x, std::size_t y) { - detail::visitor_get_pixel visitor(x, y); + detail::visitor_get_pixel visitor(x, y); return visitor(data); } -// Temporary remove these later! -template <> -MAPNIK_DECL int32_t get_pixel (image_rgba8 const& data, std::size_t x, std::size_t y) +template MAPNIK_DECL color get_pixel(image_gray8 const&, std::size_t, std::size_t); +template MAPNIK_DECL uint32_t get_pixel(image_gray8 const&, std::size_t, std::size_t); +template MAPNIK_DECL int32_t get_pixel(image_gray8 const&, std::size_t, std::size_t); +template MAPNIK_DECL uint16_t get_pixel(image_gray8 const&, std::size_t, std::size_t); +template MAPNIK_DECL int16_t get_pixel(image_gray8 const&, std::size_t, std::size_t); +template MAPNIK_DECL uint8_t get_pixel(image_gray8 const&, std::size_t, std::size_t); +template MAPNIK_DECL int8_t get_pixel(image_gray8 const&, std::size_t, std::size_t); +template MAPNIK_DECL float get_pixel(image_gray8 const&, std::size_t, std::size_t); +template MAPNIK_DECL double get_pixel(image_gray8 const&, std::size_t, std::size_t); + +template +MAPNIK_DECL T get_pixel (image_gray16 const& data, std::size_t x, std::size_t y) { - detail::visitor_get_pixel visitor(x, y); + detail::visitor_get_pixel visitor(x, y); return visitor(data); } +template MAPNIK_DECL color get_pixel(image_gray16 const&, std::size_t, std::size_t); +template MAPNIK_DECL uint32_t get_pixel(image_gray16 const&, std::size_t, std::size_t); +template MAPNIK_DECL int32_t get_pixel(image_gray16 const&, std::size_t, std::size_t); +template MAPNIK_DECL uint16_t get_pixel(image_gray16 const&, std::size_t, std::size_t); +template MAPNIK_DECL int16_t get_pixel(image_gray16 const&, std::size_t, std::size_t); +template MAPNIK_DECL uint8_t get_pixel(image_gray16 const&, std::size_t, std::size_t); +template MAPNIK_DECL int8_t get_pixel(image_gray16 const&, std::size_t, std::size_t); +template MAPNIK_DECL float get_pixel(image_gray16 const&, std::size_t, std::size_t); +template MAPNIK_DECL double get_pixel(image_gray16 const&, std::size_t, std::size_t); + +template +MAPNIK_DECL T get_pixel (image_gray32f const& data, std::size_t x, std::size_t y) +{ + detail::visitor_get_pixel visitor(x, y); + return visitor(data); +} + +template MAPNIK_DECL color get_pixel(image_gray32f const&, std::size_t, std::size_t); +template MAPNIK_DECL uint32_t get_pixel(image_gray32f const&, std::size_t, std::size_t); +template MAPNIK_DECL int32_t get_pixel(image_gray32f const&, std::size_t, std::size_t); +template MAPNIK_DECL uint16_t get_pixel(image_gray32f const&, std::size_t, std::size_t); +template MAPNIK_DECL int16_t get_pixel(image_gray32f const&, std::size_t, std::size_t); +template MAPNIK_DECL uint8_t get_pixel(image_gray32f const&, std::size_t, std::size_t); +template MAPNIK_DECL int8_t get_pixel(image_gray32f const&, std::size_t, std::size_t); +template MAPNIK_DECL float get_pixel(image_gray32f const&, std::size_t, std::size_t); +template MAPNIK_DECL double get_pixel(image_gray32f const&, std::size_t, std::size_t); + namespace detail { diff --git a/tests/python_tests/cast_test.py b/tests/python_tests/cast_test.py new file mode 100644 index 000000000..955a3fed2 --- /dev/null +++ b/tests/python_tests/cast_test.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import sys +import os, mapnik +from timeit import Timer, time +from nose.tools import * +from utilities import execution_path, run_all, get_unique_colors + +def setup(): + # All of the paths used are relative, if we run the tests + # from another directory we need to chdir() + os.chdir(execution_path('.')) + +def test_image_16_8_simple(): + im = mapnik.Image(2,2,mapnik.ImageType.gray16) + im.set_pixel(0,0, 256) + im.set_pixel(0,1, 999) + im.set_pixel(1,0, 5) + im.set_pixel(1,1, 2) + im2 = im.cast(mapnik.ImageType.gray8) + eq_(im2.get_pixel(0,0), 255) + eq_(im2.get_pixel(0,1), 255) + eq_(im2.get_pixel(1,0), 5) + eq_(im2.get_pixel(1,1), 2) + # Cast back! + im = im2.cast(mapnik.ImageType.gray16) + eq_(im.get_pixel(0,0), 255) + eq_(im.get_pixel(0,1), 255) + eq_(im.get_pixel(1,0), 5) + eq_(im.get_pixel(1,1), 2) + +def test_image_32f_8_simple(): + im = mapnik.Image(2,2,mapnik.ImageType.gray32f) + im.set_pixel(0,0, 120.1234) + im.set_pixel(0,1, -23.4) + im.set_pixel(1,0, 120.6) + im.set_pixel(1,1, 360.2) + im2 = im.cast(mapnik.ImageType.gray8) + eq_(im2.get_pixel(0,0), 120) + eq_(im2.get_pixel(0,1), 0) + eq_(im2.get_pixel(1,0), 120) # Notice this is truncated! + eq_(im2.get_pixel(1,1), 255) + +def test_image_offset_and_scale(): + im = mapnik.Image(2,2,mapnik.ImageType.gray16) + eq_(im.offset, 0.0) + eq_(im.scaling, 1.0) + im.offset = 1.0 + im.scaling = 2.0 + eq_(im.offset, 1.0) + eq_(im.scaling, 2.0) + +def test_image_16_8_scale_and_offset(): + im = mapnik.Image(2,2,mapnik.ImageType.gray16) + im.set_pixel(0,0, 256) + im.set_pixel(0,1, 258) + im.set_pixel(1,0, 99999) + im.set_pixel(1,1, 615) + offset = 255 + scaling = 3 + im2 = im.cast(mapnik.ImageType.gray8, offset, scaling) + eq_(im2.get_pixel(0,0), 0) + eq_(im2.get_pixel(0,1), 1) + eq_(im2.get_pixel(1,0), 255) + eq_(im2.get_pixel(1,1), 120) + # pixels will be a little off due to offsets in reverting! + im3 = im2.cast(mapnik.ImageType.gray16) + eq_(im3.get_pixel(0,0), 255) # Rounding error with ints + eq_(im3.get_pixel(0,1), 258) # same + eq_(im3.get_pixel(1,0), 1020) # The other one was way out of range for our scale/offset + eq_(im3.get_pixel(1,1), 615) # same + +def test_image_16_32f_scale_and_offset(): + im = mapnik.Image(2,2,mapnik.ImageType.gray16) + im.set_pixel(0,0, 256) + im.set_pixel(0,1, 258) + im.set_pixel(1,0, 0) + im.set_pixel(1,1, 615) + offset = 255 + scaling = 3.2 + im2 = im.cast(mapnik.ImageType.gray32f, offset, scaling) + eq_(im2.get_pixel(0,0), 0.3125) + eq_(im2.get_pixel(0,1), 0.9375) + eq_(im2.get_pixel(1,0), -79.6875) + eq_(im2.get_pixel(1,1), 112.5) + im3 = im2.cast(mapnik.ImageType.gray16) + eq_(im3.get_pixel(0,0), 256) + eq_(im3.get_pixel(0,1), 258) + eq_(im3.get_pixel(1,0), 0) + eq_(im3.get_pixel(1,1), 615) + +if __name__ == "__main__": + setup() + exit(run_all(eval(x) for x in dir() if x.startswith("test_"))) diff --git a/tests/python_tests/color_test.py b/tests/python_tests/color_test.py new file mode 100644 index 000000000..900faf11b --- /dev/null +++ b/tests/python_tests/color_test.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import sys +import os, mapnik +from timeit import Timer, time +from nose.tools import * +from utilities import execution_path, run_all, get_unique_colors + +def setup(): + # All of the paths used are relative, if we run the tests + # from another directory we need to chdir() + os.chdir(execution_path('.')) + +def test_color_init(): + c = mapnik.Color(12, 128, 255) + eq_(c.r, 12) + eq_(c.g, 128) + eq_(c.b, 255) + eq_(c.a, 255) + eq_(False, c.get_premultiplied()) + c = mapnik.Color(16, 32, 64, 128) + eq_(c.r, 16) + eq_(c.g, 32) + eq_(c.b, 64) + eq_(c.a, 128) + eq_(False, c.get_premultiplied()) + c = mapnik.Color(16, 32, 64, 128,True) + eq_(c.r, 16) + eq_(c.g, 32) + eq_(c.b, 64) + eq_(c.a, 128) + eq_(True, c.get_premultiplied()) + c = mapnik.Color('rgba(16,32,64,0.5)') + eq_(c.r, 16) + eq_(c.g, 32) + eq_(c.b, 64) + eq_(c.a, 128) + eq_(False, c.get_premultiplied()) + c = mapnik.Color('rgba(16,32,64,0.5)', True) + eq_(c.r, 16) + eq_(c.g, 32) + eq_(c.b, 64) + eq_(c.a, 128) + eq_(True, c.get_premultiplied()) + hex_str = '#10204080' + c = mapnik.Color(hex_str) + eq_(c.r, 16) + eq_(c.g, 32) + eq_(c.b, 64) + eq_(c.a, 128) + eq_(hex_str, c.to_hex_string()) + eq_(False, c.get_premultiplied()) + c = mapnik.Color(hex_str, True) + eq_(c.r, 16) + eq_(c.g, 32) + eq_(c.b, 64) + eq_(c.a, 128) + eq_(hex_str, c.to_hex_string()) + eq_(True, c.get_premultiplied()) + rgba_int = 2151686160 + c = mapnik.Color(rgba_int) + eq_(c.r, 16) + eq_(c.g, 32) + eq_(c.b, 64) + eq_(c.a, 128) + eq_(rgba_int, c.packed()) + eq_(False, c.get_premultiplied()) + c = mapnik.Color(rgba_int, True) + eq_(c.r, 16) + eq_(c.g, 32) + eq_(c.b, 64) + eq_(c.a, 128) + eq_(rgba_int, c.packed()) + eq_(True, c.get_premultiplied()) + +def test_color_properties(): + c = mapnik.Color(16, 32, 64, 128) + eq_(c.r, 16) + eq_(c.g, 32) + eq_(c.b, 64) + eq_(c.a, 128) + c.r = 17 + eq_(c.r, 17) + c.g = 33 + eq_(c.g, 33) + c.b = 65 + eq_(c.b, 65) + c.a = 128 + eq_(c.a, 128) + +def test_color_premultiply(): + c = mapnik.Color(16, 33, 255, 128) + eq_(c.premultiply(), True) + eq_(c.r, 8) + eq_(c.g, 17) + eq_(c.b, 128) + eq_(c.a, 128) + # Repeating it again should do nothing + eq_(c.premultiply(), False) + eq_(c.r, 8) + eq_(c.g, 17) + eq_(c.b, 128) + eq_(c.a, 128) + c.demultiply() + c.demultiply() + # This will not return the same values as before but we expect that + eq_(c.r,15) + eq_(c.g,33) + eq_(c.b,255) + eq_(c.a,128) + +if __name__ == "__main__": + setup() + exit(run_all(eval(x) for x in dir() if x.startswith("test_"))) diff --git a/tests/python_tests/image_test.py b/tests/python_tests/image_test.py index 7b233a233..a30a86719 100644 --- a/tests/python_tests/image_test.py +++ b/tests/python_tests/image_test.py @@ -127,6 +127,16 @@ def test_set_and_get_pixel(): eq_(c0_pre.b, c1.b) eq_(c0_pre.a, c1.a) +def test_pixel_overflow(): + im = mapnik.Image(4,4,mapnik.ImageType.gray8) + im.set_pixel(0,0,256) + eq_(im.get_pixel(0,0),255) + +def test_pixel_underflow(): + im = mapnik.Image(4,4,mapnik.ImageType.gray8) + im.set_pixel(0,0,-1) + eq_(im.get_pixel(0,0),0) + @raises(IndexError) def test_set_pixel_out_of_range_1(): im = mapnik.Image(4,4) diff --git a/tests/visual_tests/compare.py b/tests/visual_tests/compare.py index a08f52c0d..238625768 100644 --- a/tests/visual_tests/compare.py +++ b/tests/visual_tests/compare.py @@ -36,17 +36,17 @@ def compare_pixels(pixel1, pixel2, alpha=True, pixel_threshold=0): def compare(actual, expected, alpha=True): im1 = mapnik.Image.open(actual) im2 = mapnik.Image.open(expected) - diff = 0 pixels = im1.width() * im1.height() delta_pixels = (im2.width() * im2.height()) - pixels + #diff = 0 if delta_pixels != 0: return delta_pixels - # TODO: convert to C++ to speed this up - for x in range(0,im1.width(),2): - for y in range(0,im1.height(),2): - if compare_pixels(im1.get_pixel(x,y),im2.get_pixel(x,y),alpha=alpha): - diff += 1 - return diff + #for x in range(0,im1.width(),2): + # for y in range(0,im1.height(),2): + # if compare_pixels(im1.get_pixel(x,y),im2.get_pixel(x,y),alpha=alpha): + # diff += 1 + #return diff + return im1.compare(im2, 0, alpha) def compare_grids(actual, expected, threshold=0, alpha=True): global errors