ZBar bar code reader . http://zbar.sourceforge.net/ ZBar is licensed under the GNU LGPL 2.1 to enable development of both open source and commercial projects.

Dependents:   GR-PEACH_Camera_in_barcode levkov_ov7670

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers img_scanner.c Source File

img_scanner.c

00001 /*------------------------------------------------------------------------
00002  *  Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net>
00003  *
00004  *  This file is part of the ZBar Bar Code Reader.
00005  *
00006  *  The ZBar Bar Code Reader is free software; you can redistribute it
00007  *  and/or modify it under the terms of the GNU Lesser Public License as
00008  *  published by the Free Software Foundation; either version 2.1 of
00009  *  the License, or (at your option) any later version.
00010  *
00011  *  The ZBar Bar Code Reader is distributed in the hope that it will be
00012  *  useful, but WITHOUT ANY WARRANTY; without even the implied warranty
00013  *  of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU Lesser Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU Lesser Public License
00017  *  along with the ZBar Bar Code Reader; if not, write to the Free
00018  *  Software Foundation, Inc., 51 Franklin St, Fifth Floor,
00019  *  Boston, MA  02110-1301  USA
00020  *
00021  *  http://sourceforge.net/projects/zbar
00022  *------------------------------------------------------------------------*/
00023 
00024 #include <config.h>
00025 #if (0) // not used
00026 #include <unistd.h>
00027 #endif
00028 #ifdef HAVE_INTTYPES_H
00029 # include <inttypes.h>
00030 #endif
00031 #include <stdlib.h>     /* malloc, free */
00032 #include <time.h>       /* clock_gettime */
00033 #if (0) // not used
00034 #include <sys/time.h>   /* gettimeofday */
00035 #endif
00036 #include <string.h>     /* memcmp, memset, memcpy */
00037 #include <assert.h>
00038 
00039 #include <zbar.h>
00040 #include "error.h"
00041 #include "image.h"
00042 #ifdef ENABLE_QRCODE
00043 # include "qrcode.h"
00044 #endif
00045 #include "img_scanner.h"
00046 #include "svg.h"
00047 
00048 #if 1
00049 # define ASSERT_POS \
00050     assert(p == data + x + y * (intptr_t)w)
00051 #else
00052 # define ASSERT_POS
00053 #endif
00054 
00055 /* FIXME cache setting configurability */
00056 
00057 /* number of times the same result must be detected
00058  * in "nearby" images before being reported
00059  */
00060 #define CACHE_CONSISTENCY    3 /* images */
00061 
00062 /* time interval for which two images are considered "nearby"
00063  */
00064 #define CACHE_PROXIMITY   1000 /* ms */
00065 
00066 /* time that a result must *not* be detected before
00067  * it will be reported again
00068  */
00069 #define CACHE_HYSTERESIS  2000 /* ms */
00070 
00071 /* time after which cache entries are invalidated
00072  */
00073 #define CACHE_TIMEOUT     (CACHE_HYSTERESIS * 2) /* ms */
00074 
00075 #define NUM_SCN_CFGS (ZBAR_CFG_Y_DENSITY - ZBAR_CFG_X_DENSITY + 1)
00076 
00077 #define CFG(iscn, cfg) ((iscn)->configs[(cfg) - ZBAR_CFG_X_DENSITY])
00078 #define TEST_CFG(iscn, cfg) (((iscn)->config >> ((cfg) - ZBAR_CFG_POSITION)) & 1)
00079 
00080 #ifndef NO_STATS
00081 # define STAT(x) iscn->stat_##x++
00082 #else
00083 # define STAT(...)
00084 # define dump_stats(...)
00085 #endif
00086 
00087 #define RECYCLE_BUCKETS     5
00088 
00089 typedef struct recycle_bucket_s {
00090     int nsyms;
00091     zbar_symbol_t *head;
00092 } recycle_bucket_t;
00093 
00094 /* image scanner state */
00095 struct zbar_image_scanner_s {
00096     zbar_scanner_t *scn;        /* associated linear intensity scanner */
00097     zbar_decoder_t *dcode;      /* associated symbol decoder */
00098 #ifdef ENABLE_QRCODE
00099     qr_reader *qr;              /* QR Code 2D reader */
00100 #endif
00101 
00102     const void *userdata;       /* application data */
00103     /* user result callback */
00104     zbar_image_data_handler_t *handler;
00105 
00106     unsigned long time;         /* scan start time */
00107     zbar_image_t *img;          /* currently scanning image *root* */
00108     int dx, dy, du, umin, v;    /* current scan direction */
00109     zbar_symbol_set_t *syms;    /* previous decode results */
00110     /* recycled symbols in 4^n size buckets */
00111     recycle_bucket_t recycle[RECYCLE_BUCKETS];
00112 
00113     int enable_cache;           /* current result cache state */
00114     zbar_symbol_t *cache;       /* inter-image result cache entries */
00115 
00116     /* configuration settings */
00117     unsigned config;            /* config flags */
00118     int configs[NUM_SCN_CFGS];  /* int valued configurations */
00119 
00120 #ifndef NO_STATS
00121     int stat_syms_new;
00122     int stat_iscn_syms_inuse, stat_iscn_syms_recycle;
00123     int stat_img_syms_inuse, stat_img_syms_recycle;
00124     int stat_sym_new;
00125     int stat_sym_recycle[RECYCLE_BUCKETS];
00126 #endif
00127 };
00128 
00129 void _zbar_image_scanner_recycle_syms (zbar_image_scanner_t *iscn,
00130                                        zbar_symbol_t *sym)
00131 {
00132     zbar_symbol_t *next = NULL;
00133     for(; sym; sym = next) {
00134         next = sym->next;
00135         if(sym->refcnt && _zbar_refcnt(&sym->refcnt, -1)) {
00136             /* unlink referenced symbol */
00137             /* FIXME handle outstanding component refs (currently unsupported)
00138              */
00139             assert(sym->data_alloc);
00140             sym->next = NULL;
00141         }
00142         else {
00143             /* recycle unreferenced symbol */
00144             if(!sym->data_alloc) {
00145                 sym->data = NULL;
00146                 sym->datalen = 0;
00147             }
00148             if(sym->syms) {
00149                 if(_zbar_refcnt(&sym->syms->refcnt, -1))
00150                     assert(0);
00151                 _zbar_image_scanner_recycle_syms(iscn, sym->syms->head);
00152                 sym->syms->head = NULL;
00153                 _zbar_symbol_set_free(sym->syms);
00154                 sym->syms = NULL;
00155             }
00156             int i;
00157             for(i = 0; i < RECYCLE_BUCKETS; i++)
00158                 if(sym->data_alloc < 1 << (i * 2))
00159                     break;
00160             if(i == RECYCLE_BUCKETS) {
00161                 assert(sym->data);
00162                 free(sym->data);
00163                 sym->data = NULL;
00164                 sym->data_alloc = 0;
00165                 i = 0;
00166             }
00167             recycle_bucket_t *bucket = &iscn->recycle[i];
00168             /* FIXME cap bucket fill */
00169             bucket->nsyms++;
00170             sym->next = bucket->head;
00171             bucket->head = sym;
00172         }
00173     }
00174 }
00175 
00176 static inline int recycle_syms (zbar_image_scanner_t *iscn,
00177                                 zbar_symbol_set_t *syms)
00178 {
00179     if(_zbar_refcnt(&syms->refcnt, -1))
00180         return(1);
00181 
00182     _zbar_image_scanner_recycle_syms(iscn, syms->head);
00183     syms->head = syms->tail = NULL;
00184     syms->nsyms = 0;
00185     return(0);
00186 }
00187 
00188 inline void zbar_image_scanner_recycle_image (zbar_image_scanner_t *iscn,
00189                                               zbar_image_t *img)
00190 {
00191     zbar_symbol_set_t *syms = iscn->syms;
00192     if(syms && syms->refcnt) {
00193         if(recycle_syms(iscn, syms)) {
00194             STAT(iscn_syms_inuse);
00195             iscn->syms = NULL;
00196         }
00197         else
00198             STAT(iscn_syms_recycle);
00199     }
00200 
00201     syms = img->syms;
00202     img->syms = NULL;
00203     if(syms && recycle_syms(iscn, syms)) {
00204         STAT(img_syms_inuse);
00205         syms = iscn->syms;
00206     }
00207     else if(syms) {
00208         STAT(img_syms_recycle);
00209 
00210         /* select one set to resurrect, destroy the other */
00211         if(iscn->syms) {
00212             _zbar_symbol_set_free(syms);
00213             syms = iscn->syms;
00214         }
00215         else
00216             iscn->syms = syms;
00217     }
00218 }
00219 
00220 inline zbar_symbol_t*
00221 _zbar_image_scanner_alloc_sym (zbar_image_scanner_t *iscn,
00222                                zbar_symbol_type_t type,
00223                                int datalen)
00224 {
00225     /* recycle old or alloc new symbol */
00226     int i;
00227     for(i = 0; i < RECYCLE_BUCKETS - 1; i++)
00228         if(datalen <= 1 << (i * 2))
00229             break;
00230 
00231     zbar_symbol_t *sym = NULL;
00232     for(; i > 0; i--)
00233         if((sym = iscn->recycle[i].head)) {
00234             STAT(sym_recycle[i]);
00235             break;
00236         }
00237 
00238     if(sym) {
00239         iscn->recycle[i].head = sym->next;
00240         sym->next = NULL;
00241         assert(iscn->recycle[i].nsyms);
00242         iscn->recycle[i].nsyms--;
00243     }
00244     else {
00245         sym = calloc(1, sizeof(zbar_symbol_t));
00246         STAT(sym_new);
00247     }
00248 
00249     /* init new symbol */
00250     sym->type = type;
00251     sym->quality = 1;
00252     sym->npts = 0;
00253     sym->cache_count = 0;
00254     sym->time = iscn->time;
00255     assert(!sym->syms);
00256 
00257     if(datalen > 0) {
00258         sym->datalen = datalen - 1;
00259         if(sym->data_alloc < datalen) {
00260             if(sym->data)
00261                 free(sym->data);
00262             sym->data_alloc = datalen;
00263             sym->data = malloc(datalen);
00264         }
00265     }
00266     else {
00267         if(sym->data)
00268             free(sym->data);
00269         sym->data = NULL;
00270         sym->datalen = sym->data_alloc = 0;
00271     }
00272     return(sym);
00273 }
00274 
00275 static inline zbar_symbol_t *cache_lookup (zbar_image_scanner_t *iscn,
00276                                            zbar_symbol_t *sym)
00277 {
00278     /* search for matching entry in cache */
00279     zbar_symbol_t **entry = &iscn->cache;
00280     while(*entry) {
00281         if((*entry)->type == sym->type &&
00282            (*entry)->datalen == sym->datalen &&
00283            !memcmp((*entry)->data, sym->data, sym->datalen))
00284             break;
00285         if((sym->time - (*entry)->time) > CACHE_TIMEOUT) {
00286             /* recycle stale cache entry */
00287             zbar_symbol_t *next = (*entry)->next;
00288             (*entry)->next = NULL;
00289             _zbar_image_scanner_recycle_syms(iscn, *entry);
00290             *entry = next;
00291         }
00292         else
00293             entry = &(*entry)->next;
00294     }
00295     return(*entry);
00296 }
00297 
00298 static inline void cache_sym (zbar_image_scanner_t *iscn,
00299                               zbar_symbol_t *sym)
00300 {
00301     if(iscn->enable_cache) {
00302         zbar_symbol_t *entry = cache_lookup(iscn, sym);
00303         if(!entry) {
00304             /* FIXME reuse sym */
00305             entry = _zbar_image_scanner_alloc_sym(iscn, sym->type,
00306                                                   sym->datalen + 1);
00307             memcpy(entry->data, sym->data, sym->datalen);
00308             entry->time = sym->time - CACHE_HYSTERESIS;
00309             entry->cache_count = -CACHE_CONSISTENCY;
00310             /* add to cache */
00311             entry->next = iscn->cache;
00312             iscn->cache = entry;
00313         }
00314 
00315         /* consistency check and hysteresis */
00316         uint32_t age = sym->time - entry->time;
00317         entry->time = sym->time;
00318         int near_thresh = (age < CACHE_PROXIMITY);
00319         int far_thresh = (age >= CACHE_HYSTERESIS);
00320         int dup = (entry->cache_count >= 0);
00321         if((!dup && !near_thresh) || far_thresh)
00322             entry->cache_count = -CACHE_CONSISTENCY;
00323         else if(dup || near_thresh)
00324             entry->cache_count++;
00325 
00326         sym->cache_count = entry->cache_count;
00327     }
00328     else
00329         sym->cache_count = 0;
00330 }
00331 
00332 void _zbar_image_scanner_add_sym(zbar_image_scanner_t *iscn,
00333                                  zbar_symbol_t *sym)
00334 {
00335     cache_sym(iscn, sym);
00336 
00337     zbar_symbol_set_t *syms = iscn->syms;
00338     if(sym->cache_count || !syms->tail) {
00339         sym->next = syms->head;
00340         syms->head = sym;
00341     }
00342     else {
00343         sym->next = syms->tail->next;
00344         syms->tail->next = sym;
00345     }
00346 
00347     if(!sym->cache_count)
00348         syms->nsyms++;
00349     else if(!syms->tail)
00350         syms->tail = sym;
00351 
00352     _zbar_symbol_refcnt(sym, 1);
00353 }
00354 
00355 #ifdef ENABLE_QRCODE
00356 extern qr_finder_line *_zbar_decoder_get_qr_finder_line(zbar_decoder_t*);
00357 
00358 # define QR_FIXED(v, rnd) ((((v) << 1) + (rnd)) << (QR_FINDER_SUBPREC - 1))
00359 # define PRINT_FIXED(val, prec) \
00360     ((val) >> (prec)),         \
00361         (1000 * ((val) & ((1 << (prec)) - 1)) / (1 << (prec)))
00362 
00363 static inline void qr_handler (zbar_image_scanner_t *iscn)
00364 {
00365     qr_finder_line *line = _zbar_decoder_get_qr_finder_line(iscn->dcode);
00366     assert(line);
00367     unsigned u = zbar_scanner_get_edge(iscn->scn, line->pos[0],
00368                                        QR_FINDER_SUBPREC);
00369     line->boffs = u - zbar_scanner_get_edge(iscn->scn, line->boffs,
00370                                             QR_FINDER_SUBPREC);
00371     line->len = zbar_scanner_get_edge(iscn->scn, line->len,
00372                                       QR_FINDER_SUBPREC);
00373     line->eoffs = zbar_scanner_get_edge(iscn->scn, line->eoffs,
00374                                         QR_FINDER_SUBPREC) - line->len;
00375     line->len -= u;
00376 
00377     u = QR_FIXED(iscn->umin, 0) + iscn->du * u;
00378     if(iscn->du < 0) {
00379         u -= line->len;
00380         int tmp = line->boffs;
00381         line->boffs = line->eoffs;
00382         line->eoffs = tmp;
00383     }
00384     int vert = !iscn->dx;
00385     line->pos[vert] = u;
00386     line->pos[!vert] = QR_FIXED(iscn->v, 1);
00387 
00388     _zbar_qr_found_line(iscn->qr, vert, line);
00389 }
00390 #endif
00391 
00392 static void symbol_handler (zbar_decoder_t *dcode)
00393 {
00394     zbar_image_scanner_t *iscn = zbar_decoder_get_userdata(dcode);
00395     zbar_symbol_type_t type = zbar_decoder_get_type(dcode);
00396     /* FIXME assert(type == ZBAR_PARTIAL) */
00397     /* FIXME debug flag to save/display all PARTIALs */
00398     if(type <= ZBAR_PARTIAL)
00399         return;
00400 
00401 #ifdef ENABLE_QRCODE
00402     if(type == ZBAR_QRCODE) {
00403         qr_handler(iscn);
00404         return;
00405     }
00406 #else
00407     assert(type != ZBAR_QRCODE);
00408 #endif
00409 
00410     const char *data = zbar_decoder_get_data(dcode);
00411     unsigned datalen = zbar_decoder_get_data_length(dcode);
00412 
00413     int x = 0, y = 0;
00414     if(TEST_CFG(iscn, ZBAR_CFG_POSITION)) {
00415         /* tmp position fixup */
00416         int w = zbar_scanner_get_width(iscn->scn);
00417         int u = iscn->umin + iscn->du * zbar_scanner_get_edge(iscn->scn, w, 0);
00418         if(iscn->dx) {
00419             x = u;
00420             y = iscn->v;
00421         }
00422         else {
00423             x = iscn->v;
00424             y = u;
00425         }
00426     }
00427 
00428     /* FIXME need better symbol matching */
00429     zbar_symbol_t *sym;
00430     for(sym = iscn->syms->head; sym; sym = sym->next)
00431         if(sym->type == type &&
00432            sym->datalen == datalen &&
00433            !memcmp(sym->data, data, datalen)) {
00434             sym->quality++;
00435             if(TEST_CFG(iscn, ZBAR_CFG_POSITION))
00436                 /* add new point to existing set */
00437                 /* FIXME should be polygon */
00438                 sym_add_point(sym, x, y);
00439             return;
00440         }
00441 
00442     sym = _zbar_image_scanner_alloc_sym(iscn, type, datalen + 1);
00443     /* FIXME grab decoder buffer */
00444     memcpy(sym->data, data, datalen + 1);
00445 
00446     /* initialize first point */
00447     if(TEST_CFG(iscn, ZBAR_CFG_POSITION))
00448         sym_add_point(sym, x, y);
00449 
00450     _zbar_image_scanner_add_sym(iscn, sym);
00451 }
00452 
00453 zbar_image_scanner_t *zbar_image_scanner_create ()
00454 {
00455     zbar_image_scanner_t *iscn = calloc(1, sizeof(zbar_image_scanner_t));
00456     if(!iscn)
00457         return(NULL);
00458     iscn->dcode = zbar_decoder_create();
00459     iscn->scn = zbar_scanner_create(iscn->dcode);
00460     if(!iscn->dcode || !iscn->scn) {
00461         zbar_image_scanner_destroy(iscn);
00462         return(NULL);
00463     }
00464     zbar_decoder_set_userdata(iscn->dcode, iscn);
00465     zbar_decoder_set_handler(iscn->dcode, symbol_handler);
00466 
00467 #ifdef ENABLE_QRCODE
00468     iscn->qr = _zbar_qr_create();
00469 #endif
00470 
00471     /* apply default configuration */
00472     CFG(iscn, ZBAR_CFG_X_DENSITY) = 1;
00473     CFG(iscn, ZBAR_CFG_Y_DENSITY) = 1;
00474     zbar_image_scanner_set_config(iscn, 0, ZBAR_CFG_POSITION, 1);
00475     return(iscn);
00476 }
00477 
00478 #ifndef NO_STATS
00479 static inline void dump_stats (const zbar_image_scanner_t *iscn)
00480 {
00481     zprintf(1, "symbol sets allocated   = %-4d\n", iscn->stat_syms_new);
00482     zprintf(1, "    scanner syms in use = %-4d\trecycled  = %-4d\n",
00483             iscn->stat_iscn_syms_inuse, iscn->stat_iscn_syms_recycle);
00484     zprintf(1, "    image syms in use   = %-4d\trecycled  = %-4d\n",
00485             iscn->stat_img_syms_inuse, iscn->stat_img_syms_recycle);
00486     zprintf(1, "symbols allocated       = %-4d\n", iscn->stat_sym_new);
00487     int i;
00488     for(i = 0; i < RECYCLE_BUCKETS; i++)
00489         zprintf(1, "     recycled[%d]        = %-4d\n",
00490                 i, iscn->stat_sym_recycle[i]);
00491 }
00492 #endif
00493 
00494 void zbar_image_scanner_destroy (zbar_image_scanner_t *iscn)
00495 {
00496     dump_stats(iscn);
00497     if(iscn->syms) {
00498         if(iscn->syms->refcnt)
00499             zbar_symbol_set_ref(iscn->syms, -1);
00500         else
00501             _zbar_symbol_set_free(iscn->syms);
00502         iscn->syms = NULL;
00503     }
00504     if(iscn->scn)
00505         zbar_scanner_destroy(iscn->scn);
00506     iscn->scn = NULL;
00507     if(iscn->dcode)
00508         zbar_decoder_destroy(iscn->dcode);
00509     iscn->dcode = NULL;
00510     int i;
00511     for(i = 0; i < RECYCLE_BUCKETS; i++) {
00512         zbar_symbol_t *sym, *next;
00513         for(sym = iscn->recycle[i].head; sym; sym = next) {
00514             next = sym->next;
00515             _zbar_symbol_free(sym);
00516         }
00517     }
00518 #ifdef ENABLE_QRCODE
00519     if(iscn->qr) {
00520         _zbar_qr_destroy(iscn->qr);
00521         iscn->qr = NULL;
00522     }
00523 #endif
00524     free(iscn);
00525 }
00526 
00527 zbar_image_data_handler_t*
00528 zbar_image_scanner_set_data_handler (zbar_image_scanner_t *iscn,
00529                                      zbar_image_data_handler_t *handler,
00530                                      const void *userdata)
00531 {
00532     zbar_image_data_handler_t *result = iscn->handler;
00533     iscn->handler = handler;
00534     iscn->userdata = userdata;
00535     return(result);
00536 }
00537 
00538 int zbar_image_scanner_set_config (zbar_image_scanner_t *iscn,
00539                                    zbar_symbol_type_t sym,
00540                                    zbar_config_t cfg,
00541                                    int val)
00542 {
00543     if(cfg < ZBAR_CFG_POSITION)
00544         return(zbar_decoder_set_config(iscn->dcode, sym, cfg, val));
00545 
00546     if(sym > ZBAR_PARTIAL)
00547         return(1);
00548 
00549     if(cfg >= ZBAR_CFG_X_DENSITY && cfg <= ZBAR_CFG_Y_DENSITY) {
00550 
00551         CFG(iscn, cfg) = val;
00552         return(0);
00553     }
00554 
00555     if(cfg > ZBAR_CFG_POSITION)
00556         return(1);
00557     cfg -= ZBAR_CFG_POSITION;
00558 
00559     if(!val)
00560         iscn->config &= ~(1 << cfg);
00561     else if(val == 1)
00562         iscn->config |= (1 << cfg);
00563     else
00564         return(1);
00565 
00566     return(0);
00567 }
00568 
00569 void zbar_image_scanner_enable_cache (zbar_image_scanner_t *iscn,
00570                                       int enable)
00571 {
00572     if(iscn->cache) {
00573         /* recycle all cached syms */
00574         _zbar_image_scanner_recycle_syms(iscn, iscn->cache);
00575         iscn->cache = NULL;
00576     }
00577     iscn->enable_cache = (enable) ? 1 : 0;
00578 }
00579 
00580 const zbar_symbol_set_t *
00581 zbar_image_scanner_get_results (const zbar_image_scanner_t *iscn)
00582 {
00583     return(iscn->syms);
00584 }
00585 
00586 static inline void quiet_border (zbar_image_scanner_t *iscn)
00587 {
00588     /* flush scanner pipeline */
00589     zbar_scanner_t *scn = iscn->scn;
00590     zbar_scanner_flush(scn);
00591     zbar_scanner_flush(scn);
00592     zbar_scanner_new_scan(scn);
00593 }
00594 
00595 #define movedelta(dx, dy) do {                  \
00596         x += (dx);                              \
00597         y += (dy);                              \
00598         p += (dx) + ((intptr_t)(dy) * w);       \
00599     } while(0);
00600 
00601 int zbar_scan_image (zbar_image_scanner_t *iscn,
00602                      zbar_image_t *img)
00603 {
00604     /* timestamp image
00605      * FIXME prefer video timestamp
00606      */
00607 #if _POSIX_TIMERS > 0
00608     struct timespec abstime;
00609     clock_gettime(CLOCK_REALTIME, &abstime);
00610     iscn->time = (abstime.tv_sec * 1000) + ((abstime.tv_nsec / 500000) + 1) / 2;
00611 #else
00612 #if (0) //  not used
00613     struct timeval abstime;
00614     gettimeofday(&abstime, NULL);
00615     iscn->time = (abstime.tv_sec * 1000) + ((abstime.tv_usec / 500) + 1) / 2;
00616 #endif
00617 #endif
00618 
00619 #ifdef ENABLE_QRCODE
00620     _zbar_qr_reset(iscn->qr);
00621 #endif
00622 
00623     /* get grayscale image, convert if necessary */
00624     if(img->format != fourcc('Y','8','0','0') &&
00625        img->format != fourcc('G','R','E','Y'))
00626         return(-1);
00627     iscn->img = img;
00628 
00629     /* recycle previous scanner and image results */
00630     zbar_image_scanner_recycle_image(iscn, img);
00631     zbar_symbol_set_t *syms = iscn->syms;
00632     if(!syms) {
00633         syms = iscn->syms = _zbar_symbol_set_create();
00634         STAT(syms_new);
00635         zbar_symbol_set_ref(syms, 1);
00636     }
00637     else
00638         zbar_symbol_set_ref(syms, 2);
00639     img->syms = syms;
00640 
00641     unsigned w = img->width;
00642     unsigned h = img->height;
00643     const uint8_t *data = img->data;
00644 
00645     zbar_image_write_png(img, "debug.png");
00646     svg_open("debug.svg", 0, 0, w, h);
00647     svg_image("debug.png", w, h);
00648 
00649     zbar_scanner_t *scn = iscn->scn;
00650 
00651     int density = CFG(iscn, ZBAR_CFG_Y_DENSITY);
00652     if(density > 0) {
00653         svg_group_start("scanner", 0, 1, 1, 0, 0);
00654         const uint8_t *p = data;
00655         int x = 0, y = 0;
00656         iscn->dy = 0;
00657 
00658         int border = (((h - 1) % density) + 1) / 2;
00659         if(border > h / 2)
00660             border = h / 2;
00661         movedelta(0, border);
00662         iscn->v = y;
00663 
00664         zbar_scanner_new_scan(scn);
00665 
00666         while(y < h) {
00667             zprintf(128, "img_x+: %04d,%04d @%p\n", x, y, p);
00668             svg_path_start("vedge", 1. / 32, 0, y + 0.5);
00669             iscn->dx = iscn->du = 1;
00670             iscn->umin = 0;
00671             while(x < w) {
00672                 uint8_t d = *p;
00673                 movedelta(1, 0);
00674                 zbar_scan_y(scn, d);
00675             }
00676             ASSERT_POS;
00677             quiet_border(iscn);
00678             svg_path_end();
00679 
00680             movedelta(-1, density);
00681             iscn->v = y;
00682             if(y >= h)
00683                 break;
00684 
00685             zprintf(128, "img_x-: %04d,%04d @%p\n", x, y, p);
00686             svg_path_start("vedge", -1. / 32, w, y + 0.5);
00687             iscn->dx = iscn->du = -1;
00688             iscn->umin = w;
00689             while(x >= 0) {
00690                 uint8_t d = *p;
00691                 movedelta(-1, 0);
00692                 zbar_scan_y(scn, d);
00693             }
00694             ASSERT_POS;
00695             quiet_border(iscn);
00696             svg_path_end();
00697 
00698             movedelta(1, density);
00699             iscn->v = y;
00700         }
00701         svg_group_end();
00702     }
00703     iscn->dx = 0;
00704 
00705     density = CFG(iscn, ZBAR_CFG_X_DENSITY);
00706     if(density > 0) {
00707         svg_group_start("scanner", 90, 1, -1, 0, 0);
00708         const uint8_t *p = data;
00709         int x = 0, y = 0;
00710 
00711         int border = (((w - 1) % density) + 1) / 2;
00712         if(border > w / 2)
00713             border = w / 2;
00714         movedelta(border, 0);
00715         iscn->v = x;
00716 
00717         while(x < w) {
00718             zprintf(128, "img_y+: %04d,%04d @%p\n", x, y, p);
00719             svg_path_start("vedge", 1. / 32, 0, x + 0.5);
00720             iscn->dy = iscn->du = 1;
00721             iscn->umin = 0;
00722             while(y < h) {
00723                 uint8_t d = *p;
00724                 movedelta(0, 1);
00725                 zbar_scan_y(scn, d);
00726             }
00727             ASSERT_POS;
00728             quiet_border(iscn);
00729             svg_path_end();
00730 
00731             movedelta(density, -1);
00732             iscn->v = x;
00733             if(x >= w)
00734                 break;
00735 
00736             zprintf(128, "img_y-: %04d,%04d @%p\n", x, y, p);
00737             svg_path_start("vedge", -1. / 32, h, x + 0.5);
00738             iscn->dy = iscn->du = -1;
00739             iscn->umin = h;
00740             while(y >= 0) {
00741                 uint8_t d = *p;
00742                 movedelta(0, -1);
00743                 zbar_scan_y(scn, d);
00744             }
00745             ASSERT_POS;
00746             quiet_border(iscn);
00747             svg_path_end();
00748 
00749             movedelta(density, 1);
00750             iscn->v = x;
00751         }
00752         svg_group_end();
00753     }
00754     iscn->dy = 0;
00755     iscn->img = NULL;
00756 
00757 #ifdef ENABLE_QRCODE
00758     _zbar_qr_decode(iscn->qr, iscn, img);
00759 #endif
00760 
00761     /* FIXME tmp hack to filter bad EAN results */
00762     if(syms->nsyms && !iscn->enable_cache &&
00763        (density == 1 || CFG(iscn, ZBAR_CFG_Y_DENSITY) == 1)) {
00764         zbar_symbol_t **symp = &syms->head, *sym;
00765         while((sym = *symp)) {
00766             if(sym->type < ZBAR_I25 && sym->type > ZBAR_PARTIAL &&
00767                sym->quality < 3) {
00768                 /* recycle */
00769                 *symp = sym->next;
00770                 syms->nsyms--;
00771                 sym->next = NULL;
00772                 _zbar_image_scanner_recycle_syms(iscn, sym);
00773             }
00774             else
00775                 symp = &sym->next;
00776         }
00777     }
00778 
00779     if(syms->nsyms && iscn->handler)
00780         iscn->handler(img, iscn->userdata);
00781 
00782     svg_close();
00783     return(syms->nsyms);
00784 }
00785 
00786 #ifdef DEBUG_SVG
00787 /* FIXME lame...*/
00788 # include "svg.c"
00789 #endif
00790