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