fix pixel_cast by avoiding comparing across sign<->unsigned - refs #2893

This commit is contained in:
Dane Springmeyer 2015-06-12 00:02:59 -07:00
parent 62b0d2b06e
commit d29a0f18b1

View file

@ -23,23 +23,130 @@
#ifndef MAPNIK_PIXEL_CAST_HPP
#define MAPNIK_PIXEL_CAST_HPP
#include <type_traits>
#include <boost/numeric/conversion/bounds.hpp>
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);
}
}
template <typename T, typename S>
inline T pixel_cast(S s)
{
using namespace boost::numeric;
if (s > bounds<T>::highest() )
static T max_val = boost::numeric::bounds<T>::highest();
if (detail::greater(s,max_val))
{
return bounds<T>::highest();
return max_val;
}
else if (s < bounds<T>::lowest())
static T min_val = boost::numeric::bounds<T>::lowest();
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