diff options
Diffstat (limited to 'zbar/img_scanner.c')
-rw-r--r-- | zbar/img_scanner.c | 1174 |
1 files changed, 1174 insertions, 0 deletions
diff --git a/zbar/img_scanner.c b/zbar/img_scanner.c new file mode 100644 index 0000000..d1bf8a3 --- /dev/null +++ b/zbar/img_scanner.c @@ -0,0 +1,1174 @@ +/*------------------------------------------------------------------------ + * 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 "config.h" +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif +#ifdef HAVE_DBUS +#include <dbus/dbus.h> +#endif + +#include <assert.h> +#include <stdlib.h> /* malloc, free */ +#include <string.h> /* memcmp, memset, memcpy */ + +#include <zbar.h> +#include "error.h" +#include "image.h" +#include "timer.h" +#if ENABLE_QRCODE == 1 +#include "qrcode.h" +#endif +#if ENABLE_SQCODE == 1 +#include "sqcode.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) + +#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 */ +#if ENABLE_QRCODE == 1 + qr_reader *qr; /* QR Code 2D reader */ +#endif +#if ENABLE_SQCODE == 1 + sq_reader *sq; /* SQ 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 + +#ifdef HAVE_DBUS + int is_dbus_enabled; /* dbus enabled flag */ +#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); +} + +#if ENABLE_QRCODE == 1 +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 + +#if ENABLE_SQCODE == 1 +extern unsigned _zbar_decoder_get_sq_finder_config(zbar_decoder_t *); + +static void sq_handler(zbar_image_scanner_t *iscn) +{ + unsigned config = _zbar_decoder_get_sq_finder_config(iscn->dcode); + _zbar_sq_new_config(iscn->sq, config); +} +#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; + +#if ENABLE_QRCODE == 1 + if (type == ZBAR_QRCODE) { + qr_handler(iscn); + return; + } +#else + assert(type != ZBAR_QRCODE); +#endif + + 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); + if (TEST_CFG(iscn, ZBAR_CFG_POSITION)) + /* add new point to existing set */ + /* FIXME should be polygon */ + sym_add_point(sym, x, y); + 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); + +#if ENABLE_QRCODE == 1 + iscn->qr = _zbar_qr_create(); +#endif + +#if ENABLE_SQCODE == 1 + iscn->sq = _zbar_sq_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, 0, ZBAR_CFG_TEST_INVERTED, 0); + zbar_image_scanner_set_config(iscn, ZBAR_QRCODE, ZBAR_CFG_UNCERTAINTY, 0); + zbar_image_scanner_set_config(iscn, ZBAR_QRCODE, ZBAR_CFG_BINARY, 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); + } + } +#if ENABLE_QRCODE == 1 + if (iscn->qr) { + _zbar_qr_destroy(iscn->qr); + iscn->qr = NULL; + } +#endif +#if ENABLE_SQCODE == 1 + if (iscn->sq) { + _zbar_sq_destroy(iscn->sq); + iscn->sq = 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); + } + + /* Image scanner parameters apply only to ZBAR_PARTIAL */ + if (sym > ZBAR_PARTIAL) + return (1); + + if (cfg >= ZBAR_CFG_X_DENSITY && cfg <= ZBAR_CFG_Y_DENSITY) { + CFG(iscn, cfg) = val; + return (0); + } + + cfg -= ZBAR_CFG_POSITION; + + if (!val) + iscn->config &= ~(1 << cfg); + else if (val == 1) + iscn->config |= (1 << cfg); + else + return (1); + + return (0); +} + +int zbar_image_scanner_get_config(zbar_image_scanner_t *iscn, + zbar_symbol_type_t sym, zbar_config_t cfg, + int *val) +{ + /* Return error if symbol doesn't have config */ + if (sym < ZBAR_PARTIAL || sym > ZBAR_CODE128 || sym == ZBAR_COMPOSITE) + return 1; + + if (cfg < ZBAR_CFG_UNCERTAINTY) + return zbar_decoder_get_config(iscn->dcode, sym, cfg, val); + + if (cfg < ZBAR_CFG_POSITION) { + int i; + if (sym == ZBAR_PARTIAL) + return (1); + + i = _zbar_get_symbol_hash(sym); + + *val = iscn->sym_configs[cfg - ZBAR_CFG_UNCERTAINTY][i]; + return 0; + } + + /* Image scanner parameters apply only to ZBAR_PARTIAL */ + if (sym > ZBAR_PARTIAL) + return (1); + + if (cfg < ZBAR_CFG_X_DENSITY) { + *val = (iscn->config & (1 << (cfg - ZBAR_CFG_POSITION))) != 0; + return 0; + } + + if (cfg <= ZBAR_CFG_Y_DENSITY) { + *val = CFG(iscn, cfg); + return 0; + } + + return 1; +} + +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); +} + +#ifdef HAVE_DBUS +static int dict_add_property(DBusMessageIter *property, const char *key, + const char *value, unsigned int value_length, + int is_binary) +{ + DBusMessageIter dict_entry, dict_val, array_val; + DBusError err; + dbus_error_init(&err); + dbus_message_iter_open_container(property, DBUS_TYPE_DICT_ENTRY, NULL, + &dict_entry); + if (!dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &key)) { + fprintf(stderr, "Key Error\n"); + dbus_message_iter_close_container(property, &dict_entry); + goto error; + } + + if (is_binary) { + dbus_message_iter_open_container(&dict_entry, DBUS_TYPE_VARIANT, "ay", + &dict_val); + dbus_message_iter_open_container(&dict_val, DBUS_TYPE_ARRAY, "y", + &array_val); + if (!dbus_message_iter_append_fixed_array(&array_val, DBUS_TYPE_BYTE, + &value, value_length)) { + fprintf(stderr, "Byte Array Value Error\n"); + dbus_message_iter_close_container(&dict_val, &array_val); + dbus_message_iter_close_container(&dict_entry, &dict_val); + dbus_message_iter_close_container(property, &dict_entry); + goto error; + } + } else { + dbus_message_iter_open_container(&dict_entry, DBUS_TYPE_VARIANT, + DBUS_TYPE_STRING_AS_STRING, &dict_val); + if (!dbus_message_iter_append_basic(&dict_val, DBUS_TYPE_STRING, + &value)) { + fprintf(stderr, "String Value Error\n"); + dbus_message_iter_close_container(&dict_entry, &dict_val); + dbus_message_iter_close_container(property, &dict_entry); + goto error; + } + } + + if (is_binary) + dbus_message_iter_close_container(&dict_val, &array_val); + dbus_message_iter_close_container(&dict_entry, &dict_val); + dbus_message_iter_close_container(property, &dict_entry); + return (1); + +error: + if (dbus_error_is_set(&err)) { + fprintf(stderr, "Name Error (%s)\n", err.message); + dbus_error_free(&err); + } + return (0); +} + +static void zbar_send_dbus(int type, const char *sigvalue, unsigned int length, + int is_binary) +{ + DBusMessage *msg; + DBusMessageIter args, dict; + DBusConnection *conn; + const char *type_name; + const char *value_key = is_binary ? "BinaryData" : "Data"; + DBusError err; + int ret; + dbus_uint32_t serial = 0; + + // initialise the error value + dbus_error_init(&err); + + // connect to the DBUS system bus, and check for errors + conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err); + if (dbus_error_is_set(&err)) { + fprintf(stderr, "Connection Error (%s)\n", err.message); + dbus_error_free(&err); + } + if (NULL == conn) { + fprintf(stderr, "Connection Null\n"); + return; + } + + // register our name on the bus, and check for errors + ret = dbus_bus_request_name(conn, "org.linuxtv.Zbar", + DBUS_NAME_FLAG_REPLACE_EXISTING, &err); + if (dbus_error_is_set(&err)) { + fprintf(stderr, "Name Error (%s)\n", err.message); + dbus_error_free(&err); + } + if (DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret) { + return; + } + + // create a signal & check for errors + msg = dbus_message_new_signal( + "/org/linuxtv/Zbar1/Code", // object name of the signal + "org.linuxtv.Zbar1.Code", // interface name of the signal + "Code"); // name of the signal + if (NULL == msg) { + fprintf(stderr, "Message Null\n"); + return; + } + + // append arguments onto signal + dbus_message_iter_init_append(msg, &args); + if (!dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "{sv}", + &dict)) { + fprintf(stderr, "Out Of Dict Container Memory!\n"); + dbus_message_unref(msg); + return; + } + + type_name = zbar_get_symbol_name(type); + if (!dict_add_property(&dict, "Type", type_name, 0, 0)) { + fprintf(stderr, "Out Of Property Memory!\n"); + dbus_message_unref(msg); + return; + } + + if (!dict_add_property(&dict, value_key, sigvalue, length, is_binary)) { + fprintf(stderr, "Out Of Property Memory!\n"); + dbus_message_unref(msg); + return; + } + + dbus_message_iter_close_container(&args, &dict); + + // send the message and flush the connection + if (!dbus_connection_send(conn, msg, &serial)) { + fprintf(stderr, "Out Of Memory!\n"); + dbus_message_unref(msg); + return; + } + + dbus_connection_flush(conn); + dbus_bus_release_name(conn, "org.linuxtv.Zbar", &err); + if (dbus_error_is_set(&err)) { + fprintf(stderr, "Name Release Error (%s)\n", err.message); + dbus_error_free(&err); + } + + // free the message + dbus_message_unref(msg); +} + +static void zbar_send_code_via_dbus(zbar_image_scanner_t *iscn, + zbar_image_t *img) +{ + const zbar_symbol_t *sym = zbar_image_first_symbol(img); + + if (!sym) + return; + for (; sym; sym = zbar_symbol_next(sym)) { + if (zbar_symbol_get_count(sym)) + continue; + + zbar_symbol_type_t type = zbar_symbol_get_type(sym); + if (type == ZBAR_PARTIAL) + continue; + + int is_binary = 0; + zbar_image_scanner_get_config(iscn, type, ZBAR_CFG_BINARY, &is_binary); + + zbar_send_dbus(type, zbar_symbol_get_data(sym), + zbar_symbol_get_data_length(sym), is_binary); + } +} +#endif + +#define movedelta(dx, dy) \ + do { \ + x += (dx); \ + y += (dy); \ + p += (dx) + ((uintptr_t)(dy)*w); \ + } while (0); + +static void *_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; + char filter; + int nean, naddon; + + /* timestamp image + * FIXME prefer video timestamp + */ + iscn->time = _zbar_timer_now(); + +#if ENABLE_QRCODE == 1 + _zbar_qr_reset(iscn->qr); +#endif + +#if ENABLE_SQCODE == 1 + _zbar_sq_reset(iscn->sq); +#endif + + /* image must be in grayscale format */ + if (img->format != fourcc('Y', '8', '0', '0') && + img->format != fourcc('G', 'R', 'E', 'Y')) + return NULL; + 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; + +#if ENABLE_QRCODE == 1 + _zbar_qr_decode(iscn->qr, iscn, img); +#endif + +#if ENABLE_SQCODE == 1 + sq_handler(iscn); + _zbar_sq_decode(iscn->sq, iscn, img); +#endif + + /* FIXME tmp hack to filter bad EAN results */ + /* FIXME tmp hack to merge simple case EAN add-ons */ + filter = (!iscn->enable_cache && + (density == 1 || CFG(iscn, ZBAR_CFG_Y_DENSITY) == 1)); + 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) { + int datalen; + zbar_symbol_t *ean_sym; + /* 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); + + datalen = ean->datalen + addon->datalen + 1; + 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); + } + } + return syms; +} + +int zbar_scan_image(zbar_image_scanner_t *iscn, zbar_image_t *img) +{ + zbar_symbol_set_t *syms; + zbar_image_t *inv = NULL; + + syms = _zbar_scan_image(iscn, img); + if (!syms) + return -1; + + if (!syms->nsyms && TEST_CFG(iscn, ZBAR_CFG_TEST_INVERTED)) { + inv = _zbar_image_copy(img, 1); + if (inv) { + if (iscn->cache) { + /* recycle all cached syms, if any */ + _zbar_image_scanner_recycle_syms(iscn, iscn->cache); + iscn->cache = NULL; + } + syms = _zbar_scan_image(iscn, inv); + _zbar_image_swap_symbols(img, inv); + } + } + + if (syms->nsyms && iscn->handler) + iscn->handler(img, iscn->userdata); +#ifdef HAVE_DBUS + if (iscn->is_dbus_enabled) + zbar_send_code_via_dbus(iscn, img); +#endif + + svg_close(); + + if (inv) + zbar_image_destroy(inv); + + return (syms->nsyms); +} + +int zbar_image_scanner_request_dbus(zbar_image_scanner_t *scanner, + int req_dbus_enabled) +{ +#ifdef HAVE_DBUS + scanner->is_dbus_enabled = req_dbus_enabled; + return 0; +#else + return 1; +#endif +} + +#ifdef DEBUG_SVG +/* FIXME lame...*/ +#include "svg.c" +#endif |