Merge pull request #2908 from mapnik/fix_pixel_cast

Fix pixel cast - good catch broken signed/unsigned comparisons! 

I made bounds methods return constexpr - I don't expect noticeable performance improvements but here we go :)
This commit is contained in:
Artem Pavlenko 2015-06-12 13:45:26 +01:00
commit 8f1f15dc49
2 changed files with 159 additions and 7 deletions

View file

@ -23,23 +23,147 @@
#ifndef MAPNIK_PIXEL_CAST_HPP
#define MAPNIK_PIXEL_CAST_HPP
#include <boost/numeric/conversion/bounds.hpp>
#include <type_traits>
namespace mapnik {
namespace detail {
template<typename T, typename S, typename E = void>
struct numeric_compare;
template<typename T, typename S>
struct numeric_compare_same_sign
{
using sizeup = typename std::conditional<sizeof(T) >= sizeof(S), T, S>::type;
static inline bool less(T t, S s) {
return static_cast<sizeup>(t) < static_cast<sizeup>(s);
}
static inline bool greater(T t, S s) {
return static_cast<sizeup>(t) > static_cast<sizeup>(s);
}
};
template<typename T, typename S>
struct numeric_compare<T,S,typename std::enable_if<!std::is_floating_point<T>::value && !std::is_floating_point<S>::value &&
((std::is_unsigned<T>::value && std::is_unsigned<S>::value)
|| (std::is_signed<T>::value && std::is_signed<S>::value))>
::type> : numeric_compare_same_sign<T,S>
{};
template<typename T, typename S>
struct numeric_compare<T,S,typename std::enable_if<!std::is_floating_point<T>::value && !std::is_floating_point<S>::value &&
std::is_integral<T>::value && std::is_signed<T>::value && std::is_unsigned<S>::value>::type>
{
static inline bool less(T t, S s) {
return (t < static_cast<T>(0)) ? true : static_cast<uint64_t>(t) < static_cast<uint64_t>(s);
}
static inline bool greater(T t, S s) {
return (t < static_cast<T>(0)) ? false : static_cast<uint64_t>(t) > static_cast<uint64_t>(s);
}
};
template<typename T, typename S>
struct numeric_compare<T,S,typename std::enable_if<!std::is_floating_point<T>::value && !std::is_floating_point<S>::value &&
std::is_integral<T>::value && std::is_unsigned<T>::value && std::is_signed<S>::value>::type>
{
static inline bool less(T t, S s) {
return (s < static_cast<S>(0)) ? false : static_cast<uint64_t>(t) < static_cast<uint64_t>(s);
}
static inline bool greater(T t, S s) {
return (s < static_cast<S>(0)) ? true : static_cast<uint64_t>(t) > static_cast<uint64_t>(s);
}
};
template<typename T, typename S>
struct numeric_compare<T,S,typename std::enable_if<std::is_floating_point<T>::value && std::is_floating_point<S>::value>::type>
{
static inline bool less(T t, S s) {
return t < s;
}
static inline bool greater(T t, S s) {
return t > s;
}
};
template<typename T, typename S>
struct numeric_compare<T,S,typename std::enable_if<std::is_floating_point<T>::value && std::is_integral<S>::value>::type>
{
static inline bool less(T t, S s) {
return less(static_cast<double>(t),static_cast<double>(s));
}
static inline bool greater(T t, S s) {
return greater(static_cast<double>(t),static_cast<double>(s));
}
};
template<typename T, typename S>
struct numeric_compare<T,S,typename std::enable_if<std::is_integral<T>::value && std::is_floating_point<S>::value>::type>
{
static inline bool less(T t, S s) {
return less(static_cast<double>(t),static_cast<double>(s));
}
static inline bool greater(T t, S s) {
return greater(static_cast<double>(t),static_cast<double>(s));
}
};
template<typename T, typename S>
inline bool less(T t, S s) {
return numeric_compare<T,S>::less(t,s);
}
template<typename T, typename S>
inline bool greater(T t, S s) {
return numeric_compare<T,S>::greater(t,s);
}
// floats
template <typename T, typename Enable = void>
struct bounds
{
static constexpr T lowest() { return static_cast<T>(-std::numeric_limits<T>::max());}
static constexpr T highest() { return std::numeric_limits<T>::max();}
};
// integers
template <typename T>
struct bounds<T, typename std::enable_if<std::numeric_limits<T>::is_integer>::type >
{
static constexpr T lowest() { return std::numeric_limits<T>::min();}
static constexpr T highest() { return std::numeric_limits<T>::max();}
};
} // ns detail
template <typename T, typename S>
inline T pixel_cast(S s)
{
using namespace boost::numeric;
if (s > bounds<T>::highest() )
static constexpr auto max_val = detail::bounds<T>::highest();
static constexpr auto min_val = detail::bounds<T>::lowest();
if (detail::greater(s,max_val))
{
return bounds<T>::highest();
return max_val;
}
else if (s < bounds<T>::lowest())
else if (detail::less(s,min_val))
{
return bounds<T>::lowest();
return min_val;
}
else
{
return static_cast<T>(s);
}
else return static_cast<T>(s);
}
} // ns mapnik

View file

@ -0,0 +1,28 @@
#include "catch.hpp"
// mapnik
#include <mapnik/image_any.hpp>
#include <mapnik/image_view_any.hpp>
#include <mapnik/color.hpp>
#include <mapnik/image_util.hpp>
TEST_CASE("image set_pixel") {
SECTION("test gray32") {
mapnik::image_gray32 im(256,256);
mapnik::set_pixel(im, 0, 0, -1);
auto pixel = mapnik::get_pixel<mapnik::image_gray32::pixel_type>(im, 0, 0);
INFO( pixel );
CHECK( pixel == 0 );
}
SECTION("test gray8s") {
mapnik::image_gray8s im(256,256);
mapnik::set_pixel(im, 0, 0, std::numeric_limits<mapnik::image_gray8s::pixel_type>::max()+1);
auto pixel = mapnik::get_pixel<mapnik::image_gray8s::pixel_type>(im, 0, 0);
INFO( pixel );
CHECK( (int)pixel == (int)std::numeric_limits<mapnik::image_gray8s::pixel_type>::max() );
}
}