// // Copyright 2005-2007 Adobe Systems Incorporated // // Distributed under the Boost Software License, Version 1.0 // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt // #ifndef BOOST_GIL_GIL_CHANNEL_ALGORITHM_HPP #define BOOST_GIL_GIL_CHANNEL_ALGORITHM_HPP #include #include #include #include #include #include #include namespace boost { namespace gil { namespace detail { // some forward declarations template struct channel_converter_unsigned_impl; template struct channel_converter_unsigned_integral; template struct channel_converter_unsigned_integral_impl; template struct channel_converter_unsigned_integral_nondivisible; ////////////////////////////////////// //// unsigned_integral_max_value - given an unsigned integral channel type, //// returns its maximum value as an integral constant ////////////////////////////////////// template struct unsigned_integral_max_value : std::integral_constant < UnsignedIntegralChannel, (std::numeric_limits::max)() > {}; template <> struct unsigned_integral_max_value : std::integral_constant {}; template <> struct unsigned_integral_max_value : std::integral_constant {}; template <> struct unsigned_integral_max_value : std::integral_constant {}; template struct unsigned_integral_max_value> : std::integral_constant < typename packed_channel_value::integer_t, (uint64_t(1)< {}; ////////////////////////////////////// //// unsigned_integral_num_bits - given an unsigned integral channel type, //// returns the minimum number of bits needed to represent it ////////////////////////////////////// template struct unsigned_integral_num_bits : std::integral_constant {}; template struct unsigned_integral_num_bits> : std::integral_constant {}; } // namespace detail /// \defgroup ChannelConvertAlgorithm channel_convert /// \brief Converting from one channel type to another /// \ingroup ChannelAlgorithm /// /// Conversion is done as a simple linear mapping of one channel range to the other, /// such that the minimum/maximum value of the source maps to the minimum/maximum value of the destination. /// One implication of this is that the value 0 of signed channels may not be preserved! /// /// When creating new channel models, it is often a good idea to provide specializations for the channel conversion algorithms, for /// example, for performance optimizations. If the new model is an integral type that can be signed, it is easier to define the conversion /// only for the unsigned type (\p channel_converter_unsigned) and provide specializations of \p detail::channel_convert_to_unsigned /// and \p detail::channel_convert_from_unsigned to convert between the signed and unsigned type. /// /// Example: /// \code /// // float32_t is a floating point channel with range [0.0f ... 1.0f] /// float32_t src_channel = channel_traits::max_value(); /// assert(src_channel == 1); /// /// // uint8_t is 8-bit unsigned integral channel (aliased from unsigned char) /// uint8_t dst_channel = channel_convert(src_channel); /// assert(dst_channel == 255); // max value goes to max value /// \endcode /// /// \defgroup ChannelConvertUnsignedAlgorithm channel_converter_unsigned /// \ingroup ChannelConvertAlgorithm /// \brief Convert one unsigned/floating point channel to another. Converts both the channel type and range /// @{ ////////////////////////////////////// //// channel_converter_unsigned ////////////////////////////////////// template // Model ChannelValueConcept struct channel_converter_unsigned : detail::channel_converter_unsigned_impl < SrcChannelV, DstChannelV, detail::is_channel_integral::value, detail::is_channel_integral::value > {}; /// \brief Converting a channel to itself - identity operation template struct channel_converter_unsigned : public detail::identity {}; namespace detail { ////////////////////////////////////// //// channel_converter_unsigned_impl ////////////////////////////////////// /// \brief This is the default implementation. Performance specializatons are provided template struct channel_converter_unsigned_impl { using argument_type = SrcChannelV; using result_type = DstChannelV; DstChannelV operator()(SrcChannelV src) const { return DstChannelV(channel_traits::min_value() + (src - channel_traits::min_value()) / channel_range() * channel_range()); } private: template static double channel_range() { return double(channel_traits::max_value()) - double(channel_traits::min_value()); } }; // When both the source and the destination are integral channels, perform a faster conversion template struct channel_converter_unsigned_impl : channel_converter_unsigned_integral < SrcChannelV, DstChannelV, mp11::mp_less < unsigned_integral_max_value, unsigned_integral_max_value >::value > {}; ////////////////////////////////////// //// channel_converter_unsigned_integral ////////////////////////////////////// template struct channel_converter_unsigned_integral : public channel_converter_unsigned_integral_impl::value % unsigned_integral_max_value::value) > {}; template struct channel_converter_unsigned_integral : public channel_converter_unsigned_integral_impl::value % unsigned_integral_max_value::value) > {}; ////////////////////////////////////// //// channel_converter_unsigned_integral_impl ////////////////////////////////////// // Both source and destination are unsigned integral channels, // the src max value is less than the dst max value, // and the dst max value is divisible by the src max value template struct channel_converter_unsigned_integral_impl { DstChannelV operator()(SrcChannelV src) const { using integer_t = typename unsigned_integral_max_value::value_type; static const integer_t mul = unsigned_integral_max_value::value / unsigned_integral_max_value::value; return DstChannelV(src * mul); } }; // Both source and destination are unsigned integral channels, // the dst max value is less than (or equal to) the src max value, // and the src max value is divisible by the dst max value template struct channel_converter_unsigned_integral_impl { DstChannelV operator()(SrcChannelV src) const { using integer_t = typename unsigned_integral_max_value::value_type; static const integer_t div = unsigned_integral_max_value::value / unsigned_integral_max_value::value; static const integer_t div2 = div/2; return DstChannelV((src + div2) / div); } }; // Prevent overflow for the largest integral type template struct channel_converter_unsigned_integral_impl { DstChannelV operator()(uintmax_t src) const { static const uintmax_t div = unsigned_integral_max_value::value / unsigned_integral_max_value::value; static const uintmax_t div2 = div/2; if (src > unsigned_integral_max_value::value - div2) return unsigned_integral_max_value::value; return DstChannelV((src + div2) / div); } }; // Both source and destination are unsigned integral channels, // and the dst max value is not divisible by the src max value // See if you can represent the expression (src * dst_max) / src_max in integral form template struct channel_converter_unsigned_integral_impl : channel_converter_unsigned_integral_nondivisible < SrcChannelV, DstChannelV, SrcLessThanDst, mp11::mp_less < unsigned_integral_num_bits, mp11::mp_plus < unsigned_integral_num_bits, unsigned_integral_num_bits > >::value > {}; // Both source and destination are unsigned integral channels, // the src max value is less than the dst max value, // and the dst max value is not divisible by the src max value // The expression (src * dst_max) / src_max fits in an integer template struct channel_converter_unsigned_integral_nondivisible { DstChannelV operator()(SrcChannelV src) const { using dest_t = typename base_channel_type::type; return DstChannelV( static_cast(src * unsigned_integral_max_value::value) / unsigned_integral_max_value::value); } }; // Both source and destination are unsigned integral channels, // the src max value is less than the dst max value, // and the dst max value is not divisible by the src max value // The expression (src * dst_max) / src_max cannot fit in an integer (overflows). Use a double template struct channel_converter_unsigned_integral_nondivisible { DstChannelV operator()(SrcChannelV src) const { static const double mul = unsigned_integral_max_value::value / double(unsigned_integral_max_value::value); return DstChannelV(src * mul); } }; // Both source and destination are unsigned integral channels, // the dst max value is less than (or equal to) the src max value, // and the src max value is not divisible by the dst max value template struct channel_converter_unsigned_integral_nondivisible { DstChannelV operator()(SrcChannelV src) const { using src_integer_t = typename detail::unsigned_integral_max_value::value_type; using dst_integer_t = typename detail::unsigned_integral_max_value::value_type; static const double div = unsigned_integral_max_value::value / static_cast< double >( unsigned_integral_max_value::value ); static const src_integer_t div2 = static_cast< src_integer_t >( div / 2.0 ); return DstChannelV( static_cast< dst_integer_t >(( static_cast< double >( src + div2 ) / div ))); } }; } // namespace detail ///////////////////////////////////////////////////// /// float32_t conversion ///////////////////////////////////////////////////// template struct channel_converter_unsigned { using argument_type = float32_t; using result_type = DstChannelV; DstChannelV operator()(float32_t x) const { using dst_integer_t = typename detail::unsigned_integral_max_value::value_type; return DstChannelV( static_cast< dst_integer_t >(x*channel_traits::max_value()+0.5f )); } }; template struct channel_converter_unsigned { using argument_type = float32_t; using result_type = SrcChannelV; float32_t operator()(SrcChannelV x) const { return float32_t(x/float(channel_traits::max_value())); } }; template <> struct channel_converter_unsigned { using argument_type = float32_t; using result_type = float32_t; float32_t operator()(float32_t x) const { return x; } }; /// \brief 32 bit <-> float channel conversion template <> struct channel_converter_unsigned { using argument_type = uint32_t; using result_type = float32_t; float32_t operator()(uint32_t x) const { // unfortunately without an explicit check it is possible to get a round-off error. We must ensure that max_value of uint32_t matches max_value of float32_t if (x>=channel_traits::max_value()) return channel_traits::max_value(); return float(x) / float(channel_traits::max_value()); } }; /// \brief 32 bit <-> float channel conversion template <> struct channel_converter_unsigned { using argument_type = float32_t; using result_type = uint32_t; uint32_t operator()(float32_t x) const { // unfortunately without an explicit check it is possible to get a round-off error. We must ensure that max_value of uint32_t matches max_value of float32_t if (x>=channel_traits::max_value()) return channel_traits::max_value(); auto const max_value = channel_traits::max_value(); auto const result = x * static_cast(max_value) + 0.5f; return static_cast(result); } }; /// @} namespace detail { // Converting from signed to unsigned integral channel. // It is both a unary function, and a metafunction (thus requires the 'type' nested alias, which equals result_type) template // Model ChannelValueConcept struct channel_convert_to_unsigned : public detail::identity { using type = ChannelValue; }; template <> struct channel_convert_to_unsigned { using argument_type = int8_t; using result_type = uint8_t; using type = uint8_t; type operator()(int8_t val) const { return static_cast(static_cast(val) + 128u); } }; template <> struct channel_convert_to_unsigned { using argument_type = int16_t; using result_type = uint16_t; using type = uint16_t; type operator()(int16_t val) const { return static_cast(static_cast(val) + 32768u); } }; template <> struct channel_convert_to_unsigned { using argument_type = int32_t; using result_type = uint32_t; using type = uint32_t; type operator()(int32_t val) const { return static_cast(val)+(1u<<31); } }; // Converting from unsigned to signed integral channel // It is both a unary function, and a metafunction (thus requires the 'type' nested alias, which equals result_type) template // Model ChannelValueConcept struct channel_convert_from_unsigned : public detail::identity { using type = ChannelValue; }; template <> struct channel_convert_from_unsigned { using argument_type = uint8_t; using result_type = int8_t; using type = int8_t; type operator()(uint8_t val) const { return static_cast(static_cast(val) - 128); } }; template <> struct channel_convert_from_unsigned { using argument_type = uint16_t; using result_type = int16_t; using type = int16_t; type operator()(uint16_t val) const { return static_cast(static_cast(val) - 32768); } }; template <> struct channel_convert_from_unsigned { using argument_type = uint32_t; using result_type = int32_t; using type = int32_t; type operator()(uint32_t val) const { return static_cast(val - (1u<<31)); } }; } // namespace detail /// \ingroup ChannelConvertAlgorithm /// \brief A unary function object converting between channel types template // Model ChannelValueConcept struct channel_converter { using argument_type = SrcChannelV; using result_type = DstChannelV; DstChannelV operator()(const SrcChannelV& src) const { using to_unsigned = detail::channel_convert_to_unsigned; using from_unsigned = detail::channel_convert_from_unsigned; using converter_unsigned = channel_converter_unsigned; return from_unsigned()(converter_unsigned()(to_unsigned()(src))); } }; /// \ingroup ChannelConvertAlgorithm /// \brief Converting from one channel type to another. template // Model ChannelConcept (could be channel references) inline typename channel_traits::value_type channel_convert(const SrcChannel& src) { return channel_converter::value_type, typename channel_traits::value_type>()(src); } /// \ingroup ChannelConvertAlgorithm /// \brief Same as channel_converter, except it takes the destination channel by reference, which allows /// us to move the templates from the class level to the method level. This is important when invoking it /// on heterogeneous pixels. struct default_channel_converter { template void operator()(const Ch1& src, Ch2& dst) const { dst=channel_convert(src); } }; namespace detail { // fast integer division by 255 inline uint32_t div255(uint32_t in) { uint32_t tmp=in+128; return (tmp + (tmp>>8))>>8; } // fast integer divison by 32768 inline uint32_t div32768(uint32_t in) { return (in+16384)>>15; } } /// \defgroup ChannelMultiplyAlgorithm channel_multiply /// \ingroup ChannelAlgorithm /// \brief Multiplying unsigned channel values of the same type. Performs scaled multiplication result = a * b / max_value /// /// Example: /// \code /// uint8_t x=128; /// uint8_t y=128; /// uint8_t mul = channel_multiply(x,y); /// assert(mul == 64); // 64 = 128 * 128 / 255 /// \endcode /// @{ /// \brief This is the default implementation. Performance specializatons are provided template struct channel_multiplier_unsigned { using first_argument_type = ChannelValue; using second_argument_type = ChannelValue; using result_type = ChannelValue; ChannelValue operator()(ChannelValue a, ChannelValue b) const { return ChannelValue(static_cast::type>(a / double(channel_traits::max_value()) * b)); } }; /// \brief Specialization of channel_multiply for 8-bit unsigned channels template<> struct channel_multiplier_unsigned { using first_argument_type = uint8_t; using second_argument_type = uint8_t; using result_type = uint8_t; uint8_t operator()(uint8_t a, uint8_t b) const { return uint8_t(detail::div255(uint32_t(a) * uint32_t(b))); } }; /// \brief Specialization of channel_multiply for 16-bit unsigned channels template<> struct channel_multiplier_unsigned { using first_argument_type = uint16_t; using second_argument_type = uint16_t; using result_type = uint16_t; uint16_t operator()(uint16_t a, uint16_t b) const { return uint16_t((uint32_t(a) * uint32_t(b))/65535); } }; /// \brief Specialization of channel_multiply for float 0..1 channels template<> struct channel_multiplier_unsigned { using first_argument_type = float32_t; using second_argument_type = float32_t; using result_type = float32_t; float32_t operator()(float32_t a, float32_t b) const { return a*b; } }; /// \brief A function object to multiply two channels. result = a * b / max_value template struct channel_multiplier { using first_argument_type = ChannelValue; using second_argument_type = ChannelValue; using result_type = ChannelValue; ChannelValue operator()(ChannelValue a, ChannelValue b) const { using to_unsigned = detail::channel_convert_to_unsigned; using from_unsigned = detail::channel_convert_from_unsigned; using multiplier_unsigned = channel_multiplier_unsigned; return from_unsigned()(multiplier_unsigned()(to_unsigned()(a), to_unsigned()(b))); } }; /// \brief A function multiplying two channels. result = a * b / max_value template // Models ChannelConcept (could be a channel reference) inline typename channel_traits::value_type channel_multiply(Channel a, Channel b) { return channel_multiplier::value_type>()(a,b); } /// @} /// \defgroup ChannelInvertAlgorithm channel_invert /// \ingroup ChannelAlgorithm /// \brief Returns the inverse of a channel. result = max_value - x + min_value /// /// Example: /// \code /// // uint8_t == uint8_t == unsigned char /// uint8_t x=255; /// uint8_t inv = channel_invert(x); /// assert(inv == 0); /// \endcode /// \brief Default implementation. Provide overloads for performance /// \ingroup ChannelInvertAlgorithm channel_invert template // Models ChannelConcept (could be a channel reference) inline typename channel_traits::value_type channel_invert(Channel x) { using base_t = typename base_channel_type::type; using promoted_t = typename promote_integral::type; promoted_t const promoted_x = x; promoted_t const promoted_max = channel_traits::max_value(); promoted_t const promoted_min = channel_traits::min_value(); promoted_t const promoted_inverted_x = promoted_max - promoted_x + promoted_min; auto const inverted_x = static_cast(promoted_inverted_x); return inverted_x; } } } // namespace boost::gil #endif