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
|