1 /*------------------------------------------------------------------------
2 * Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net>
3 *
4 * This file is part of the ZBar Bar Code Reader.
5 *
6 * The ZBar Bar Code Reader is free software; you can redistribute it
7 * and/or modify it under the terms of the GNU Lesser Public License as
8 * published by the Free Software Foundation; either version 2.1 of
9 * the License, or (at your option) any later version.
10 *
11 * The ZBar Bar Code Reader is distributed in the hope that it will be
12 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser Public License
17 * along with the ZBar Bar Code Reader; if not, write to the Free
18 * Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301 USA
20 *
21 * http://sourceforge.net/projects/zbar
22 *------------------------------------------------------------------------*/
23
24 #include <config.h>
25 #ifdef HAVE_UNISTD_H
26 # include <unistd.h>
27 #endif
28 #ifdef HAVE_INTTYPES_H
29 # include <inttypes.h>
30 #endif
31 #ifdef HAVE_DBUS
32 #include <dbus/dbus.h>
33 #endif
34
35 #include <stdlib.h> /* malloc, free */
36 #include <string.h> /* memcmp, memset, memcpy */
37 #include <assert.h>
38
39 #include <zbar.h>
40 #include "error.h"
41 #include "image.h"
42 #include "timer.h"
43 #if ENABLE_QRCODE == 1
44 # include "qrcode.h"
45 #endif
46 #if ENABLE_SQCODE == 1
47 # include "sqcode.h"
48 #endif
49 #include "img_scanner.h"
50 #include "svg.h"
51
52 #if 1
53 # define ASSERT_POS \
54 assert(p == data + x + y * (intptr_t)w)
55 #else
56 # define ASSERT_POS
57 #endif
58
59 /* FIXME cache setting configurability */
60
61 /* time interval for which two images are considered "nearby"
62 */
63 #define CACHE_PROXIMITY 1000 /* ms */
64
65 /* time that a result must *not* be detected before
66 * it will be reported again
67 */
68 #define CACHE_HYSTERESIS 2000 /* ms */
69
70 /* time after which cache entries are invalidated
71 */
72 #define CACHE_TIMEOUT (CACHE_HYSTERESIS * 2) /* ms */
73
74 #define NUM_SCN_CFGS (ZBAR_CFG_Y_DENSITY - ZBAR_CFG_X_DENSITY + 1)
75
76 #define CFG(iscn, cfg) ((iscn)->configs[(cfg) - ZBAR_CFG_X_DENSITY])
77 #define TEST_CFG(iscn, cfg) (((iscn)->config >> ((cfg) - ZBAR_CFG_POSITION)) & 1)
78
79 #ifndef NO_STATS
80 # define STAT(x) iscn->stat_##x++
81 #else
82 # define STAT(...)
83 # define dump_stats(...)
84 #endif
85
86 #define RECYCLE_BUCKETS 5
87
88 typedef struct recycle_bucket_s {
89 int nsyms;
90 zbar_symbol_t *head;
91 } recycle_bucket_t;
92
93 /* image scanner state */
94 struct zbar_image_scanner_s {
95 zbar_scanner_t *scn; /* associated linear intensity scanner */
96 zbar_decoder_t *dcode; /* associated symbol decoder */
97 #if ENABLE_QRCODE == 1
98 qr_reader *qr; /* QR Code 2D reader */
99 #endif
100 #if ENABLE_SQCODE == 1
101 sq_reader *sq; /* SQ Code 2D reader */
102 #endif
103
104 const void *userdata; /* application data */
105 /* user result callback */
106 zbar_image_data_handler_t *handler;
107
108 unsigned long time; /* scan start time */
109 zbar_image_t *img; /* currently scanning image *root* */
110 int dx, dy, du, umin, v; /* current scan direction */
111 zbar_symbol_set_t *syms; /* previous decode results */
112 /* recycled symbols in 4^n size buckets */
113 recycle_bucket_t recycle[RECYCLE_BUCKETS];
114
115 int enable_cache; /* current result cache state */
116 zbar_symbol_t *cache; /* inter-image result cache entries */
117
118 /* configuration settings */
119 unsigned config; /* config flags */
120 unsigned ean_config;
121 int configs[NUM_SCN_CFGS]; /* int valued configurations */
122 int sym_configs[1][NUM_SYMS]; /* per-symbology configurations */
123
124 #ifndef NO_STATS
125 int stat_syms_new;
126 int stat_iscn_syms_inuse, stat_iscn_syms_recycle;
127 int stat_img_syms_inuse, stat_img_syms_recycle;
128 int stat_sym_new;
129 int stat_sym_recycle[RECYCLE_BUCKETS];
130 #endif
131
132 #ifdef HAVE_DBUS
133 int is_dbus_enabled; /* dbus enabled flag */
134 #endif
135 };
136
_zbar_image_scanner_recycle_syms(zbar_image_scanner_t * iscn,zbar_symbol_t * sym)137 void _zbar_image_scanner_recycle_syms (zbar_image_scanner_t *iscn,
138 zbar_symbol_t *sym)
139 {
140 zbar_symbol_t *next = NULL;
141 for(; sym; sym = next) {
142 next = sym->next;
143 if(sym->refcnt && _zbar_refcnt(&sym->refcnt, -1)) {
144 /* unlink referenced symbol */
145 /* FIXME handle outstanding component refs (currently unsupported)
146 */
147 assert(sym->data_alloc);
148 sym->next = NULL;
149 }
150 else {
151 int i;
152 recycle_bucket_t *bucket;
153 /* recycle unreferenced symbol */
154 if(!sym->data_alloc) {
155 sym->data = NULL;
156 sym->datalen = 0;
157 }
158 if(sym->syms) {
159 if(_zbar_refcnt(&sym->syms->refcnt, -1))
160 assert(0);
161 _zbar_image_scanner_recycle_syms(iscn, sym->syms->head);
162 sym->syms->head = NULL;
163 _zbar_symbol_set_free(sym->syms);
164 sym->syms = NULL;
165 }
166 for(i = 0; i < RECYCLE_BUCKETS; i++)
167 if(sym->data_alloc < 1 << (i * 2))
168 break;
169 if(i == RECYCLE_BUCKETS) {
170 assert(sym->data);
171 free(sym->data);
172 sym->data = NULL;
173 sym->data_alloc = 0;
174 i = 0;
175 }
176 bucket = &iscn->recycle[i];
177 /* FIXME cap bucket fill */
178 bucket->nsyms++;
179 sym->next = bucket->head;
180 bucket->head = sym;
181 }
182 }
183 }
184
recycle_syms(zbar_image_scanner_t * iscn,zbar_symbol_set_t * syms)185 static inline int recycle_syms (zbar_image_scanner_t *iscn,
186 zbar_symbol_set_t *syms)
187 {
188 if(_zbar_refcnt(&syms->refcnt, -1))
189 return(1);
190
191 _zbar_image_scanner_recycle_syms(iscn, syms->head);
192 syms->head = syms->tail = NULL;
193 syms->nsyms = 0;
194 return(0);
195 }
196
zbar_image_scanner_recycle_image(zbar_image_scanner_t * iscn,zbar_image_t * img)197 inline void zbar_image_scanner_recycle_image (zbar_image_scanner_t *iscn,
198 zbar_image_t *img)
199 {
200 zbar_symbol_set_t *syms = iscn->syms;
201 if(syms && syms->refcnt) {
202 if(recycle_syms(iscn, syms)) {
203 STAT(iscn_syms_inuse);
204 iscn->syms = NULL;
205 }
206 else
207 STAT(iscn_syms_recycle);
208 }
209
210 syms = img->syms;
211 img->syms = NULL;
212 if(syms && recycle_syms(iscn, syms))
213 STAT(img_syms_inuse);
214 else if(syms) {
215 STAT(img_syms_recycle);
216
217 /* select one set to resurrect, destroy the other */
218 if(iscn->syms)
219 _zbar_symbol_set_free(syms);
220 else
221 iscn->syms = syms;
222 }
223 }
224
225 inline zbar_symbol_t*
_zbar_image_scanner_alloc_sym(zbar_image_scanner_t * iscn,zbar_symbol_type_t type,int datalen)226 _zbar_image_scanner_alloc_sym (zbar_image_scanner_t *iscn,
227 zbar_symbol_type_t type,
228 int datalen)
229 {
230 /* recycle old or alloc new symbol */
231 zbar_symbol_t *sym = NULL;
232 int i;
233 for(i = 0; i < RECYCLE_BUCKETS - 1; i++)
234 if(datalen <= 1 << (i * 2))
235 break;
236
237 for(; i > 0; i--)
238 if((sym = iscn->recycle[i].head)) {
239 STAT(sym_recycle[i]);
240 break;
241 }
242
243 if(sym) {
244 iscn->recycle[i].head = sym->next;
245 sym->next = NULL;
246 assert(iscn->recycle[i].nsyms);
247 iscn->recycle[i].nsyms--;
248 }
249 else {
250 sym = calloc(1, sizeof(zbar_symbol_t));
251 STAT(sym_new);
252 }
253
254 /* init new symbol */
255 sym->type = type;
256 sym->quality = 1;
257 sym->npts = 0;
258 sym->orient = ZBAR_ORIENT_UNKNOWN;
259 sym->cache_count = 0;
260 sym->time = iscn->time;
261 assert(!sym->syms);
262
263 if(datalen > 0) {
264 sym->datalen = datalen - 1;
265 if(sym->data_alloc < datalen) {
266 if(sym->data)
267 free(sym->data);
268 sym->data_alloc = datalen;
269 sym->data = malloc(datalen);
270 }
271 }
272 else {
273 if(sym->data)
274 free(sym->data);
275 sym->data = NULL;
276 sym->datalen = sym->data_alloc = 0;
277 }
278 return(sym);
279 }
280
cache_lookup(zbar_image_scanner_t * iscn,zbar_symbol_t * sym)281 static inline zbar_symbol_t *cache_lookup (zbar_image_scanner_t *iscn,
282 zbar_symbol_t *sym)
283 {
284 /* search for matching entry in cache */
285 zbar_symbol_t **entry = &iscn->cache;
286 while(*entry) {
287 if((*entry)->type == sym->type &&
288 (*entry)->datalen == sym->datalen &&
289 !memcmp((*entry)->data, sym->data, sym->datalen))
290 break;
291 if((sym->time - (*entry)->time) > CACHE_TIMEOUT) {
292 /* recycle stale cache entry */
293 zbar_symbol_t *next = (*entry)->next;
294 (*entry)->next = NULL;
295 _zbar_image_scanner_recycle_syms(iscn, *entry);
296 *entry = next;
297 }
298 else
299 entry = &(*entry)->next;
300 }
301 return(*entry);
302 }
303
cache_sym(zbar_image_scanner_t * iscn,zbar_symbol_t * sym)304 static inline void cache_sym (zbar_image_scanner_t *iscn,
305 zbar_symbol_t *sym)
306 {
307 if(iscn->enable_cache) {
308 uint32_t age, near_thresh, far_thresh, dup;
309 zbar_symbol_t *entry = cache_lookup(iscn, sym);
310 if(!entry) {
311 /* FIXME reuse sym */
312 entry = _zbar_image_scanner_alloc_sym(iscn, sym->type,
313 sym->datalen + 1);
314 entry->configs = sym->configs;
315 entry->modifiers = sym->modifiers;
316 memcpy(entry->data, sym->data, sym->datalen);
317 entry->time = sym->time - CACHE_HYSTERESIS;
318 entry->cache_count = 0;
319 /* add to cache */
320 entry->next = iscn->cache;
321 iscn->cache = entry;
322 }
323
324 /* consistency check and hysteresis */
325 age = sym->time - entry->time;
326 entry->time = sym->time;
327 near_thresh = (age < CACHE_PROXIMITY);
328 far_thresh = (age >= CACHE_HYSTERESIS);
329 dup = (entry->cache_count >= 0);
330 if((!dup && !near_thresh) || far_thresh) {
331 int type = sym->type;
332 int h = _zbar_get_symbol_hash(type);
333 entry->cache_count = -iscn->sym_configs[0][h];
334 }
335 else if(dup || near_thresh)
336 entry->cache_count++;
337
338 sym->cache_count = entry->cache_count;
339 }
340 else
341 sym->cache_count = 0;
342 }
343
_zbar_image_scanner_add_sym(zbar_image_scanner_t * iscn,zbar_symbol_t * sym)344 void _zbar_image_scanner_add_sym(zbar_image_scanner_t *iscn,
345 zbar_symbol_t *sym)
346 {
347 zbar_symbol_set_t *syms;
348 cache_sym(iscn, sym);
349
350 syms = iscn->syms;
351 if(sym->cache_count || !syms->tail) {
352 sym->next = syms->head;
353 syms->head = sym;
354 }
355 else {
356 sym->next = syms->tail->next;
357 syms->tail->next = sym;
358 }
359
360 if(!sym->cache_count)
361 syms->nsyms++;
362 else if(!syms->tail)
363 syms->tail = sym;
364
365 _zbar_symbol_refcnt(sym, 1);
366 }
367
368 #if ENABLE_QRCODE == 1
369 extern qr_finder_line *_zbar_decoder_get_qr_finder_line(zbar_decoder_t*);
370
371 # define QR_FIXED(v, rnd) ((((v) << 1) + (rnd)) << (QR_FINDER_SUBPREC - 1))
372 # define PRINT_FIXED(val, prec) \
373 ((val) >> (prec)), \
374 (1000 * ((val) & ((1 << (prec)) - 1)) / (1 << (prec)))
375
qr_handler(zbar_image_scanner_t * iscn)376 static inline void qr_handler (zbar_image_scanner_t *iscn)
377 {
378 unsigned u;
379 int vert;
380 qr_finder_line *line = _zbar_decoder_get_qr_finder_line(iscn->dcode);
381 assert(line);
382 u = zbar_scanner_get_edge(iscn->scn, line->pos[0],
383 QR_FINDER_SUBPREC);
384 line->boffs = u - zbar_scanner_get_edge(iscn->scn, line->boffs,
385 QR_FINDER_SUBPREC);
386 line->len = zbar_scanner_get_edge(iscn->scn, line->len,
387 QR_FINDER_SUBPREC);
388 line->eoffs = zbar_scanner_get_edge(iscn->scn, line->eoffs,
389 QR_FINDER_SUBPREC) - line->len;
390 line->len -= u;
391
392 u = QR_FIXED(iscn->umin, 0) + iscn->du * u;
393 if(iscn->du < 0) {
394 int tmp = line->boffs;
395 line->boffs = line->eoffs;
396 line->eoffs = tmp;
397 u -= line->len;
398 }
399 vert = !iscn->dx;
400 line->pos[vert] = u;
401 line->pos[!vert] = QR_FIXED(iscn->v, 1);
402
403 _zbar_qr_found_line(iscn->qr, vert, line);
404 }
405 #endif
406
407 #if ENABLE_SQCODE == 1
408 extern unsigned _zbar_decoder_get_sq_finder_config(zbar_decoder_t*);
409
sq_handler(zbar_image_scanner_t * iscn)410 static void sq_handler (zbar_image_scanner_t *iscn)
411 {
412 unsigned config = _zbar_decoder_get_sq_finder_config(iscn->dcode);
413 _zbar_sq_new_config(iscn->sq, config);
414 }
415 #endif
416
symbol_handler(zbar_decoder_t * dcode)417 static void symbol_handler (zbar_decoder_t *dcode)
418 {
419 zbar_image_scanner_t *iscn = zbar_decoder_get_userdata(dcode);
420 zbar_symbol_type_t type = zbar_decoder_get_type(dcode);
421 int x = 0, y = 0, dir;
422 const char *data;
423 unsigned datalen;
424 zbar_symbol_t *sym;
425
426 #if ENABLE_QRCODE == 1
427 if(type == ZBAR_QRCODE) {
428 qr_handler(iscn);
429 return;
430 }
431 #else
432 assert(type != ZBAR_QRCODE);
433 #endif
434
435 if(TEST_CFG(iscn, ZBAR_CFG_POSITION)) {
436 /* tmp position fixup */
437 int w = zbar_scanner_get_width(iscn->scn);
438 int u = iscn->umin + iscn->du * zbar_scanner_get_edge(iscn->scn, w, 0);
439 if(iscn->dx) {
440 x = u;
441 y = iscn->v;
442 }
443 else {
444 x = iscn->v;
445 y = u;
446 }
447 }
448
449 /* FIXME debug flag to save/display all PARTIALs */
450 if(type <= ZBAR_PARTIAL) {
451 zprintf(256, "partial symbol @(%d,%d)\n", x, y);
452 return;
453 }
454
455 data = zbar_decoder_get_data(dcode);
456 datalen = zbar_decoder_get_data_length(dcode);
457
458 /* FIXME need better symbol matching */
459 for(sym = iscn->syms->head; sym; sym = sym->next)
460 if(sym->type == type &&
461 sym->datalen == datalen &&
462 !memcmp(sym->data, data, datalen)) {
463 sym->quality++;
464 zprintf(224, "dup symbol @(%d,%d): dup %s: %.20s\n",
465 x, y, zbar_get_symbol_name(type), data);
466 if(TEST_CFG(iscn, ZBAR_CFG_POSITION))
467 /* add new point to existing set */
468 /* FIXME should be polygon */
469 sym_add_point(sym, x, y);
470 return;
471 }
472
473 sym = _zbar_image_scanner_alloc_sym(iscn, type, datalen + 1);
474 sym->configs = zbar_decoder_get_configs(dcode, type);
475 sym->modifiers = zbar_decoder_get_modifiers(dcode);
476 /* FIXME grab decoder buffer */
477 memcpy(sym->data, data, datalen + 1);
478
479 /* initialize first point */
480 if(TEST_CFG(iscn, ZBAR_CFG_POSITION)) {
481 zprintf(192, "new symbol @(%d,%d): %s: %.20s\n",
482 x, y, zbar_get_symbol_name(type), data);
483 sym_add_point(sym, x, y);
484 }
485
486 dir = zbar_decoder_get_direction(dcode);
487 if(dir)
488 sym->orient = (iscn->dy != 0) + ((iscn->du ^ dir) & 2);
489
490 _zbar_image_scanner_add_sym(iscn, sym);
491 }
492
zbar_image_scanner_create()493 zbar_image_scanner_t *zbar_image_scanner_create ()
494 {
495 zbar_image_scanner_t *iscn = calloc(1, sizeof(zbar_image_scanner_t));
496 if(!iscn)
497 return(NULL);
498 iscn->dcode = zbar_decoder_create();
499 iscn->scn = zbar_scanner_create(iscn->dcode);
500 if(!iscn->dcode || !iscn->scn) {
501 zbar_image_scanner_destroy(iscn);
502 return(NULL);
503 }
504 zbar_decoder_set_userdata(iscn->dcode, iscn);
505 zbar_decoder_set_handler(iscn->dcode, symbol_handler);
506
507 #if ENABLE_QRCODE == 1
508 iscn->qr = _zbar_qr_create();
509 #endif
510
511 #if ENABLE_SQCODE == 1
512 iscn->sq = _zbar_sq_create();
513 #endif
514
515 /* apply default configuration */
516 CFG(iscn, ZBAR_CFG_X_DENSITY) = 1;
517 CFG(iscn, ZBAR_CFG_Y_DENSITY) = 1;
518 zbar_image_scanner_set_config(iscn, 0, ZBAR_CFG_POSITION, 1);
519 zbar_image_scanner_set_config(iscn, 0, ZBAR_CFG_UNCERTAINTY, 2);
520 zbar_image_scanner_set_config(iscn, 0, ZBAR_CFG_TEST_INVERTED, 0);
521 zbar_image_scanner_set_config(iscn, ZBAR_QRCODE, ZBAR_CFG_UNCERTAINTY, 0);
522 zbar_image_scanner_set_config(iscn, ZBAR_QRCODE, ZBAR_CFG_BINARY, 0);
523 zbar_image_scanner_set_config(iscn, ZBAR_CODE128, ZBAR_CFG_UNCERTAINTY, 0);
524 zbar_image_scanner_set_config(iscn, ZBAR_CODE93, ZBAR_CFG_UNCERTAINTY, 0);
525 zbar_image_scanner_set_config(iscn, ZBAR_CODE39, ZBAR_CFG_UNCERTAINTY, 0);
526 zbar_image_scanner_set_config(iscn, ZBAR_CODABAR, ZBAR_CFG_UNCERTAINTY, 1);
527 zbar_image_scanner_set_config(iscn, ZBAR_COMPOSITE, ZBAR_CFG_UNCERTAINTY, 0);
528 return(iscn);
529 }
530
531 #ifndef NO_STATS
dump_stats(const zbar_image_scanner_t * iscn)532 static inline void dump_stats (const zbar_image_scanner_t *iscn)
533 {
534 int i;
535 zprintf(1, "symbol sets allocated = %-4d\n", iscn->stat_syms_new);
536 zprintf(1, " scanner syms in use = %-4d\trecycled = %-4d\n",
537 iscn->stat_iscn_syms_inuse, iscn->stat_iscn_syms_recycle);
538 zprintf(1, " image syms in use = %-4d\trecycled = %-4d\n",
539 iscn->stat_img_syms_inuse, iscn->stat_img_syms_recycle);
540 zprintf(1, "symbols allocated = %-4d\n", iscn->stat_sym_new);
541 for(i = 0; i < RECYCLE_BUCKETS; i++)
542 zprintf(1, " recycled[%d] = %-4d\n",
543 i, iscn->stat_sym_recycle[i]);
544 }
545 #endif
546
zbar_image_scanner_destroy(zbar_image_scanner_t * iscn)547 void zbar_image_scanner_destroy (zbar_image_scanner_t *iscn)
548 {
549 int i;
550 dump_stats(iscn);
551 if(iscn->syms) {
552 if(iscn->syms->refcnt)
553 zbar_symbol_set_ref(iscn->syms, -1);
554 else
555 _zbar_symbol_set_free(iscn->syms);
556 iscn->syms = NULL;
557 }
558 if(iscn->scn)
559 zbar_scanner_destroy(iscn->scn);
560 iscn->scn = NULL;
561 if(iscn->dcode)
562 zbar_decoder_destroy(iscn->dcode);
563 iscn->dcode = NULL;
564 for(i = 0; i < RECYCLE_BUCKETS; i++) {
565 zbar_symbol_t *sym, *next;
566 for(sym = iscn->recycle[i].head; sym; sym = next) {
567 next = sym->next;
568 _zbar_symbol_free(sym);
569 }
570 }
571 #if ENABLE_QRCODE == 1
572 if(iscn->qr) {
573 _zbar_qr_destroy(iscn->qr);
574 iscn->qr = NULL;
575 }
576 #endif
577 #if ENABLE_SQCODE == 1
578 if(iscn->sq) {
579 _zbar_sq_destroy(iscn->sq);
580 iscn->sq = NULL;
581 }
582 #endif
583 free(iscn);
584 }
585
586 zbar_image_data_handler_t*
zbar_image_scanner_set_data_handler(zbar_image_scanner_t * iscn,zbar_image_data_handler_t * handler,const void * userdata)587 zbar_image_scanner_set_data_handler (zbar_image_scanner_t *iscn,
588 zbar_image_data_handler_t *handler,
589 const void *userdata)
590 {
591 zbar_image_data_handler_t *result = iscn->handler;
592 iscn->handler = handler;
593 iscn->userdata = userdata;
594 return(result);
595 }
596
zbar_image_scanner_set_config(zbar_image_scanner_t * iscn,zbar_symbol_type_t sym,zbar_config_t cfg,int val)597 int zbar_image_scanner_set_config (zbar_image_scanner_t *iscn,
598 zbar_symbol_type_t sym,
599 zbar_config_t cfg,
600 int val)
601 {
602 if((sym == 0 || sym == ZBAR_COMPOSITE) && cfg == ZBAR_CFG_ENABLE) {
603 iscn->ean_config = !!val;
604 if(sym)
605 return(0);
606 }
607
608 if(cfg < ZBAR_CFG_UNCERTAINTY)
609 return(zbar_decoder_set_config(iscn->dcode, sym, cfg, val));
610
611 if(cfg < ZBAR_CFG_POSITION) {
612 int c, i;
613 if(cfg > ZBAR_CFG_UNCERTAINTY)
614 return(1);
615 c = cfg - ZBAR_CFG_UNCERTAINTY;
616 if(sym > ZBAR_PARTIAL) {
617 i = _zbar_get_symbol_hash(sym);
618 iscn->sym_configs[c][i] = val;
619 }
620 else
621 for(i = 0; i < NUM_SYMS; i++)
622 iscn->sym_configs[c][i] = val;
623 return(0);
624 }
625
626 /* Image scanner parameters apply only to ZBAR_PARTIAL */
627 if(sym > ZBAR_PARTIAL)
628 return(1);
629
630 if(cfg >= ZBAR_CFG_X_DENSITY && cfg <= ZBAR_CFG_Y_DENSITY) {
631 CFG(iscn, cfg) = val;
632 return(0);
633 }
634
635 cfg -= ZBAR_CFG_POSITION;
636
637 if(!val)
638 iscn->config &= ~(1 << cfg);
639 else if(val == 1)
640 iscn->config |= (1 << cfg);
641 else
642 return(1);
643
644 return(0);
645 }
646
zbar_image_scanner_get_config(zbar_image_scanner_t * iscn,zbar_symbol_type_t sym,zbar_config_t cfg,int * val)647 int zbar_image_scanner_get_config(zbar_image_scanner_t *iscn,
648 zbar_symbol_type_t sym,
649 zbar_config_t cfg,
650 int *val)
651 {
652 /* Return error if symbol doesn't have config */
653 if(sym < ZBAR_PARTIAL || sym > ZBAR_CODE128 || sym == ZBAR_COMPOSITE)
654 return 1;
655
656 if (cfg < ZBAR_CFG_UNCERTAINTY)
657 return zbar_decoder_get_config(iscn->dcode, sym, cfg, val);
658
659 if(cfg < ZBAR_CFG_POSITION) {
660 if(sym == ZBAR_PARTIAL)
661 return(1);
662
663 int i = _zbar_get_symbol_hash(sym);
664
665 *val = iscn->sym_configs[cfg - ZBAR_CFG_UNCERTAINTY][i];
666 return 0;
667 }
668
669 /* Image scanner parameters apply only to ZBAR_PARTIAL */
670 if(sym > ZBAR_PARTIAL)
671 return(1);
672
673 if(cfg < ZBAR_CFG_X_DENSITY) {
674 *val = (iscn->config & (1 << (cfg - ZBAR_CFG_POSITION))) != 0;
675 return 0;
676 }
677
678 if(cfg <= ZBAR_CFG_Y_DENSITY) {
679 *val = CFG(iscn, cfg);
680 return 0;
681 }
682
683 return 1;
684 }
685
zbar_image_scanner_enable_cache(zbar_image_scanner_t * iscn,int enable)686 void zbar_image_scanner_enable_cache (zbar_image_scanner_t *iscn,
687 int enable)
688 {
689 if(iscn->cache) {
690 /* recycle all cached syms */
691 _zbar_image_scanner_recycle_syms(iscn, iscn->cache);
692 iscn->cache = NULL;
693 }
694 iscn->enable_cache = (enable) ? 1 : 0;
695 }
696
697 const zbar_symbol_set_t *
zbar_image_scanner_get_results(const zbar_image_scanner_t * iscn)698 zbar_image_scanner_get_results (const zbar_image_scanner_t *iscn)
699 {
700 return(iscn->syms);
701 }
702
quiet_border(zbar_image_scanner_t * iscn)703 static inline void quiet_border (zbar_image_scanner_t *iscn)
704 {
705 /* flush scanner pipeline */
706 zbar_scanner_t *scn = iscn->scn;
707 zbar_scanner_flush(scn);
708 zbar_scanner_flush(scn);
709 zbar_scanner_new_scan(scn);
710 }
711
712
713 #ifdef HAVE_DBUS
dict_add_property(DBusMessageIter * property,const char * key,const char * value,unsigned int value_length,int is_binary)714 static int dict_add_property (DBusMessageIter *property,
715 const char *key,
716 const char *value,
717 unsigned int value_length,
718 int is_binary)
719 {
720
721 DBusMessageIter dict_entry, dict_val, array_val;
722 DBusError err;
723 dbus_error_init(&err);
724 dbus_message_iter_open_container(property, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry);
725 if (!dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &key)){
726 fprintf(stderr, "Key Error\n");
727 dbus_message_iter_close_container(property, &dict_entry);
728 goto error;
729 }
730
731 if (is_binary) {
732 dbus_message_iter_open_container(&dict_entry, DBUS_TYPE_VARIANT, "ay", &dict_val);
733 dbus_message_iter_open_container(&dict_val, DBUS_TYPE_ARRAY, "y", &array_val);
734 if (!dbus_message_iter_append_fixed_array(&array_val, DBUS_TYPE_BYTE, &value, value_length)) {
735 fprintf(stderr, "Byte Array Value Error\n");
736 dbus_message_iter_close_container(&dict_val, &array_val);
737 dbus_message_iter_close_container(&dict_entry, &dict_val);
738 dbus_message_iter_close_container(property, &dict_entry);
739 goto error;
740 }
741 } else {
742 dbus_message_iter_open_container(&dict_entry, DBUS_TYPE_VARIANT, DBUS_TYPE_STRING_AS_STRING, &dict_val);
743 if (!dbus_message_iter_append_basic(&dict_val, DBUS_TYPE_STRING, &value)) {
744 fprintf(stderr, "String Value Error\n");
745 dbus_message_iter_close_container(&dict_entry, &dict_val);
746 dbus_message_iter_close_container(property, &dict_entry);
747 goto error;
748 }
749 }
750
751 if (is_binary)
752 dbus_message_iter_close_container(&dict_val, &array_val);
753 dbus_message_iter_close_container(&dict_entry, &dict_val);
754 dbus_message_iter_close_container(property, &dict_entry);
755 return(1);
756
757 error:
758 if (dbus_error_is_set(&err)) {
759 fprintf(stderr, "Name Error (%s)\n", err.message);
760 dbus_error_free(&err);
761 }
762 return(0);
763 }
764
zbar_send_dbus(int type,const char * sigvalue,unsigned int length,int is_binary)765 static void zbar_send_dbus(int type, const char* sigvalue,
766 unsigned int length, int is_binary)
767 {
768 DBusMessage* msg;
769 DBusMessageIter args, dict;
770 DBusConnection* conn;
771 const char *type_name;
772 const char *value_key = is_binary ? "BinaryData" : "Data";
773 DBusError err;
774 int ret;
775 dbus_uint32_t serial = 0;
776
777 // initialise the error value
778 dbus_error_init(&err);
779
780 // connect to the DBUS system bus, and check for errors
781 conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
782 if (dbus_error_is_set(&err)) {
783 fprintf(stderr, "Connection Error (%s)\n", err.message);
784 dbus_error_free(&err);
785 }
786 if (NULL == conn) {
787 fprintf(stderr, "Connection Null\n");
788 return;
789 }
790
791 // register our name on the bus, and check for errors
792 ret = dbus_bus_request_name(conn, "org.linuxtv.Zbar", DBUS_NAME_FLAG_REPLACE_EXISTING, &err);
793 if (dbus_error_is_set(&err)) {
794 fprintf(stderr, "Name Error (%s)\n", err.message);
795 dbus_error_free(&err);
796 }
797 if (DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret) {
798 return;
799 }
800
801 // create a signal & check for errors
802 msg = dbus_message_new_signal("/org/linuxtv/Zbar1/Code", // object name of the signal
803 "org.linuxtv.Zbar1.Code", // interface name of the signal
804 "Code"); // name of the signal
805 if (NULL == msg)
806 {
807 fprintf(stderr, "Message Null\n");
808 return;
809 }
810
811 // append arguments onto signal
812 dbus_message_iter_init_append(msg, &args);
813 if (!dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "{sv}", &dict)) {
814 fprintf(stderr, "Out Of Dict Container Memory!\n");
815 dbus_message_unref(msg);
816 return;
817 }
818
819 type_name = zbar_get_symbol_name(type);
820 if (!dict_add_property(&dict, "Type", type_name, 0, 0)) {
821 fprintf(stderr, "Out Of Property Memory!\n");
822 dbus_message_unref(msg);
823 return;
824 }
825
826 if (!dict_add_property(&dict, value_key, sigvalue, length, is_binary)) {
827 fprintf(stderr, "Out Of Property Memory!\n");
828 dbus_message_unref(msg);
829 return;
830 }
831
832 dbus_message_iter_close_container(&args, &dict);
833
834 // send the message and flush the connection
835 if (!dbus_connection_send(conn, msg, &serial)) {
836 fprintf(stderr, "Out Of Memory!\n");
837 dbus_message_unref(msg);
838 return;
839 }
840
841 dbus_connection_flush(conn);
842 dbus_bus_release_name(conn, "org.linuxtv.Zbar", &err);
843 if (dbus_error_is_set(&err)) {
844 fprintf(stderr, "Name Release Error (%s)\n", err.message);
845 dbus_error_free(&err);
846 }
847
848 // free the message
849 dbus_message_unref(msg);
850 }
851
zbar_send_code_via_dbus(zbar_image_scanner_t * iscn,zbar_image_t * img)852 static void zbar_send_code_via_dbus(zbar_image_scanner_t *iscn,
853 zbar_image_t *img)
854 {
855 const zbar_symbol_t *sym = zbar_image_first_symbol(img);
856
857 if (!sym)
858 return;
859 for(; sym; sym = zbar_symbol_next(sym)) {
860 if(zbar_symbol_get_count(sym))
861 continue;
862
863 zbar_symbol_type_t type = zbar_symbol_get_type(sym);
864 if(type == ZBAR_PARTIAL)
865 continue;
866
867 int is_binary = 0;
868 zbar_image_scanner_get_config(iscn, type, ZBAR_CFG_BINARY, &is_binary);
869
870 zbar_send_dbus(type,
871 zbar_symbol_get_data(sym),
872 zbar_symbol_get_data_length(sym),
873 is_binary);
874 }
875 }
876 #endif
877
878 #define movedelta(dx, dy) do { \
879 x += (dx); \
880 y += (dy); \
881 p += (dx) + ((uintptr_t)(dy) * w); \
882 } while(0);
883
_zbar_scan_image(zbar_image_scanner_t * iscn,zbar_image_t * img)884 static void *_zbar_scan_image(zbar_image_scanner_t *iscn,
885 zbar_image_t *img)
886 {
887 zbar_symbol_set_t *syms;
888 const uint8_t *data;
889 zbar_scanner_t *scn = iscn->scn;
890 unsigned w, h, cx1, cy1;
891 int density;
892
893 /* timestamp image
894 * FIXME prefer video timestamp
895 */
896 iscn->time = _zbar_timer_now();
897
898 #if ENABLE_QRCODE == 1
899 _zbar_qr_reset(iscn->qr);
900 #endif
901
902 #if ENABLE_SQCODE == 1
903 _zbar_sq_reset(iscn->sq);
904 #endif
905
906 /* image must be in grayscale format */
907 if(img->format != fourcc('Y','8','0','0') &&
908 img->format != fourcc('G','R','E','Y'))
909 return NULL;
910 iscn->img = img;
911
912 /* recycle previous scanner and image results */
913 zbar_image_scanner_recycle_image(iscn, img);
914 syms = iscn->syms;
915 if(!syms) {
916 syms = iscn->syms = _zbar_symbol_set_create();
917 STAT(syms_new);
918 zbar_symbol_set_ref(syms, 1);
919 }
920 else
921 zbar_symbol_set_ref(syms, 2);
922 img->syms = syms;
923
924 w = img->width;
925 h = img->height;
926 cx1 = img->crop_x + img->crop_w;
927 assert(cx1 <= w);
928 cy1 = img->crop_y + img->crop_h;
929 assert(cy1 <= h);
930 data = img->data;
931
932 zbar_image_write_png(img, "debug.png");
933 svg_open("debug.svg", 0, 0, w, h);
934 svg_image("debug.png", w, h);
935
936 zbar_scanner_new_scan(scn);
937
938 density = CFG(iscn, ZBAR_CFG_Y_DENSITY);
939 if(density > 0) {
940 const uint8_t *p = data;
941 int x = 0, y = 0;
942
943 int border = (((img->crop_h - 1) % density) + 1) / 2;
944 if(border > img->crop_h / 2)
945 border = img->crop_h / 2;
946 border += img->crop_y;
947 assert(border <= h);
948 svg_group_start("scanner", 0, 1, 1, 0, 0);
949 iscn->dy = 0;
950
951 movedelta(img->crop_x, border);
952 iscn->v = y;
953
954 while(y < cy1) {
955 int cx0 = img->crop_x;;
956 zprintf(128, "img_x+: %04d,%04d @%p\n", x, y, p);
957 svg_path_start("vedge", 1. / 32, 0, y + 0.5);
958 iscn->dx = iscn->du = 1;
959 iscn->umin = cx0;
960 while(x < cx1) {
961 uint8_t d = *p;
962 movedelta(1, 0);
963 zbar_scan_y(scn, d);
964 }
965 ASSERT_POS;
966 quiet_border(iscn);
967 svg_path_end();
968
969 movedelta(-1, density);
970 iscn->v = y;
971 if(y >= cy1)
972 break;
973
974 zprintf(128, "img_x-: %04d,%04d @%p\n", x, y, p);
975 svg_path_start("vedge", -1. / 32, w, y + 0.5);
976 iscn->dx = iscn->du = -1;
977 iscn->umin = cx1;
978 while(x >= cx0) {
979 uint8_t d = *p;
980 movedelta(-1, 0);
981 zbar_scan_y(scn, d);
982 }
983 ASSERT_POS;
984 quiet_border(iscn);
985 svg_path_end();
986
987 movedelta(1, density);
988 iscn->v = y;
989 }
990 svg_group_end();
991 }
992 iscn->dx = 0;
993
994 density = CFG(iscn, ZBAR_CFG_X_DENSITY);
995 if(density > 0) {
996 const uint8_t *p = data;
997 int x = 0, y = 0;
998
999 int border = (((img->crop_w - 1) % density) + 1) / 2;
1000 if(border > img->crop_w / 2)
1001 border = img->crop_w / 2;
1002 border += img->crop_x;
1003 assert(border <= w);
1004 svg_group_start("scanner", 90, 1, -1, 0, 0);
1005 movedelta(border, img->crop_y);
1006 iscn->v = x;
1007
1008 while(x < cx1) {
1009 int cy0 = img->crop_y;
1010 zprintf(128, "img_y+: %04d,%04d @%p\n", x, y, p);
1011 svg_path_start("vedge", 1. / 32, 0, x + 0.5);
1012 iscn->dy = iscn->du = 1;
1013 iscn->umin = cy0;
1014 while(y < cy1) {
1015 uint8_t d = *p;
1016 movedelta(0, 1);
1017 zbar_scan_y(scn, d);
1018 }
1019 ASSERT_POS;
1020 quiet_border(iscn);
1021 svg_path_end();
1022
1023 movedelta(density, -1);
1024 iscn->v = x;
1025 if(x >= cx1)
1026 break;
1027
1028 zprintf(128, "img_y-: %04d,%04d @%p\n", x, y, p);
1029 svg_path_start("vedge", -1. / 32, h, x + 0.5);
1030 iscn->dy = iscn->du = -1;
1031 iscn->umin = cy1;
1032 while(y >= cy0) {
1033 uint8_t d = *p;
1034 movedelta(0, -1);
1035 zbar_scan_y(scn, d);
1036 }
1037 ASSERT_POS;
1038 quiet_border(iscn);
1039 svg_path_end();
1040
1041 movedelta(density, 1);
1042 iscn->v = x;
1043 }
1044 svg_group_end();
1045 }
1046 iscn->dy = 0;
1047 iscn->img = NULL;
1048
1049 #if ENABLE_QRCODE == 1
1050 _zbar_qr_decode(iscn->qr, iscn, img);
1051 #endif
1052
1053 #if ENABLE_SQCODE == 1
1054 sq_handler(iscn);
1055 _zbar_sq_decode(iscn->sq, iscn, img);
1056 #endif
1057
1058 /* FIXME tmp hack to filter bad EAN results */
1059 /* FIXME tmp hack to merge simple case EAN add-ons */
1060 char filter = (!iscn->enable_cache &&
1061 (density == 1 || CFG(iscn, ZBAR_CFG_Y_DENSITY) == 1));
1062 int nean = 0, naddon = 0;
1063 if(syms->nsyms) {
1064 zbar_symbol_t **symp;
1065 for(symp = &syms->head; *symp; ) {
1066 zbar_symbol_t *sym = *symp;
1067 if(sym->cache_count <= 0 &&
1068 ((sym->type < ZBAR_COMPOSITE && sym->type > ZBAR_PARTIAL) ||
1069 sym->type == ZBAR_DATABAR ||
1070 sym->type == ZBAR_DATABAR_EXP ||
1071 sym->type == ZBAR_CODABAR))
1072 {
1073 if((sym->type == ZBAR_CODABAR || filter) && sym->quality < 4) {
1074 if(iscn->enable_cache) {
1075 /* revert cache update */
1076 zbar_symbol_t *entry = cache_lookup(iscn, sym);
1077 if(entry)
1078 entry->cache_count--;
1079 else
1080 assert(0);
1081 }
1082
1083 /* recycle */
1084 *symp = sym->next;
1085 syms->nsyms--;
1086 sym->next = NULL;
1087 _zbar_image_scanner_recycle_syms(iscn, sym);
1088 continue;
1089 }
1090 else if(sym->type < ZBAR_COMPOSITE &&
1091 sym->type != ZBAR_ISBN10)
1092 {
1093 if(sym->type > ZBAR_EAN5)
1094 nean++;
1095 else
1096 naddon++;
1097 }
1098 }
1099 symp = &sym->next;
1100 }
1101
1102 if(nean == 1 && naddon == 1 && iscn->ean_config) {
1103 /* create container symbol for composite result */
1104 zbar_symbol_t *ean = NULL, *addon = NULL;
1105 for(symp = &syms->head; *symp; ) {
1106 zbar_symbol_t *sym = *symp;
1107 if(sym->type < ZBAR_COMPOSITE && sym->type > ZBAR_PARTIAL) {
1108 /* move to composite */
1109 *symp = sym->next;
1110 syms->nsyms--;
1111 sym->next = NULL;
1112 if(sym->type <= ZBAR_EAN5)
1113 addon = sym;
1114 else
1115 ean = sym;
1116 }
1117 else
1118 symp = &sym->next;
1119 }
1120 assert(ean);
1121 assert(addon);
1122
1123 int datalen = ean->datalen + addon->datalen + 1;
1124 zbar_symbol_t *ean_sym =
1125 _zbar_image_scanner_alloc_sym(iscn, ZBAR_COMPOSITE, datalen);
1126 ean_sym->orient = ean->orient;
1127 ean_sym->syms = _zbar_symbol_set_create();
1128 memcpy(ean_sym->data, ean->data, ean->datalen);
1129 memcpy(ean_sym->data + ean->datalen,
1130 addon->data, addon->datalen + 1);
1131 ean_sym->syms->head = ean;
1132 ean->next = addon;
1133 ean_sym->syms->nsyms = 2;
1134 _zbar_image_scanner_add_sym(iscn, ean_sym);
1135 }
1136 }
1137 return syms;
1138 }
1139
zbar_scan_image(zbar_image_scanner_t * iscn,zbar_image_t * img)1140 int zbar_scan_image(zbar_image_scanner_t *iscn,
1141 zbar_image_t *img)
1142 {
1143 zbar_symbol_set_t *syms;
1144 zbar_image_t *inv = NULL;
1145
1146 syms = _zbar_scan_image(iscn, img);
1147 if (!syms)
1148 return -1;
1149
1150 if(!syms->nsyms && TEST_CFG(iscn, ZBAR_CFG_TEST_INVERTED)) {
1151 inv = _zbar_image_copy (img, 1);
1152 if (inv) {
1153 if(iscn->cache) {
1154 /* recycle all cached syms, if any */
1155 _zbar_image_scanner_recycle_syms(iscn, iscn->cache);
1156 iscn->cache = NULL;
1157 }
1158 syms = _zbar_scan_image(iscn, inv);
1159 _zbar_image_swap_symbols(img, inv);
1160 }
1161 }
1162
1163 if(syms->nsyms && iscn->handler)
1164 iscn->handler(img, iscn->userdata);
1165 #ifdef HAVE_DBUS
1166 if(iscn->is_dbus_enabled)
1167 zbar_send_code_via_dbus(iscn, img);
1168 #endif
1169
1170 svg_close();
1171
1172 if (inv)
1173 zbar_image_destroy(inv);
1174
1175 return(syms->nsyms);
1176 }
1177
zbar_image_scanner_request_dbus(zbar_image_scanner_t * scanner,int req_dbus_enabled)1178 int zbar_image_scanner_request_dbus(zbar_image_scanner_t *scanner,
1179 int req_dbus_enabled)
1180 {
1181 #ifdef HAVE_DBUS
1182 scanner->is_dbus_enabled = req_dbus_enabled;
1183 return 0;
1184 #else
1185 return 1;
1186 #endif
1187 }
1188
1189 #ifdef DEBUG_SVG
1190 /* FIXME lame...*/
1191 # include "svg.c"
1192 #endif
1193