1 /*------------------------------------------------------------------------
2 * Copyright 2007-2009 (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 <unistd.h>
25 #ifdef HAVE_INTTYPES_H
26 # include <inttypes.h>
27 #endif
28 #include <stdlib.h> /* malloc, free */
29 #include <time.h> /* clock_gettime */
30 #include <sys/time.h> /* gettimeofday */
31 #include <string.h> /* memcmp, memset, memcpy */
32 #include <assert.h>
33
34 #include <zbar.h>
35 #include "error.h"
36 #include "symbol.h"
37
38 #ifdef ENABLE_QRCODE
39 # include "qrcode.h"
40 #endif
41 #include "img_scanner.h"
42 #include "svg.h"
43
44 #if 1
45 # define ASSERT_POS \
46 assert(p == data + x + y * (intptr_t)w)
47 #else
48 # define ASSERT_POS
49 #endif
50
51 #define NUM_SCN_CFGS (ZBAR_CFG_Y_DENSITY - ZBAR_CFG_X_DENSITY + 1)
52
53 #define CFG(iscn, cfg) ((iscn)->configs[(cfg) - ZBAR_CFG_X_DENSITY])
54 #define TEST_CFG(iscn, cfg) (((iscn)->config >> ((cfg) - ZBAR_CFG_POSITION)) & 1)
55
56 #ifndef NO_STATS
57 # define STAT(x) iscn->stat_##x++
58 #else
59 # define STAT(...)
60 # define dump_stats(...)
61 #endif
62
63 #define RECYCLE_BUCKETS 5
64
65 typedef struct recycle_bucket_s {
66 int nsyms;
67 zbar_symbol_t *head;
68 } recycle_bucket_t;
69
70 /* image scanner state */
71 struct zbar_image_scanner_s {
72 zbar_scanner_t *scn; /* associated linear intensity scanner */
73 zbar_decoder_t *dcode; /* associated symbol decoder */
74 #ifdef ENABLE_QRCODE
75 qr_reader *qr; /* QR Code 2D reader */
76 #endif
77
78 unsigned long time; /* scan start time */
79 int dx, dy, du, umin, v; /* current scan direction */
80 zbar_symbol_set_t *syms; /* previous decode results */
81 /* recycled symbols in 4^n size buckets */
82 recycle_bucket_t recycle[RECYCLE_BUCKETS];
83
84 /* configuration settings */
85 unsigned config; /* config flags */
86 int configs[NUM_SCN_CFGS]; /* int valued configurations */
87
88 #ifndef NO_STATS
89 int stat_syms_new;
90 int stat_iscn_syms_inuse, stat_iscn_syms_recycle;
91 int stat_img_syms_inuse, stat_img_syms_recycle;
92 int stat_sym_new;
93 int stat_sym_recycle[RECYCLE_BUCKETS];
94 #endif
95 };
96
_zbar_image_scanner_recycle_syms(zbar_image_scanner_t * iscn,zbar_symbol_t * sym)97 void _zbar_image_scanner_recycle_syms (zbar_image_scanner_t *iscn,
98 zbar_symbol_t *sym)
99 {
100 zbar_symbol_t *next = NULL;
101 for(; sym; sym = next) {
102 next = sym->next;
103 if(sym->refcnt && _zbar_refcnt(&sym->refcnt, -1)) {
104 /* unlink referenced symbol */
105 /* FIXME handle outstanding component refs (currently unsupported)
106 */
107 assert(sym->data_alloc);
108 sym->next = NULL;
109 }
110 else {
111 /* recycle unreferenced symbol */
112 if(!sym->data_alloc) {
113 sym->data = NULL;
114 sym->datalen = 0;
115 }
116 if(sym->syms) {
117 if(_zbar_refcnt(&sym->syms->refcnt, -1))
118 assert(0);
119 _zbar_image_scanner_recycle_syms(iscn, sym->syms->head);
120 sym->syms->head = NULL;
121 _zbar_symbol_set_free(sym->syms);
122 sym->syms = NULL;
123 }
124 int i;
125 for(i = 0; i < RECYCLE_BUCKETS; i++)
126 if(sym->data_alloc < 1 << (i * 2))
127 break;
128 if(i == RECYCLE_BUCKETS) {
129 assert(sym->data);
130 free(sym->data);
131 sym->data = NULL;
132 sym->data_alloc = 0;
133 i = 0;
134 }
135 recycle_bucket_t *bucket = &iscn->recycle[i];
136 /* FIXME cap bucket fill */
137 bucket->nsyms++;
138 sym->next = bucket->head;
139 bucket->head = sym;
140 }
141 }
142 }
143
recycle_syms(zbar_image_scanner_t * iscn,zbar_symbol_set_t * syms)144 static inline int recycle_syms (zbar_image_scanner_t *iscn,
145 zbar_symbol_set_t *syms)
146 {
147 if(_zbar_refcnt(&syms->refcnt, -1))
148 return(1);
149
150 _zbar_image_scanner_recycle_syms(iscn, syms->head);
151 syms->head = syms->tail = NULL;
152 syms->nsyms = 0;
153 return(0);
154 }
155
zbar_image_scanner_recycle(zbar_image_scanner_t * iscn)156 inline void zbar_image_scanner_recycle(zbar_image_scanner_t *iscn)
157 {
158 zbar_symbol_set_t *syms = iscn->syms;
159 if(syms && syms->refcnt) {
160 if(recycle_syms(iscn, syms)) {
161 STAT(iscn_syms_inuse);
162 iscn->syms = NULL;
163 }
164 else
165 STAT(iscn_syms_recycle);
166 }
167 }
168
169 inline zbar_symbol_t*
_zbar_image_scanner_alloc_sym(zbar_image_scanner_t * iscn,zbar_symbol_type_t type,int datalen)170 _zbar_image_scanner_alloc_sym (zbar_image_scanner_t *iscn,
171 zbar_symbol_type_t type,
172 int datalen)
173 {
174 /* recycle old or alloc new symbol */
175 int i;
176 for(i = 0; i < RECYCLE_BUCKETS - 1; i++)
177 if(datalen <= 1 << (i * 2))
178 break;
179
180 zbar_symbol_t *sym = NULL;
181 for(; i > 0; i--)
182 if((sym = iscn->recycle[i].head)) {
183 STAT(sym_recycle[i]);
184 break;
185 }
186
187 if(sym) {
188 iscn->recycle[i].head = sym->next;
189 sym->next = NULL;
190 assert(iscn->recycle[i].nsyms);
191 iscn->recycle[i].nsyms--;
192 }
193 else {
194 sym = calloc(1, sizeof(zbar_symbol_t));
195 STAT(sym_new);
196 }
197
198 /* init new symbol */
199 sym->type = type;
200 sym->quality = 1;
201 sym->npts = 0;
202 sym->cache_count = 0;
203 sym->time = iscn->time;
204 assert(!sym->syms);
205
206 if(datalen > 0) {
207 sym->datalen = datalen - 1;
208 if(sym->data_alloc < datalen) {
209 if(sym->data)
210 free(sym->data);
211 sym->data_alloc = datalen;
212 sym->data = malloc(datalen);
213 }
214 }
215 else {
216 if(sym->data)
217 free(sym->data);
218 sym->data = NULL;
219 sym->datalen = sym->data_alloc = 0;
220 }
221 return(sym);
222 }
223
_zbar_image_scanner_add_sym(zbar_image_scanner_t * iscn,zbar_symbol_t * sym)224 void _zbar_image_scanner_add_sym(zbar_image_scanner_t *iscn,
225 zbar_symbol_t *sym)
226 {
227 zbar_symbol_set_t *syms = iscn->syms;
228 if(sym->cache_count || !syms->tail) {
229 sym->next = syms->head;
230 syms->head = sym;
231 }
232 else {
233 sym->next = syms->tail->next;
234 syms->tail->next = sym;
235 }
236
237 if(!sym->cache_count)
238 syms->nsyms++;
239 else if(!syms->tail)
240 syms->tail = sym;
241
242 _zbar_symbol_refcnt(sym, 1);
243 }
244
245 #ifdef ENABLE_QRCODE
246 extern qr_finder_line *_zbar_decoder_get_qr_finder_line(zbar_decoder_t*);
247
248 # define QR_FIXED(v, rnd) ((((v) << 1) + (rnd)) << (QR_FINDER_SUBPREC - 1))
249 # define PRINT_FIXED(val, prec) \
250 ((val) >> (prec)), \
251 (1000 * ((val) & ((1 << (prec)) - 1)) / (1 << (prec)))
252
qr_handler(zbar_image_scanner_t * iscn)253 static inline void qr_handler (zbar_image_scanner_t *iscn)
254 {
255 qr_finder_line *line = _zbar_decoder_get_qr_finder_line(iscn->dcode);
256 assert(line);
257 unsigned u = zbar_scanner_get_edge(iscn->scn, line->pos[0],
258 QR_FINDER_SUBPREC);
259 line->boffs = u - zbar_scanner_get_edge(iscn->scn, line->boffs,
260 QR_FINDER_SUBPREC);
261 line->len = zbar_scanner_get_edge(iscn->scn, line->len,
262 QR_FINDER_SUBPREC);
263 line->eoffs = zbar_scanner_get_edge(iscn->scn, line->eoffs,
264 QR_FINDER_SUBPREC) - line->len;
265 line->len -= u;
266
267 u = QR_FIXED(iscn->umin, 0) + iscn->du * u;
268 if(iscn->du < 0) {
269 u -= line->len;
270 int tmp = line->boffs;
271 line->boffs = line->eoffs;
272 line->eoffs = tmp;
273 }
274 int vert = !iscn->dx;
275 line->pos[vert] = u;
276 line->pos[!vert] = QR_FIXED(iscn->v, 1);
277
278 _zbar_qr_found_line(iscn->qr, vert, line);
279 }
280 #endif
281
symbol_handler(zbar_decoder_t * dcode)282 static void symbol_handler (zbar_decoder_t *dcode)
283 {
284 zbar_image_scanner_t *iscn = zbar_decoder_get_userdata(dcode);
285 zbar_symbol_type_t type = zbar_decoder_get_type(dcode);
286 /* FIXME assert(type == ZBAR_PARTIAL) */
287 /* FIXME debug flag to save/display all PARTIALs */
288 if(type <= ZBAR_PARTIAL)
289 return;
290
291 #ifdef ENABLE_QRCODE
292 if(type == ZBAR_QRCODE) {
293 qr_handler(iscn);
294 return;
295 }
296 #else
297 assert(type != ZBAR_QRCODE);
298 #endif
299
300 const char *data = zbar_decoder_get_data(dcode);
301 unsigned datalen = zbar_decoder_get_data_length(dcode);
302
303 int x = 0, y = 0;
304 if(TEST_CFG(iscn, ZBAR_CFG_POSITION)) {
305 /* tmp position fixup */
306 int w = zbar_scanner_get_width(iscn->scn);
307 int u = iscn->umin + iscn->du * zbar_scanner_get_edge(iscn->scn, w, 0);
308 if(iscn->dx) {
309 x = u;
310 y = iscn->v;
311 }
312 else {
313 x = iscn->v;
314 y = u;
315 }
316 }
317
318 /* FIXME need better symbol matching */
319 zbar_symbol_t *sym;
320 for(sym = iscn->syms->head; sym; sym = sym->next)
321 if(sym->type == type &&
322 sym->datalen == datalen &&
323 !memcmp(sym->data, data, datalen)) {
324 sym->quality++;
325 if(TEST_CFG(iscn, ZBAR_CFG_POSITION))
326 /* add new point to existing set */
327 /* FIXME should be polygon */
328 sym_add_point(sym, x, y);
329 return;
330 }
331
332 sym = _zbar_image_scanner_alloc_sym(iscn, type, datalen + 1);
333 /* FIXME grab decoder buffer */
334 memcpy(sym->data, data, datalen + 1);
335
336 /* initialize first point */
337 if(TEST_CFG(iscn, ZBAR_CFG_POSITION))
338 sym_add_point(sym, x, y);
339
340 _zbar_image_scanner_add_sym(iscn, sym);
341 }
342
zbar_image_scanner_create()343 zbar_image_scanner_t *zbar_image_scanner_create ()
344 {
345 zbar_image_scanner_t *iscn = calloc(1, sizeof(zbar_image_scanner_t));
346 if(!iscn)
347 return(NULL);
348 iscn->dcode = zbar_decoder_create();
349 iscn->scn = zbar_scanner_create(iscn->dcode);
350 if(!iscn->dcode || !iscn->scn) {
351 zbar_image_scanner_destroy(iscn);
352 return(NULL);
353 }
354 zbar_decoder_set_userdata(iscn->dcode, iscn);
355 zbar_decoder_set_handler(iscn->dcode, symbol_handler);
356
357 #ifdef ENABLE_QRCODE
358 iscn->qr = _zbar_qr_create();
359 #endif
360
361 /* apply default configuration */
362 CFG(iscn, ZBAR_CFG_X_DENSITY) = 1;
363 CFG(iscn, ZBAR_CFG_Y_DENSITY) = 1;
364 zbar_image_scanner_set_config(iscn, 0, ZBAR_CFG_POSITION, 1);
365 return(iscn);
366 }
367
zbar_image_scanner_first_symbol(const zbar_image_scanner_t * iscn)368 const zbar_symbol_t *zbar_image_scanner_first_symbol (const zbar_image_scanner_t *iscn)
369 {
370 return((iscn->syms) ? iscn->syms->head : NULL);
371 }
372
373
374 #ifndef NO_STATS
dump_stats(const zbar_image_scanner_t * iscn)375 static inline void dump_stats (const zbar_image_scanner_t *iscn)
376 {
377 zprintf(1, "symbol sets allocated = %-4d\n", iscn->stat_syms_new);
378 zprintf(1, " scanner syms in use = %-4d\trecycled = %-4d\n",
379 iscn->stat_iscn_syms_inuse, iscn->stat_iscn_syms_recycle);
380 zprintf(1, " image syms in use = %-4d\trecycled = %-4d\n",
381 iscn->stat_img_syms_inuse, iscn->stat_img_syms_recycle);
382 zprintf(1, "symbols allocated = %-4d\n", iscn->stat_sym_new);
383 int i;
384 for(i = 0; i < RECYCLE_BUCKETS; i++)
385 zprintf(1, " recycled[%d] = %-4d\n",
386 i, iscn->stat_sym_recycle[i]);
387 }
388 #endif
389
zbar_image_scanner_destroy(zbar_image_scanner_t * iscn)390 void zbar_image_scanner_destroy (zbar_image_scanner_t *iscn)
391 {
392 dump_stats(iscn);
393 if(iscn->syms) {
394 if(iscn->syms->refcnt)
395 zbar_symbol_set_ref(iscn->syms, -1);
396 else
397 _zbar_symbol_set_free(iscn->syms);
398 iscn->syms = NULL;
399 }
400 if(iscn->scn)
401 zbar_scanner_destroy(iscn->scn);
402 iscn->scn = NULL;
403 if(iscn->dcode)
404 zbar_decoder_destroy(iscn->dcode);
405 iscn->dcode = NULL;
406 int i;
407 for(i = 0; i < RECYCLE_BUCKETS; i++) {
408 zbar_symbol_t *sym, *next;
409 for(sym = iscn->recycle[i].head; sym; sym = next) {
410 next = sym->next;
411 _zbar_symbol_free(sym);
412 }
413 }
414 #ifdef ENABLE_QRCODE
415 if(iscn->qr) {
416 _zbar_qr_destroy(iscn->qr);
417 iscn->qr = NULL;
418 }
419 #endif
420 free(iscn);
421 }
422
423
zbar_image_scanner_set_config(zbar_image_scanner_t * iscn,zbar_symbol_type_t sym,zbar_config_t cfg,int val)424 int zbar_image_scanner_set_config (zbar_image_scanner_t *iscn,
425 zbar_symbol_type_t sym,
426 zbar_config_t cfg,
427 int val)
428 {
429 if(cfg < ZBAR_CFG_POSITION)
430 return(zbar_decoder_set_config(iscn->dcode, sym, cfg, val));
431
432 if(sym > ZBAR_PARTIAL)
433 return(1);
434
435 if(cfg >= ZBAR_CFG_X_DENSITY && cfg <= ZBAR_CFG_Y_DENSITY) {
436
437 CFG(iscn, cfg) = val;
438 return(0);
439 }
440
441 if(cfg > ZBAR_CFG_POSITION)
442 return(1);
443 cfg -= ZBAR_CFG_POSITION;
444
445 if(!val)
446 iscn->config &= ~(1 << cfg);
447 else if(val == 1)
448 iscn->config |= (1 << cfg);
449 else
450 return(1);
451
452 return(0);
453 }
454
455
456 const zbar_symbol_set_t *
zbar_image_scanner_get_results(const zbar_image_scanner_t * iscn)457 zbar_image_scanner_get_results (const zbar_image_scanner_t *iscn)
458 {
459 return(iscn->syms);
460 }
461
quiet_border(zbar_image_scanner_t * iscn)462 static inline void quiet_border (zbar_image_scanner_t *iscn)
463 {
464 /* flush scanner pipeline */
465 zbar_scanner_t *scn = iscn->scn;
466 zbar_scanner_flush(scn);
467 zbar_scanner_flush(scn);
468 zbar_scanner_new_scan(scn);
469 }
470
471 #define movedelta(dx, dy) do { \
472 x += (dx); \
473 y += (dy); \
474 p += (dx) + ((intptr_t)(dy) * w); \
475 } while(0);
476
zbar_scan_image(zbar_image_scanner_t * iscn,unsigned width,unsigned height,const unsigned char * data)477 int zbar_scan_image(zbar_image_scanner_t *iscn,
478 unsigned width, unsigned height, const unsigned char *data)
479 {
480 /* timestamp image
481 * FIXME prefer video timestamp
482 */
483 #if _POSIX_TIMERS > 0
484 struct timespec abstime;
485 clock_gettime(CLOCK_REALTIME, &abstime);
486 iscn->time = (abstime.tv_sec * 1000) + ((abstime.tv_nsec / 500000) + 1) / 2;
487 #else
488 struct timeval abstime;
489 gettimeofday(&abstime, NULL);
490 iscn->time = (abstime.tv_sec * 1000) + ((abstime.tv_usec / 500) + 1) / 2;
491 #endif
492
493 #ifdef ENABLE_QRCODE
494 _zbar_qr_reset(iscn->qr);
495 #endif
496
497 /* recycle previous scanner and image results */
498 zbar_image_scanner_recycle(iscn);
499 zbar_symbol_set_t *syms = iscn->syms;
500 if(!syms) {
501 syms = iscn->syms = _zbar_symbol_set_create();
502 STAT(syms_new);
503 zbar_symbol_set_ref(syms, 1);
504 }
505 else
506 zbar_symbol_set_ref(syms, 2);
507
508 unsigned w = width;
509 unsigned h = height;
510
511 // zbar_image_write_png(img, "debug.png");
512 svg_open("debug.svg", 0, 0, w, h);
513 svg_image("debug.png", w, h);
514
515 zbar_scanner_t *scn = iscn->scn;
516
517 int density = CFG(iscn, ZBAR_CFG_Y_DENSITY);
518 if(density > 0) {
519 svg_group_start("scanner", 0, 1, 1, 0, 0);
520 const uint8_t *p = data;
521 int x = 0, y = 0;
522 iscn->dy = 0;
523
524 int border = (((h - 1) % density) + 1) / 2;
525 if(border > h / 2)
526 border = h / 2;
527 movedelta(0, border);
528 iscn->v = y;
529
530 zbar_scanner_new_scan(scn);
531
532 while(y < h) {
533 zprintf(128, "img_x+: %04d,%04d @%p\n", x, y, p);
534 svg_path_start("vedge", 1. / 32, 0, y + 0.5);
535 iscn->dx = iscn->du = 1;
536 iscn->umin = 0;
537 while(x < w) {
538 uint8_t d = *p;
539 movedelta(1, 0);
540 zbar_scan_y(scn, d);
541 }
542 ASSERT_POS;
543 quiet_border(iscn);
544 svg_path_end();
545
546 movedelta(-1, density);
547 iscn->v = y;
548 if(y >= h)
549 break;
550
551 zprintf(128, "img_x-: %04d,%04d @%p\n", x, y, p);
552 svg_path_start("vedge", -1. / 32, w, y + 0.5);
553 iscn->dx = iscn->du = -1;
554 iscn->umin = w;
555 while(x >= 0) {
556 uint8_t d = *p;
557 movedelta(-1, 0);
558 zbar_scan_y(scn, d);
559 }
560 ASSERT_POS;
561 quiet_border(iscn);
562 svg_path_end();
563
564 movedelta(1, density);
565 iscn->v = y;
566 }
567 svg_group_end();
568 }
569 iscn->dx = 0;
570
571 density = CFG(iscn, ZBAR_CFG_X_DENSITY);
572 if(density > 0) {
573 svg_group_start("scanner", 90, 1, -1, 0, 0);
574 const uint8_t *p = data;
575 int x = 0, y = 0;
576
577 int border = (((w - 1) % density) + 1) / 2;
578 if(border > w / 2)
579 border = w / 2;
580 movedelta(border, 0);
581 iscn->v = x;
582
583 while(x < w) {
584 zprintf(128, "img_y+: %04d,%04d @%p\n", x, y, p);
585 svg_path_start("vedge", 1. / 32, 0, x + 0.5);
586 iscn->dy = iscn->du = 1;
587 iscn->umin = 0;
588 while(y < h) {
589 uint8_t d = *p;
590 movedelta(0, 1);
591 zbar_scan_y(scn, d);
592 }
593 ASSERT_POS;
594 quiet_border(iscn);
595 svg_path_end();
596
597 movedelta(density, -1);
598 iscn->v = x;
599 if(x >= w)
600 break;
601
602 zprintf(128, "img_y-: %04d,%04d @%p\n", x, y, p);
603 svg_path_start("vedge", -1. / 32, h, x + 0.5);
604 iscn->dy = iscn->du = -1;
605 iscn->umin = h;
606 while(y >= 0) {
607 uint8_t d = *p;
608 movedelta(0, -1);
609 zbar_scan_y(scn, d);
610 }
611 ASSERT_POS;
612 quiet_border(iscn);
613 svg_path_end();
614
615 movedelta(density, 1);
616 iscn->v = x;
617 }
618 svg_group_end();
619 }
620 iscn->dy = 0;
621
622 #ifdef ENABLE_QRCODE
623 _zbar_qr_decode(iscn->qr, iscn, width, height, data);
624 #endif
625
626 /* FIXME tmp hack to filter bad EAN results */
627 if(syms->nsyms &&
628 (density == 1 || CFG(iscn, ZBAR_CFG_Y_DENSITY) == 1)) {
629 zbar_symbol_t **symp = &syms->head, *sym;
630 while((sym = *symp)) {
631 if(sym->type < ZBAR_I25 && sym->type > ZBAR_PARTIAL &&
632 sym->quality < 3) {
633 /* recycle */
634 *symp = sym->next;
635 syms->nsyms--;
636 sym->next = NULL;
637 _zbar_image_scanner_recycle_syms(iscn, sym);
638 }
639 else
640 symp = &sym->next;
641 }
642 }
643
644 svg_close();
645 return(syms->nsyms);
646 }
647