914 lines
27 KiB
C
914 lines
27 KiB
C
/*------------------------------------------------------------------------
|
|
* Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net>
|
|
*
|
|
* This file is part of the ZBar Bar Code Reader.
|
|
*
|
|
* The ZBar Bar Code Reader is free software; you can redistribute it
|
|
* and/or modify it under the terms of the GNU Lesser Public License as
|
|
* published by the Free Software Foundation; either version 2.1 of
|
|
* the License, or (at your option) any later version.
|
|
*
|
|
* The ZBar Bar Code Reader is distributed in the hope that it will be
|
|
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser Public License
|
|
* along with the ZBar Bar Code Reader; if not, write to the Free
|
|
* Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301 USA
|
|
*
|
|
* http://sourceforge.net/projects/zbar
|
|
*------------------------------------------------------------------------*/
|
|
|
|
#include <zbar_config.h>
|
|
#ifdef HAVE_UNISTD_H
|
|
# include <unistd.h>
|
|
#endif
|
|
#ifdef HAVE_INTTYPES_H
|
|
# include <inttypes.h>
|
|
#endif
|
|
#include <stdlib.h> /* malloc, free */
|
|
#include <string.h> /* memcmp, memset, memcpy */
|
|
#include <assert.h>
|
|
#include "decoder.h"
|
|
#include <zbar.h>
|
|
#include "error.h"
|
|
#include "image.h"
|
|
#include "timer.h"
|
|
#ifdef ENABLE_QRCODE
|
|
# include "qrcode.h"
|
|
#endif
|
|
#include "img_scanner.h"
|
|
#include "svg.h"
|
|
|
|
#if 1
|
|
# define ASSERT_POS \
|
|
assert(p == data + x + y * (intptr_t)w)
|
|
#else
|
|
# define ASSERT_POS
|
|
#endif
|
|
|
|
/* FIXME cache setting configurability */
|
|
|
|
/* time interval for which two images are considered "nearby"
|
|
*/
|
|
#define CACHE_PROXIMITY 1000 /* ms */
|
|
|
|
/* time that a result must *not* be detected before
|
|
* it will be reported again
|
|
*/
|
|
#define CACHE_HYSTERESIS 2000 /* ms */
|
|
|
|
/* time after which cache entries are invalidated
|
|
*/
|
|
#define CACHE_TIMEOUT (CACHE_HYSTERESIS * 2) /* ms */
|
|
|
|
#define NUM_SCN_CFGS (ZBAR_CFG_Y_DENSITY - ZBAR_CFG_X_DENSITY + 1)
|
|
|
|
#undef CFG
|
|
#undef TEST_CFG
|
|
#define CFG(iscn, cfg) ((iscn)->configs[(cfg) - ZBAR_CFG_X_DENSITY])
|
|
#define TEST_CFG(iscn, cfg) (((iscn)->config >> ((cfg) - ZBAR_CFG_POSITION)) & 1)
|
|
|
|
#ifndef NO_STATS
|
|
# define STAT(x) iscn->stat_##x++
|
|
#else
|
|
# define STAT(...)
|
|
# define dump_stats(...)
|
|
#endif
|
|
|
|
#define RECYCLE_BUCKETS 5
|
|
|
|
typedef struct recycle_bucket_s {
|
|
int nsyms;
|
|
zbar_symbol_t *head;
|
|
} recycle_bucket_t;
|
|
|
|
/* image scanner state */
|
|
struct zbar_image_scanner_s {
|
|
zbar_scanner_t *scn; /* associated linear intensity scanner */
|
|
zbar_decoder_t *dcode; /* associated symbol decoder */
|
|
#ifdef ENABLE_QRCODE
|
|
qr_reader *qr; /* QR Code 2D reader */
|
|
#endif
|
|
|
|
const void *userdata; /* application data */
|
|
/* user result callback */
|
|
zbar_image_data_handler_t *handler;
|
|
|
|
unsigned long time; /* scan start time */
|
|
zbar_image_t *img; /* currently scanning image *root* */
|
|
int dx, dy, du, umin, v; /* current scan direction */
|
|
zbar_symbol_set_t *syms; /* previous decode results */
|
|
/* recycled symbols in 4^n size buckets */
|
|
recycle_bucket_t recycle[RECYCLE_BUCKETS];
|
|
|
|
int enable_cache; /* current result cache state */
|
|
zbar_symbol_t *cache; /* inter-image result cache entries */
|
|
|
|
/* configuration settings */
|
|
unsigned config; /* config flags */
|
|
unsigned ean_config;
|
|
int configs[NUM_SCN_CFGS]; /* int valued configurations */
|
|
int sym_configs[1][NUM_SYMS]; /* per-symbology configurations */
|
|
|
|
#ifndef NO_STATS
|
|
int stat_syms_new;
|
|
int stat_iscn_syms_inuse, stat_iscn_syms_recycle;
|
|
int stat_img_syms_inuse, stat_img_syms_recycle;
|
|
int stat_sym_new;
|
|
int stat_sym_recycle[RECYCLE_BUCKETS];
|
|
#endif
|
|
};
|
|
|
|
void _zbar_image_scanner_recycle_syms (zbar_image_scanner_t *iscn,
|
|
zbar_symbol_t *sym)
|
|
{
|
|
zbar_symbol_t *next = NULL;
|
|
for(; sym; sym = next) {
|
|
next = sym->next;
|
|
if(sym->refcnt && _zbar_refcnt(&sym->refcnt, -1)) {
|
|
/* unlink referenced symbol */
|
|
/* FIXME handle outstanding component refs (currently unsupported)
|
|
*/
|
|
assert(sym->data_alloc);
|
|
sym->next = NULL;
|
|
}
|
|
else {
|
|
int i;
|
|
recycle_bucket_t *bucket;
|
|
/* recycle unreferenced symbol */
|
|
if(!sym->data_alloc) {
|
|
sym->data = NULL;
|
|
sym->datalen = 0;
|
|
}
|
|
if(sym->syms) {
|
|
if(_zbar_refcnt(&sym->syms->refcnt, -1))
|
|
assert(0);
|
|
_zbar_image_scanner_recycle_syms(iscn, sym->syms->head);
|
|
sym->syms->head = NULL;
|
|
_zbar_symbol_set_free(sym->syms);
|
|
sym->syms = NULL;
|
|
}
|
|
for(i = 0; i < RECYCLE_BUCKETS; i++)
|
|
if(sym->data_alloc < 1 << (i * 2))
|
|
break;
|
|
if(i == RECYCLE_BUCKETS) {
|
|
assert(sym->data);
|
|
free(sym->data);
|
|
sym->data = NULL;
|
|
sym->data_alloc = 0;
|
|
i = 0;
|
|
}
|
|
bucket = &iscn->recycle[i];
|
|
/* FIXME cap bucket fill */
|
|
bucket->nsyms++;
|
|
sym->next = bucket->head;
|
|
bucket->head = sym;
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline int recycle_syms (zbar_image_scanner_t *iscn,
|
|
zbar_symbol_set_t *syms)
|
|
{
|
|
if(_zbar_refcnt(&syms->refcnt, -1))
|
|
return(1);
|
|
|
|
_zbar_image_scanner_recycle_syms(iscn, syms->head);
|
|
syms->head = syms->tail = NULL;
|
|
syms->nsyms = 0;
|
|
return(0);
|
|
}
|
|
|
|
inline void zbar_image_scanner_recycle_image (zbar_image_scanner_t *iscn,
|
|
zbar_image_t *img)
|
|
{
|
|
zbar_symbol_set_t *syms = iscn->syms;
|
|
if(syms && syms->refcnt) {
|
|
if(recycle_syms(iscn, syms)) {
|
|
STAT(iscn_syms_inuse);
|
|
iscn->syms = NULL;
|
|
}
|
|
else
|
|
STAT(iscn_syms_recycle);
|
|
}
|
|
|
|
syms = img->syms;
|
|
img->syms = NULL;
|
|
if(syms && recycle_syms(iscn, syms))
|
|
STAT(img_syms_inuse);
|
|
else if(syms) {
|
|
STAT(img_syms_recycle);
|
|
|
|
/* select one set to resurrect, destroy the other */
|
|
if(iscn->syms)
|
|
_zbar_symbol_set_free(syms);
|
|
else
|
|
iscn->syms = syms;
|
|
}
|
|
}
|
|
|
|
inline zbar_symbol_t*
|
|
_zbar_image_scanner_alloc_sym (zbar_image_scanner_t *iscn,
|
|
zbar_symbol_type_t type,
|
|
int datalen)
|
|
{
|
|
/* recycle old or alloc new symbol */
|
|
zbar_symbol_t *sym = NULL;
|
|
int i;
|
|
for(i = 0; i < RECYCLE_BUCKETS - 1; i++)
|
|
if(datalen <= 1 << (i * 2))
|
|
break;
|
|
|
|
for(; i > 0; i--)
|
|
if((sym = iscn->recycle[i].head)) {
|
|
STAT(sym_recycle[i]);
|
|
break;
|
|
}
|
|
|
|
if(sym) {
|
|
iscn->recycle[i].head = sym->next;
|
|
sym->next = NULL;
|
|
assert(iscn->recycle[i].nsyms);
|
|
iscn->recycle[i].nsyms--;
|
|
}
|
|
else {
|
|
sym = calloc(1, sizeof(zbar_symbol_t));
|
|
STAT(sym_new);
|
|
}
|
|
|
|
/* init new symbol */
|
|
sym->type = type;
|
|
sym->quality = 1;
|
|
sym->npts = 0;
|
|
sym->orient = ZBAR_ORIENT_UNKNOWN;
|
|
sym->cache_count = 0;
|
|
sym->time = iscn->time;
|
|
assert(!sym->syms);
|
|
|
|
if(datalen > 0) {
|
|
sym->datalen = datalen - 1;
|
|
if(sym->data_alloc < datalen) {
|
|
if(sym->data)
|
|
free(sym->data);
|
|
sym->data_alloc = datalen;
|
|
sym->data = malloc(datalen);
|
|
}
|
|
}
|
|
else {
|
|
if(sym->data)
|
|
free(sym->data);
|
|
sym->data = NULL;
|
|
sym->datalen = sym->data_alloc = 0;
|
|
}
|
|
return(sym);
|
|
}
|
|
|
|
static inline zbar_symbol_t *cache_lookup (zbar_image_scanner_t *iscn,
|
|
zbar_symbol_t *sym)
|
|
{
|
|
/* search for matching entry in cache */
|
|
zbar_symbol_t **entry = &iscn->cache;
|
|
while(*entry) {
|
|
if((*entry)->type == sym->type &&
|
|
(*entry)->datalen == sym->datalen &&
|
|
!memcmp((*entry)->data, sym->data, sym->datalen))
|
|
break;
|
|
if((sym->time - (*entry)->time) > CACHE_TIMEOUT) {
|
|
/* recycle stale cache entry */
|
|
zbar_symbol_t *next = (*entry)->next;
|
|
(*entry)->next = NULL;
|
|
_zbar_image_scanner_recycle_syms(iscn, *entry);
|
|
*entry = next;
|
|
}
|
|
else
|
|
entry = &(*entry)->next;
|
|
}
|
|
return(*entry);
|
|
}
|
|
|
|
static inline void cache_sym (zbar_image_scanner_t *iscn,
|
|
zbar_symbol_t *sym)
|
|
{
|
|
if(iscn->enable_cache) {
|
|
uint32_t age, near_thresh, far_thresh, dup;
|
|
zbar_symbol_t *entry = cache_lookup(iscn, sym);
|
|
if(!entry) {
|
|
/* FIXME reuse sym */
|
|
entry = _zbar_image_scanner_alloc_sym(iscn, sym->type,
|
|
sym->datalen + 1);
|
|
entry->configs = sym->configs;
|
|
entry->modifiers = sym->modifiers;
|
|
memcpy(entry->data, sym->data, sym->datalen);
|
|
entry->time = sym->time - CACHE_HYSTERESIS;
|
|
entry->cache_count = 0;
|
|
/* add to cache */
|
|
entry->next = iscn->cache;
|
|
iscn->cache = entry;
|
|
}
|
|
|
|
/* consistency check and hysteresis */
|
|
age = sym->time - entry->time;
|
|
entry->time = sym->time;
|
|
near_thresh = (age < CACHE_PROXIMITY);
|
|
far_thresh = (age >= CACHE_HYSTERESIS);
|
|
dup = (entry->cache_count >= 0);
|
|
if((!dup && !near_thresh) || far_thresh) {
|
|
int type = sym->type;
|
|
int h = _zbar_get_symbol_hash(type);
|
|
entry->cache_count = -iscn->sym_configs[0][h];
|
|
}
|
|
else if(dup || near_thresh)
|
|
entry->cache_count++;
|
|
|
|
sym->cache_count = entry->cache_count;
|
|
}
|
|
else
|
|
sym->cache_count = 0;
|
|
}
|
|
|
|
void _zbar_image_scanner_add_sym(zbar_image_scanner_t *iscn,
|
|
zbar_symbol_t *sym)
|
|
{
|
|
zbar_symbol_set_t *syms;
|
|
cache_sym(iscn, sym);
|
|
|
|
syms = iscn->syms;
|
|
if(sym->cache_count || !syms->tail) {
|
|
sym->next = syms->head;
|
|
syms->head = sym;
|
|
}
|
|
else {
|
|
sym->next = syms->tail->next;
|
|
syms->tail->next = sym;
|
|
}
|
|
|
|
if(!sym->cache_count)
|
|
syms->nsyms++;
|
|
else if(!syms->tail)
|
|
syms->tail = sym;
|
|
|
|
_zbar_symbol_refcnt(sym, 1);
|
|
}
|
|
|
|
#ifdef ENABLE_QRCODE
|
|
extern qr_finder_line *_zbar_decoder_get_qr_finder_line(zbar_decoder_t*);
|
|
|
|
# define QR_FIXED(v, rnd) ((((v) << 1) + (rnd)) << (QR_FINDER_SUBPREC - 1))
|
|
# define PRINT_FIXED(val, prec) \
|
|
((val) >> (prec)), \
|
|
(1000 * ((val) & ((1 << (prec)) - 1)) / (1 << (prec)))
|
|
|
|
static inline void qr_handler (zbar_image_scanner_t *iscn)
|
|
{
|
|
unsigned u;
|
|
int vert;
|
|
qr_finder_line *line = _zbar_decoder_get_qr_finder_line(iscn->dcode);
|
|
assert(line);
|
|
u = zbar_scanner_get_edge(iscn->scn, line->pos[0],
|
|
QR_FINDER_SUBPREC);
|
|
line->boffs = u - zbar_scanner_get_edge(iscn->scn, line->boffs,
|
|
QR_FINDER_SUBPREC);
|
|
line->len = zbar_scanner_get_edge(iscn->scn, line->len,
|
|
QR_FINDER_SUBPREC);
|
|
line->eoffs = zbar_scanner_get_edge(iscn->scn, line->eoffs,
|
|
QR_FINDER_SUBPREC) - line->len;
|
|
line->len -= u;
|
|
|
|
u = QR_FIXED(iscn->umin, 0) + iscn->du * u;
|
|
if(iscn->du < 0) {
|
|
int tmp = line->boffs;
|
|
line->boffs = line->eoffs;
|
|
line->eoffs = tmp;
|
|
u -= line->len;
|
|
}
|
|
vert = !iscn->dx;
|
|
line->pos[vert] = u;
|
|
line->pos[!vert] = QR_FIXED(iscn->v, 1);
|
|
|
|
_zbar_qr_found_line(iscn->qr, vert, line);
|
|
}
|
|
#endif
|
|
|
|
static void symbol_handler (zbar_decoder_t *dcode)
|
|
{
|
|
zbar_image_scanner_t *iscn = zbar_decoder_get_userdata(dcode);
|
|
zbar_symbol_type_t type = zbar_decoder_get_type(dcode);
|
|
int x = 0, y = 0, dir;
|
|
const char *data;
|
|
unsigned datalen;
|
|
zbar_symbol_t *sym;
|
|
//printf("%s %d\r\n",__func__,__LINE__);
|
|
|
|
#ifdef ENABLE_QRCODE
|
|
if(type == ZBAR_QRCODE) {
|
|
qr_handler(iscn);
|
|
|
|
//printf("%s %d\r\n",__func__,__LINE__);
|
|
return;
|
|
}
|
|
#else
|
|
assert(type != ZBAR_QRCODE);
|
|
#endif
|
|
//printf("%s %d\r\n",__func__,__LINE__);
|
|
|
|
if(TEST_CFG(iscn, ZBAR_CFG_POSITION)) {
|
|
/* tmp position fixup */
|
|
int w = zbar_scanner_get_width(iscn->scn);
|
|
int u = iscn->umin + iscn->du * zbar_scanner_get_edge(iscn->scn, w, 0);
|
|
if(iscn->dx) {
|
|
x = u;
|
|
y = iscn->v;
|
|
}
|
|
else {
|
|
x = iscn->v;
|
|
y = u;
|
|
}
|
|
}
|
|
|
|
/* FIXME debug flag to save/display all PARTIALs */
|
|
if(type <= ZBAR_PARTIAL) {
|
|
zprintf(256, "partial symbol @(%d,%d)\n", x, y);
|
|
return;
|
|
}
|
|
|
|
data = zbar_decoder_get_data(dcode);
|
|
datalen = zbar_decoder_get_data_length(dcode);
|
|
|
|
/* FIXME need better symbol matching */
|
|
for(sym = iscn->syms->head; sym; sym = sym->next)
|
|
if(sym->type == type &&
|
|
sym->datalen == datalen &&
|
|
!memcmp(sym->data, data, datalen)) {
|
|
sym->quality++;
|
|
zprintf(224, "dup symbol @(%d,%d): dup %s: %.20s\n",
|
|
x, y, zbar_get_symbol_name(type), data);
|
|
//printf("%s %d\r\n",__func__,__LINE__);
|
|
if(TEST_CFG(iscn, ZBAR_CFG_POSITION))
|
|
/* add new point to existing set */
|
|
/* FIXME should be polygon */
|
|
sym_add_point(sym, x, y);
|
|
|
|
//printf("%s %d\r\n",__func__,__LINE__);
|
|
return;
|
|
}
|
|
|
|
sym = _zbar_image_scanner_alloc_sym(iscn, type, datalen + 1);
|
|
sym->configs = zbar_decoder_get_configs(dcode, type);
|
|
sym->modifiers = zbar_decoder_get_modifiers(dcode);
|
|
/* FIXME grab decoder buffer */
|
|
memcpy(sym->data, data, datalen + 1);
|
|
|
|
/* initialize first point */
|
|
if(TEST_CFG(iscn, ZBAR_CFG_POSITION)) {
|
|
zprintf(192, "new symbol @(%d,%d): %s: %.20s\n",
|
|
x, y, zbar_get_symbol_name(type), data);
|
|
sym_add_point(sym, x, y);
|
|
}
|
|
|
|
dir = zbar_decoder_get_direction(dcode);
|
|
if(dir)
|
|
sym->orient = (iscn->dy != 0) + ((iscn->du ^ dir) & 2);
|
|
|
|
_zbar_image_scanner_add_sym(iscn, sym);
|
|
}
|
|
|
|
zbar_image_scanner_t *zbar_image_scanner_create ()
|
|
{
|
|
zbar_image_scanner_t *iscn = calloc(1, sizeof(zbar_image_scanner_t));
|
|
if(!iscn)
|
|
return(NULL);
|
|
iscn->dcode = zbar_decoder_create();
|
|
iscn->scn = zbar_scanner_create(iscn->dcode);
|
|
if(!iscn->dcode || !iscn->scn) {
|
|
zbar_image_scanner_destroy(iscn);
|
|
return(NULL);
|
|
}
|
|
zbar_decoder_set_userdata(iscn->dcode, iscn);
|
|
zbar_decoder_set_handler(iscn->dcode, symbol_handler);
|
|
|
|
#ifdef ENABLE_QRCODE
|
|
iscn->qr = _zbar_qr_create();
|
|
#endif
|
|
|
|
/* apply default configuration */
|
|
CFG(iscn, ZBAR_CFG_X_DENSITY) = 1;
|
|
CFG(iscn, ZBAR_CFG_Y_DENSITY) = 1;
|
|
zbar_image_scanner_set_config(iscn, 0, ZBAR_CFG_POSITION, 1);
|
|
zbar_image_scanner_set_config(iscn, 0, ZBAR_CFG_UNCERTAINTY, 2);
|
|
zbar_image_scanner_set_config(iscn, ZBAR_QRCODE, ZBAR_CFG_UNCERTAINTY, 0);
|
|
zbar_image_scanner_set_config(iscn, ZBAR_CODE128, ZBAR_CFG_UNCERTAINTY, 0);
|
|
zbar_image_scanner_set_config(iscn, ZBAR_CODE93, ZBAR_CFG_UNCERTAINTY, 0);
|
|
zbar_image_scanner_set_config(iscn, ZBAR_CODE39, ZBAR_CFG_UNCERTAINTY, 0);
|
|
zbar_image_scanner_set_config(iscn, ZBAR_CODABAR, ZBAR_CFG_UNCERTAINTY, 1);
|
|
zbar_image_scanner_set_config(iscn, ZBAR_COMPOSITE, ZBAR_CFG_UNCERTAINTY, 0);
|
|
return(iscn);
|
|
}
|
|
|
|
#ifndef NO_STATS
|
|
static inline void dump_stats (const zbar_image_scanner_t *iscn)
|
|
{
|
|
int i;
|
|
zprintf(1, "symbol sets allocated = %-4d\n", iscn->stat_syms_new);
|
|
zprintf(1, " scanner syms in use = %-4d\trecycled = %-4d\n",
|
|
iscn->stat_iscn_syms_inuse, iscn->stat_iscn_syms_recycle);
|
|
zprintf(1, " image syms in use = %-4d\trecycled = %-4d\n",
|
|
iscn->stat_img_syms_inuse, iscn->stat_img_syms_recycle);
|
|
zprintf(1, "symbols allocated = %-4d\n", iscn->stat_sym_new);
|
|
for(i = 0; i < RECYCLE_BUCKETS; i++)
|
|
zprintf(1, " recycled[%d] = %-4d\n",
|
|
i, iscn->stat_sym_recycle[i]);
|
|
}
|
|
#endif
|
|
|
|
void zbar_image_scanner_destroy (zbar_image_scanner_t *iscn)
|
|
{
|
|
int i;
|
|
dump_stats(iscn);
|
|
if(iscn->syms) {
|
|
if(iscn->syms->refcnt)
|
|
zbar_symbol_set_ref(iscn->syms, -1);
|
|
else
|
|
_zbar_symbol_set_free(iscn->syms);
|
|
iscn->syms = NULL;
|
|
}
|
|
if(iscn->scn)
|
|
zbar_scanner_destroy(iscn->scn);
|
|
iscn->scn = NULL;
|
|
if(iscn->dcode)
|
|
zbar_decoder_destroy(iscn->dcode);
|
|
iscn->dcode = NULL;
|
|
for(i = 0; i < RECYCLE_BUCKETS; i++) {
|
|
zbar_symbol_t *sym, *next;
|
|
for(sym = iscn->recycle[i].head; sym; sym = next) {
|
|
next = sym->next;
|
|
_zbar_symbol_free(sym);
|
|
}
|
|
}
|
|
#ifdef ENABLE_QRCODE
|
|
if(iscn->qr) {
|
|
_zbar_qr_destroy(iscn->qr);
|
|
iscn->qr = NULL;
|
|
}
|
|
#endif
|
|
free(iscn);
|
|
}
|
|
|
|
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)
|
|
{
|
|
zbar_image_data_handler_t *result = iscn->handler;
|
|
iscn->handler = handler;
|
|
iscn->userdata = userdata;
|
|
return(result);
|
|
}
|
|
|
|
int zbar_image_scanner_set_config (zbar_image_scanner_t *iscn,
|
|
zbar_symbol_type_t sym,
|
|
zbar_config_t cfg,
|
|
int val)
|
|
{
|
|
if((sym == 0 || sym == ZBAR_COMPOSITE) && cfg == ZBAR_CFG_ENABLE) {
|
|
iscn->ean_config = !!val;
|
|
if(sym)
|
|
return(0);
|
|
}
|
|
|
|
if(cfg < ZBAR_CFG_UNCERTAINTY)
|
|
return(zbar_decoder_set_config(iscn->dcode, sym, cfg, val));
|
|
|
|
if(cfg < ZBAR_CFG_POSITION) {
|
|
int c, i;
|
|
if(cfg > ZBAR_CFG_UNCERTAINTY)
|
|
return(1);
|
|
c = cfg - ZBAR_CFG_UNCERTAINTY;
|
|
if(sym > ZBAR_PARTIAL) {
|
|
i = _zbar_get_symbol_hash(sym);
|
|
iscn->sym_configs[c][i] = val;
|
|
}
|
|
else
|
|
for(i = 0; i < NUM_SYMS; i++)
|
|
iscn->sym_configs[c][i] = val;
|
|
return(0);
|
|
}
|
|
|
|
if(sym > ZBAR_PARTIAL)
|
|
return(1);
|
|
|
|
if(cfg >= ZBAR_CFG_X_DENSITY && cfg <= ZBAR_CFG_Y_DENSITY) {
|
|
CFG(iscn, cfg) = val;
|
|
return(0);
|
|
}
|
|
|
|
if(cfg > ZBAR_CFG_POSITION)
|
|
return(1);
|
|
cfg -= ZBAR_CFG_POSITION;
|
|
|
|
if(!val)
|
|
iscn->config &= ~(1 << cfg);
|
|
else if(val == 1)
|
|
iscn->config |= (1 << cfg);
|
|
else
|
|
return(1);
|
|
|
|
return(0);
|
|
}
|
|
|
|
void zbar_image_scanner_enable_cache (zbar_image_scanner_t *iscn,
|
|
int enable)
|
|
{
|
|
if(iscn->cache) {
|
|
/* recycle all cached syms */
|
|
_zbar_image_scanner_recycle_syms(iscn, iscn->cache);
|
|
iscn->cache = NULL;
|
|
}
|
|
iscn->enable_cache = (enable) ? 1 : 0;
|
|
}
|
|
|
|
const zbar_symbol_set_t *
|
|
zbar_image_scanner_get_results (const zbar_image_scanner_t *iscn)
|
|
{
|
|
return(iscn->syms);
|
|
}
|
|
|
|
static inline void quiet_border (zbar_image_scanner_t *iscn)
|
|
{
|
|
/* flush scanner pipeline */
|
|
zbar_scanner_t *scn = iscn->scn;
|
|
zbar_scanner_flush(scn);
|
|
zbar_scanner_flush(scn);
|
|
zbar_scanner_new_scan(scn);
|
|
}
|
|
|
|
#define movedelta(dx, dy) do { \
|
|
x += (dx); \
|
|
y += (dy); \
|
|
p += (dx) + ((uintptr_t)(dy) * w); \
|
|
} while(0);
|
|
|
|
int zbar_scan_image (zbar_image_scanner_t *iscn,
|
|
zbar_image_t *img)
|
|
{
|
|
zbar_symbol_set_t *syms;
|
|
const uint8_t *data;
|
|
zbar_scanner_t *scn = iscn->scn;
|
|
unsigned w, h, cx1, cy1;
|
|
int density;
|
|
if(!img->tmp_buf)
|
|
{
|
|
img->syms = 0;
|
|
printf("%s:%d not alloc space\n",__FUNCTION__,__LINE__);
|
|
return 0;
|
|
}
|
|
/* timestamp image
|
|
* FIXME prefer video timestamp
|
|
*/
|
|
iscn->time = 0;
|
|
|
|
#ifdef ENABLE_QRCODE
|
|
_zbar_qr_reset(iscn->qr);
|
|
#endif
|
|
|
|
/* image must be in grayscale format */
|
|
if(img->format != fourcc('Y','8','0','0') &&
|
|
img->format != fourcc('G','R','E','Y'))
|
|
return(-1);
|
|
iscn->img = img;
|
|
|
|
/* recycle previous scanner and image results */
|
|
zbar_image_scanner_recycle_image(iscn, img);
|
|
syms = iscn->syms;
|
|
if(!syms) {
|
|
syms = iscn->syms = _zbar_symbol_set_create();
|
|
STAT(syms_new);
|
|
zbar_symbol_set_ref(syms, 1);
|
|
}
|
|
else
|
|
zbar_symbol_set_ref(syms, 2);
|
|
img->syms = syms;
|
|
|
|
w = img->width;
|
|
h = img->height;
|
|
cx1 = img->crop_x + img->crop_w;
|
|
assert(cx1 <= w);
|
|
cy1 = img->crop_y + img->crop_h;
|
|
assert(cy1 <= h);
|
|
data = img->data;
|
|
|
|
zbar_image_write_png(img, "debug.png");
|
|
svg_open("debug.svg", 0, 0, w, h);
|
|
svg_image("debug.png", w, h);
|
|
|
|
zbar_scanner_new_scan(scn);
|
|
|
|
density = CFG(iscn, ZBAR_CFG_Y_DENSITY);
|
|
if(density > 0) {
|
|
const uint8_t *p = data;
|
|
int x = 0, y = 0;
|
|
|
|
int border = (((img->crop_h - 1) % density) + 1) / 2;
|
|
if(border > img->crop_h / 2)
|
|
border = img->crop_h / 2;
|
|
border += img->crop_y;
|
|
assert(border <= h);
|
|
svg_group_start("scanner", 0, 1, 1, 0, 0);
|
|
iscn->dy = 0;
|
|
|
|
movedelta(img->crop_x, border);
|
|
iscn->v = y;
|
|
|
|
while(y < cy1) {
|
|
int cx0 = img->crop_x;;
|
|
zprintf(128, "img_x+: %04d,%04d @%p\n", x, y, p);
|
|
svg_path_start("vedge", 1. / 32, 0, y + 0.5);
|
|
iscn->dx = iscn->du = 1;
|
|
iscn->umin = cx0;
|
|
while(x < cx1) {
|
|
uint8_t d = *p;
|
|
movedelta(1, 0);
|
|
zbar_scan_y(scn, d);
|
|
}
|
|
ASSERT_POS;
|
|
quiet_border(iscn);
|
|
svg_path_end();
|
|
|
|
movedelta(-1, density);
|
|
iscn->v = y;
|
|
if(y >= cy1)
|
|
break;
|
|
|
|
zprintf(128, "img_x-: %04d,%04d @%p\n", x, y, p);
|
|
svg_path_start("vedge", -1. / 32, w, y + 0.5);
|
|
iscn->dx = iscn->du = -1;
|
|
iscn->umin = cx1;
|
|
while(x >= cx0) {
|
|
uint8_t d = *p;
|
|
movedelta(-1, 0);
|
|
zbar_scan_y(scn, d);
|
|
}
|
|
ASSERT_POS;
|
|
quiet_border(iscn);
|
|
svg_path_end();
|
|
|
|
movedelta(1, density);
|
|
iscn->v = y;
|
|
}
|
|
svg_group_end();
|
|
}
|
|
iscn->dx = 0;
|
|
|
|
density = CFG(iscn, ZBAR_CFG_X_DENSITY);
|
|
if(density > 0) {
|
|
const uint8_t *p = data;
|
|
int x = 0, y = 0;
|
|
|
|
int border = (((img->crop_w - 1) % density) + 1) / 2;
|
|
if(border > img->crop_w / 2)
|
|
border = img->crop_w / 2;
|
|
border += img->crop_x;
|
|
assert(border <= w);
|
|
svg_group_start("scanner", 90, 1, -1, 0, 0);
|
|
movedelta(border, img->crop_y);
|
|
iscn->v = x;
|
|
|
|
while(x < cx1) {
|
|
int cy0 = img->crop_y;
|
|
zprintf(128, "img_y+: %04d,%04d @%p\n", x, y, p);
|
|
svg_path_start("vedge", 1. / 32, 0, x + 0.5);
|
|
iscn->dy = iscn->du = 1;
|
|
iscn->umin = cy0;
|
|
while(y < cy1) {
|
|
uint8_t d = *p;
|
|
movedelta(0, 1);
|
|
zbar_scan_y(scn, d);
|
|
}
|
|
ASSERT_POS;
|
|
quiet_border(iscn);
|
|
svg_path_end();
|
|
|
|
movedelta(density, -1);
|
|
iscn->v = x;
|
|
if(x >= cx1)
|
|
break;
|
|
|
|
zprintf(128, "img_y-: %04d,%04d @%p\n", x, y, p);
|
|
svg_path_start("vedge", -1. / 32, h, x + 0.5);
|
|
iscn->dy = iscn->du = -1;
|
|
iscn->umin = cy1;
|
|
while(y >= cy0) {
|
|
uint8_t d = *p;
|
|
movedelta(0, -1);
|
|
zbar_scan_y(scn, d);
|
|
}
|
|
ASSERT_POS;
|
|
quiet_border(iscn);
|
|
svg_path_end();
|
|
|
|
movedelta(density, 1);
|
|
iscn->v = x;
|
|
}
|
|
svg_group_end();
|
|
}
|
|
iscn->dy = 0;
|
|
iscn->img = NULL;
|
|
|
|
#ifdef ENABLE_QRCODE
|
|
_zbar_qr_decode(iscn->qr, iscn, img);
|
|
#endif
|
|
|
|
/* FIXME tmp hack to filter bad EAN results */
|
|
/* FIXME tmp hack to merge simple case EAN add-ons */
|
|
char filter = (!iscn->enable_cache &&
|
|
(density == 1 || CFG(iscn, ZBAR_CFG_Y_DENSITY) == 1));
|
|
int nean = 0, naddon = 0;
|
|
if(syms->nsyms) {
|
|
zbar_symbol_t **symp;
|
|
for(symp = &syms->head; *symp; ) {
|
|
zbar_symbol_t *sym = *symp;
|
|
if(sym->cache_count <= 0 &&
|
|
((sym->type < ZBAR_COMPOSITE && sym->type > ZBAR_PARTIAL) ||
|
|
sym->type == ZBAR_DATABAR ||
|
|
sym->type == ZBAR_DATABAR_EXP ||
|
|
sym->type == ZBAR_CODABAR))
|
|
{
|
|
if((sym->type == ZBAR_CODABAR || filter) && sym->quality < 4) {
|
|
if(iscn->enable_cache) {
|
|
/* revert cache update */
|
|
zbar_symbol_t *entry = cache_lookup(iscn, sym);
|
|
if(entry)
|
|
entry->cache_count--;
|
|
else
|
|
assert(0);
|
|
}
|
|
|
|
/* recycle */
|
|
*symp = sym->next;
|
|
syms->nsyms--;
|
|
sym->next = NULL;
|
|
_zbar_image_scanner_recycle_syms(iscn, sym);
|
|
continue;
|
|
}
|
|
else if(sym->type < ZBAR_COMPOSITE &&
|
|
sym->type != ZBAR_ISBN10)
|
|
{
|
|
if(sym->type > ZBAR_EAN5)
|
|
nean++;
|
|
else
|
|
naddon++;
|
|
}
|
|
}
|
|
symp = &sym->next;
|
|
}
|
|
|
|
if(nean == 1 && naddon == 1 && iscn->ean_config) {
|
|
/* create container symbol for composite result */
|
|
zbar_symbol_t *ean = NULL, *addon = NULL;
|
|
for(symp = &syms->head; *symp; ) {
|
|
zbar_symbol_t *sym = *symp;
|
|
if(sym->type < ZBAR_COMPOSITE && sym->type > ZBAR_PARTIAL) {
|
|
/* move to composite */
|
|
*symp = sym->next;
|
|
syms->nsyms--;
|
|
sym->next = NULL;
|
|
if(sym->type <= ZBAR_EAN5)
|
|
addon = sym;
|
|
else
|
|
ean = sym;
|
|
}
|
|
else
|
|
symp = &sym->next;
|
|
}
|
|
assert(ean);
|
|
assert(addon);
|
|
|
|
int datalen = ean->datalen + addon->datalen + 1;
|
|
zbar_symbol_t *ean_sym =
|
|
_zbar_image_scanner_alloc_sym(iscn, ZBAR_COMPOSITE, datalen);
|
|
ean_sym->orient = ean->orient;
|
|
ean_sym->syms = _zbar_symbol_set_create();
|
|
memcpy(ean_sym->data, ean->data, ean->datalen);
|
|
memcpy(ean_sym->data + ean->datalen,
|
|
addon->data, addon->datalen + 1);
|
|
ean_sym->syms->head = ean;
|
|
ean->next = addon;
|
|
ean_sym->syms->nsyms = 2;
|
|
_zbar_image_scanner_add_sym(iscn, ean_sym);
|
|
}
|
|
}
|
|
|
|
if(syms->nsyms && iscn->handler)
|
|
iscn->handler(img, iscn->userdata);
|
|
|
|
svg_close();
|
|
return(syms->nsyms);
|
|
}
|
|
|
|
#ifdef DEBUG_SVG
|
|
/* FIXME lame...*/
|
|
# include "svg.c"
|
|
#endif
|