1 /* AirScan (a.k.a. eSCL) backend for SANE
2 *
3 * Copyright (C) 2019 and up by Alexander Pevzner (pzz@apevzner.com)
4 * See LICENSE for license terms and conditions
5 *
6 * Device capabilities
7 */
8
9 #include "airscan.h"
10
11 #include <string.h>
12
13 /* Allocate devcaps_source
14 */
15 devcaps_source*
devcaps_source_new(void)16 devcaps_source_new (void)
17 {
18 devcaps_source *src = mem_new(devcaps_source, 1);
19 src->resolutions = sane_word_array_new();
20 return src;
21 }
22
23 /* Free devcaps_source
24 */
25 void
devcaps_source_free(devcaps_source * src)26 devcaps_source_free (devcaps_source *src)
27 {
28 if (src != NULL) {
29 sane_word_array_free(src->resolutions);
30 mem_free(src);
31 }
32 }
33
34 /* Clone a source
35 */
36 devcaps_source*
devcaps_source_clone(const devcaps_source * src)37 devcaps_source_clone (const devcaps_source *src)
38 {
39 devcaps_source *src2 = mem_new(devcaps_source, 1);
40 unsigned int i, len;
41
42 *src2 = *src;
43
44 src2->resolutions = sane_word_array_new();
45
46 len = sane_word_array_len(src->resolutions);
47 for (i = 1; i <= len; i ++) {
48 SANE_Word res = src->resolutions[i];
49 src2->resolutions = sane_word_array_append(src2->resolutions, res);
50 }
51
52 return src2;
53 }
54
55 /* Merge two sources, resulting the source that contains
56 * only capabilities, supported by two input sources
57 *
58 * Returns NULL, if sources cannot be merged
59 */
60 devcaps_source*
devcaps_source_merge(const devcaps_source * s1,const devcaps_source * s2)61 devcaps_source_merge (const devcaps_source *s1, const devcaps_source *s2)
62 {
63 devcaps_source *src = devcaps_source_new();
64
65 /* Merge flags */
66 src->flags = s1->flags & s2->flags;
67
68 /* Merge formats */
69 src->formats = s1->formats & s2->formats;
70 if ((src->formats & DEVCAPS_FORMATS_SUPPORTED) == 0) {
71 goto FAIL;
72 }
73
74 /* Merge colormodes */
75 src->colormodes = s1->colormodes & s2->colormodes;
76 if ((src->colormodes & DEVCAPS_COLORMODES_SUPPORTED) == 0) {
77 goto FAIL;
78 }
79
80 /* Merge dimensions */
81 src->min_wid_px = math_max(s1->min_wid_px, s2->min_wid_px);
82 src->max_wid_px = math_min(s1->max_wid_px, s2->max_wid_px);
83 src->min_hei_px = math_max(s1->min_hei_px, s2->min_hei_px);
84 src->max_hei_px = math_min(s1->max_hei_px, s2->max_hei_px);
85
86 if ((src->min_wid_px > src->max_wid_px) ||
87 (src->min_hei_px > src->max_hei_px)) {
88 goto FAIL;
89 }
90
91 if (!math_range_merge(&src->win_x_range_mm,
92 &s1->win_x_range_mm, &s2->win_x_range_mm)) {
93 goto FAIL;
94 }
95
96 if (!math_range_merge(&src->win_y_range_mm,
97 &s1->win_y_range_mm, &s2->win_y_range_mm)) {
98 goto FAIL;
99 }
100
101 /* Merge resolutions */
102 if ((src->flags & DEVCAPS_SOURCE_RES_DISCRETE) != 0) {
103 sane_word_array_free(src->resolutions);
104 src->resolutions = sane_word_array_intersect_sorted(
105 s1->resolutions, s2->resolutions);
106 if (sane_word_array_len(src->resolutions) == 0) {
107 src->flags &= ~DEVCAPS_SOURCE_RES_DISCRETE;
108 }
109 }
110
111 if ((src->flags & DEVCAPS_SOURCE_RES_RANGE) != 0) {
112 if (!math_range_merge(&src->res_range,
113 &s1->res_range, &s2->res_range)) {
114 src->flags &= ~DEVCAPS_SOURCE_RES_RANGE;
115 }
116 }
117
118 if ((src->flags & DEVCAPS_SOURCE_RES_ALL) == 0) {
119 goto FAIL;
120 }
121
122 return src;
123
124 FAIL:
125 devcaps_source_free(src);
126 return NULL;
127 }
128
129 /* Initialize Device Capabilities
130 */
131 void
devcaps_init(devcaps * caps)132 devcaps_init (devcaps *caps)
133 {
134 (void) caps;
135 }
136
137 /* Cleanup Device Capabilities
138 */
139 void
devcaps_cleanup(devcaps * caps)140 devcaps_cleanup (devcaps *caps)
141 {
142 unsigned int i;
143 for (i = 0; i < NUM_ID_SOURCE; i ++) {
144 devcaps_source_free(caps->src[i]);
145 }
146 }
147
148 /* Reset Device Capabilities into initial state
149 */
150 void
devcaps_reset(devcaps * caps)151 devcaps_reset (devcaps *caps)
152 {
153 devcaps_cleanup(caps);
154 memset(caps, 0, sizeof(*caps));
155 devcaps_init(caps);
156 }
157
158 /* Dump device capabilities, for debugging
159 */
160 void
devcaps_dump(log_ctx * log,devcaps * caps)161 devcaps_dump (log_ctx *log, devcaps *caps)
162 {
163 int i;
164 char *buf = str_new();
165
166 log_trace(log, "===== device capabilities =====");
167 log_trace(log, " Size units: %d DPI", caps->units);
168 log_trace(log, " Protocol: %s", caps->protocol);
169
170 if (caps->compression_ok) {
171 log_trace(log, " Compression min: %d", caps->compression_range.min);
172 log_trace(log, " Compression max: %d", caps->compression_range.max);
173 log_trace(log, " Compression step: %d", caps->compression_range.quant);
174 log_trace(log, " Compression norm: %d", caps->compression_norm);
175 }
176
177 str_trunc(buf);
178 for (i = 0; i < NUM_ID_SOURCE; i ++) {
179 if (caps->src[i] != NULL) {
180 if (buf[0] != '\0') {
181 buf = str_append(buf, ", ");
182 }
183 buf = str_append(buf, id_source_sane_name(i));
184 }
185 }
186
187 log_trace(log, " Sources: %s", buf);
188
189 ID_SOURCE id_src;
190 for (id_src = (ID_SOURCE) 0; id_src < NUM_ID_SOURCE; id_src ++) {
191 devcaps_source *src = caps->src[id_src];
192 char xbuf[64], ybuf[64];
193
194 if (src == NULL) {
195 continue;
196 }
197
198 log_trace(log, "");
199 log_trace(log, " %s:", id_source_sane_name(id_src));
200
201 math_fmt_mm(math_px2mm_res(src->min_wid_px, caps->units), xbuf);
202 math_fmt_mm(math_px2mm_res(src->min_hei_px, caps->units), ybuf);
203
204 log_trace(log, " Min window: %dx%d px, %sx%s mm",
205 src->min_wid_px, src->min_hei_px, xbuf, ybuf);
206
207 math_fmt_mm(math_px2mm_res(src->max_wid_px, caps->units), xbuf);
208 math_fmt_mm(math_px2mm_res(src->max_hei_px, caps->units), ybuf);
209
210 log_trace(log, " Max window: %dx%d px, %sx%s mm",
211 src->max_wid_px, src->max_hei_px, xbuf, ybuf);
212
213 if (src->flags & DEVCAPS_SOURCE_RES_DISCRETE) {
214 str_trunc(buf);
215 for (i = 0; i < (int) sane_word_array_len(src->resolutions); i ++) {
216 if (i != 0) {
217 buf = str_append_c(buf, ' ');
218 }
219 buf = str_append_printf(buf, "%d", src->resolutions[i+1]);
220 }
221
222 log_trace(log, " Resolutions: %s", buf);
223 }
224
225 str_trunc(buf);
226
227 for (i = 0; i < NUM_ID_COLORMODE; i ++) {
228 if ((src->colormodes & (1 << i)) != 0) {
229 if (buf[0] != '\0') {
230 buf = str_append(buf, ", ");
231 }
232 buf = str_append(buf, id_colormode_sane_name(i));
233 }
234 }
235
236 log_trace(log, " Color modes: %s", buf);
237
238 str_trunc(buf);
239
240 for (i = 0; i < NUM_ID_FORMAT; i ++) {
241 if ((src->formats & (1 << i)) != 0) {
242 if (buf[0] != '\0') {
243 buf = str_append(buf, ", ");
244 }
245 buf = str_append(buf, id_format_short_name(i));
246 }
247 }
248
249 log_trace(log, " Formats: %s", buf);
250 }
251
252 mem_free(buf);
253 log_trace(log, "");
254 }
255
256 /* vim:ts=8:sw=4:et
257 */
258