Line data Source code
1 : #pragma once 2 : 3 : #include <AH/STL/type_traits> // enable_if 4 : #include <limits.h> // CHAR_BIT 5 : #include <stddef.h> // size_t 6 : 7 : BEGIN_AH_NAMESPACE 8 : 9 : namespace detail { 10 : 11 : template <size_t Bits_out, size_t Bits_in, class T_out, class T_in> 12 : std::enable_if_t<(Bits_out <= 2 * Bits_in) && (Bits_out > Bits_in), T_out> 13 101 : increaseBitDepthImpl(T_in in) { 14 101 : constexpr size_t leftShift = Bits_out - Bits_in; 15 101 : constexpr size_t rightShift = Bits_in - leftShift; 16 101 : return (static_cast<T_out>(in) << leftShift) | (in >> rightShift); 17 : } 18 : 19 : template <size_t Bits_out, size_t Bits_in, class T_out, class T_in> 20 2 : std::enable_if_t<(Bits_out <= Bits_in), T_out> increaseBitDepthImpl(T_in in) { 21 2 : constexpr size_t rightShift = Bits_in - Bits_out; 22 2 : return static_cast<T_out>(in >> rightShift); 23 : } 24 : 25 : template <size_t Bits_out, size_t Bits_in, class T_out, class T_in> 26 : std::enable_if_t<(Bits_out > 2 * Bits_in), T_out> 27 38 : increaseBitDepthImpl(T_in in) { 28 38 : constexpr size_t leftShift = Bits_out - Bits_in; 29 30 : return (static_cast<T_out>(in) << leftShift) | 30 38 : increaseBitDepthImpl<leftShift, Bits_in, T_out>(in); 31 : } 32 : 33 : } // namespace detail 34 : 35 : /// @addtogroup AH_Math 36 : /// @{ 37 : 38 : /** 39 : * @brief Increase the bit depth of the given value from `Bits_in` bits wide 40 : * to `Bits_out` bits wide, (approximately) evenly distributing the 41 : * error across the entire range, such that the error for each element 42 : * is between -1 and +1. 43 : * 44 : * @see @ref increaseBitDepthMiddle 45 : * 46 : * For example, converting 3-bit numbers to 7-bit numbers would result in the 47 : * following: 48 : * 49 : * | in (dec) | in (bin) | out (bin) | out (dec) | exact | error | 50 : * |:--------:|:--------:|:---------:|:---------:|:------:|:-----:| 51 : * | 0 | 000 | 000'0000 | 0 | 0.00 | +0.00 | 52 : * | 1 | 001 | 001'0010 | 18 | 18.14 | +0.14 | 53 : * | 2 | 010 | 010'0100 | 36 | 36.29 | +0.29 | 54 : * | 3 | 011 | 011'0110 | 54 | 54.43 | +0.43 | 55 : * | 4 | 100 | 100'1001 | 73 | 72.57 | -0.43 | 56 : * | 5 | 101 | 101'1011 | 91 | 90.71 | -0.29 | 57 : * | 6 | 110 | 110'1101 | 109 | 108.86 | -0.14 | 58 : * | 7 | 111 | 111'1111 | 127 | 127.00 | +0.00 | 59 : * 60 : * The following is a comparison to the @ref increaseBitDepthMiddle function. 61 : * 62 : * @image html increase-bit-depth.svg 63 : * 64 : * @tparam Bits_out 65 : * The number of bits of the output range. 66 : * @tparam Bits_in 67 : * The number of bits of the input range. 68 : * @tparam T_out 69 : * The type of the output (return type). 70 : * @tparam T_in 71 : * The type of the input. 72 : * @param in 73 : * The value to scale up. 74 : * @return The scaled up value. 75 : */ 76 : template <size_t Bits_out, size_t Bits_in, class T_out, class T_in> 77 103 : T_out increaseBitDepth(T_in in) { 78 : static_assert(Bits_in <= sizeof(T_in) * CHAR_BIT, 79 : "Error: Bits_in > bits(T_in)"); 80 : static_assert(Bits_out <= sizeof(T_out) * CHAR_BIT, 81 : "Error: Bits_out > bits(T_out)"); 82 103 : return detail::increaseBitDepthImpl<Bits_out, Bits_in, T_out>(in); 83 : } 84 : 85 : /** 86 : * @brief Increase the bit depth of the given value from `Bits_in` bits wide 87 : * to `Bits_out` bits wide, while ensuring that the middle of the input 88 : * range maps exactly to the middle of the output range, i.e. 89 : * @f$ 2^{\texttt{Bits\_in} - 1} @f$ maps to 90 : * @f$ 2^{\texttt{Bits\_out} - 1} @f$. 91 : * 92 : * @see @ref increaseBitDepth 93 : * 94 : * For example, converting 3-bit numbers to 7-bit numbers would result in the 95 : * following: 96 : * 97 : * | in (dec) | in (bin) | out (bin) | out (dec) | exact | error | 98 : * |:--------:|:--------:|:---------:|:---------:|:------:|:-----:| 99 : * | 0 | 000 | 000'0000 | 0 | 0.00 | +0.00 | 100 : * | 1 | 001 | 001'0000 | 16 | 18.14 | -2.14 | 101 : * | 2 | 010 | 010'0000 | 32 | 36.29 | -4.29 | 102 : * | 3 | 011 | 011'0000 | 48 | 54.43 | -6.43 | 103 : * | 4 | 100 | 100'0000 | 64 | 72.57 | -8.57 | 104 : * | 5 | 101 | 101'0101 | 85 | 90.71 | -5.71 | 105 : * | 6 | 110 | 110'1010 | 106 | 108.86 | -2.86 | 106 : * | 7 | 111 | 111'1111 | 127 | 127.00 | +0.00 | 107 : * 108 : * The following is a comparison to the @ref increaseBitDepth function. 109 : * 110 : * @image html increase-bit-depth.svg 111 : * 112 : * @tparam Bits_out 113 : * The number of bits of the output range. 114 : * @tparam Bits_in 115 : * The number of bits of the input range. 116 : * @tparam T_out 117 : * The type of the output (return type). 118 : * @tparam T_in 119 : * The type of the input. 120 : * @param in 121 : * The value to scale up. 122 : * @return The scaled up value. 123 : */ 124 : template <size_t Bits_out, size_t Bits_in, class T_out, class T_in> 125 20 : T_out increaseBitDepthMiddle(T_in in) { 126 : static_assert(Bits_in <= sizeof(T_in) * CHAR_BIT, 127 : "Error: Bits_in > bits(T_in)"); 128 : static_assert(Bits_out <= sizeof(T_out) * CHAR_BIT, 129 : "Error: Bits_out > bits(T_out)"); 130 20 : constexpr size_t leftShift = Bits_out - Bits_in; 131 20 : T_in half = T_in {1} << (Bits_in - 1); 132 20 : T_out out = static_cast<T_out>(in) << leftShift; 133 20 : if (in > half) { 134 9 : T_in repeat = in & (half - 1); 135 9 : out |= increaseBitDepth<leftShift, Bits_in - 1, T_out>(repeat); 136 : } 137 20 : return out; 138 : } 139 : 140 : /// @} 141 : 142 : END_AH_NAMESPACE