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