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