1 /* sane - Scanner Access Now Easy.
2
3 Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
4
5 This file is part of the SANE package.
6
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the
10 License, or (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21 #ifndef BACKEND_GENESYS_UTILITIES_H
22 #define BACKEND_GENESYS_UTILITIES_H
23
24 #include "error.h"
25 #include <algorithm>
26 #include <cstdint>
27 #include <iostream>
28 #include <sstream>
29 #include <vector>
30
31
32 namespace genesys {
33
34 // just like SANE_FIX and SANE_UNFIX except that the conversion is done by a function and argument
35 // precision is handled correctly
double_to_fixed(double v)36 inline SANE_Word double_to_fixed(double v)
37 {
38 return static_cast<SANE_Word>(v * (1 << SANE_FIXED_SCALE_SHIFT));
39 }
40
float_to_fixed(float v)41 inline SANE_Word float_to_fixed(float v)
42 {
43 return static_cast<SANE_Word>(v * (1 << SANE_FIXED_SCALE_SHIFT));
44 }
45
fixed_to_float(SANE_Word v)46 inline float fixed_to_float(SANE_Word v)
47 {
48 return static_cast<float>(v) / (1 << SANE_FIXED_SCALE_SHIFT);
49 }
50
fixed_to_double(SANE_Word v)51 inline double fixed_to_double(SANE_Word v)
52 {
53 return static_cast<double>(v) / (1 << SANE_FIXED_SCALE_SHIFT);
54 }
55
56 template<class T>
abs_diff(T a, T b)57 inline T abs_diff(T a, T b)
58 {
59 if (a < b) {
60 return b - a;
61 } else {
62 return a - b;
63 }
64 }
65
align_multiple_floor(std::uint64_t x, std::uint64_t multiple)66 inline std::uint64_t align_multiple_floor(std::uint64_t x, std::uint64_t multiple)
67 {
68 if (multiple == 0) {
69 return x;
70 }
71 return (x / multiple) * multiple;
72 }
73
align_multiple_ceil(std::uint64_t x, std::uint64_t multiple)74 inline std::uint64_t align_multiple_ceil(std::uint64_t x, std::uint64_t multiple)
75 {
76 if (multiple == 0) {
77 return x;
78 }
79 return ((x + multiple - 1) / multiple) * multiple;
80 }
81
multiply_by_depth_ceil(std::uint64_t pixels, std::uint64_t depth)82 inline std::uint64_t multiply_by_depth_ceil(std::uint64_t pixels, std::uint64_t depth)
83 {
84 if (depth == 1) {
85 return (pixels / 8) + ((pixels % 8) ? 1 : 0);
86 } else {
87 return pixels * (depth / 8);
88 }
89 }
90
91 template<class T>
clamp(const T& value, const T& lo, const T& hi)92 inline T clamp(const T& value, const T& lo, const T& hi)
93 {
94 if (value < lo)
95 return lo;
96 if (value > hi)
97 return hi;
98 return value;
99 }
100
101 template<class T>
compute_array_percentile_approx(T* result, const T* data, std::size_t line_count, std::size_t elements_per_line, float percentile)102 void compute_array_percentile_approx(T* result, const T* data,
103 std::size_t line_count, std::size_t elements_per_line,
104 float percentile)
105 {
106 if (line_count == 0) {
107 throw SaneException("invalid line count");
108 }
109
110 if (line_count == 1) {
111 std::copy(data, data + elements_per_line, result);
112 return;
113 }
114
115 std::vector<T> column_elems;
116 column_elems.resize(line_count, 0);
117
118 std::size_t select_elem = std::min(static_cast<std::size_t>(line_count * percentile),
119 line_count - 1);
120
121 auto select_it = column_elems.begin() + select_elem;
122
123 for (std::size_t ix = 0; ix < elements_per_line; ++ix) {
124 for (std::size_t iy = 0; iy < line_count; ++iy) {
125 column_elems[iy] = data[iy * elements_per_line + ix];
126 }
127
128 std::nth_element(column_elems.begin(), select_it, column_elems.end());
129
130 *result++ = *select_it;
131 }
132 }
133
134 class Ratio
135 {
136 public:
Ratio()137 Ratio() : multiplier_{1}, divisor_{1}
138 {
139 }
140
Ratio(unsigned multiplier, unsigned divisor)141 Ratio(unsigned multiplier, unsigned divisor) : multiplier_{multiplier}, divisor_{divisor}
142 {
143 }
144
multiplier() const145 unsigned multiplier() const { return multiplier_; }
divisor() const146 unsigned divisor() const { return divisor_; }
147
apply(unsigned arg) const148 unsigned apply(unsigned arg) const
149 {
150 return static_cast<std::uint64_t>(arg) * multiplier_ / divisor_;
151 }
152
apply(int arg) const153 int apply(int arg) const
154 {
155 return static_cast<std::int64_t>(arg) * multiplier_ / divisor_;
156 }
157
apply(float arg) const158 float apply(float arg) const
159 {
160 return arg * multiplier_ / divisor_;
161 }
162
apply_inverse(unsigned arg) const163 unsigned apply_inverse(unsigned arg) const
164 {
165 return static_cast<std::uint64_t>(arg) * divisor_ / multiplier_;
166 }
167
apply_inverse(int arg) const168 int apply_inverse(int arg) const
169 {
170 return static_cast<std::int64_t>(arg) * divisor_ / multiplier_;
171 }
172
apply_inverse(float arg) const173 float apply_inverse(float arg) const
174 {
175 return arg * divisor_ / multiplier_;
176 }
177
operator ==(const Ratio& other) const178 bool operator==(const Ratio& other) const
179 {
180 return multiplier_ == other.multiplier_ && divisor_ == other.divisor_;
181 }
182 private:
183 unsigned multiplier_;
184 unsigned divisor_;
185
186 template<class Stream>
187 friend void serialize(Stream& str, Ratio& x);
188 };
189
190 template<class Stream>
serialize(Stream& str, Ratio& x)191 void serialize(Stream& str, Ratio& x)
192 {
193 serialize(str, x.multiplier_);
194 serialize(str, x.divisor_);
195 }
196
operator <<(std::ostream& out, const Ratio& ratio)197 inline std::ostream& operator<<(std::ostream& out, const Ratio& ratio)
198 {
199 out << ratio.multiplier() << "/" << ratio.divisor();
200 return out;
201 }
202
203 template<class Char, class Traits>
204 class BasicStreamStateSaver
205 {
206 public:
BasicStreamStateSaver(std::basic_ios<Char, Traits>& stream)207 explicit BasicStreamStateSaver(std::basic_ios<Char, Traits>& stream) :
208 stream_(stream)
209 {
210 flags_ = stream_.flags();
211 width_ = stream_.width();
212 precision_ = stream_.precision();
213 fill_ = stream_.fill();
214 }
215
~BasicStreamStateSaver()216 ~BasicStreamStateSaver()
217 {
218 stream_.flags(flags_);
219 stream_.width(width_);
220 stream_.precision(precision_);
221 stream_.fill(fill_);
222 }
223
224 BasicStreamStateSaver(const BasicStreamStateSaver&) = delete;
225 BasicStreamStateSaver& operator=(const BasicStreamStateSaver&) = delete;
226
227 private:
228 std::basic_ios<Char, Traits>& stream_;
229 std::ios_base::fmtflags flags_;
230 std::streamsize width_ = 0;
231 std::streamsize precision_ = 0;
232 Char fill_ = ' ';
233 };
234
235 using StreamStateSaver = BasicStreamStateSaver<char, std::char_traits<char>>;
236
237 template<class T>
format_indent_braced_list(unsigned indent, const T& x)238 std::string format_indent_braced_list(unsigned indent, const T& x)
239 {
240 std::string indent_str(indent, ' ');
241 std::ostringstream out;
242 out << x;
243 auto formatted_str = out.str();
244 if (formatted_str.empty()) {
245 return formatted_str;
246 }
247
248 std::string out_str;
249 for (std::size_t i = 0; i < formatted_str.size(); ++i) {
250 out_str += formatted_str[i];
251
252 if (formatted_str[i] == '\n' &&
253 i < formatted_str.size() - 1 &&
254 formatted_str[i + 1] != '\n')
255 {
256 out_str += indent_str;
257 }
258 }
259 return out_str;
260 }
261
262 template<class T>
format_vector_unsigned(unsigned indent, const std::vector<T>& arg)263 std::string format_vector_unsigned(unsigned indent, const std::vector<T>& arg)
264 {
265 std::ostringstream out;
266 std::string indent_str(indent, ' ');
267
268 out << "std::vector<T>{ ";
269 for (const auto& el : arg) {
270 out << indent_str << static_cast<unsigned>(el) << "\n";
271 }
272 out << "}";
273 return out.str();
274 }
275
276 template<class T>
format_vector_indent_braced(unsigned indent, const char* type, const std::vector<T>& arg)277 std::string format_vector_indent_braced(unsigned indent, const char* type,
278 const std::vector<T>& arg)
279 {
280 if (arg.empty()) {
281 return "{}";
282 }
283 std::string indent_str(indent, ' ');
284 std::stringstream out;
285 out << "std::vector<" << type << ">{\n";
286 for (const auto& item : arg) {
287 out << indent_str << format_indent_braced_list(indent, item) << '\n';
288 }
289 out << "}";
290 return out.str();
291 }
292
293 } // namespace genesys
294
295 #endif // BACKEND_GENESYS_UTILITIES_H
296