diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-03-09 00:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-03-09 00:06:44 +0000 |
commit | 44cf8ec67278bd1ab6c7f83a9993f7a5686a9541 (patch) | |
tree | 5eec4b0d1a3f163d279c3c27c03324ba49fa235a /zbar | |
parent | Initial commit. (diff) | |
download | zbar-44cf8ec67278bd1ab6c7f83a9993f7a5686a9541.tar.xz zbar-44cf8ec67278bd1ab6c7f83a9993f7a5686a9541.zip |
Adding upstream version 0.23.93.upstream/0.23.93upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'zbar')
91 files changed, 28505 insertions, 0 deletions
diff --git a/zbar/Makefile.am b/zbar/Makefile.am new file mode 100644 index 0000000..ca54d01 --- /dev/null +++ b/zbar/Makefile.am @@ -0,0 +1,137 @@ +lib_LTLIBRARIES = libzbar.la +libzbar_la_CPPFLAGS = $(AM_CPPFLAGS) +libzbar_la_LDFLAGS = -no-undefined -version-info $(LIB_VERSION) \ + -export-symbols-regex "^(zbar|_zbar.*_error)_.*" $(AM_LDFLAGS) +libzbar_la_LIBADD = $(LTLIBICONV) + +libzbar_la_SOURCES = debug.h config.c \ + error.h error.c symbol.h symbol.c \ + image.h image.c convert.c \ + processor.c processor.h processor/lock.c \ + refcnt.h refcnt.c timer.h mutex.h \ + event.h thread.h \ + window.h window.c video.h video.c \ + img_scanner.h img_scanner.c scanner.c \ + decoder.h decoder.c misc.h misc.c + +EXTRA_libzbar_la_SOURCES = svg.h svg.c + +if ENABLE_EAN +libzbar_la_SOURCES += decoder/ean.h decoder/ean.c +endif +if ENABLE_DATABAR +libzbar_la_SOURCES += decoder/databar.h decoder/databar.c +endif +if ENABLE_CODE128 +libzbar_la_SOURCES += decoder/code128.h decoder/code128.c +endif +if ENABLE_CODE93 +libzbar_la_SOURCES += decoder/code93.h decoder/code93.c +endif +if ENABLE_CODE39 +libzbar_la_SOURCES += decoder/code39.h decoder/code39.c +endif +if ENABLE_CODABAR +libzbar_la_SOURCES += decoder/codabar.h decoder/codabar.c +endif +if ENABLE_I25 +libzbar_la_SOURCES += decoder/i25.h decoder/i25.c +endif +if ENABLE_PDF417 +libzbar_la_SOURCES += decoder/pdf417.h decoder/pdf417.c \ + decoder/pdf417_hash.h +endif +if ENABLE_QRCODE +libzbar_la_SOURCES += qrcode.h \ + decoder/qr_finder.h decoder/qr_finder.c \ + qrcode/qrdec.h qrcode/qrdec.c qrcode/qrdectxt.c \ + qrcode/rs.h qrcode/rs.c \ + qrcode/isaac.h qrcode/isaac.c \ + qrcode/bch15_5.h qrcode/bch15_5.c \ + qrcode/binarize.h qrcode/binarize.c \ + qrcode/util.h qrcode/util.c +endif +if ENABLE_SQCODE +libzbar_la_SOURCES += sqcode.h sqcode.c \ + decoder/sq_finder.h decoder/sq_finder.c +endif + +if WIN32 + +%-rc.o: %.rc + $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) -o $@ $< +%-rc.lo: %.rc + $(LIBTOOL) --tag=RC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) -o $@ $< + +libzbar_la_SOURCES += processor/win.c libzbar.rc +libzbar_la_CPPFLAGS += -mthreads +libzbar_la_LDFLAGS += -mthreads +# FIXME broken +libzbar_la_LIBADD += libzbar-rc.lo +else +libzbar_la_SOURCES += processor/posix.h processor/posix.c +endif + +if HAVE_V4L2 +libzbar_la_SOURCES += video/v4l.c video/v4l2.c +endif +if HAVE_V4L1 +if !HAVE_V4L2 +libzbar_la_SOURCES += video/v4l.c +endif +libzbar_la_SOURCES += video/v4l1.c +endif +if WIN32 +if HAVE_VIDEO +if !WITH_DIRECTSHOW +libzbar_la_SOURCES += video/vfw.c +libzbar_la_LIBADD += -lvfw32 +else +libzbar_la_SOURCES += video/dshow.c +libzbar_la_CPPFLAGS += -DCOBJMACROS +libzbar_la_LIBADD += -loleaut32 -lole32 +endif +endif +endif +if !HAVE_VIDEO +libzbar_la_SOURCES += video/null.c +endif + +if HAVE_LIBV4L +libzbar_la_LIBADD += $(V4L2_LIBS) +endif + +if HAVE_JPEG +libzbar_la_SOURCES += jpeg.c +endif + +if HAVE_X +libzbar_la_SOURCES += processor/x.c \ + window/x.h window/x.c window/ximage.c +libzbar_la_CPPFLAGS += $(X_CFLAGS) +libzbar_la_LDFLAGS += $(X_LIBS) +libzbar_la_LIBADD += $(X_PRE_LIBS) -lX11 $(X_EXTRA_LIBS) +if HAVE_XV +libzbar_la_SOURCES += window/xv.c +libzbar_la_LIBADD += $(XV_LIBS) +endif +else +if WIN32 +libzbar_la_SOURCES += window/win.h window/win.c \ + window/dib.c +# window/vfw.c -lvfw32 +libzbar_la_LIBADD += -lgdi32 -lwinmm +else +libzbar_la_SOURCES += processor/null.c window/null.c +endif +endif + +if HAVE_DBUS +libzbar_la_LDFLAGS += $(DBUS_LIBS) +endif + +libzbar_la_LDFLAGS += $(AM_LDFLAGS) +libzbar_la_LIBADD += $(AM_LIBADD) diff --git a/zbar/config.c b/zbar/config.c new file mode 100644 index 0000000..1b2a39b --- /dev/null +++ b/zbar/config.c @@ -0,0 +1,171 @@ +/*------------------------------------------------------------------------ + * Copyright 2008-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" +#include <stdlib.h> /* strtol */ +#include <string.h> /* strchr, strncmp, strlen */ +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +#include <zbar.h> + +int zbar_parse_config(const char *cfgstr, zbar_symbol_type_t *sym, + zbar_config_t *cfg, int *val) +{ + const char *dot, *eq; + int len; + char negate; + if (!cfgstr) + return (1); + + dot = strchr(cfgstr, '.'); + if (dot) { + int len = dot - cfgstr; + if (!len || (len == 1 && !strncmp(cfgstr, "*", len))) + *sym = 0; + else if (len < 2) + return (1); + else if (!strncmp(cfgstr, "qrcode", len)) + *sym = ZBAR_QRCODE; + else if (!strncmp(cfgstr, "sqcode", len)) + *sym = ZBAR_SQCODE; + else if (!strncmp(cfgstr, "db", len)) + *sym = ZBAR_DATABAR; + else if (len < 3) + return (1); + else if (!strncmp(cfgstr, "upca", len)) + *sym = ZBAR_UPCA; + else if (!strncmp(cfgstr, "upce", len)) + *sym = ZBAR_UPCE; + else if (!strncmp(cfgstr, "ean13", len)) + *sym = ZBAR_EAN13; + else if (!strncmp(cfgstr, "ean8", len)) + *sym = ZBAR_EAN8; + else if (!strncmp(cfgstr, "ean5", len)) + *sym = ZBAR_EAN5; + else if (!strncmp(cfgstr, "ean2", len)) + *sym = ZBAR_EAN2; + else if (!strncmp(cfgstr, "composite", len)) + *sym = ZBAR_COMPOSITE; + else if (!strncmp(cfgstr, "i25", len)) + *sym = ZBAR_I25; + else if (len < 4) + return (1); + else if (!strncmp(cfgstr, "scanner", len)) + *sym = ZBAR_PARTIAL; /* FIXME lame */ + else if (!strncmp(cfgstr, "isbn13", len)) + *sym = ZBAR_ISBN13; + else if (!strncmp(cfgstr, "isbn10", len)) + *sym = ZBAR_ISBN10; + else if (!strncmp(cfgstr, "db-exp", len)) + *sym = ZBAR_DATABAR_EXP; + else if (!strncmp(cfgstr, "codabar", len)) + *sym = ZBAR_CODABAR; + else if (len < 6) + return (1); + else if (!strncmp(cfgstr, "code93", len)) + *sym = ZBAR_CODE93; + else if (!strncmp(cfgstr, "code39", len)) + *sym = ZBAR_CODE39; + else if (!strncmp(cfgstr, "pdf417", len)) + *sym = ZBAR_PDF417; + else if (len < 7) + return (1); + else if (!strncmp(cfgstr, "code128", len)) + *sym = ZBAR_CODE128; + else if (!strncmp(cfgstr, "databar", len)) + *sym = ZBAR_DATABAR; + else if (!strncmp(cfgstr, "databar-exp", len)) + *sym = ZBAR_DATABAR_EXP; + else + return (1); + cfgstr = dot + 1; + } else + *sym = 0; + + len = strlen(cfgstr); + eq = strchr(cfgstr, '='); + if (eq) + len = eq - cfgstr; + else + *val = 1; /* handle this here so we can override later */ + negate = 0; + + if (len > 3 && !strncmp(cfgstr, "no-", 3)) { + negate = 1; + cfgstr += 3; + len -= 3; + } + + if (len < 1) + return (1); + else if (!strncmp(cfgstr, "y-density", len)) + *cfg = ZBAR_CFG_Y_DENSITY; + else if (!strncmp(cfgstr, "x-density", len)) + *cfg = ZBAR_CFG_X_DENSITY; + else if (len < 2) + return (1); + else if (!strncmp(cfgstr, "enable", len)) + *cfg = ZBAR_CFG_ENABLE; + else if (len < 3) + return (1); + else if (!strncmp(cfgstr, "disable", len)) { + *cfg = ZBAR_CFG_ENABLE; + negate = !negate; /* no-disable ?!? */ + } else if (!strncmp(cfgstr, "min-length", len)) + *cfg = ZBAR_CFG_MIN_LEN; + else if (!strncmp(cfgstr, "max-length", len)) + *cfg = ZBAR_CFG_MAX_LEN; + else if (!strncmp(cfgstr, "ascii", len)) + *cfg = ZBAR_CFG_ASCII; + else if (!strncmp(cfgstr, "binary", len)) + *cfg = ZBAR_CFG_BINARY; + else if (!strncmp(cfgstr, "add-check", len)) + *cfg = ZBAR_CFG_ADD_CHECK; + else if (!strncmp(cfgstr, "emit-check", len)) + *cfg = ZBAR_CFG_EMIT_CHECK; + else if (!strncmp(cfgstr, "uncertainty", len)) + *cfg = ZBAR_CFG_UNCERTAINTY; + else if (!strncmp(cfgstr, "test-inverted", len)) + *cfg = ZBAR_CFG_TEST_INVERTED; + else if (!strncmp(cfgstr, "position", len)) + *cfg = ZBAR_CFG_POSITION; + else + return (1); + + if (eq) { +#ifdef HAVE_ERRNO_H + errno = 0; +#endif + *val = strtol(eq + 1, NULL, 0); +#ifdef HAVE_ERRNO_H + if (errno) + return (1); +#endif + } + if (negate) + *val = !*val; + + return (0); +} diff --git a/zbar/convert.c b/zbar/convert.c new file mode 100644 index 0000000..98cd94c --- /dev/null +++ b/zbar/convert.c @@ -0,0 +1,1229 @@ +/*------------------------------------------------------------------------ + * 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 "image.h" +#include "video.h" +#include "window.h" + +/* pack bit size and location offset of a component into one byte + */ +#define RGB_BITS(off, size) ((((8 - (size)) & 0x7) << 5) | ((off)&0x1f)) + +typedef void(conversion_handler_t)(zbar_image_t *, const zbar_format_def_t *, + const zbar_image_t *, + const zbar_format_def_t *); + +typedef struct conversion_def_s { + int cost; /* conversion "badness" */ + conversion_handler_t *func; /* function that accomplishes it */ +} conversion_def_t; + +/* NULL terminated list of known formats, in order of preference + * (NB Cr=V Cb=U) + */ +const uint32_t _zbar_formats[] = { + + /* planar YUV formats */ + fourcc('4', '2', '2', 'P'), /* FIXME also YV16? */ + fourcc('I', '4', '2', '0'), + fourcc('Y', 'U', '1', '2'), /* FIXME also IYUV? */ + fourcc('Y', 'V', '1', '2'), fourcc('4', '1', '1', 'P'), + + /* planar Y + packed UV plane */ + fourcc('N', 'V', '1', '2'), fourcc('N', 'V', '2', '1'), + + /* packed YUV formats */ + fourcc('Y', 'U', 'Y', 'V'), fourcc('U', 'Y', 'V', 'Y'), + fourcc('Y', 'U', 'Y', '2'), /* FIXME add YVYU */ + fourcc('Y', 'U', 'V', '4'), /* FIXME where is this from? */ + + /* packed rgb formats */ + fourcc('R', 'G', 'B', '3'), fourcc(3, 0, 0, 0), fourcc('B', 'G', 'R', '3'), + fourcc('R', 'G', 'B', '4'), fourcc('B', 'G', 'R', '4'), + + fourcc('R', 'G', 'B', 'P'), fourcc('R', 'G', 'B', 'O'), + fourcc('R', 'G', 'B', 'R'), fourcc('R', 'G', 'B', 'Q'), + + fourcc('Y', 'U', 'V', '9'), fourcc('Y', 'V', 'U', '9'), + + /* basic grayscale format */ + fourcc('G', 'R', 'E', 'Y'), fourcc('Y', '8', '0', '0'), + fourcc('Y', '8', ' ', ' '), fourcc('Y', '8', 0, 0), + + /* low quality RGB formats */ + fourcc('R', 'G', 'B', '1'), fourcc('R', '4', '4', '4'), + fourcc('B', 'A', '8', '1'), + + /* unsupported packed YUV formats */ + fourcc('Y', '4', '1', 'P'), fourcc('Y', '4', '4', '4'), + fourcc('Y', 'U', 'V', 'O'), fourcc('H', 'M', '1', '2'), + + /* unsupported packed RGB format */ + fourcc('H', 'I', '2', '4'), + + /* unsupported compressed formats */ + fourcc('J', 'P', 'E', 'G'), fourcc('M', 'J', 'P', 'G'), + fourcc('M', 'P', 'E', 'G'), + + /* terminator */ + 0 +}; + +const int _zbar_num_formats = sizeof(_zbar_formats) / sizeof(uint32_t); + +/* format definitions */ +static const zbar_format_def_t format_defs[] = { + + { fourcc('R', 'G', 'B', '4'), + ZBAR_FMT_RGB_PACKED, + { { 4, RGB_BITS(8, 8), RGB_BITS(16, 8), RGB_BITS(24, 8) } } }, + { fourcc('B', 'G', 'R', '1'), + ZBAR_FMT_RGB_PACKED, + { { 1, RGB_BITS(0, 3), RGB_BITS(3, 3), RGB_BITS(6, 2) } } }, + { fourcc('4', '2', '2', 'P'), ZBAR_FMT_YUV_PLANAR, { { 1, 0, 0 /*UV*/ } } }, + { + fourcc('Y', '8', '0', '0'), + ZBAR_FMT_GRAY, + }, + { fourcc('Y', 'U', 'Y', '2'), + ZBAR_FMT_YUV_PACKED, + { { 1, 0, 0, /*YUYV*/ } } }, + { + fourcc('J', 'P', 'E', 'G'), + ZBAR_FMT_JPEG, + }, + { fourcc('Y', 'V', 'Y', 'U'), + ZBAR_FMT_YUV_PACKED, + { { 1, 0, 1, /*YVYU*/ } } }, + { + fourcc('Y', '8', 0, 0), + ZBAR_FMT_GRAY, + }, + { fourcc('N', 'V', '2', '1'), ZBAR_FMT_YUV_NV, { { 1, 1, 1 /*VU*/ } } }, + { fourcc('N', 'V', '1', '2'), ZBAR_FMT_YUV_NV, { { 1, 1, 0 /*UV*/ } } }, + { fourcc('B', 'G', 'R', '3'), + ZBAR_FMT_RGB_PACKED, + { { 3, RGB_BITS(16, 8), RGB_BITS(8, 8), RGB_BITS(0, 8) } } }, + { fourcc('Y', 'V', 'U', '9'), ZBAR_FMT_YUV_PLANAR, { { 2, 2, 1 /*VU*/ } } }, + { fourcc('R', 'G', 'B', 'O'), + ZBAR_FMT_RGB_PACKED, + { { 2, RGB_BITS(10, 5), RGB_BITS(5, 5), RGB_BITS(0, 5) } } }, + { fourcc('R', 'G', 'B', 'Q'), + ZBAR_FMT_RGB_PACKED, + { { 2, RGB_BITS(2, 5), RGB_BITS(13, 5), RGB_BITS(8, 5) } } }, + { + fourcc('G', 'R', 'E', 'Y'), + ZBAR_FMT_GRAY, + }, + { fourcc(3, 0, 0, 0), + ZBAR_FMT_RGB_PACKED, + { { 4, RGB_BITS(16, 8), RGB_BITS(8, 8), RGB_BITS(0, 8) } } }, + { + fourcc('Y', '8', ' ', ' '), + ZBAR_FMT_GRAY, + }, + { fourcc('I', '4', '2', '0'), ZBAR_FMT_YUV_PLANAR, { { 1, 1, 0 /*UV*/ } } }, + { fourcc('R', 'G', 'B', '1'), + ZBAR_FMT_RGB_PACKED, + { { 1, RGB_BITS(5, 3), RGB_BITS(2, 3), RGB_BITS(0, 2) } } }, + { fourcc('Y', 'U', '1', '2'), ZBAR_FMT_YUV_PLANAR, { { 1, 1, 0 /*UV*/ } } }, + { fourcc('Y', 'V', '1', '2'), ZBAR_FMT_YUV_PLANAR, { { 1, 1, 1 /*VU*/ } } }, + { fourcc('R', 'G', 'B', '3'), + ZBAR_FMT_RGB_PACKED, + { { 3, RGB_BITS(0, 8), RGB_BITS(8, 8), RGB_BITS(16, 8) } } }, + { fourcc('R', '4', '4', '4'), + ZBAR_FMT_RGB_PACKED, + { { 2, RGB_BITS(8, 4), RGB_BITS(4, 4), RGB_BITS(0, 4) } } }, + { fourcc('B', 'G', 'R', '4'), + ZBAR_FMT_RGB_PACKED, + { { 4, RGB_BITS(16, 8), RGB_BITS(8, 8), RGB_BITS(0, 8) } } }, + { fourcc('Y', 'U', 'V', '9'), ZBAR_FMT_YUV_PLANAR, { { 2, 2, 0 /*UV*/ } } }, + { + fourcc('M', 'J', 'P', 'G'), + ZBAR_FMT_JPEG, + }, + { fourcc('4', '1', '1', 'P'), ZBAR_FMT_YUV_PLANAR, { { 2, 0, 0 /*UV*/ } } }, + { fourcc('R', 'G', 'B', 'P'), + ZBAR_FMT_RGB_PACKED, + { { 2, RGB_BITS(11, 5), RGB_BITS(5, 6), RGB_BITS(0, 5) } } }, + { fourcc('R', 'G', 'B', 'R'), + ZBAR_FMT_RGB_PACKED, + { { 2, RGB_BITS(3, 5), RGB_BITS(13, 6), RGB_BITS(8, 5) } } }, + { fourcc('Y', 'U', 'Y', 'V'), + ZBAR_FMT_YUV_PACKED, + { { 1, 0, 0, /*YUYV*/ } } }, + { fourcc('U', 'Y', 'V', 'Y'), + ZBAR_FMT_YUV_PACKED, + { { 1, 0, 2, /*UYVY*/ } } }, +}; + +static const int num_format_defs = + sizeof(format_defs) / sizeof(zbar_format_def_t); + +#ifdef DEBUG_CONVERT +static int intsort(const void *a, const void *b) +{ + return (*(uint32_t *)a - *(uint32_t *)b); +} +#endif + +/* verify that format list is in required sort order */ +static inline int verify_format_sort(void) +{ + int i; + for (i = 0; i < num_format_defs; i++) { + int j = i * 2 + 1; + if ((j < num_format_defs && + format_defs[i].format < format_defs[j].format) || + (j + 1 < num_format_defs && + format_defs[j + 1].format < format_defs[i].format)) + break; + } + if (i == num_format_defs) + return (0); + + /* spew correct order for fix */ + fprintf(stderr, "ERROR: image format list is not sorted!?\n"); + +#ifdef DEBUG_CONVERT + assert(num_format_defs); + uint32_t sorted[num_format_defs]; + uint32_t ordered[num_format_defs]; + for (i = 0; i < num_format_defs; i++) + sorted[i] = format_defs[i].format; + qsort(sorted, num_format_defs, sizeof(uint32_t), intsort); + for (i = 0; i < num_format_defs; i = i << 1 | 1) + ; + i = (i - 1) / 2; + ordered[i] = sorted[0]; + int j, k; + for (j = 1; j < num_format_defs; j++) { + k = i * 2 + 2; + if (k < num_format_defs) { + i = k; + for (k = k * 2 + 1; k < num_format_defs; k = k * 2 + 1) + i = k; + } else { + for (k = (i - 1) / 2; i != k * 2 + 1; k = (i - 1) / 2) { + assert(i); + i = k; + } + i = k; + } + ordered[i] = sorted[j]; + } + fprintf(stderr, "correct sort order is:"); + for (i = 0; i < num_format_defs; i++) + fprintf(stderr, " %4.4s", (char *)&ordered[i]); + fprintf(stderr, "\n"); +#endif + return (-1); +} + +static inline void uv_round(zbar_image_t *img, const zbar_format_def_t *fmt) +{ + img->width >>= fmt->p.yuv.xsub2; + img->width <<= fmt->p.yuv.xsub2; + img->height >>= fmt->p.yuv.ysub2; + img->height <<= fmt->p.yuv.ysub2; +} + +static inline void uv_roundup(zbar_image_t *img, const zbar_format_def_t *fmt) +{ + unsigned xmask, ymask; + if (fmt->group == ZBAR_FMT_GRAY) + return; + xmask = (1 << fmt->p.yuv.xsub2) - 1; + if (img->width & xmask) + img->width = (img->width + xmask) & ~xmask; + ymask = (1 << fmt->p.yuv.ysub2) - 1; + if (img->height & ymask) + img->height = (img->height + ymask) & ~ymask; +} + +static inline unsigned long uvp_size(const zbar_image_t *img, + const zbar_format_def_t *fmt) +{ + if (fmt->group == ZBAR_FMT_GRAY) + return (0); + return ((img->width >> fmt->p.yuv.xsub2) * + (img->height >> fmt->p.yuv.ysub2)); +} + +static inline uint32_t convert_read_rgb(const uint8_t *srcp, int bpp) +{ + uint32_t p; + if (bpp == 3) { + p = *srcp; + p |= *(srcp + 1) << 8; + p |= *(srcp + 2) << 16; + } else if (bpp == 4) + p = *((uint32_t *)(srcp)); + else if (bpp == 2) + p = *((uint16_t *)(srcp)); + else + p = *srcp; + return (p); +} + +static inline void convert_write_rgb(uint8_t *dstp, uint32_t p, int bpp) +{ + if (bpp == 3) { + *dstp = p & 0xff; + *(dstp + 1) = (p >> 8) & 0xff; + *(dstp + 2) = (p >> 16) & 0xff; + } else if (bpp == 4) + *((uint32_t *)dstp) = p; + else if (bpp == 2) + *((uint16_t *)dstp) = p; + else + *dstp = p; +} + +/* cleanup linked image by unrefing */ +static void cleanup_ref(zbar_image_t *img) +{ + if (img->next) + _zbar_image_refcnt(img->next, -1); +} + +/* resize y plane, drop extra columns/rows from the right/bottom, + * or duplicate last column/row to pad missing data + */ +static inline void convert_y_resize(zbar_image_t *dst, + const zbar_format_def_t *dstfmt, + const zbar_image_t *src, + const zbar_format_def_t *srcfmt, size_t n) +{ + uint8_t *psrc, *pdst; + unsigned width, height, xpad, y; + + if (dst->width == src->width && dst->height == src->height) { + memcpy((void *)dst->data, src->data, n); + return; + } + psrc = (void *)src->data; + pdst = (void *)dst->data; + width = (dst->width > src->width) ? src->width : dst->width; + xpad = (dst->width > src->width) ? dst->width - src->width : 0; + height = (dst->height > src->height) ? src->height : dst->height; + for (y = 0; y < height; y++) { + memcpy(pdst, psrc, width); + pdst += width; + psrc += src->width; + if (xpad) { + memset(pdst, *(psrc - 1), xpad); + pdst += xpad; + } + } + psrc -= src->width; + for (; y < dst->height; y++) { + memcpy(pdst, psrc, width); + pdst += width; + if (xpad) { + memset(pdst, *(psrc - 1), xpad); + pdst += xpad; + } + } +} + +/* make new image w/reference to the same image data */ +static void convert_copy(zbar_image_t *dst, const zbar_format_def_t *dstfmt, + const zbar_image_t *src, + const zbar_format_def_t *srcfmt) +{ + if (src->width == dst->width && src->height == dst->height) { + zbar_image_t *s = (zbar_image_t *)src; + dst->data = src->data; + dst->datalen = src->datalen; + dst->cleanup = cleanup_ref; + dst->next = s; + _zbar_image_refcnt(s, 1); + } else + /* NB only for GRAY/YUV_PLANAR formats */ + convert_y_resize(dst, dstfmt, src, srcfmt, dst->width * dst->height); +} + +/* append neutral UV plane to grayscale image */ +static void convert_uvp_append(zbar_image_t *dst, + const zbar_format_def_t *dstfmt, + const zbar_image_t *src, + const zbar_format_def_t *srcfmt) +{ + unsigned long n; + uv_roundup(dst, dstfmt); + dst->datalen = uvp_size(dst, dstfmt) * 2; + n = dst->width * dst->height; + dst->datalen += n; + assert(src->datalen >= src->width * src->height); + zprintf(24, "dst=%dx%d (%lx) %lx src=%dx%d %lx\n", dst->width, dst->height, + n, dst->datalen, src->width, src->height, src->datalen); + dst->data = malloc(dst->datalen); + if (!dst->data) + return; + convert_y_resize(dst, dstfmt, src, srcfmt, n); + memset((uint8_t *)dst->data + n, 0x80, dst->datalen - n); +} + +/* interleave YUV planes into packed YUV */ +static void convert_yuv_pack(zbar_image_t *dst, const zbar_format_def_t *dstfmt, + const zbar_image_t *src, + const zbar_format_def_t *srcfmt) +{ + unsigned long srcm, srcn; + uint8_t flags, *srcy, *dstp; + const uint8_t *srcu, *srcv; + unsigned srcl, xmask, ymask, x, y; + uint8_t y0 = 0, y1 = 0, u = 0x80, v = 0x80; + + uv_roundup(dst, dstfmt); + dst->datalen = dst->width * dst->height + uvp_size(dst, dstfmt) * 2; + dst->data = malloc(dst->datalen); + if (!dst->data) + return; + dstp = (void *)dst->data; + + srcm = uvp_size(src, srcfmt); + srcn = src->width * src->height; + assert(src->datalen >= srcn + 2 * srcn); + flags = dstfmt->p.yuv.packorder ^ srcfmt->p.yuv.packorder; + srcy = (void *)src->data; + if (flags & 1) { + srcv = (uint8_t *)src->data + srcn; + srcu = srcv + srcm; + } else { + srcu = (uint8_t *)src->data + srcn; + srcv = srcu + srcm; + } + flags = dstfmt->p.yuv.packorder & 2; + + srcl = src->width >> srcfmt->p.yuv.xsub2; + xmask = (1 << srcfmt->p.yuv.xsub2) - 1; + ymask = (1 << srcfmt->p.yuv.ysub2) - 1; + for (y = 0; y < dst->height; y++) { + if (y >= src->height) { + srcy -= src->width; + srcu -= srcl; + srcv -= srcl; + } else if (y & ymask) { + srcu -= srcl; + srcv -= srcl; + } + for (x = 0; x < dst->width; x += 2) { + if (x < src->width) { + y0 = *(srcy++); + y1 = *(srcy++); + if (!(x & xmask)) { + u = *(srcu++); + v = *(srcv++); + } + } + if (flags) { + *(dstp++) = u; + *(dstp++) = y0; + *(dstp++) = v; + *(dstp++) = y1; + } else { + *(dstp++) = y0; + *(dstp++) = u; + *(dstp++) = y1; + *(dstp++) = v; + } + } + for (; x < src->width; x += 2) { + srcy += 2; + if (!(x & xmask)) { + srcu++; + srcv++; + } + } + } +} + +/* split packed YUV samples and join into YUV planes + * FIXME currently ignores color and grayscales the image + */ +static void convert_yuv_unpack(zbar_image_t *dst, + const zbar_format_def_t *dstfmt, + const zbar_image_t *src, + const zbar_format_def_t *srcfmt) +{ + unsigned long dstn, dstm2; + uint8_t *dsty, flags; + const uint8_t *srcp; + unsigned srcl, x, y; + uint8_t y0 = 0, y1 = 0; + + uv_roundup(dst, dstfmt); + dstn = dst->width * dst->height; + dstm2 = uvp_size(dst, dstfmt) * 2; + dst->datalen = dstn + dstm2; + dst->data = malloc(dst->datalen); + if (!dst->data) + return; + if (dstm2) + memset((uint8_t *)dst->data + dstn, 0x80, dstm2); + dsty = (uint8_t *)dst->data; + + flags = srcfmt->p.yuv.packorder ^ dstfmt->p.yuv.packorder; + flags &= 2; + srcp = src->data; + if (flags) + srcp++; + + srcl = src->width + (src->width >> srcfmt->p.yuv.xsub2); + for (y = 0; y < dst->height; y++) { + if (y >= src->height) + srcp -= srcl; + for (x = 0; x < dst->width; x += 2) { + if (x < src->width) { + y0 = *(srcp++); + srcp++; + y1 = *(srcp++); + srcp++; + } + *(dsty++) = y0; + *(dsty++) = y1; + } + if (x < src->width) + srcp += (src->width - x) * 2; + } +} + +/* resample and resize UV plane(s) + * FIXME currently ignores color and grayscales the image + */ +static void convert_uvp_resample(zbar_image_t *dst, + const zbar_format_def_t *dstfmt, + const zbar_image_t *src, + const zbar_format_def_t *srcfmt) +{ + unsigned long dstn, dstm2; + uv_roundup(dst, dstfmt); + dstn = dst->width * dst->height; + dstm2 = uvp_size(dst, dstfmt) * 2; + dst->datalen = dstn + dstm2; + dst->data = malloc(dst->datalen); + if (!dst->data) + return; + convert_y_resize(dst, dstfmt, src, srcfmt, dstn); + if (dstm2) + memset((uint8_t *)dst->data + dstn, 0x80, dstm2); +} + +/* rearrange interleaved UV componets */ +static void convert_uv_resample(zbar_image_t *dst, + const zbar_format_def_t *dstfmt, + const zbar_image_t *src, + const zbar_format_def_t *srcfmt) +{ + unsigned long dstn; + uint8_t *dstp, flags; + const uint8_t *srcp; + unsigned srcl, x, y; + uint8_t y0 = 0, y1 = 0, u = 0x80, v = 0x80; + + uv_roundup(dst, dstfmt); + dstn = dst->width * dst->height; + dst->datalen = dstn + uvp_size(dst, dstfmt) * 2; + dst->data = malloc(dst->datalen); + if (!dst->data) + return; + dstp = (void *)dst->data; + + flags = (srcfmt->p.yuv.packorder ^ dstfmt->p.yuv.packorder) & 1; + srcp = src->data; + + srcl = src->width + (src->width >> srcfmt->p.yuv.xsub2); + for (y = 0; y < dst->height; y++) { + if (y >= src->height) + srcp -= srcl; + for (x = 0; x < dst->width; x += 2) { + if (x < src->width) { + if (!(srcfmt->p.yuv.packorder & 2)) { + y0 = *(srcp++); + u = *(srcp++); + y1 = *(srcp++); + v = *(srcp++); + } else { + u = *(srcp++); + y0 = *(srcp++); + v = *(srcp++); + y1 = *(srcp++); + } + if (flags) { + uint8_t tmp = u; + u = v; + v = tmp; + } + } + if (!(dstfmt->p.yuv.packorder & 2)) { + *(dstp++) = y0; + *(dstp++) = u; + *(dstp++) = y1; + *(dstp++) = v; + } else { + *(dstp++) = u; + *(dstp++) = y0; + *(dstp++) = v; + *(dstp++) = y1; + } + } + if (x < src->width) + srcp += (src->width - x) * 2; + } +} + +/* YUV planes to packed RGB + * FIXME currently ignores color and grayscales the image + */ +static void convert_yuvp_to_rgb(zbar_image_t *dst, + const zbar_format_def_t *dstfmt, + const zbar_image_t *src, + const zbar_format_def_t *srcfmt) +{ + uint8_t *dstp, *srcy; + int drbits, drbit0, dgbits, dgbit0, dbbits, dbbit0; + unsigned long srcm, srcn; + unsigned x, y; + uint32_t p = 0; + + dst->datalen = dst->width * dst->height * dstfmt->p.rgb.bpp; + dst->data = malloc(dst->datalen); + if (!dst->data) + return; + dstp = (void *)dst->data; + + drbits = RGB_SIZE(dstfmt->p.rgb.red); + drbit0 = RGB_OFFSET(dstfmt->p.rgb.red); + dgbits = RGB_SIZE(dstfmt->p.rgb.green); + dgbit0 = RGB_OFFSET(dstfmt->p.rgb.green); + dbbits = RGB_SIZE(dstfmt->p.rgb.blue); + dbbit0 = RGB_OFFSET(dstfmt->p.rgb.blue); + + srcm = uvp_size(src, srcfmt); + srcn = src->width * src->height; + assert(src->datalen >= srcn + 2 * srcm); + srcy = (void *)src->data; + + for (y = 0; y < dst->height; y++) { + if (y >= src->height) + srcy -= src->width; + for (x = 0; x < dst->width; x++) { + if (x < src->width) { + /* FIXME color space? */ + unsigned y0 = *(srcy++); + p = (((y0 >> drbits) << drbit0) | ((y0 >> dgbits) << dgbit0) | + ((y0 >> dbbits) << dbbit0)); + } + convert_write_rgb(dstp, p, dstfmt->p.rgb.bpp); + dstp += dstfmt->p.rgb.bpp; + } + if (x < src->width) + srcy += (src->width - x); + } +} + +/* packed RGB to YUV planes + * FIXME currently ignores color and grayscales the image + */ +static void convert_rgb_to_yuvp(zbar_image_t *dst, + const zbar_format_def_t *dstfmt, + const zbar_image_t *src, + const zbar_format_def_t *srcfmt) +{ + unsigned long dstn, dstm2; + uint8_t *dsty; + const uint8_t *srcp; + int rbits, rbit0, gbits, gbit0, bbits, bbit0; + unsigned srcl, x, y; + uint16_t y0 = 0; + + uv_roundup(dst, dstfmt); + dstn = dst->width * dst->height; + dstm2 = uvp_size(dst, dstfmt) * 2; + dst->datalen = dstn + dstm2; + dst->data = malloc(dst->datalen); + if (!dst->data) + return; + if (dstm2) + memset((uint8_t *)dst->data + dstn, 0x80, dstm2); + dsty = (void *)dst->data; + + assert(src->datalen >= (src->width * src->height * srcfmt->p.rgb.bpp)); + srcp = src->data; + + rbits = RGB_SIZE(srcfmt->p.rgb.red); + rbit0 = RGB_OFFSET(srcfmt->p.rgb.red); + gbits = RGB_SIZE(srcfmt->p.rgb.green); + gbit0 = RGB_OFFSET(srcfmt->p.rgb.green); + bbits = RGB_SIZE(srcfmt->p.rgb.blue); + bbit0 = RGB_OFFSET(srcfmt->p.rgb.blue); + + srcl = src->width * srcfmt->p.rgb.bpp; + for (y = 0; y < dst->height; y++) { + if (y >= src->height) + srcp -= srcl; + for (x = 0; x < dst->width; x++) { + if (x < src->width) { + uint8_t r, g, b; + uint32_t p = convert_read_rgb(srcp, srcfmt->p.rgb.bpp); + srcp += srcfmt->p.rgb.bpp; + + /* FIXME endianness? */ + r = ((p >> rbit0) << rbits) & 0xff; + g = ((p >> gbit0) << gbits) & 0xff; + b = ((p >> bbit0) << bbits) & 0xff; + + /* FIXME color space? */ + y0 = ((77 * r + 150 * g + 29 * b) + 0x80) >> 8; + } + *(dsty++) = y0; + } + if (x < src->width) + srcp += (src->width - x) * srcfmt->p.rgb.bpp; + } +} + +/* packed YUV to packed RGB */ +static void convert_yuv_to_rgb(zbar_image_t *dst, + const zbar_format_def_t *dstfmt, + const zbar_image_t *src, + const zbar_format_def_t *srcfmt) +{ + uint8_t *dstp; + unsigned long dstn = dst->width * dst->height; + int drbits, drbit0, dgbits, dgbit0, dbbits, dbbit0; + const uint8_t *srcp; + unsigned srcl, x, y; + uint32_t p = 0; + + dst->datalen = dstn * dstfmt->p.rgb.bpp; + dst->data = malloc(dst->datalen); + if (!dst->data) + return; + dstp = (void *)dst->data; + + drbits = RGB_SIZE(dstfmt->p.rgb.red); + drbit0 = RGB_OFFSET(dstfmt->p.rgb.red); + dgbits = RGB_SIZE(dstfmt->p.rgb.green); + dgbit0 = RGB_OFFSET(dstfmt->p.rgb.green); + dbbits = RGB_SIZE(dstfmt->p.rgb.blue); + dbbit0 = RGB_OFFSET(dstfmt->p.rgb.blue); + + assert(src->datalen >= + (src->width * src->height + uvp_size(src, srcfmt) * 2)); + srcp = src->data; + if (srcfmt->p.yuv.packorder & 2) + srcp++; + + assert(srcfmt->p.yuv.xsub2 == 1); + srcl = src->width + (src->width >> 1); + for (y = 0; y < dst->height; y++) { + if (y >= src->height) + srcp -= srcl; + for (x = 0; x < dst->width; x++) { + if (x < src->width) { + uint8_t y0 = *(srcp++); + srcp++; + + if (y0 <= 16) + y0 = 0; + else if (y0 >= 235) + y0 = 255; + else + y0 = (uint16_t)(y0 - 16) * 255 / 219; + + p = (((y0 >> drbits) << drbit0) | ((y0 >> dgbits) << dgbit0) | + ((y0 >> dbbits) << dbbit0)); + } + convert_write_rgb(dstp, p, dstfmt->p.rgb.bpp); + dstp += dstfmt->p.rgb.bpp; + } + if (x < src->width) + srcp += (src->width - x) * 2; + } +} + +/* packed RGB to packed YUV + * FIXME currently ignores color and grayscales the image + */ +static void convert_rgb_to_yuv(zbar_image_t *dst, + const zbar_format_def_t *dstfmt, + const zbar_image_t *src, + const zbar_format_def_t *srcfmt) +{ + uint8_t *dstp, flags; + const uint8_t *srcp; + int rbits, rbit0, gbits, gbit0, bbits, bbit0; + unsigned srcl, x, y; + uint16_t y0 = 0; + + uv_roundup(dst, dstfmt); + dst->datalen = dst->width * dst->height + uvp_size(dst, dstfmt) * 2; + dst->data = malloc(dst->datalen); + if (!dst->data) + return; + dstp = (void *)dst->data; + flags = dstfmt->p.yuv.packorder & 2; + + assert(src->datalen >= (src->width * src->height * srcfmt->p.rgb.bpp)); + srcp = src->data; + + rbits = RGB_SIZE(srcfmt->p.rgb.red); + rbit0 = RGB_OFFSET(srcfmt->p.rgb.red); + gbits = RGB_SIZE(srcfmt->p.rgb.green); + gbit0 = RGB_OFFSET(srcfmt->p.rgb.green); + bbits = RGB_SIZE(srcfmt->p.rgb.blue); + bbit0 = RGB_OFFSET(srcfmt->p.rgb.blue); + + srcl = src->width * srcfmt->p.rgb.bpp; + for (y = 0; y < dst->height; y++) { + if (y >= src->height) + srcp -= srcl; + for (x = 0; x < dst->width; x++) { + if (x < src->width) { + uint8_t r, g, b; + uint32_t p = convert_read_rgb(srcp, srcfmt->p.rgb.bpp); + srcp += srcfmt->p.rgb.bpp; + + /* FIXME endianness? */ + r = ((p >> rbit0) << rbits) & 0xff; + g = ((p >> gbit0) << gbits) & 0xff; + b = ((p >> bbit0) << bbits) & 0xff; + + /* FIXME color space? */ + y0 = ((77 * r + 150 * g + 29 * b) + 0x80) >> 8; + } + if (flags) { + *(dstp++) = 0x80; + *(dstp++) = y0; + } else { + *(dstp++) = y0; + *(dstp++) = 0x80; + } + } + if (x < src->width) + srcp += (src->width - x) * srcfmt->p.rgb.bpp; + } +} + +/* resample and resize packed RGB components */ +static void convert_rgb_resample(zbar_image_t *dst, + const zbar_format_def_t *dstfmt, + const zbar_image_t *src, + const zbar_format_def_t *srcfmt) +{ + unsigned long dstn = dst->width * dst->height; + uint8_t *dstp; + int drbits, drbit0, dgbits, dgbit0, dbbits, dbbit0; + int srbits, srbit0, sgbits, sgbit0, sbbits, sbbit0; + const uint8_t *srcp; + unsigned srcl, x, y; + uint32_t p = 0; + + dst->datalen = dstn * dstfmt->p.rgb.bpp; + dst->data = malloc(dst->datalen); + if (!dst->data) + return; + dstp = (void *)dst->data; + + drbits = RGB_SIZE(dstfmt->p.rgb.red); + drbit0 = RGB_OFFSET(dstfmt->p.rgb.red); + dgbits = RGB_SIZE(dstfmt->p.rgb.green); + dgbit0 = RGB_OFFSET(dstfmt->p.rgb.green); + dbbits = RGB_SIZE(dstfmt->p.rgb.blue); + dbbit0 = RGB_OFFSET(dstfmt->p.rgb.blue); + + assert(src->datalen >= (src->width * src->height * srcfmt->p.rgb.bpp)); + srcp = src->data; + + srbits = RGB_SIZE(srcfmt->p.rgb.red); + srbit0 = RGB_OFFSET(srcfmt->p.rgb.red); + sgbits = RGB_SIZE(srcfmt->p.rgb.green); + sgbit0 = RGB_OFFSET(srcfmt->p.rgb.green); + sbbits = RGB_SIZE(srcfmt->p.rgb.blue); + sbbit0 = RGB_OFFSET(srcfmt->p.rgb.blue); + + srcl = src->width * srcfmt->p.rgb.bpp; + for (y = 0; y < dst->height; y++) { + if (y >= src->height) + y -= srcl; + for (x = 0; x < dst->width; x++) { + if (x < src->width) { + uint8_t r, g, b; + p = convert_read_rgb(srcp, srcfmt->p.rgb.bpp); + srcp += srcfmt->p.rgb.bpp; + + /* FIXME endianness? */ + r = (p >> srbit0) << srbits; + g = (p >> sgbit0) << sgbits; + b = (p >> sbbit0) << sbbits; + + p = (((r >> drbits) << drbit0) | ((g >> dgbits) << dgbit0) | + ((b >> dbbits) << dbbit0)); + } + convert_write_rgb(dstp, p, dstfmt->p.rgb.bpp); + dstp += dstfmt->p.rgb.bpp; + } + if (x < src->width) + srcp += (src->width - x) * srcfmt->p.rgb.bpp; + } +} + +#ifdef HAVE_LIBJPEG +void _zbar_convert_jpeg_to_y(zbar_image_t *dst, const zbar_format_def_t *dstfmt, + const zbar_image_t *src, + const zbar_format_def_t *srcfmt); + +static void convert_jpeg(zbar_image_t *dst, const zbar_format_def_t *dstfmt, + const zbar_image_t *src, + const zbar_format_def_t *srcfmt); +#endif + +/* group conversion matrix */ +static conversion_def_t conversions[][ZBAR_FMT_NUM] = { + { + /* *from* GRAY */ + { 0, convert_copy }, /* to GRAY */ + { 8, convert_uvp_append }, /* to YUV_PLANAR */ + { 24, convert_yuv_pack }, /* to YUV_PACKED */ + { 32, convert_yuvp_to_rgb }, /* to RGB_PACKED */ + { 8, convert_uvp_append }, /* to YUV_NV */ + { -1, NULL }, /* to JPEG */ + }, + { + /* from YUV_PLANAR */ + { 1, convert_copy }, /* to GRAY */ + { 48, convert_uvp_resample }, /* to YUV_PLANAR */ + { 64, convert_yuv_pack }, /* to YUV_PACKED */ + { 128, convert_yuvp_to_rgb }, /* to RGB_PACKED */ + { 40, convert_uvp_append }, /* to YUV_NV */ + { -1, NULL }, /* to JPEG */ + }, + { + /* from YUV_PACKED */ + { 24, convert_yuv_unpack }, /* to GRAY */ + { 52, convert_yuv_unpack }, /* to YUV_PLANAR */ + { 20, convert_uv_resample }, /* to YUV_PACKED */ + { 144, convert_yuv_to_rgb }, /* to RGB_PACKED */ + { 18, convert_yuv_unpack }, /* to YUV_NV */ + { -1, NULL }, /* to JPEG */ + }, + { + /* from RGB_PACKED */ + { 112, convert_rgb_to_yuvp }, /* to GRAY */ + { 160, convert_rgb_to_yuvp }, /* to YUV_PLANAR */ + { 144, convert_rgb_to_yuv }, /* to YUV_PACKED */ + { 120, convert_rgb_resample }, /* to RGB_PACKED */ + { 152, convert_rgb_to_yuvp }, /* to YUV_NV */ + { -1, NULL }, /* to JPEG */ + }, + { + /* from YUV_NV (FIXME treated as GRAY) */ + { 1, convert_copy }, /* to GRAY */ + { 8, convert_uvp_append }, /* to YUV_PLANAR */ + { 24, convert_yuv_pack }, /* to YUV_PACKED */ + { 32, convert_yuvp_to_rgb }, /* to RGB_PACKED */ + { 8, convert_uvp_append }, /* to YUV_NV */ + { -1, NULL }, /* to JPEG */ + }, +#ifdef HAVE_LIBJPEG + { + /* from JPEG */ + { 96, _zbar_convert_jpeg_to_y }, /* to GRAY */ + { 104, convert_jpeg }, /* to YUV_PLANAR */ + { 116, convert_jpeg }, /* to YUV_PACKED */ + { 256, convert_jpeg }, /* to RGB_PACKED */ + { 104, convert_jpeg }, /* to YUV_NV */ + { -1, NULL }, /* to JPEG */ + }, +#else + { + /* from JPEG */ + { -1, NULL }, /* to GRAY */ + { -1, NULL }, /* to YUV_PLANAR */ + { -1, NULL }, /* to YUV_PACKED */ + { -1, NULL }, /* to RGB_PACKED */ + { -1, NULL }, /* to YUV_NV */ + { -1, NULL }, /* to JPEG */ + }, +#endif +}; + +const zbar_format_def_t *_zbar_format_lookup(uint32_t fmt) +{ + const zbar_format_def_t *def = NULL; + int i = 0; + while (i < num_format_defs) { + def = &format_defs[i]; + if (fmt == def->format) + return (def); + i = i * 2 + 1; + if (fmt > def->format) + i++; + } + return (NULL); +} + +#ifdef HAVE_LIBJPEG +/* convert JPEG data via an intermediate format supported by libjpeg */ +static void convert_jpeg(zbar_image_t *dst, const zbar_format_def_t *dstfmt, + const zbar_image_t *src, + const zbar_format_def_t *srcfmt) +{ + /* define intermediate image in a format supported by libjpeg + * (currently only grayscale) + */ + zbar_image_t *tmp; + if (!src->src) { + tmp = zbar_image_create(); + tmp->format = fourcc('Y', '8', '0', '0'); + _zbar_image_copy_size(tmp, dst); + } else { + tmp = src->src->jpeg_img; + assert(tmp); + _zbar_image_copy_size(dst, tmp); + } + + const zbar_format_def_t *tmpfmt = _zbar_format_lookup(tmp->format); + assert(tmpfmt); + + /* convert to intermediate format */ + _zbar_convert_jpeg_to_y(tmp, tmpfmt, src, srcfmt); + + /* now convert to dst */ + _zbar_image_copy_size(dst, tmp); + + conversion_handler_t *func = conversions[tmpfmt->group][dstfmt->group].func; + + func(dst, dstfmt, tmp, tmpfmt); + + if (!src->src) + zbar_image_destroy(tmp); +} +#endif + +zbar_image_t *zbar_image_convert_resize(const zbar_image_t *src, + unsigned long fmt, unsigned width, + unsigned height) +{ + const zbar_format_def_t *srcfmt, *dstfmt; + conversion_handler_t *func; + zbar_image_t *dst = zbar_image_create(); + dst->format = fmt; + dst->width = width; + dst->height = height; + zbar_image_set_crop(dst, src->crop_x, src->crop_y, src->crop_w, + src->crop_h); + if (src->format == fmt && src->width == width && src->height == height) { + convert_copy(dst, NULL, src, NULL); + return (dst); + } + + srcfmt = _zbar_format_lookup(src->format); + dstfmt = _zbar_format_lookup(dst->format); + if (!srcfmt || !dstfmt) + /* FIXME free dst */ + return (NULL); + + if (srcfmt->group == dstfmt->group && srcfmt->p.cmp == dstfmt->p.cmp && + src->width == width && src->height == height) { + convert_copy(dst, NULL, src, NULL); + return (dst); + } + + func = conversions[srcfmt->group][dstfmt->group].func; + + dst->cleanup = zbar_image_free_data; + func(dst, dstfmt, src, srcfmt); + if (!dst->data) { + /* conversion failed */ + zbar_image_destroy(dst); + return (NULL); + } + return (dst); +} + +zbar_image_t *zbar_image_convert(const zbar_image_t *src, unsigned long fmt) +{ + return (zbar_image_convert_resize(src, fmt, src->width, src->height)); +} + +static inline int has_format(uint32_t fmt, const uint32_t *fmts) +{ + for (; *fmts; fmts++) + if (*fmts == fmt) + return (1); + return (0); +} + +/* select least cost conversion from src format to available dsts */ +int _zbar_best_format(uint32_t src, uint32_t *dst, const uint32_t *dsts) +{ + const zbar_format_def_t *srcfmt; + unsigned min_cost = -1; + + if (dst) + *dst = 0; + if (!dsts) + return (-1); + if (has_format(src, dsts)) { + zprintf(8, "shared format: %4.4s\n", (char *)&src); + if (dst) + *dst = src; + return (0); + } + srcfmt = _zbar_format_lookup(src); + if (!srcfmt) + return (-1); + + zprintf(8, "from %.4s(%08" PRIx32 ") to", (char *)&src, src); + for (; *dsts; dsts++) { + const zbar_format_def_t *dstfmt = _zbar_format_lookup(*dsts); + int cost; + if (!dstfmt) + continue; + if (srcfmt->group == dstfmt->group && srcfmt->p.cmp == dstfmt->p.cmp) + cost = 0; + else + cost = conversions[srcfmt->group][dstfmt->group].cost; + + if (_zbar_verbosity >= 8) + fprintf(stderr, " %.4s(%08" PRIx32 ")=%d", (char *)dsts, *dsts, + cost); + if (cost >= 0 && min_cost > cost) { + min_cost = cost; + if (dst) + *dst = *dsts; + } + } + if (_zbar_verbosity >= 8) + fprintf(stderr, "\n"); + return (min_cost); +} + +int zbar_negotiate_format(zbar_video_t *vdo, zbar_window_t *win) +{ + static const uint32_t y800[2] = { fourcc('Y','8','0','0'), 0 }; + errinfo_t *errdst; + const uint32_t *srcs, *dsts; + unsigned min_cost = -1; + uint32_t min_fmt = 0; + const uint32_t *fmt; + + if (!vdo) + return (0); + + if (win) + (void)window_lock(win); + + errdst = &vdo->err; + if (verify_format_sort()) { + if (win) + (void)window_unlock(win); + return (err_capture(errdst, SEV_FATAL, ZBAR_ERR_INTERNAL, __func__, + "image format list is not sorted!?")); + } + + if (!vdo->formats || (win && !win->formats)) { + if (win) + (void)window_unlock(win); + return (err_capture(errdst, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, __func__, + "no input or output formats available")); + } + + srcs = vdo->formats; + dsts = (win) ? win->formats : y800; + + for (fmt = _zbar_formats; *fmt; fmt++) { + /* only consider formats supported by video device */ + uint32_t win_fmt = 0; + int cost; + if (!has_format(*fmt, srcs)) + continue; + cost = _zbar_best_format(*fmt, &win_fmt, dsts); + if (cost < 0) { + zprintf(4, "%.4s(%08" PRIx32 ") -> ? (unsupported)\n", (char *)fmt, + *fmt); + continue; + } + zprintf(4, "%.4s(%08" PRIx32 ") -> %.4s(%08" PRIx32 ") (%d)\n", + (char *)fmt, *fmt, (char *)&win_fmt, win_fmt, cost); + if (min_cost > cost) { + min_cost = cost; + min_fmt = *fmt; + if (!cost) + break; + } + } + if (!min_fmt && vdo->emu_formats) { + /* As vdo->formats aren't compatible, just free them */ + free(vdo->formats); + vdo->formats = vdo->emu_formats; + vdo->emu_formats = NULL; + + srcs = vdo->formats; + dsts = (win) ? win->formats : y800; + + /* + * Use the same cost algorithm to select emulated formats. + * This might select a sub-optimal conversion, but, in practice, + * it will select a conversion to YUV at libv4l, and a YUY->Y8 + * in zbar, with it is OK. Yet, it is better to not select the + * most performatic conversion than to not support the webcam. + */ + for (fmt = _zbar_formats; *fmt; fmt++) { + /* only consider formats supported by video device */ + uint32_t win_fmt = 0; + int cost; + if (!has_format(*fmt, srcs)) + continue; + cost = _zbar_best_format(*fmt, &win_fmt, dsts); + if (cost < 0) { + zprintf(4, "%.4s(%08" PRIx32 ") -> ? (unsupported)\n", + (char *)fmt, *fmt); + continue; + } + zprintf(4, "%.4s(%08" PRIx32 ") -> %.4s(%08" PRIx32 ") (%d)\n", + (char *)fmt, *fmt, (char *)&win_fmt, win_fmt, cost); + if (min_cost > cost) { + min_cost = cost; + min_fmt = *fmt; + if (!cost) + break; + } + } + } + + if (win) + (void)window_unlock(win); + + if (!min_fmt) + return (err_capture(errdst, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, __func__, + "no supported image formats available")); + + zprintf(2, "setting best format %.4s(%08" PRIx32 ") (%d)\n", + (char *)&min_fmt, min_fmt, min_cost); + return (zbar_video_init(vdo, min_fmt)); +} diff --git a/zbar/debug.h b/zbar/debug.h new file mode 100644 index 0000000..a78419b --- /dev/null +++ b/zbar/debug.h @@ -0,0 +1,93 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (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 + *------------------------------------------------------------------------*/ + +/* varargs variations on compile time debug spew */ + +#include <stdio.h> + +#ifndef DEBUG_LEVEL + +#ifdef __GNUC__ +/* older versions of gcc (< 2.95) require a named varargs parameter */ +#define dbprintf(args...) while (0) +#else +/* unfortunately named vararg parameter is a gcc-specific extension */ +#define dbprintf(...) while (0) +#endif + +#else + +#ifdef __GNUC__ +#define dbprintf(level, args...) \ + do { \ + if ((level) <= DEBUG_LEVEL) \ + fprintf(stderr, args); \ + } while (0) +#else +#define dbprintf(level, ...) \ + do { \ + if ((level) <= DEBUG_LEVEL) \ + fprintf(stderr, __VA_ARGS__); \ + } while (0) +#endif + +#endif /* DEBUG_LEVEL */ + +/* spew warnings for non-fatal assertions. + * returns specified error code if assertion fails. + * NB check/return is still performed for NDEBUG + * only the message is inhibited + * FIXME don't we need varargs hacks here? + */ +#ifndef NDEBUG + +#include <stdio.h> + +#if __STDC_VERSION__ < 199901L && !defined(__func__) +#if __GNUC__ >= 2 +#define __func__ __FUNCTION__ +#else +#define __func__ "<unknown>" +#endif +#endif + +#define zassert(condition, retval, format, ...) \ + do { \ + if (!(condition)) { \ + fprintf(stderr, \ + "WARNING: %s:%d: %s:" \ + " Assertion \"%s\" failed.\n\t" format, \ + __FILE__, __LINE__, __func__, #condition, ##__VA_ARGS__); \ + return (retval); \ + } \ + } while (0) + +#else + +#define zassert(condition, retval, format, ...) \ + do { \ + if (!(condition)) \ + return (retval); \ + } while (0) + +#endif diff --git a/zbar/decoder.c b/zbar/decoder.c new file mode 100644 index 0000000..6c41b7f --- /dev/null +++ b/zbar/decoder.c @@ -0,0 +1,592 @@ +/*------------------------------------------------------------------------ + * 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" +#include <stdio.h> /* snprintf */ +#include <stdlib.h> /* malloc, calloc, free */ +#include <string.h> /* memset, strlen */ + +#include <zbar.h> + +#if defined(DEBUG_DECODER) || defined(DEBUG_EAN) || defined(DEBUG_CODE93) || \ + defined(DEBUG_CODE39) || defined(DEBUG_CODABAR) || defined(DEBUG_I25) || \ + defined(DEBUG_DATABAR) || defined(DEBUG_CODE128) || \ + defined(DEBUG_SQ_FINDER) || defined(DEBUG_QR_FINDER) || \ + (defined(DEBUG_PDF417) && (DEBUG_PDF417 >= 4)) +#define DEBUG_LEVEL 1 +#endif +#include "debug.h" +#include "decoder.h" + +zbar_decoder_t *zbar_decoder_create() +{ + zbar_decoder_t *dcode = calloc(1, sizeof(zbar_decoder_t)); + dcode->buf_alloc = BUFFER_MIN; + dcode->buf = malloc(dcode->buf_alloc); + + /* initialize default configs */ +#if ENABLE_EAN == 1 + dcode->ean.enable = 1; + dcode->ean.ean13_config = + ((1 << ZBAR_CFG_ENABLE) | (1 << ZBAR_CFG_EMIT_CHECK)); + dcode->ean.ean8_config = + ((1 << ZBAR_CFG_ENABLE) | (1 << ZBAR_CFG_EMIT_CHECK)); + dcode->ean.upca_config = 1 << ZBAR_CFG_EMIT_CHECK; + dcode->ean.upce_config = 1 << ZBAR_CFG_EMIT_CHECK; + dcode->ean.isbn10_config = 1 << ZBAR_CFG_EMIT_CHECK; + dcode->ean.isbn13_config = 1 << ZBAR_CFG_EMIT_CHECK; +#ifdef FIXME_ADDON_SYNC + dcode->ean.ean2_config = 1 << ZBAR_CFG_ENABLE; + dcode->ean.ean5_config = 1 << ZBAR_CFG_ENABLE; +#endif +#endif +#if ENABLE_I25 == 1 + dcode->i25.config = 1 << ZBAR_CFG_ENABLE; + CFG(dcode->i25, ZBAR_CFG_MIN_LEN) = 6; +#endif +#if ENABLE_DATABAR == 1 + dcode->databar.config = + ((1 << ZBAR_CFG_ENABLE) | (1 << ZBAR_CFG_EMIT_CHECK)); + dcode->databar.config_exp = + ((1 << ZBAR_CFG_ENABLE) | (1 << ZBAR_CFG_EMIT_CHECK)); + dcode->databar.csegs = 4; + dcode->databar.segs = calloc(4, sizeof(*dcode->databar.segs)); +#endif +#if ENABLE_CODABAR == 1 + dcode->codabar.config = 1 << ZBAR_CFG_ENABLE; + CFG(dcode->codabar, ZBAR_CFG_MIN_LEN) = 4; +#endif +#if ENABLE_CODE39 == 1 + dcode->code39.config = 1 << ZBAR_CFG_ENABLE; + CFG(dcode->code39, ZBAR_CFG_MIN_LEN) = 1; +#endif +#if ENABLE_CODE93 == 1 + dcode->code93.config = 1 << ZBAR_CFG_ENABLE; +#endif +#if ENABLE_CODE128 == 1 + dcode->code128.config = 1 << ZBAR_CFG_ENABLE; +#endif +#if ENABLE_PDF417 == 1 + dcode->pdf417.config = 1 << ZBAR_CFG_ENABLE; +#endif +#if ENABLE_QRCODE == 1 + dcode->qrf.config = 1 << ZBAR_CFG_ENABLE; +#endif +#if ENABLE_SQCODE == 1 + dcode->sqf.config = 1 << ZBAR_CFG_ENABLE; +#endif + + zbar_decoder_reset(dcode); + return (dcode); +} + +void zbar_decoder_destroy(zbar_decoder_t *dcode) +{ +#if ENABLE_DATABAR == 1 + if (dcode->databar.segs) + free(dcode->databar.segs); +#endif + if (dcode->buf) + free(dcode->buf); + free(dcode); +} + +void zbar_decoder_reset(zbar_decoder_t *dcode) +{ + memset(dcode, 0, (long)&dcode->buf_alloc - (long)dcode); +#if ENABLE_EAN == 1 + ean_reset(&dcode->ean); +#endif +#if ENABLE_I25 == 1 + i25_reset(&dcode->i25); +#endif +#if ENABLE_DATABAR == 1 + databar_reset(&dcode->databar); +#endif +#if ENABLE_CODABAR == 1 + codabar_reset(&dcode->codabar); +#endif +#if ENABLE_CODE39 == 1 + code39_reset(&dcode->code39); +#endif +#if ENABLE_CODE93 == 1 + code93_reset(&dcode->code93); +#endif +#if ENABLE_CODE128 == 1 + code128_reset(&dcode->code128); +#endif +#if ENABLE_PDF417 == 1 + pdf417_reset(&dcode->pdf417); +#endif +#if ENABLE_QRCODE == 1 + qr_finder_reset(&dcode->qrf); +#endif +} + +void zbar_decoder_new_scan(zbar_decoder_t *dcode) +{ + /* soft reset decoder */ + memset(dcode->w, 0, sizeof(dcode->w)); + dcode->lock = 0; + dcode->idx = 0; + dcode->s6 = 0; +#if ENABLE_EAN == 1 + ean_new_scan(&dcode->ean); +#endif +#if ENABLE_I25 == 1 + i25_reset(&dcode->i25); +#endif +#if ENABLE_DATABAR == 1 + databar_new_scan(&dcode->databar); +#endif +#if ENABLE_CODABAR == 1 + codabar_reset(&dcode->codabar); +#endif +#if ENABLE_CODE39 == 1 + code39_reset(&dcode->code39); +#endif +#if ENABLE_CODE93 == 1 + code93_reset(&dcode->code93); +#endif +#if ENABLE_CODE128 == 1 + code128_reset(&dcode->code128); +#endif +#if ENABLE_PDF417 == 1 + pdf417_reset(&dcode->pdf417); +#endif +#if ENABLE_QRCODE == 1 + qr_finder_reset(&dcode->qrf); +#endif +} + +zbar_color_t zbar_decoder_get_color(const zbar_decoder_t *dcode) +{ + return (get_color(dcode)); +} + +const char *zbar_decoder_get_data(const zbar_decoder_t *dcode) +{ + return ((char *)dcode->buf); +} + +unsigned int zbar_decoder_get_data_length(const zbar_decoder_t *dcode) +{ + return (dcode->buflen); +} + +int zbar_decoder_get_direction(const zbar_decoder_t *dcode) +{ + return (dcode->direction); +} + +zbar_decoder_handler_t * +zbar_decoder_set_handler(zbar_decoder_t *dcode, zbar_decoder_handler_t *handler) +{ + zbar_decoder_handler_t *result = dcode->handler; + dcode->handler = handler; + return (result); +} + +void zbar_decoder_set_userdata(zbar_decoder_t *dcode, void *userdata) +{ + dcode->userdata = userdata; +} + +void *zbar_decoder_get_userdata(const zbar_decoder_t *dcode) +{ + return (dcode->userdata); +} + +zbar_symbol_type_t zbar_decoder_get_type(const zbar_decoder_t *dcode) +{ + return (dcode->type); +} + +unsigned int zbar_decoder_get_modifiers(const zbar_decoder_t *dcode) +{ + return (dcode->modifiers); +} + +zbar_symbol_type_t zbar_decode_width(zbar_decoder_t *dcode, unsigned w) +{ + zbar_symbol_type_t tmp, sym = ZBAR_NONE; + + dcode->w[dcode->idx & (DECODE_WINDOW - 1)] = w; + dbprintf(1, " decode[%x]: w=%d (%g)\n", dcode->idx, w, (w / 32.)); + + /* update shared character width */ + dcode->s6 -= get_width(dcode, 7); + dcode->s6 += get_width(dcode, 1); + + /* each decoder processes width stream in parallel */ +#if ENABLE_QRCODE == 1 + if (TEST_CFG(dcode->qrf.config, ZBAR_CFG_ENABLE) && + (tmp = _zbar_find_qr(dcode)) > ZBAR_PARTIAL) + sym = tmp; +#endif +#if ENABLE_EAN == 1 + if ((dcode->ean.enable) && (tmp = _zbar_decode_ean(dcode))) + sym = tmp; +#endif +#if ENABLE_CODE39 == 1 + if (TEST_CFG(dcode->code39.config, ZBAR_CFG_ENABLE) && + (tmp = _zbar_decode_code39(dcode)) > ZBAR_PARTIAL) + sym = tmp; +#endif +#if ENABLE_CODE93 == 1 + if (TEST_CFG(dcode->code93.config, ZBAR_CFG_ENABLE) && + (tmp = _zbar_decode_code93(dcode)) > ZBAR_PARTIAL) + sym = tmp; +#endif +#if ENABLE_CODE128 == 1 + if (TEST_CFG(dcode->code128.config, ZBAR_CFG_ENABLE) && + (tmp = _zbar_decode_code128(dcode)) > ZBAR_PARTIAL) + sym = tmp; +#endif +#if ENABLE_DATABAR == 1 + if (TEST_CFG(dcode->databar.config | dcode->databar.config_exp, + ZBAR_CFG_ENABLE) && + (tmp = _zbar_decode_databar(dcode)) > ZBAR_PARTIAL) + sym = tmp; +#endif +#if ENABLE_CODABAR == 1 + if (TEST_CFG(dcode->codabar.config, ZBAR_CFG_ENABLE) && + (tmp = _zbar_decode_codabar(dcode)) > ZBAR_PARTIAL) + sym = tmp; +#endif +#if ENABLE_I25 == 1 + if (TEST_CFG(dcode->i25.config, ZBAR_CFG_ENABLE) && + (tmp = _zbar_decode_i25(dcode)) > ZBAR_PARTIAL) + sym = tmp; +#endif +#if ENABLE_PDF417 == 1 + if (TEST_CFG(dcode->pdf417.config, ZBAR_CFG_ENABLE) && + (tmp = _zbar_decode_pdf417(dcode)) > ZBAR_PARTIAL) + sym = tmp; +#endif + + dcode->idx++; + dcode->type = sym; + if (sym) { + if (dcode->lock && sym > ZBAR_PARTIAL && sym != ZBAR_QRCODE) + release_lock(dcode, sym); + if (dcode->handler) + dcode->handler(dcode); + } + return (sym); +} + +static inline const unsigned int * +decoder_get_configp(const zbar_decoder_t *dcode, zbar_symbol_type_t sym) +{ + const unsigned int *config; + switch (sym) { +#if ENABLE_EAN == 1 + case ZBAR_EAN13: + config = &dcode->ean.ean13_config; + break; + + case ZBAR_EAN2: + config = &dcode->ean.ean2_config; + break; + + case ZBAR_EAN5: + config = &dcode->ean.ean5_config; + break; + + case ZBAR_EAN8: + config = &dcode->ean.ean8_config; + break; + + case ZBAR_UPCA: + config = &dcode->ean.upca_config; + break; + + case ZBAR_UPCE: + config = &dcode->ean.upce_config; + break; + + case ZBAR_ISBN10: + config = &dcode->ean.isbn10_config; + break; + + case ZBAR_ISBN13: + config = &dcode->ean.isbn13_config; + break; +#endif + +#if ENABLE_I25 == 1 + case ZBAR_I25: + config = &dcode->i25.config; + break; +#endif + +#if ENABLE_DATABAR == 1 + case ZBAR_DATABAR: + config = &dcode->databar.config; + break; + case ZBAR_DATABAR_EXP: + config = &dcode->databar.config_exp; + break; +#endif + +#if ENABLE_CODABAR == 1 + case ZBAR_CODABAR: + config = &dcode->codabar.config; + break; +#endif + +#if ENABLE_CODE39 == 1 + case ZBAR_CODE39: + config = &dcode->code39.config; + break; +#endif + +#if ENABLE_CODE93 == 1 + case ZBAR_CODE93: + config = &dcode->code93.config; + break; +#endif + +#if ENABLE_CODE128 == 1 + case ZBAR_CODE128: + config = &dcode->code128.config; + break; +#endif + +#if ENABLE_PDF417 == 1 + case ZBAR_PDF417: + config = &dcode->pdf417.config; + break; +#endif + +#if ENABLE_QRCODE == 1 + case ZBAR_QRCODE: + config = &dcode->qrf.config; + break; +#endif + +#if ENABLE_SQCODE == 1 + case ZBAR_SQCODE: + config = &dcode->sqf.config; + break; +#endif + + default: + config = NULL; + } + return (config); +} + +unsigned int zbar_decoder_get_configs(const zbar_decoder_t *dcode, + zbar_symbol_type_t sym) +{ + const unsigned *config = decoder_get_configp(dcode, sym); + if (!config) + return (0); + return (*config); +} + +static inline int decoder_set_config_bool(zbar_decoder_t *dcode, + zbar_symbol_type_t sym, + zbar_config_t cfg, int val) +{ + unsigned *config = (void *)decoder_get_configp(dcode, sym); + if (!config || cfg >= ZBAR_CFG_NUM) + return (1); + + if (!val) + *config &= ~(1 << cfg); + else if (val == 1) + *config |= (1 << cfg); + else + return (1); + +#if ENABLE_EAN == 1 + dcode->ean.enable = + TEST_CFG(dcode->ean.ean13_config | dcode->ean.ean2_config | + dcode->ean.ean5_config | dcode->ean.ean8_config | + dcode->ean.upca_config | dcode->ean.upce_config | + dcode->ean.isbn10_config | dcode->ean.isbn13_config, + ZBAR_CFG_ENABLE); +#endif + + return (0); +} + +static inline int decoder_set_config_int(zbar_decoder_t *dcode, + zbar_symbol_type_t sym, + zbar_config_t cfg, int val) +{ + switch (sym) { +#if ENABLE_I25 == 1 + case ZBAR_I25: + CFG(dcode->i25, cfg) = val; + break; +#endif +#if ENABLE_CODABAR == 1 + case ZBAR_CODABAR: + CFG(dcode->codabar, cfg) = val; + break; +#endif +#if ENABLE_CODE39 == 1 + case ZBAR_CODE39: + CFG(dcode->code39, cfg) = val; + break; +#endif +#if ENABLE_CODE93 == 1 + case ZBAR_CODE93: + CFG(dcode->code93, cfg) = val; + break; +#endif +#if ENABLE_CODE128 == 1 + case ZBAR_CODE128: + CFG(dcode->code128, cfg) = val; + break; +#endif +#if ENABLE_PDF417 == 1 + case ZBAR_PDF417: + CFG(dcode->pdf417, cfg) = val; + break; +#endif + + default: + return (1); + } + return (0); +} + +int zbar_decoder_get_config(zbar_decoder_t *dcode, zbar_symbol_type_t sym, + zbar_config_t cfg, int *val) +{ + const unsigned *config = decoder_get_configp(dcode, sym); + + /* Return error if symbol doesn't have config */ + if (sym <= ZBAR_PARTIAL || sym > ZBAR_CODE128 || sym == ZBAR_COMPOSITE) + return 1; + + /* Return decoder boolean configs */ + if (cfg < ZBAR_CFG_NUM) { + *val = (*config & (1 << cfg)) != 0; + return 0; + } + + /* Return decoder integer configs */ + if (cfg >= ZBAR_CFG_MIN_LEN && cfg <= ZBAR_CFG_MAX_LEN) { + switch (sym) { +#if ENABLE_I25 == 1 + case ZBAR_I25: + *val = CFG(dcode->i25, cfg); + return 0; +#endif +#if ENABLE_CODABAR == 1 + case ZBAR_CODABAR: + *val = CFG(dcode->codabar, cfg); + return 0; +#endif +#if ENABLE_CODE39 == 1 + case ZBAR_CODE39: + *val = CFG(dcode->code39, cfg); + return 0; +#endif +#if ENABLE_CODE93 == 1 + case ZBAR_CODE93: + *val = CFG(dcode->code93, cfg); + return 0; +#endif +#if ENABLE_CODE128 == 1 + case ZBAR_CODE128: + *val = CFG(dcode->code128, cfg); + return 0; +#endif +#if ENABLE_PDF417 == 1 + case ZBAR_PDF417: + *val = CFG(dcode->pdf417, cfg); + return 0; +#endif + default: + return 1; + } + } + return 1; +} + +int zbar_decoder_set_config(zbar_decoder_t *dcode, zbar_symbol_type_t sym, + zbar_config_t cfg, int val) +{ + if (sym == ZBAR_NONE) { + static const zbar_symbol_type_t all[] = { ZBAR_EAN13, + ZBAR_EAN2, + ZBAR_EAN5, + ZBAR_EAN8, + ZBAR_UPCA, + ZBAR_UPCE, + ZBAR_ISBN10, + ZBAR_ISBN13, + ZBAR_I25, + ZBAR_DATABAR, + ZBAR_DATABAR_EXP, + ZBAR_CODABAR, + ZBAR_CODE39, + ZBAR_CODE93, + ZBAR_CODE128, + ZBAR_QRCODE, + ZBAR_SQCODE, + ZBAR_PDF417, + 0 }; + const zbar_symbol_type_t *symp; + for (symp = all; *symp; symp++) + zbar_decoder_set_config(dcode, *symp, cfg, val); + return (0); + } + + if (cfg >= 0 && cfg < ZBAR_CFG_NUM) + return (decoder_set_config_bool(dcode, sym, cfg, val)); + else if (cfg >= ZBAR_CFG_MIN_LEN && cfg <= ZBAR_CFG_MAX_LEN) + return (decoder_set_config_int(dcode, sym, cfg, val)); + else + return (1); +} + +static char *decoder_dump = NULL; +static unsigned decoder_dumplen = 0; + +const char *_zbar_decoder_buf_dump(unsigned char *buf, unsigned int buflen) +{ + int dumplen = (buflen * 3) + 12; + char *p; + int i; + + if (!decoder_dump || dumplen > decoder_dumplen) { + if (decoder_dump) + free(decoder_dump); + decoder_dump = malloc(dumplen); + decoder_dumplen = dumplen; + } + p = decoder_dump + + snprintf(decoder_dump, 12, + "buf[%04x]=", (buflen > 0xffff) ? 0xffff : buflen); + for (i = 0; i < buflen; i++) + p += snprintf(p, 4, "%s%02x", (i) ? " " : "", buf[i]); + return (decoder_dump); +} diff --git a/zbar/decoder.h b/zbar/decoder.h new file mode 100644 index 0000000..260dc43 --- /dev/null +++ b/zbar/decoder.h @@ -0,0 +1,292 @@ +/*------------------------------------------------------------------------ + * 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 + *------------------------------------------------------------------------*/ +#ifndef _DECODER_H_ +#define _DECODER_H_ + +#include "config.h" +#include <limits.h> +#include <stdlib.h> /* realloc */ + +#include <zbar.h> + +#include "debug.h" + +#define NUM_CFGS (ZBAR_CFG_MAX_LEN - ZBAR_CFG_MIN_LEN + 1) + +#if ENABLE_EAN == 1 +#include "decoder/ean.h" +#endif +#if ENABLE_I25 == 1 +#include "decoder/i25.h" +#endif +#if ENABLE_DATABAR == 1 +#include "decoder/databar.h" +#endif +#if ENABLE_CODABAR == 1 +#include "decoder/codabar.h" +#endif +#if ENABLE_CODE39 == 1 +#include "decoder/code39.h" +#endif +#if ENABLE_CODE93 == 1 +#include "decoder/code93.h" +#endif +#if ENABLE_CODE128 == 1 +#include "decoder/code128.h" +#endif +#if ENABLE_PDF417 == 1 +#include "decoder/pdf417.h" +#endif +#if ENABLE_QRCODE == 1 +#include "decoder/qr_finder.h" +#endif +#if ENABLE_SQCODE == 1 +#include "decoder/sq_finder.h" +#endif + +/* size of bar width history (implementation assumes power of two) */ +#ifndef DECODE_WINDOW +#define DECODE_WINDOW 16 +#endif + +/* initial data buffer allocation */ +#ifndef BUFFER_MIN +#define BUFFER_MIN 0x20 +#endif + +/* maximum data buffer allocation + * (longer symbols are rejected) + */ +#ifndef BUFFER_MAX +#define BUFFER_MAX 0x100 +#endif + +/* buffer allocation increment */ +#ifndef BUFFER_INCR +#define BUFFER_INCR 0x10 +#endif + +#define CFG(dcode, cfg) ((dcode).configs[(cfg)-ZBAR_CFG_MIN_LEN]) +#define TEST_CFG(config, cfg) (((config) >> (cfg)) & 1) +#define MOD(mod) (1 << (mod)) + +/* symbology independent decoder state */ +struct zbar_decoder_s { + unsigned char idx; /* current width index */ + unsigned w[DECODE_WINDOW]; /* window of last N bar widths */ + zbar_symbol_type_t type; /* type of last decoded data */ + zbar_symbol_type_t lock; /* buffer lock */ + unsigned modifiers; /* symbology modifier */ + int direction; /* direction of last decoded data */ + unsigned s6; /* 6-element character width */ + + /* everything above here is automatically reset */ + unsigned buf_alloc; /* dynamic buffer allocation */ + unsigned buflen; /* binary data length */ + unsigned char *buf; /* decoded characters */ + void *userdata; /* application data */ + zbar_decoder_handler_t *handler; /* application callback */ + + /* symbology specific state */ +#if ENABLE_EAN == 1 + ean_decoder_t ean; /* EAN/UPC parallel decode attempts */ +#endif +#if ENABLE_I25 == 1 + i25_decoder_t i25; /* Interleaved 2 of 5 decode state */ +#endif +#if ENABLE_DATABAR == 1 + databar_decoder_t databar; /* DataBar decode state */ +#endif +#if ENABLE_CODABAR == 1 + codabar_decoder_t codabar; /* Codabar decode state */ +#endif +#if ENABLE_CODE39 == 1 + code39_decoder_t code39; /* Code 39 decode state */ +#endif +#if ENABLE_CODE93 == 1 + code93_decoder_t code93; /* Code 93 decode state */ +#endif +#if ENABLE_CODE128 == 1 + code128_decoder_t code128; /* Code 128 decode state */ +#endif +#if ENABLE_PDF417 == 1 + pdf417_decoder_t pdf417; /* PDF417 decode state */ +#endif +#if ENABLE_QRCODE == 1 + qr_finder_t qrf; /* QR Code finder state */ +#endif +#if ENABLE_SQCODE == 1 + sq_finder_t sqf; /* SQ Code finder state */ +#endif +}; + +/* return current element color */ +static inline char get_color(const zbar_decoder_t *dcode) +{ + return (dcode->idx & 1); +} + +/* retrieve i-th previous element width */ +static inline unsigned get_width(const zbar_decoder_t *dcode, + unsigned char offset) +{ + return (dcode->w[(dcode->idx - offset) & (DECODE_WINDOW - 1)]); +} + +/* retrieve bar+space pair width starting at offset i */ +static inline unsigned pair_width(const zbar_decoder_t *dcode, + unsigned char offset) +{ + return (get_width(dcode, offset) + get_width(dcode, offset + 1)); +} + +/* calculate total character width "s" + * - start of character identified by context sensitive offset + * (<= DECODE_WINDOW - n) + * - size of character is n elements + */ +static inline unsigned calc_s(const zbar_decoder_t *dcode, unsigned char offset, + unsigned char n) +{ + /* FIXME check that this gets unrolled for constant n */ + unsigned s = 0; + while (n--) + s += get_width(dcode, offset++); + return (s); +} + +/* fixed character width decode assist + * bar+space width are compared as a fraction of the reference dimension "x" + * - +/- 1/2 x tolerance + * - measured total character width (s) compared to symbology baseline (n) + * (n = 7 for EAN/UPC, 11 for Code 128) + * - bar+space *pair width* "e" is used to factor out bad "exposures" + * ("blooming" or "swelling" of dark or light areas) + * => using like-edge measurements avoids these issues + * - n should be > 3 + */ +static inline int decode_e(unsigned e, unsigned s, unsigned n) +{ + /* result is encoded number of units - 2 + * (for use as zero based index) + * or -1 if invalid + */ + unsigned char E = ((e * n * 2 + 1) / s - 3) / 2; + return ((E >= n - 3) ? -1 : E); +} + +/* sort three like-colored elements and return ordering + */ +static inline unsigned decode_sort3(zbar_decoder_t *dcode, int i0) +{ + unsigned w0 = get_width(dcode, i0); + unsigned w2 = get_width(dcode, i0 + 2); + unsigned w4 = get_width(dcode, i0 + 4); + if (w0 < w2) { + if (w2 < w4) + return ((i0 << 8) | ((i0 + 2) << 4) | (i0 + 4)); + if (w0 < w4) + return ((i0 << 8) | ((i0 + 4) << 4) | (i0 + 2)); + return (((i0 + 4) << 8) | (i0 << 4) | (i0 + 2)); + } + if (w4 < w2) + return (((i0 + 4) << 8) | ((i0 + 2) << 4) | i0); + if (w0 < w4) + return (((i0 + 2) << 8) | (i0 << 4) | (i0 + 4)); + return (((i0 + 2) << 8) | ((i0 + 4) << 4) | i0); +} + +/* sort N like-colored elements and return ordering + */ +static inline unsigned decode_sortn(zbar_decoder_t *dcode, int n, int i0) +{ + unsigned mask = 0, sort = 0; + int i; + for (i = n - 1; i >= 0; i--) { + unsigned wmin = UINT_MAX; + int jmin = -1, j; + for (j = n - 1; j >= 0; j--) { + unsigned w; + if ((mask >> j) & 1) + continue; + w = get_width(dcode, i0 + j * 2); + if (wmin >= w) { + wmin = w; + jmin = j; + } + } + zassert(jmin >= 0, 0, "sortn(%d,%d) jmin=%d", n, i0, jmin); + sort <<= 4; + mask |= 1 << jmin; + sort |= i0 + jmin * 2; + } + return (sort); +} + +/* acquire shared state lock */ +static inline char acquire_lock(zbar_decoder_t *dcode, zbar_symbol_type_t req) +{ + if (dcode->lock) { + dbprintf(2, " [locked %d]\n", dcode->lock); + return (1); + } + dcode->lock = req; + return (0); +} + +/* check and release shared state lock */ +static inline char release_lock(zbar_decoder_t *dcode, zbar_symbol_type_t req) +{ + zassert(dcode->lock == req, 1, "lock=%d req=%d\n", dcode->lock, req); + dcode->lock = 0; + return (0); +} + +/* ensure output buffer has sufficient allocation for request */ +static inline char size_buf(zbar_decoder_t *dcode, unsigned len) +{ + unsigned char *buf; + if (len <= BUFFER_MIN) + return (0); + if (len < dcode->buf_alloc) + /* FIXME size reduction heuristic? */ + return (0); + if (len > BUFFER_MAX) + return (1); + if (len < dcode->buf_alloc + BUFFER_INCR) { + len = dcode->buf_alloc + BUFFER_INCR; + if (len > BUFFER_MAX) + len = BUFFER_MAX; + } + buf = realloc(dcode->buf, len); + if (!buf) + return (1); + dcode->buf = buf; + dcode->buf_alloc = len; + return (0); +} + +extern const char *_zbar_decoder_buf_dump(unsigned char *buf, + unsigned int buflen); + +#endif diff --git a/zbar/decoder/codabar.c b/zbar/decoder/codabar.c new file mode 100644 index 0000000..6e2c74a --- /dev/null +++ b/zbar/decoder/codabar.c @@ -0,0 +1,397 @@ +/*------------------------------------------------------------------------ + * Copyright 2011 (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" +#include <string.h> /* memmove */ + +#include <zbar.h> + +#ifdef DEBUG_CODABAR +#define DEBUG_LEVEL (DEBUG_CODABAR) +#endif +#include "debug.h" +#include "decoder.h" + +#define NIBUF 6 /* initial scan buffer size */ + +static const signed char codabar_lo[12] = { 0x0, 0x1, 0x4, 0x5, 0x2, 0xa, + 0xb, 0x9, 0x6, 0x7, 0x8, 0x3 }; + +static const unsigned char codabar_hi[8] = { 0x1, 0x4, 0x7, 0x6, + 0x2, 0x3, 0x0, 0x5 }; + +static const unsigned char codabar_characters[20] = "0123456789-$:/.+ABCD"; + +static inline int check_width(unsigned ref, unsigned w) +{ + unsigned dref = ref; + ref *= 4; + w *= 4; + return (ref - dref <= w && w <= ref + dref); +} + +static inline signed char codabar_decode7(zbar_decoder_t *dcode) +{ + unsigned ibar, wbmax, wbmin, wb1, wb2; + unsigned long b0b3, b1b2; + unsigned ispc, wsmax, wsmid, wsmin; + unsigned long s0s2, s1s1; + codabar_decoder_t *codabar = &dcode->codabar; + unsigned s = codabar->s7; + dbprintf(2, " s=%d", s); + if (s < 7) + return (-1); + + /* check width */ + if (!check_width(codabar->width, s)) { + dbprintf(2, " [width]"); + return (-1); + } + + /* extract min/max bar */ + ibar = decode_sortn(dcode, 4, 1); + dbprintf(2, " bar=%04x", ibar); + + wbmax = get_width(dcode, ibar & 0xf); + wbmin = get_width(dcode, ibar >> 12); + if (8 * wbmin < wbmax || 3 * wbmin > 2 * wbmax) { + dbprintf(2, " [bar outer ratio]"); + return (-1); + } + + wb1 = get_width(dcode, (ibar >> 8) & 0xf); + wb2 = get_width(dcode, (ibar >> 4) & 0xf); + b0b3 = wbmin * wbmax; + b1b2 = wb1 * wb2; + if (b1b2 + b1b2 / 8 < b0b3) { + /* single wide bar combinations */ + if (8 * wbmin < 5 * wb1 || 8 * wb1 < 5 * wb2 || 4 * wb2 > 3 * wbmax || + wb2 * wb2 >= wb1 * wbmax) { + dbprintf(2, " [1bar inner ratios]"); + return (-1); + } + ibar = (ibar >> 1) & 0x3; + } else if (b1b2 > b0b3 + b0b3 / 8) { + /* three wide bars, no wide spaces */ + if (4 * wbmin > 3 * wb1 || 8 * wb1 < 5 * wb2 || 8 * wb2 < 5 * wbmax || + wbmin * wb2 >= wb1 * wb1) { + dbprintf(2, " [3bar inner ratios]"); + return (-1); + } + ibar = (ibar >> 13) + 4; + } else { + dbprintf(2, " [bar inner ratios]"); + return (-1); + } + + ispc = decode_sort3(dcode, 2); + dbprintf(2, "(%x) spc=%03x", ibar, ispc); + + wsmax = get_width(dcode, ispc & 0xf); + wsmid = get_width(dcode, (ispc >> 4) & 0xf); + wsmin = get_width(dcode, (ispc >> 8) & 0xf); + if (ibar >> 2) { + int c; + /* verify no wide spaces */ + if (8 * wsmin < wsmax || 8 * wsmin < 5 * wsmid || + 8 * wsmid < 5 * wsmax) { + dbprintf(2, " [0space inner ratios]"); + return (-1); + } + ibar &= 0x3; + if (codabar->direction) + ibar = 3 - ibar; + c = (0xfcde >> (ibar << 2)) & 0xf; + dbprintf(2, " ex[%d]=%x", ibar, c); + return (c); + } else if (8 * wsmin < wsmax || 3 * wsmin > 2 * wsmax) { + dbprintf(2, " [space outer ratio]"); + return (-1); + } + + s0s2 = wsmin * wsmax; + s1s1 = wsmid * wsmid; + if (s1s1 + s1s1 / 8 < s0s2) { + unsigned ic; + int c; + /* single wide space */ + if (8 * wsmin < 5 * wsmid || 4 * wsmid > 3 * wsmax) { + dbprintf(2, " [1space inner ratios]"); + return (-1); + } + ispc = ((ispc & 0xf) >> 1) - 1; + ic = (ispc << 2) | ibar; + if (codabar->direction) + ic = 11 - ic; + c = codabar_lo[ic]; + dbprintf(2, "(%d) lo[%d]=%x", ispc, ic, c); + return (c); + } else if (s1s1 > s0s2 + s0s2 / 8) { + unsigned ic; + unsigned char c; + /* two wide spaces, check start/stop */ + if (4 * wsmin > 3 * wsmid || 8 * wsmid < 5 * wsmax) { + dbprintf(2, " [2space inner ratios]"); + return (-1); + } + if ((ispc >> 8) == 4) { + dbprintf(2, " [space comb]"); + return (-1); + } + ispc >>= 10; + dbprintf(2, "(%d)", ispc); + ic = ispc * 4 + ibar; + zassert(ic < 8, -1, "ic=%d ispc=%d ibar=%d", ic, ispc, ibar); + c = codabar_hi[ic]; + if (c >> 2 != codabar->direction) { + dbprintf(2, " [invalid stop]"); + return (-1); + } + c = (c & 0x3) | 0x10; + dbprintf(2, " hi[%d]=%x", ic, c); + return (c); + } else { + dbprintf(2, " [space inner ratios]"); + return (-1); + } +} + +static inline signed char codabar_decode_start(zbar_decoder_t *dcode) +{ + unsigned qz, ispc, wsmax, wsmin, wsmid, ibar, wbmax, wbmin, wb1, wb2; + int ic, c; + codabar_decoder_t *codabar = &dcode->codabar; + unsigned s = codabar->s7; + if (s < 8) + return (ZBAR_NONE); + dbprintf(2, " codabar: s=%d", s); + + /* check leading quiet zone - spec is 10x */ + qz = get_width(dcode, 8); + if ((qz && qz * 2 < s) || 4 * get_width(dcode, 0) > 3 * s) { + dbprintf(2, " [invalid qz/ics]\n"); + return (ZBAR_NONE); + } + + /* check space ratios first */ + ispc = decode_sort3(dcode, 2); + dbprintf(2, " spc=%03x", ispc); + if ((ispc >> 8) == 4) { + dbprintf(2, " [space comb]\n"); + return (ZBAR_NONE); + } + + /* require 2 wide and 1 narrow spaces */ + wsmax = get_width(dcode, ispc & 0xf); + wsmin = get_width(dcode, ispc >> 8); + wsmid = get_width(dcode, (ispc >> 4) & 0xf); + if (8 * wsmin < wsmax || 3 * wsmin > 2 * wsmax || 4 * wsmin > 3 * wsmid || + 8 * wsmid < 5 * wsmax || wsmid * wsmid <= wsmax * wsmin) { + dbprintf(2, " [space ratio]\n"); + return (ZBAR_NONE); + } + ispc >>= 10; + dbprintf(2, "(%d)", ispc); + + /* check bar ratios */ + ibar = decode_sortn(dcode, 4, 1); + dbprintf(2, " bar=%04x", ibar); + + wbmax = get_width(dcode, ibar & 0xf); + wbmin = get_width(dcode, ibar >> 12); + if (8 * wbmin < wbmax || 3 * wbmin > 2 * wbmax) { + dbprintf(2, " [bar outer ratio]\n"); + return (ZBAR_NONE); + } + + /* require 1 wide & 3 narrow bars */ + wb1 = get_width(dcode, (ibar >> 8) & 0xf); + wb2 = get_width(dcode, (ibar >> 4) & 0xf); + if (8 * wbmin < 5 * wb1 || 8 * wb1 < 5 * wb2 || 4 * wb2 > 3 * wbmax || + wb1 * wb2 >= wbmin * wbmax || wb2 * wb2 >= wb1 * wbmax) { + dbprintf(2, " [bar inner ratios]\n"); + return (ZBAR_NONE); + } + ibar = ((ibar & 0xf) - 1) >> 1; + dbprintf(2, "(%d)", ibar); + + /* decode combination */ + ic = ispc * 4 + ibar; + zassert(ic < 8, ZBAR_NONE, "ic=%d ispc=%d ibar=%d", ic, ispc, ibar); + c = codabar_hi[ic]; + codabar->buf[0] = (c & 0x3) | 0x10; + + /* set character direction */ + codabar->direction = c >> 2; + + codabar->element = 4; + codabar->character = 1; + codabar->width = codabar->s7; + dbprintf(1, " start=%c dir=%x [valid start]\n", codabar->buf[0] + 0x31, + codabar->direction); + return (ZBAR_PARTIAL); +} + +static inline int codabar_checksum(zbar_decoder_t *dcode, unsigned n) +{ + unsigned chk = 0; + unsigned char *buf = dcode->buf; + while (n--) + chk += *(buf++); + return (!!(chk & 0xf)); +} + +static inline zbar_symbol_type_t codabar_postprocess(zbar_decoder_t *dcode) +{ + int dir, i, n; + codabar_decoder_t *codabar = &dcode->codabar; + dir = codabar->direction; + dcode->direction = 1 - 2 * dir; + n = codabar->character; + for (i = 0; i < NIBUF; i++) + dcode->buf[i] = codabar->buf[i]; + if (dir) + /* reverse buffer */ + for (i = 0; i < n / 2; i++) { + unsigned j = n - 1 - i; + char code = dcode->buf[i]; + dcode->buf[i] = dcode->buf[j]; + dcode->buf[j] = code; + } + + if (TEST_CFG(codabar->config, ZBAR_CFG_ADD_CHECK)) { + /* validate checksum */ + if (codabar_checksum(dcode, n)) + return (ZBAR_NONE); + if (!TEST_CFG(codabar->config, ZBAR_CFG_EMIT_CHECK)) { + dcode->buf[n - 2] = dcode->buf[n - 1]; + n--; + } + } + + for (i = 0; i < n; i++) { + unsigned c = dcode->buf[i]; + dcode->buf[i] = ((c < 0x14) ? codabar_characters[c] : '?'); + } + dcode->buflen = i; + dcode->buf[i] = '\0'; + dcode->modifiers = 0; + + codabar->character = -1; + return (ZBAR_CODABAR); +} + +zbar_symbol_type_t _zbar_decode_codabar(zbar_decoder_t *dcode) +{ + signed char c; + unsigned char *buf; + unsigned s; + codabar_decoder_t *codabar = &dcode->codabar; + + /* update latest character width */ + codabar->s7 -= get_width(dcode, 8); + codabar->s7 += get_width(dcode, 1); + + if (get_color(dcode) != ZBAR_SPACE) + return (ZBAR_NONE); + if (codabar->character < 0) + return (codabar_decode_start(dcode)); + if (codabar->character < 2 && codabar_decode_start(dcode)) + return (ZBAR_PARTIAL); + if (--codabar->element) + return (ZBAR_NONE); + codabar->element = 4; + + dbprintf(1, " codabar[%c%02d+%x]", (codabar->direction) ? '<' : '>', + codabar->character, codabar->element); + + c = codabar_decode7(dcode); + dbprintf(1, " %d", c); + if (c < 0) { + dbprintf(1, " [aborted]\n"); + goto reset; + } + + if (codabar->character < NIBUF) + buf = codabar->buf; + else { + if (codabar->character >= BUFFER_MIN && + size_buf(dcode, codabar->character + 1)) { + dbprintf(1, " [overflow]\n"); + goto reset; + } + buf = dcode->buf; + } + buf[codabar->character++] = c; + + /* lock shared resources */ + if (codabar->character == NIBUF && acquire_lock(dcode, ZBAR_CODABAR)) { + codabar->character = -1; + return (ZBAR_PARTIAL); + } + + s = codabar->s7; + if (c & 0x10) { + zbar_symbol_type_t sym; + unsigned n; + unsigned qz = get_width(dcode, 0); + if (qz && qz * 2 < s) { + dbprintf(2, " [invalid qz]\n"); + goto reset; + } + n = codabar->character; + if (n < CFG(*codabar, ZBAR_CFG_MIN_LEN) || + (CFG(*codabar, ZBAR_CFG_MAX_LEN) > 0 && + n > CFG(*codabar, ZBAR_CFG_MAX_LEN))) { + dbprintf(2, " [invalid len]\n"); + goto reset; + } + if (codabar->character < NIBUF && acquire_lock(dcode, ZBAR_CODABAR)) { + codabar->character = -1; + return (ZBAR_PARTIAL); + } + dbprintf(2, " stop=%c", c + 0x31); + + sym = codabar_postprocess(dcode); + if (sym > ZBAR_PARTIAL) + dbprintf(2, " [valid stop]"); + else { + release_lock(dcode, ZBAR_CODABAR); + codabar->character = -1; + } + dbprintf(2, "\n"); + return (sym); + } else if (4 * get_width(dcode, 0) > 3 * s) { + dbprintf(2, " [ics]\n"); + goto reset; + } + + dbprintf(2, "\n"); + return (ZBAR_NONE); + +reset: + if (codabar->character >= NIBUF) + release_lock(dcode, ZBAR_CODABAR); + codabar->character = -1; + return (ZBAR_NONE); +} diff --git a/zbar/decoder/codabar.h b/zbar/decoder/codabar.h new file mode 100644 index 0000000..30ad022 --- /dev/null +++ b/zbar/decoder/codabar.h @@ -0,0 +1,51 @@ +/*------------------------------------------------------------------------ + * Copyright 2011 (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 + *------------------------------------------------------------------------*/ +#ifndef _CODABAR_H_ +#define _CODABAR_H_ + +/* Codabar specific decode state */ +typedef struct codabar_decoder_s { + unsigned direction : 1; /* scan direction: 0=fwd, 1=rev */ + unsigned element : 4; /* element offset 0-7 */ + int character : 12; /* character position in symbol */ + unsigned s7; /* current character width */ + unsigned width; /* last character width */ + unsigned char buf[6]; /* initial scan buffer */ + + unsigned config; + int configs[NUM_CFGS]; /* int valued configurations */ +} codabar_decoder_t; + +/* reset Codabar specific state */ +static inline void codabar_reset(codabar_decoder_t *codabar) +{ + codabar->direction = 0; + codabar->element = 0; + codabar->character = -1; + codabar->s7 = 0; +} + +/* decode Codabar symbols */ +zbar_symbol_type_t _zbar_decode_codabar(zbar_decoder_t *dcode); + +#endif diff --git a/zbar/decoder/code128.c b/zbar/decoder/code128.c new file mode 100644 index 0000000..c9a6b09 --- /dev/null +++ b/zbar/decoder/code128.c @@ -0,0 +1,582 @@ +/*------------------------------------------------------------------------ + * 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" +#include <string.h> /* memmove */ + +#include <zbar.h> + +#ifdef DEBUG_CODE128 +#define DEBUG_LEVEL (DEBUG_CODE128) +#endif +#include "debug.h" +#include "decoder.h" + +#define NUM_CHARS 108 /* total number of character codes */ + +typedef enum code128_char_e +{ + FNC3 = 0x60, + FNC2 = 0x61, + SHIFT = 0x62, + CODE_C = 0x63, + CODE_B = 0x64, + CODE_A = 0x65, + FNC1 = 0x66, + START_A = 0x67, + START_B = 0x68, + START_C = 0x69, + STOP_FWD = 0x6a, + STOP_REV = 0x6b, + FNC4 = 0x6c, +} code128_char_t; + +static const unsigned char characters[NUM_CHARS] = { + 0x5c, 0xbf, 0xa1, /* [00] 00 */ + 0x2a, 0xc5, 0x0c, 0xa4, /* [03] 01 */ + 0x2d, 0xe3, 0x0f, /* [07] 02 */ + 0x5f, 0xe4, /* [0a] 03 */ + + 0x6b, 0xe8, 0x69, 0xa7, 0xe7, /* [0c] 10 */ + 0xc1, 0x51, 0x1e, 0x83, 0xd9, 0x00, 0x84, 0x1f, /* [11] 11 */ + 0xc7, 0x0d, 0x33, 0x86, 0xb5, 0x0e, 0x15, 0x87, /* [19] 12 */ + 0x10, 0xda, 0x11, /* [21] 13 */ + + 0x36, 0xe5, 0x18, 0x37, /* [24] 20 */ + 0xcc, 0x13, 0x39, 0x89, 0x97, 0x14, 0x1b, 0x8a, 0x3a, 0xbd, /* [28] 21 */ + 0xa2, 0x5e, 0x01, 0x85, 0xb0, 0x02, 0xa3, /* [32] 22 */ + 0xa5, 0x2c, 0x16, 0x88, 0xbc, 0x12, 0xa6, /* [39] 23 */ + + 0x61, 0xe6, 0x56, 0x62, /* [40] 30 */ + 0x19, 0xdb, 0x1a, /* [44] 31 */ + 0xa8, 0x32, 0x1c, 0x8b, 0xcd, 0x1d, 0xa9, /* [47] 32 */ + 0xc3, 0x20, 0xc4, /* [4e] 33 */ + + 0x50, 0x5d, 0xc0, /* [51] 0014 0025 0034 */ + 0x2b, 0xc6, /* [54] 0134 0143 */ + 0x2e, /* [56] 0243 */ + 0x53, 0x60, /* [57] 0341 0352 */ + 0x31, /* [59] 1024 */ + 0x52, 0xc2, /* [5a] 1114 1134 */ + 0x34, 0xc8, /* [5c] 1242 1243 */ + 0x55, /* [5e] 1441 */ + + 0x57, 0x3e, 0xce, /* [5f] 4100 5200 4300 */ + 0x3b, 0xc9, /* [62] 4310 3410 */ + 0x6a, /* [64] 3420 */ + 0x54, 0x4f, /* [65] 1430 2530 */ + 0x38, /* [67] 4201 */ + 0x58, 0xcb, /* [68] 4111 4311 */ + 0x2f, 0xca, /* [6a] 2421 3421 */ +}; + +static const unsigned char lo_base[8] = { 0x00, 0x07, 0x0c, 0x19, + 0x24, 0x32, 0x40, 0x47 }; + +static const unsigned char lo_offset[0x80] = { + 0xff, 0xf0, 0xff, 0x1f, 0xff, 0xf2, 0xff, 0xff, /* 00 [00] */ + 0xff, 0xff, 0xff, 0x3f, 0xf4, 0xf5, 0xff, 0x6f, /* 01 */ + 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf1, 0xff, 0x2f, /* 02 [07] */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x4f, /* 03 */ + 0xff, 0x0f, 0xf1, 0xf2, 0xff, 0x3f, 0xff, 0xf4, /* 10 [0c] */ + 0xf5, 0xf6, 0xf7, 0x89, 0xff, 0xab, 0xff, 0xfc, /* 11 */ + 0xff, 0xff, 0x0f, 0x1f, 0x23, 0x45, 0xf6, 0x7f, /* 12 [19] */ + 0xff, 0xff, 0xff, 0xff, 0xf8, 0xff, 0xf9, 0xaf, /* 13 */ + + 0xf0, 0xf1, 0xff, 0x2f, 0xff, 0xf3, 0xff, 0xff, /* 20 [24] */ + 0x4f, 0x5f, 0x67, 0x89, 0xfa, 0xbf, 0xff, 0xcd, /* 21 */ + 0xf0, 0xf1, 0xf2, 0x3f, 0xf4, 0x56, 0xff, 0xff, /* 22 [32] */ + 0xff, 0xff, 0x7f, 0x8f, 0x9a, 0xff, 0xbc, 0xdf, /* 23 */ + 0x0f, 0x1f, 0xf2, 0xff, 0xff, 0x3f, 0xff, 0xff, /* 30 [40] */ + 0xf4, 0xff, 0xf5, 0x6f, 0xff, 0xff, 0xff, 0xff, /* 31 */ + 0x0f, 0x1f, 0x23, 0xff, 0x45, 0x6f, 0xff, 0xff, /* 32 [47] */ + 0xf7, 0xff, 0xf8, 0x9f, 0xff, 0xff, 0xff, 0xff, /* 33 */ +}; + +static inline signed char decode_lo(int sig) +{ + unsigned char offset = (((sig >> 1) & 0x01) | ((sig >> 3) & 0x06) | + ((sig >> 5) & 0x18) | ((sig >> 7) & 0x60)); + unsigned char idx = lo_offset[offset]; + unsigned char base, c; + + if (sig & 1) + idx &= 0xf; + else + idx >>= 4; + if (idx == 0xf) + return (-1); + + base = (sig >> 11) | ((sig >> 9) & 1); + zassert(base < 8, -1, "sig=%x offset=%x idx=%x base=%x\n", sig, offset, idx, + base); + idx += lo_base[base]; + + zassert(idx <= 0x50, -1, "sig=%x offset=%x base=%x idx=%x\n", sig, offset, + base, idx); + c = characters[idx]; + dbprintf(2, " %02x(%x(%02x)/%x(%02x)) => %02x", idx, base, lo_base[base], + offset, lo_offset[offset], (unsigned char)c); + return (c); +} + +static inline signed char decode_hi(int sig) +{ + unsigned char rev = (sig & 0x4400) != 0; + unsigned char idx, c; + if (rev) + sig = (((sig >> 12) & 0x000f) | ((sig >> 4) & 0x00f0) | + ((sig << 4) & 0x0f00) | ((sig << 12) & 0xf000)); + dbprintf(2, " rev=%x", rev != 0); + + switch (sig) { + case 0x0014: + idx = 0x0; + break; + case 0x0025: + idx = 0x1; + break; + case 0x0034: + idx = 0x2; + break; + case 0x0134: + idx = 0x3; + break; + case 0x0143: + idx = 0x4; + break; + case 0x0243: + idx = 0x5; + break; + case 0x0341: + idx = 0x6; + break; + case 0x0352: + idx = 0x7; + break; + case 0x1024: + idx = 0x8; + break; + case 0x1114: + idx = 0x9; + break; + case 0x1134: + idx = 0xa; + break; + case 0x1242: + idx = 0xb; + break; + case 0x1243: + idx = 0xc; + break; + case 0x1441: + idx = 0xd; + rev = 0; + break; + default: + return (-1); + } + if (rev) + idx += 0xe; + c = characters[0x51 + idx]; + dbprintf(2, " %02x => %02x", idx, c); + return (c); +} + +static inline unsigned char calc_check(unsigned char c) +{ + if (!(c & 0x80)) + return (0x18); + c &= 0x7f; + if (c < 0x3d) + return ((c < 0x30 && c != 0x17) ? 0x10 : 0x20); + if (c < 0x50) + return ((c == 0x4d) ? 0x20 : 0x10); + return ((c < 0x67) ? 0x20 : 0x10); +} + +static inline signed char decode6(zbar_decoder_t *dcode) +{ + int sig; + signed char c, chk; + unsigned bars; + + /* build edge signature of character */ + unsigned s = dcode->code128.s6; + + dbprintf(2, " s=%d", s); + if (s < 5) + return (-1); + /* calculate similar edge measurements */ + sig = + (get_color(dcode) == ZBAR_BAR) ? + ((decode_e(get_width(dcode, 0) + get_width(dcode, 1), s, 11) + << 12) | + (decode_e(get_width(dcode, 1) + get_width(dcode, 2), s, 11) << 8) | + (decode_e(get_width(dcode, 2) + get_width(dcode, 3), s, 11) << 4) | + (decode_e(get_width(dcode, 3) + get_width(dcode, 4), s, 11))) : + ((decode_e(get_width(dcode, 5) + get_width(dcode, 4), s, 11) + << 12) | + (decode_e(get_width(dcode, 4) + get_width(dcode, 3), s, 11) << 8) | + (decode_e(get_width(dcode, 3) + get_width(dcode, 2), s, 11) << 4) | + (decode_e(get_width(dcode, 2) + get_width(dcode, 1), s, 11))); + if (sig < 0) + return (-1); + dbprintf(2, " sig=%04x", sig); + /* lookup edge signature */ + c = (sig & 0x4444) ? decode_hi(sig) : decode_lo(sig); + if (c == -1) + return (-1); + + /* character validation */ + bars = + (get_color(dcode) == ZBAR_BAR) ? + (get_width(dcode, 0) + get_width(dcode, 2) + get_width(dcode, 4)) : + (get_width(dcode, 1) + get_width(dcode, 3) + get_width(dcode, 5)); + bars = bars * 11 * 4 / s; + chk = calc_check(c); + dbprintf(2, " bars=%d chk=%d", bars, chk); + if (chk - 7 > bars || bars > chk + 7) + return (-1); + + return (c & 0x7f); +} + +static inline unsigned char validate_checksum(zbar_decoder_t *dcode) +{ + unsigned idx, sum, i, acc = 0; + unsigned char check, err; + + code128_decoder_t *dcode128 = &dcode->code128; + if (dcode128->character < 3) + return (1); + + /* add in irregularly weighted start character */ + idx = (dcode128->direction) ? dcode128->character - 1 : 0; + sum = dcode->buf[idx]; + if (sum >= 103) + sum -= 103; + + /* calculate sum in reverse to avoid multiply operations */ + for (i = dcode128->character - 3; i; i--) { + zassert(sum < 103, -1, "dir=%x i=%x sum=%x acc=%x %s\n", + dcode128->direction, i, sum, acc, + _zbar_decoder_buf_dump(dcode->buf, dcode128->character)); + idx = (dcode128->direction) ? dcode128->character - 1 - i : i; + acc += dcode->buf[idx]; + if (acc >= 103) + acc -= 103; + zassert(acc < 103, -1, "dir=%x i=%x sum=%x acc=%x %s\n", + dcode128->direction, i, sum, acc, + _zbar_decoder_buf_dump(dcode->buf, dcode128->character)); + sum += acc; + if (sum >= 103) + sum -= 103; + } + + /* and compare to check character */ + idx = (dcode128->direction) ? 1 : dcode128->character - 2; + check = dcode->buf[idx]; + dbprintf(2, " chk=%02x(%02x)", sum, check); + err = (sum != check); + if (err) + dbprintf(1, " [checksum error]\n"); + return (err); +} + +/* expand and decode character set C */ +static inline unsigned postprocess_c(zbar_decoder_t *dcode, unsigned start, + unsigned end, unsigned dst) +{ + unsigned i, j; + + /* expand buffer to accommodate 2x set C characters (2 digits per-char) */ + unsigned delta = end - start; + unsigned newlen = dcode->code128.character + delta; + if (size_buf(dcode, newlen)) { + dbprintf(2, " [overflow]\n"); + return ZBAR_NONE; + } + + /* relocate unprocessed data to end of buffer */ + memmove(dcode->buf + start + delta, dcode->buf + start, + dcode->code128.character - start); + dcode->code128.character = newlen; + + for (i = 0, j = dst; i < delta; i++, j += 2) { + /* convert each set C character into two ASCII digits */ + unsigned char code = dcode->buf[start + delta + i]; + dcode->buf[j] = '0'; + if (code >= 50) { + code -= 50; + dcode->buf[j] += 5; + } + if (code >= 30) { + code -= 30; + dcode->buf[j] += 3; + } + if (code >= 20) { + code -= 20; + dcode->buf[j] += 2; + } + if (code >= 10) { + code -= 10; + dcode->buf[j] += 1; + } + zassert(dcode->buf[j] <= '9', delta, "start=%x end=%x i=%x j=%x %s\n", + start, end, i, j, + _zbar_decoder_buf_dump(dcode->buf, dcode->code128.character)); + zassert(code <= 9, delta, "start=%x end=%x i=%x j=%x %s\n", start, end, + i, j, + _zbar_decoder_buf_dump(dcode->buf, dcode->code128.character)); + dcode->buf[j + 1] = '0' + code; + } + return (delta); +} + +/* resolve scan direction and convert to ASCII */ +static inline unsigned char postprocess(zbar_decoder_t *dcode) +{ + unsigned i, j, cexp; + unsigned char code = 0, charset; + code128_decoder_t *dcode128 = &dcode->code128; + dbprintf(2, "\n postproc len=%d", dcode128->character); + dcode->modifiers = 0; + dcode->direction = 1 - 2 * dcode128->direction; + if (dcode128->direction) { + /* reverse buffer */ + dbprintf(2, " (rev)"); + for (i = 0; i < dcode128->character / 2; i++) { + unsigned j = dcode128->character - 1 - i; + code = dcode->buf[i]; + dcode->buf[i] = dcode->buf[j]; + dcode->buf[j] = code; + } + zassert(dcode->buf[dcode128->character - 1] == STOP_REV, 1, + "dir=%x %s\n", dcode128->direction, + _zbar_decoder_buf_dump(dcode->buf, dcode->code128.character)); + } else + zassert(dcode->buf[dcode128->character - 1] == STOP_FWD, 1, + "dir=%x %s\n", dcode128->direction, + _zbar_decoder_buf_dump(dcode->buf, dcode->code128.character)); + + code = dcode->buf[0]; + zassert(code >= START_A && code <= START_C, 1, "%s\n", + _zbar_decoder_buf_dump(dcode->buf, dcode->code128.character)); + + charset = code - START_A; + cexp = (code == START_C) ? 1 : 0; + dbprintf(2, " start=%c", 'A' + charset); + + for (i = 1, j = 0; i < dcode128->character - 2; i++) { + unsigned char code = dcode->buf[i]; + zassert(!(code & 0x80), 1, + "i=%x j=%x code=%02x charset=%x cexp=%x %s\n", i, j, code, + charset, cexp, + _zbar_decoder_buf_dump(dcode->buf, dcode->code128.character)); + + if ((charset & 0x2) && (code < 100)) + /* defer character set C for expansion */ + continue; + else if (code < 0x60) { + /* convert character set B to ASCII */ + code = code + 0x20; + if ((!charset || (charset == 0x81)) && (code >= 0x60)) + /* convert character set A to ASCII */ + code -= 0x60; + dcode->buf[j++] = code; + if (charset & 0x80) + charset &= 0x7f; + } else { + dbprintf(2, " %02x", code); + if (charset & 0x2) { + unsigned delta; + /* expand character set C to ASCII */ + zassert(cexp, 1, "i=%x j=%x code=%02x charset=%x cexp=%x %s\n", + i, j, code, charset, cexp, + _zbar_decoder_buf_dump(dcode->buf, + dcode->code128.character)); + delta = postprocess_c(dcode, cexp, i, j); + i += delta; + j += delta * 2; + cexp = 0; + } + if (code < CODE_C) { + if (code == SHIFT) + charset |= 0x80; + else if (code == FNC2) { + /* FIXME FNC2 - message append */ + } else if (code == FNC3) { + /* FIXME FNC3 - initialize */ + } + } else if (code == FNC1) { + /* FNC1 - Code 128 subsets or ASCII 0x1d */ + if (i == 1) + dcode->modifiers |= MOD(ZBAR_MOD_GS1); + else if (i == 2) + dcode->modifiers |= MOD(ZBAR_MOD_AIM); + else if (i < dcode->code128.character - 3) + dcode->buf[j++] = 0x1d; + /*else drop trailing FNC1 */ + } else if (code >= START_A) { + dbprintf(1, " [truncated]\n"); + return (1); + } else { + unsigned char newset = CODE_A - code; + zassert(code >= CODE_C && code <= CODE_A, 1, + "i=%x j=%x code=%02x charset=%x cexp=%x %s\n", i, j, + code, charset, cexp, + _zbar_decoder_buf_dump(dcode->buf, + dcode->code128.character)); + if (newset != charset) + charset = newset; + else { + /* FIXME FNC4 - extended ASCII */ + } + } + if (charset & 0x2) + cexp = i + 1; + } + } + if (charset & 0x2) { + zassert(cexp, 1, "i=%x j=%x code=%02x charset=%x cexp=%x %s\n", i, j, + code, charset, cexp, + _zbar_decoder_buf_dump(dcode->buf, dcode->code128.character)); + j += postprocess_c(dcode, cexp, i, j) * 2; + } + zassert(j < dcode->buf_alloc, 1, "j=%02x %s\n", j, + _zbar_decoder_buf_dump(dcode->buf, dcode->code128.character)); + dcode->buflen = j; + dcode->buf[j] = '\0'; + dcode->code128.character = j; + return (0); +} + +zbar_symbol_type_t _zbar_decode_code128(zbar_decoder_t *dcode) +{ + code128_decoder_t *dcode128 = &dcode->code128; + signed char c; + + /* update latest character width */ + dcode128->s6 -= get_width(dcode, 6); + dcode128->s6 += get_width(dcode, 0); + + if ((dcode128->character < 0) ? + get_color(dcode) != ZBAR_SPACE : + (/* process every 6th element of active symbol */ + ++dcode128->element != 6 || + /* decode color based on direction */ + get_color(dcode) != dcode128->direction)) + return (0); + dcode128->element = 0; + + dbprintf(2, " code128[%c%02d+%x]:", (dcode128->direction) ? '<' : '>', + dcode128->character, dcode128->element); + + c = decode6(dcode); + if (dcode128->character < 0) { + unsigned qz; + dbprintf(2, " c=%02x", c); + if (c < START_A || c > STOP_REV || c == STOP_FWD) { + dbprintf(2, " [invalid]\n"); + return (0); + } + qz = get_width(dcode, 6); + if (qz && qz < (dcode128->s6 * 3) / 4) { + dbprintf(2, " [invalid qz %d]\n", qz); + return (0); + } + /* decoded valid start/stop */ + /* initialize state */ + dcode128->character = 1; + if (c == STOP_REV) { + dcode128->direction = ZBAR_BAR; + dcode128->element = 7; + } else + dcode128->direction = ZBAR_SPACE; + dcode128->start = c; + dcode128->width = dcode128->s6; + dbprintf(2, " dir=%x [valid start]\n", dcode128->direction); + return (0); + } else if (c < 0 || size_buf(dcode, dcode128->character + 1)) { + dbprintf(1, (c < 0) ? " [aborted]\n" : " [overflow]\n"); + if (dcode128->character > 1) + release_lock(dcode, ZBAR_CODE128); + dcode128->character = -1; + return (0); + } else { + unsigned dw; + if (dcode128->width > dcode128->s6) + dw = dcode128->width - dcode128->s6; + else + dw = dcode128->s6 - dcode128->width; + dw *= 4; + if (dw > dcode128->width) { + dbprintf(1, " [width var]\n"); + if (dcode128->character > 1) + release_lock(dcode, ZBAR_CODE128); + dcode128->character = -1; + return (0); + } + } + dcode128->width = dcode128->s6; + + zassert(dcode->buf_alloc > dcode128->character, 0, + "alloc=%x idx=%x c=%02x %s\n", dcode->buf_alloc, + dcode128->character, c, + _zbar_decoder_buf_dump(dcode->buf, dcode->buf_alloc)); + + if (dcode128->character == 1) { + /* lock shared resources */ + if (acquire_lock(dcode, ZBAR_CODE128)) { + dcode128->character = -1; + return (0); + } + dcode->buf[0] = dcode128->start; + } + + dcode->buf[dcode128->character++] = c; + + if (dcode128->character > 2 && + ((dcode128->direction) ? c >= START_A && c <= START_C : + c == STOP_FWD)) { + /* FIXME STOP_FWD should check extra bar (and QZ!) */ + zbar_symbol_type_t sym = ZBAR_CODE128; + if (validate_checksum(dcode) || postprocess(dcode)) + sym = ZBAR_NONE; + else if (dcode128->character < CFG(*dcode128, ZBAR_CFG_MIN_LEN) || + (CFG(*dcode128, ZBAR_CFG_MAX_LEN) > 0 && + dcode128->character > CFG(*dcode128, ZBAR_CFG_MAX_LEN))) { + dbprintf(2, " [invalid len]\n"); + sym = ZBAR_NONE; + } else + dbprintf(2, " [valid end]\n"); + dcode128->character = -1; + if (!sym) + release_lock(dcode, ZBAR_CODE128); + return (sym); + } + + dbprintf(2, "\n"); + return (0); +} diff --git a/zbar/decoder/code128.h b/zbar/decoder/code128.h new file mode 100644 index 0000000..6f4bd1e --- /dev/null +++ b/zbar/decoder/code128.h @@ -0,0 +1,51 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (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 + *------------------------------------------------------------------------*/ +#ifndef _CODE128_H_ +#define _CODE128_H_ + +/* Code 128 specific decode state */ +typedef struct code128_decoder_s { + unsigned direction : 1; /* scan direction: 0=fwd/space, 1=rev/bar */ + unsigned element : 3; /* element offset 0-5 */ + int character : 12; /* character position in symbol */ + unsigned char start; /* start character */ + unsigned s6; /* character width */ + unsigned width; /* last character width */ + + unsigned config; + int configs[NUM_CFGS]; /* int valued configurations */ +} code128_decoder_t; + +/* reset Code 128 specific state */ +static inline void code128_reset(code128_decoder_t *dcode128) +{ + dcode128->direction = 0; + dcode128->element = 0; + dcode128->character = -1; + dcode128->s6 = 0; +} + +/* decode Code 128 symbols */ +zbar_symbol_type_t _zbar_decode_code128(zbar_decoder_t *dcode); + +#endif diff --git a/zbar/decoder/code39.c b/zbar/decoder/code39.c new file mode 100644 index 0000000..97f7e0e --- /dev/null +++ b/zbar/decoder/code39.c @@ -0,0 +1,336 @@ +/*------------------------------------------------------------------------ + * Copyright 2008-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" +#include <string.h> /* memmove */ + +#include <zbar.h> + +#ifdef DEBUG_CODE39 +#define DEBUG_LEVEL (DEBUG_CODE39) +#endif +#include "debug.h" +#include "decoder.h" + +#define NUM_CHARS (0x2c) + +static const unsigned char code39_hi[32] = { + 0x80 | 0x00, /* 2 next */ + 0x40 | 0x02, /* 4 */ + 0x80 | 0x06, /* 2 next */ + 0xc0 | 0x08, /* 2 skip */ + 0x40 | 0x0a, /* 4 */ + 0x80 | 0x0e, /* 2 next */ + 0xc0 | 0x10, /* 2 skip */ + 0x00 | 0x12, /* direct */ + + 0x80 | 0x13, /* 2 next */ + 0xc0 | 0x15, /* 2 skip */ + 0x80 | 0x17, /* 2 next */ + 0xff, 0xc0 | 0x19, /* 2 skip */ + 0x00 | 0x1b, /* direct */ + 0xff, 0xff, + + 0x40 | 0x1c, /* 4 */ + 0x80 | 0x20, /* 2 next */ + 0xc0 | 0x22, /* 2 skip */ + 0x00 | 0x24, /* direct */ + 0x80 | 0x25, /* 2 next */ + 0xff, 0x00 | 0x27, /* direct */ + 0xff, + + 0xc0 | 0x28, /* 2 skip */ + 0x00 | 0x2a, /* direct */ + 0xff, 0xff, 0x00 | 0x2b, /* direct */ + 0xff, 0xff, 0xff, +}; + +typedef struct char39_s { + unsigned char chk, rev, fwd; +} char39_t; + +static const char39_t code39_encodings[NUM_CHARS] = { + { 0x07, 0x1a, 0x20 }, /* 00 */ + { 0x0d, 0x10, 0x03 }, /* 01 */ + { 0x13, 0x17, 0x22 }, /* 02 */ + { 0x16, 0x1d, 0x23 }, /* 03 */ + { 0x19, 0x0d, 0x05 }, /* 04 */ + { 0x1c, 0x13, 0x06 }, /* 05 */ + { 0x25, 0x07, 0x0c }, /* 06 */ + { 0x2a, 0x2a, 0x27 }, /* 07 */ + { 0x31, 0x04, 0x0e }, /* 08 */ + { 0x34, 0x00, 0x0f }, /* 09 */ + { 0x43, 0x15, 0x25 }, /* 0a */ + { 0x46, 0x1c, 0x26 }, /* 0b */ + { 0x49, 0x0b, 0x08 }, /* 0c */ + { 0x4c, 0x12, 0x09 }, /* 0d */ + { 0x52, 0x19, 0x2b }, /* 0e */ + { 0x58, 0x0f, 0x00 }, /* 0f */ + { 0x61, 0x02, 0x11 }, /* 10 */ + { 0x64, 0x09, 0x12 }, /* 11 */ + { 0x70, 0x06, 0x13 }, /* 12 */ + { 0x85, 0x24, 0x16 }, /* 13 */ + { 0x8a, 0x29, 0x28 }, /* 14 */ + { 0x91, 0x21, 0x18 }, /* 15 */ + { 0x94, 0x2b, 0x19 }, /* 16 */ + { 0xa2, 0x28, 0x29 }, /* 17 */ + { 0xa8, 0x27, 0x2a }, /* 18 */ + { 0xc1, 0x1f, 0x1b }, /* 19 */ + { 0xc4, 0x26, 0x1c }, /* 1a */ + { 0xd0, 0x23, 0x1d }, /* 1b */ + { 0x03, 0x14, 0x1e }, /* 1c */ + { 0x06, 0x1b, 0x1f }, /* 1d */ + { 0x09, 0x0a, 0x01 }, /* 1e */ + { 0x0c, 0x11, 0x02 }, /* 1f */ + { 0x12, 0x18, 0x21 }, /* 20 */ + { 0x18, 0x0e, 0x04 }, /* 21 */ + { 0x21, 0x01, 0x0a }, /* 22 */ + { 0x24, 0x08, 0x0b }, /* 23 */ + { 0x30, 0x05, 0x0d }, /* 24 */ + { 0x42, 0x16, 0x24 }, /* 25 */ + { 0x48, 0x0c, 0x07 }, /* 26 */ + { 0x60, 0x03, 0x10 }, /* 27 */ + { 0x81, 0x1e, 0x14 }, /* 28 */ + { 0x84, 0x25, 0x15 }, /* 29 */ + { 0x90, 0x22, 0x17 }, /* 2a */ + { 0xc0, 0x20, 0x1a }, /* 2b */ +}; + +static const unsigned char code39_characters[NUM_CHARS] = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%*"; + +static inline unsigned char code39_decode1(unsigned char enc, unsigned e, + unsigned s) +{ + unsigned char E = decode_e(e, s, 72); + if (E > 18) + return (0xff); + enc <<= 1; + if (E > 6) { + enc |= 1; + dbprintf(2, "1"); + } else + dbprintf(2, "0"); + return (enc); +} + +static inline signed char code39_decode9(zbar_decoder_t *dcode) +{ + unsigned char i, enc = 0, idx; + const char39_t *c; + code39_decoder_t *dcode39 = &dcode->code39; + + if (dcode39->s9 < 9) + return (-1); + + /* threshold bar width ratios */ + for (i = 0; i < 5; i++) { + enc = code39_decode1(enc, get_width(dcode, i), dcode39->s9); + if (enc == 0xff) + return (-1); + } + zassert(enc < 0x20, -1, " enc=%x s9=%x\n", enc, dcode39->s9); + + /* lookup first 5 encoded widths for coarse decode */ + idx = code39_hi[enc]; + if (idx == 0xff) + return (-1); + + /* encode remaining widths (NB first encoded width is lost) */ + for (; i < 9; i++) { + enc = code39_decode1(enc, get_width(dcode, i), dcode39->s9); + if (enc == 0xff) + return (-1); + } + + if ((idx & 0xc0) == 0x80) + idx = (idx & 0x3f) + ((enc >> 3) & 1); + else if ((idx & 0xc0) == 0xc0) + idx = (idx & 0x3f) + ((enc >> 2) & 1); + else if (idx & 0xc0) + idx = (idx & 0x3f) + ((enc >> 2) & 3); + zassert(idx < 0x2c, -1, " idx=%x enc=%x s9=%x\n", idx, enc, dcode39->s9); + + c = &code39_encodings[idx]; + dbprintf(2, " i=%02x chk=%02x c=%02x/%02x", idx, c->chk, c->fwd, c->rev); + if (enc != c->chk) + return (-1); + + dcode39->width = dcode39->s9; + return ((dcode39->direction) ? c->rev : c->fwd); +} + +static inline signed char code39_decode_start(zbar_decoder_t *dcode) +{ + signed char c; + unsigned quiet; + code39_decoder_t *dcode39 = &dcode->code39; + dbprintf(2, " s=%d ", dcode39->s9); + + c = code39_decode9(dcode); + if (c != 0x19 && c != 0x2b) { + dbprintf(2, "\n"); + return (ZBAR_NONE); + } + dcode39->direction ^= (c == 0x19); + + /* check leading quiet zone - spec is 10x */ + quiet = get_width(dcode, 9); + if (quiet && quiet < dcode39->s9 / 2) { + dbprintf(2, " [invalid quiet]\n"); + return (ZBAR_NONE); + } + + dcode39->element = 9; + dcode39->character = 0; + dbprintf(1, " dir=%x [valid start]\n", dcode39->direction); + return (ZBAR_PARTIAL); +} + +static inline int code39_postprocess(zbar_decoder_t *dcode) +{ + int i; + code39_decoder_t *dcode39 = &dcode->code39; + dcode->direction = 1 - 2 * dcode39->direction; + if (dcode39->direction) { + /* reverse buffer */ + dbprintf(2, " (rev)"); + for (i = 0; i < dcode39->character / 2; i++) { + unsigned j = dcode39->character - 1 - i; + char code = dcode->buf[i]; + dcode->buf[i] = dcode->buf[j]; + dcode->buf[j] = code; + } + } + for (i = 0; i < dcode39->character; i++) + dcode->buf[i] = ((dcode->buf[i] < 0x2b) ? + code39_characters[(unsigned)dcode->buf[i]] : + '?'); + zassert(i < dcode->buf_alloc, -1, "i=%02x %s\n", i, + _zbar_decoder_buf_dump(dcode->buf, dcode39->character)); + dcode->buflen = i; + dcode->buf[i] = '\0'; + dcode->modifiers = 0; + return (0); +} + +static inline int check_width(unsigned ref, unsigned w) +{ + unsigned dref = ref; + ref *= 4; + w *= 4; + return (ref - dref <= w && w <= ref + dref); +} + +zbar_symbol_type_t _zbar_decode_code39(zbar_decoder_t *dcode) +{ + code39_decoder_t *dcode39 = &dcode->code39; + signed char c; + + /* update latest character width */ + dcode39->s9 -= get_width(dcode, 9); + dcode39->s9 += get_width(dcode, 0); + + if (dcode39->character < 0) { + if (get_color(dcode) != ZBAR_BAR) + return (ZBAR_NONE); + dbprintf(2, " code39:"); + return (code39_decode_start(dcode)); + } + + if (++dcode39->element < 9) + return (ZBAR_NONE); + + dbprintf(2, " code39[%c%02d+%x]", (dcode39->direction) ? '<' : '>', + dcode39->character, dcode39->element); + + if (dcode39->element == 10) { + unsigned space = get_width(dcode, 0); + if (dcode39->character && + dcode->buf[dcode39->character - 1] == 0x2b) { /* STOP */ + zbar_symbol_type_t sym; + /* trim STOP character */ + dcode39->character--; + sym = ZBAR_NONE; + + /* trailing quiet zone check */ + if (space && space < dcode39->width / 2) + dbprintf(2, " [invalid qz]\n"); + else if (dcode39->character < CFG(*dcode39, ZBAR_CFG_MIN_LEN) || + (CFG(*dcode39, ZBAR_CFG_MAX_LEN) > 0 && + dcode39->character > CFG(*dcode39, ZBAR_CFG_MAX_LEN))) + dbprintf(2, " [invalid len]\n"); + else if (!code39_postprocess(dcode)) { + /* FIXME checksum */ + dbprintf(2, " [valid end]\n"); + sym = ZBAR_CODE39; + } + dcode39->character = -1; + if (!sym) + release_lock(dcode, ZBAR_CODE39); + return (sym); + } + if (space > dcode39->width / 2) { + /* inter-character space check failure */ + dbprintf(2, " ics>%d [invalid ics]", dcode39->width); + if (dcode39->character) + release_lock(dcode, ZBAR_CODE39); + dcode39->character = -1; + } + dcode39->element = 0; + dbprintf(2, "\n"); + return (ZBAR_NONE); + } + + dbprintf(2, " s=%d ", dcode39->s9); + if (!check_width(dcode39->width, dcode39->s9)) { + dbprintf(2, " [width]\n"); + if (dcode39->character) + release_lock(dcode, ZBAR_CODE39); + dcode39->character = -1; + return (ZBAR_NONE); + } + + c = code39_decode9(dcode); + dbprintf(2, " c=%d", c); + + /* lock shared resources */ + if (!dcode39->character && acquire_lock(dcode, ZBAR_CODE39)) { + dcode39->character = -1; + return (ZBAR_PARTIAL); + } + + if (c < 0 || size_buf(dcode, dcode39->character + 1)) { + dbprintf(1, (c < 0) ? " [aborted]\n" : " [overflow]\n"); + release_lock(dcode, ZBAR_CODE39); + dcode39->character = -1; + return (ZBAR_NONE); + } else { + zassert(c < 0x2c, ZBAR_NONE, "c=%02x s9=%x\n", c, dcode39->s9); + dbprintf(2, "\n"); + } + + dcode->buf[dcode39->character++] = c; + + return (ZBAR_NONE); +} diff --git a/zbar/decoder/code39.h b/zbar/decoder/code39.h new file mode 100644 index 0000000..d103121 --- /dev/null +++ b/zbar/decoder/code39.h @@ -0,0 +1,50 @@ +/*------------------------------------------------------------------------ + * Copyright 2008-2009 (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 + *------------------------------------------------------------------------*/ +#ifndef _CODE39_H_ +#define _CODE39_H_ + +/* Code 39 specific decode state */ +typedef struct code39_decoder_s { + unsigned direction : 1; /* scan direction: 0=fwd, 1=rev */ + unsigned element : 4; /* element offset 0-8 */ + int character : 12; /* character position in symbol */ + unsigned s9; /* current character width */ + unsigned width; /* last character width */ + + unsigned config; + int configs[NUM_CFGS]; /* int valued configurations */ +} code39_decoder_t; + +/* reset Code 39 specific state */ +static inline void code39_reset(code39_decoder_t *dcode39) +{ + dcode39->direction = 0; + dcode39->element = 0; + dcode39->character = -1; + dcode39->s9 = 0; +} + +/* decode Code 39 symbols */ +zbar_symbol_type_t _zbar_decode_code39(zbar_decoder_t *dcode); + +#endif diff --git a/zbar/decoder/code93.c b/zbar/decoder/code93.c new file mode 100644 index 0000000..a1acff7 --- /dev/null +++ b/zbar/decoder/code93.c @@ -0,0 +1,387 @@ +/*------------------------------------------------------------------------ + * Copyright 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" +#include <zbar.h> + +#ifdef DEBUG_CODE93 +#define DEBUG_LEVEL (DEBUG_CODE93) +#endif +#include "debug.h" +#include "decoder.h" + +static const signed char code93_hash[0x40] = { + 0x0f, 0x2b, 0x30, 0x38, 0x13, 0x1b, 0x11, 0x2a, 0x0a, -1, 0x2f, + 0x0f, 0x38, 0x38, 0x2f, 0x37, 0x24, 0x3a, 0x1b, 0x36, 0x18, 0x26, + 0x02, 0x2c, 0x2b, 0x05, 0x21, 0x3b, 0x04, 0x15, 0x12, 0x0c, 0x00, + 0x26, 0x23, 0x00, -1, 0x2e, 0x3f, 0x13, 0x2e, 0x36, -1, 0x08, + 0x09, -1, 0x15, 0x14, -1, 0x00, 0x21, 0x3b, -1, 0x33, 0x00, + -1, 0x2d, 0x0c, 0x1b, 0x0a, 0x3f, 0x3f, 0x29, 0x1c, +}; + +static inline int check_width(unsigned cur, unsigned prev) +{ + unsigned dw; + if (prev > cur) + dw = prev - cur; + else + dw = cur - prev; + dw *= 4; + return (dw > prev); +} + +static inline int encode6(zbar_decoder_t *dcode) +{ + /* build edge signature of character */ + unsigned s = dcode->s6; + int sig = 0, i; + + dbprintf(2, " s=%d ", s); + if (s < 9) + return (-1); + + for (i = 6; --i > 0;) { + unsigned c = decode_e(pair_width(dcode, i), s, 9); + if (c > 3) + return (-1); + sig = (sig << 2) | c; + dbprintf(2, "%d", c); + } + dbprintf(2, " sig=%03x", sig); + + return (sig); +} + +static inline int validate_sig(int sig) +{ + int i, sum = 0, emin = 0, sig0 = 0, sig1 = 0; + dbprintf(3, " sum=0"); + for (i = 3; --i >= 0;) { + int e = sig & 3; + sig >>= 2; + sum = e - sum; + sig1 <<= 4; + sig1 += sum; + dbprintf(3, "%d", sum); + if (!i) + break; + + e = sig & 3; + sig >>= 2; + sum = e - sum; + sig0 <<= 4; + if (emin > sum) + emin = sum; + sig0 += sum; + dbprintf(3, "%d", sum); + } + + dbprintf(3, " emin=%d sig=%03x/%03x", emin, sig1 & 0xfff, sig0 & 0xfff); + + emin = emin + (emin << 4) + (emin << 8); + sig0 -= emin; + sig1 += emin; + + dbprintf(3, "=%03x/%03x", sig1 & 0xfff, sig0 & 0xfff); + return ((sig0 | sig1) & 0x888); +} + +static inline int decode6(zbar_decoder_t *dcode) +{ + int sig = encode6(dcode); + int g0, g1, c; + if (sig < 0 || (sig & 0x3) + ((sig >> 4) & 0x3) + ((sig >> 8) & 0x3) != 3 || + validate_sig(sig)) + return (-1); + + if (dcode->code93.direction) { + /* reverse signature */ + unsigned tmp = sig & 0x030; + sig = ((sig & 0x3c0) >> 6) | ((sig & 0x00f) << 6); + sig = ((sig & 0x30c) >> 2) | ((sig & 0x0c3) << 2) | tmp; + } + + g0 = code93_hash[(sig - (sig >> 4)) & 0x3f]; + g1 = code93_hash[((sig >> 2) - (sig >> 7)) & 0x3f]; + zassert(g0 >= 0 && g1 >= 0, -1, "dir=%x sig=%03x g0=%03x g1=%03x %s\n", + dcode->code93.direction, sig, g0, g1, + _zbar_decoder_buf_dump(dcode->buf, dcode->code93.character)); + + c = (g0 + g1) & 0x3f; + dbprintf(2, " g0=%x g1=%x c=%02x", g0, g1, c); + return (c); +} + +static inline zbar_symbol_type_t decode_start(zbar_decoder_t *dcode) +{ + code93_decoder_t *dcode93 = &dcode->code93; + unsigned dir, qz, s = dcode->s6; + int c; + + dbprintf(2, " code93:"); + c = encode6(dcode); + if (c < 0 || (c != 0x00f && c != 0x0f0)) + return (ZBAR_NONE); + + dir = (c >> 7); + + if (dir) { + if (decode_e(pair_width(dcode, 0), s, 9)) + return (ZBAR_NONE); + qz = get_width(dcode, 8); + } + + qz = get_width(dcode, 7); + if (qz && qz < (s * 3) / 4) { + dbprintf(2, " [invalid qz %d]", qz); + return (ZBAR_NONE); + } + + /* decoded valid start/stop - initialize state */ + dcode93->direction = dir; + dcode93->element = (!dir) ? 0 : 7; + dcode93->character = 0; + dcode93->width = s; + + dbprintf(2, " dir=%x [valid start]", dir); + return (ZBAR_PARTIAL); +} + +static inline zbar_symbol_type_t decode_abort(zbar_decoder_t *dcode, + const char *reason) +{ + code93_decoder_t *dcode93 = &dcode->code93; + if (dcode93->character > 1) + release_lock(dcode, ZBAR_CODE93); + dcode93->character = -1; + if (reason) + dbprintf(1, " [%s]\n", reason); + return (ZBAR_NONE); +} + +static inline zbar_symbol_type_t check_stop(zbar_decoder_t *dcode) +{ + code93_decoder_t *dcode93 = &dcode->code93; + unsigned n = dcode93->character, s = dcode->s6; + int max_len = CFG(*dcode93, ZBAR_CFG_MAX_LEN); + if (n < 2 || n < CFG(*dcode93, ZBAR_CFG_MIN_LEN) || + (max_len && n > max_len)) + return (decode_abort(dcode, "invalid len")); + + if (dcode93->direction) { + unsigned qz = get_width(dcode, 0); + if (qz && qz < (s * 3) / 4) + return (decode_abort(dcode, "invalid qz")); + } else if (decode_e(pair_width(dcode, 0), s, 9)) + /* FIXME forward-trailing QZ check */ + return (decode_abort(dcode, "invalid stop")); + + return (ZBAR_CODE93); +} + +#define CHKMOD (47) + +static inline int plusmod47(int acc, int add) +{ + acc += add; + if (acc >= CHKMOD) + acc -= CHKMOD; + return (acc); +} + +static inline int validate_checksums(zbar_decoder_t *dcode) +{ + code93_decoder_t *dcode93 = &dcode->code93; + unsigned d, i, n = dcode93->character; + unsigned sum_c = 0, acc_c = 0, i_c = (n - 2) % 20; + unsigned sum_k = 0, acc_k = 0, i_k = (n - 1) % 15; + + for (i = 0; i < n - 2; i++) { + d = dcode->buf[(dcode93->direction) ? n - 1 - i : i]; + + if (!i_c--) { + acc_c = 0; + i_c = 19; + } + acc_c = plusmod47(acc_c, d); + sum_c = plusmod47(sum_c, acc_c); + + if (!i_k--) { + acc_k = 0; + i_k = 14; + } + acc_k = plusmod47(acc_k, d); + sum_k = plusmod47(sum_k, acc_k); + } + + d = dcode->buf[(dcode93->direction) ? 1 : n - 2]; + dbprintf(2, " C=%02x?=%02x", d, sum_c); + if (d != sum_c) + return (1); + + acc_k = plusmod47(acc_k, sum_c); + sum_k = plusmod47(sum_k, acc_k); + d = dcode->buf[(dcode93->direction) ? 0 : n - 1]; + dbprintf(2, " K=%02x?=%02x", d, sum_k); + if (d != sum_k) + return (1); + + return (0); +} + +/* resolve scan direction and convert to ASCII */ +static inline int postprocess(zbar_decoder_t *dcode) +{ + code93_decoder_t *dcode93 = &dcode->code93; + unsigned i, j, n = dcode93->character; + static const unsigned char code93_graph[] = "-. $/+%"; + static const unsigned char code93_s2[] = + "\x1b\x1c\x1d\x1e\x1f;<=>?[\\]^_{|}~\x7f\x00\x40`\x7f\x7f\x7f"; + + dbprintf(2, "\n postproc len=%d", n); + dcode->direction = 1 - 2 * dcode93->direction; + if (dcode93->direction) { + /* reverse buffer */ + dbprintf(2, " (rev)"); + for (i = 0; i < n / 2; i++) { + unsigned j = n - 1 - i; + unsigned char d = dcode->buf[i]; + dcode->buf[i] = dcode->buf[j]; + dcode->buf[j] = d; + } + } + + n -= 2; + for (i = 0, j = 0; i < n;) { + unsigned char d = dcode->buf[i++]; + if (d < 0xa) + d = '0' + d; + else if (d < 0x24) + d = 'A' + d - 0xa; + else if (d < 0x2b) + d = code93_graph[d - 0x24]; + else { + unsigned shift = d; + zassert(shift < 0x2f, -1, "%s\n", + _zbar_decoder_buf_dump(dcode->buf, dcode93->character)); + d = dcode->buf[i++]; + if (d < 0xa || d >= 0x24) + return (1); + d -= 0xa; + switch (shift) { + case 0x2b: + d++; + break; + case 0x2c: + d = code93_s2[d]; + break; + case 0x2d: + d += 0x21; + break; + case 0x2e: + d += 0x61; + break; + default: + return (1); + } + } + dcode->buf[j++] = d; + } + + zassert(j < dcode->buf_alloc, 1, "j=%02x %s\n", j, + _zbar_decoder_buf_dump(dcode->buf, dcode->code93.character)); + dcode->buflen = j; + dcode->buf[j] = '\0'; + dcode->modifiers = 0; + return (0); +} + +zbar_symbol_type_t _zbar_decode_code93(zbar_decoder_t *dcode) +{ + code93_decoder_t *dcode93 = &dcode->code93; + int c; + + if (dcode93->character < 0) { + zbar_symbol_type_t sym; + if (get_color(dcode) != ZBAR_BAR) + return (ZBAR_NONE); + sym = decode_start(dcode); + dbprintf(2, "\n"); + return (sym); + } + + if (/* process every 6th element of active symbol */ + ++dcode93->element != 6 || + /* decode color based on direction */ + get_color(dcode) == dcode93->direction) + return (ZBAR_NONE); + + dcode93->element = 0; + + dbprintf(2, " code93[%c%02d+%x]:", (dcode93->direction) ? '<' : '>', + dcode93->character, dcode93->element); + + if (check_width(dcode->s6, dcode93->width)) + return (decode_abort(dcode, "width var")); + + c = decode6(dcode); + if (c < 0) + return (decode_abort(dcode, "aborted")); + + if (c == 0x2f) { + if (!check_stop(dcode)) + return (ZBAR_NONE); + if (validate_checksums(dcode)) + return (decode_abort(dcode, "checksum error")); + if (postprocess(dcode)) + return (decode_abort(dcode, "invalid encoding")); + + dbprintf(2, " [valid end]\n"); + dbprintf(3, " %s\n", + _zbar_decoder_buf_dump(dcode->buf, dcode93->character)); + + dcode93->character = -1; + return (ZBAR_CODE93); + } + + if (size_buf(dcode, dcode93->character + 1)) + return (decode_abort(dcode, "overflow")); + + dcode93->width = dcode->s6; + + if (dcode93->character == 1) { + /* lock shared resources */ + if (acquire_lock(dcode, ZBAR_CODE93)) + return (decode_abort(dcode, NULL)); + dcode->buf[0] = dcode93->buf; + } + + if (!dcode93->character) + dcode93->buf = c; + else + dcode->buf[dcode93->character] = c; + dcode93->character++; + + dbprintf(2, "\n"); + return (ZBAR_NONE); +} diff --git a/zbar/decoder/code93.h b/zbar/decoder/code93.h new file mode 100644 index 0000000..cf4ef3a --- /dev/null +++ b/zbar/decoder/code93.h @@ -0,0 +1,49 @@ +/*------------------------------------------------------------------------ + * Copyright 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 + *------------------------------------------------------------------------*/ +#ifndef _CODE93_H_ +#define _CODE93_H_ + +/* Code 93 specific decode state */ +typedef struct code93_decoder_s { + unsigned direction : 1; /* scan direction: 0=fwd/space, 1=rev/bar */ + unsigned element : 3; /* element offset 0-5 */ + int character : 12; /* character position in symbol */ + unsigned width; /* last character width */ + unsigned char buf; /* first character */ + + unsigned config; + int configs[NUM_CFGS]; /* int valued configurations */ +} code93_decoder_t; + +/* reset Code 93 specific state */ +static inline void code93_reset(code93_decoder_t *dcode93) +{ + dcode93->direction = 0; + dcode93->element = 0; + dcode93->character = -1; +} + +/* decode Code 93 symbols */ +zbar_symbol_type_t _zbar_decode_code93(zbar_decoder_t *dcode); + +#endif diff --git a/zbar/decoder/databar.c b/zbar/decoder/databar.c new file mode 100644 index 0000000..5722ef6 --- /dev/null +++ b/zbar/decoder/databar.c @@ -0,0 +1,1240 @@ +/*------------------------------------------------------------------------ + * Copyright 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" +#include <zbar.h> +#include <stdlib.h> +#include <stdio.h> + +#ifdef DEBUG_DATABAR +#define DEBUG_LEVEL (DEBUG_DATABAR) +#endif +#include "debug.h" +#include "decoder.h" + +#define GS ('\035') + +enum +{ + SCH_NUM, + SCH_ALNUM, + SCH_ISO646 +}; + +static const signed char finder_hash[0x20] = { + 0x16, 0x1f, 0x02, 0x00, 0x03, 0x00, 0x06, 0x0b, 0x1f, 0x0e, 0x17, + 0x0c, 0x0b, 0x14, 0x11, 0x0c, 0x1f, 0x03, 0x13, 0x08, 0x00, 0x0a, + -1, 0x16, 0x0c, 0x09, -1, 0x1a, 0x1f, 0x1c, 0x00, -1, +}; + +/* DataBar character encoding groups */ +struct group_s { + unsigned short sum; + unsigned char wmax; + unsigned char todd; + unsigned char teven; +} groups[] = { + /* (17,4) DataBar Expanded character groups */ + { 0, 7, 87, 4 }, + { 348, 5, 52, 20 }, + { 1388, 4, 30, 52 }, + { 2948, 3, 10, 104 }, + { 3988, 1, 1, 204 }, + + /* (16,4) DataBar outer character groups */ + { 0, 8, 161, 1 }, + { 161, 6, 80, 10 }, + { 961, 4, 31, 34 }, + { 2015, 3, 10, 70 }, + { 2715, 1, 1, 126 }, + + /* (15,4) DataBar inner character groups */ + { 1516, 8, 81, 1 }, + { 1036, 6, 48, 10 }, + { 336, 4, 20, 35 }, + { 0, 2, 4, 84 }, +}; + +static const unsigned char exp_sequences[] = { + /* sequence Group 1 */ + 0x01, 0x23, 0x25, 0x07, 0x29, 0x47, 0x29, 0x67, 0x0b, 0x29, 0x87, 0xab, + /* sequence Group 2 */ + 0x21, 0x43, 0x65, 0x07, 0x21, 0x43, 0x65, 0x89, 0x21, 0x43, 0x65, 0xa9, + 0x0b, 0x21, 0x43, 0x67, 0x89, 0xab +}; + +/* DataBar expanded checksum multipliers */ +static const unsigned char exp_checksums[] = { 1, 189, 62, 113, 46, 43, + 109, 134, 6, 79, 161, 45 }; + +static inline void append_check14(unsigned char *buf) +{ + unsigned char chk = 0, d; + int i; + for (i = 13; --i >= 0;) { + d = *(buf++) - '0'; + chk += d; + if (!(i & 1)) + chk += d << 1; + } + chk %= 10; + if (chk) + chk = 10 - chk; + *buf = chk + '0'; +} + +static inline void decode10(unsigned char *buf, unsigned long n, int i) +{ + buf += i; + while (--i >= 0) { + unsigned char d = n % 10; + n /= 10; + *--buf = '0' + d; + } +} + +#define VAR_MAX(l, i) ((((l)*12 + (i)) * 2 + 6) / 7) + +#define FEED_BITS(b) \ + while (i < (b) && len) { \ + d = (d << 12) | (*(data++) & 0xfff); \ + i += 12; \ + len--; \ + dbprintf(2, " %03lx", d & 0xfff); \ + } + +#define PUSH_CHAR(c) *(buf++) = (c) + +#define PUSH_CHAR4(c0, c1, c2, c3) \ + do { \ + PUSH_CHAR(c0); \ + PUSH_CHAR(c1); \ + PUSH_CHAR(c2); \ + PUSH_CHAR(c3); \ + } while (0); + +static inline int databar_postprocess_exp(zbar_decoder_t *dcode, int *data) +{ + int i = 0, enc; + unsigned n; + unsigned char *buf; + unsigned long d = *(data++); + int len = d / 211 + 4, buflen; + + /* grok encodation method */ + d = *(data++); + dbprintf(2, "\n len=%d %03lx", len, d & 0xfff); + n = (d >> 4) & 0x7f; + if (n >= 0x40) { + i = 10; + enc = 1; + buflen = 2 + 14 + VAR_MAX(len, 10 - 2 - 44 + 6) + 2; + } else if (n >= 0x38) { + i = 4; + enc = 6 + (n & 7); + buflen = 2 + 14 + 4 + 6 + 2 + 6 + 2; + } else if (n >= 0x30) { + i = 6; + enc = 2 + ((n >> 2) & 1); + buflen = 2 + 14 + 4 + 3 + VAR_MAX(len, 6 - 2 - 44 - 2 - 10) + 2; + } else if (n >= 0x20) { + i = 7; + enc = 4 + ((n >> 3) & 1); + buflen = 2 + 14 + 4 + 6; + } else { + i = 9; + enc = 0; + buflen = VAR_MAX(len, 9 - 2) + 2; + } + dbprintf(2, " buflen=%d enc=%d", buflen, enc); + zassert(buflen > 2, -1, "buflen=%d\n", buflen); + + if (enc < 4) { + /* grok variable length symbol bit field */ + if ((len ^ (d >> (--i))) & 1) + /* even/odd length mismatch */ + return (-1); + if (((d >> (--i)) & 1) != (len > 14)) + /* size group mismatch */ + return (-1); + } + len -= 2; + dbprintf(2, " [%d+%d]", i, len); + + if (size_buf(dcode, buflen)) + return (-1); + buf = dcode->buf; + + /* handle compressed fields */ + if (enc) { + PUSH_CHAR('0'); + PUSH_CHAR('1'); + } + + if (enc == 1) { + i -= 4; + n = (d >> i) & 0xf; + if (i >= 10) + return (-1); + PUSH_CHAR('0' + n); + } else if (enc) + PUSH_CHAR('9'); + + if (enc) { + int j; + for (j = 0; j < 4; j++) { + FEED_BITS(10); + i -= 10; + n = (d >> i) & 0x3ff; + if (n >= 1000) + return (-1); + decode10(buf, n, 3); + buf += 3; + } + append_check14(buf - 13); + buf++; + } + + switch (enc) { + case 2: /* 01100: AI 392x */ + FEED_BITS(2); + i -= 2; + n = (d >> i) & 0x3; + PUSH_CHAR4('3', '9', '2', '0' + n); + break; + + case 3: /* 01101: AI 393x */ + FEED_BITS(12); + i -= 2; + n = (d >> i) & 0x3; + PUSH_CHAR4('3', '9', '3', '0' + n); + i -= 10; + n = (d >> i) & 0x3ff; + if (n >= 1000) + return (-1); + decode10(buf, n, 3); + buf += 3; + break; + + case 4: /* 0100: AI 3103 */ + FEED_BITS(15); + i -= 15; + n = (d >> i) & 0x7fff; + PUSH_CHAR4('3', '1', '0', '3'); + decode10(buf, n, 6); + buf += 6; + break; + + case 5: /* 0101: AI 3202/3203 */ + FEED_BITS(15); + i -= 15; + n = (d >> i) & 0x7fff; + dbprintf(2, " v=%d", n); + PUSH_CHAR4('3', '2', '0', (n >= 10000) ? '3' : '2'); + if (n >= 10000) + n -= 10000; + decode10(buf, n, 6); + buf += 6; + break; + } + if (enc >= 6) { + /* 0111000 - 0111111: AI 310x/320x + AI 11/13/15/17 */ + PUSH_CHAR4('3', '1' + (enc & 1), '0', 'x'); + FEED_BITS(20); + i -= 20; + n = (d >> i) & 0xfffff; + dbprintf(2, " [%d+%d] %d", i, len, n); + if (n >= 1000000) + return (-1); + decode10(buf, n, 6); + *(buf - 1) = *buf; + *buf = '0'; + buf += 6; + + FEED_BITS(16); + i -= 16; + n = (d >> i) & 0xffff; + if (n < 38400) { + int dd, mm, yy; + dd = n % 32; + n /= 32; + mm = n % 12 + 1; + n /= 12; + yy = n; + PUSH_CHAR('1'); + PUSH_CHAR('0' + ((enc - 6) | 1)); + decode10(buf, yy, 2); + buf += 2; + decode10(buf, mm, 2); + buf += 2; + decode10(buf, dd, 2); + buf += 2; + } else if (n > 38400) + return (-1); + } + + if (enc < 4) { + /* remainder is general-purpose data compaction */ + int scheme = SCH_NUM; + while (i > 0 || len > 0) { + FEED_BITS(8); + dbprintf(2, " [%d+%d]", i, len); + + if (scheme == SCH_NUM) { + int n1; + i -= 4; + if (i < 0) + break; + if (!((d >> i) & 0xf)) { + scheme = SCH_ALNUM; + dbprintf(2, ">A"); + continue; + } + if (!len && i < 3) { + /* special case last digit */ + n = ((d >> i) & 0xf) - 1; + if (n > 9) + return (-1); + *(buf++) = '0' + n; + break; + } + i -= 3; + zassert(i >= 0, -1, "\n"); + n = ((d >> i) & 0x7f) - 8; + n1 = n % 11; + n = n / 11; + dbprintf(2, "N%d%d", n, n1); + *(buf++) = (n < 10) ? '0' + n : GS; + *(buf++) = (n1 < 10) ? '0' + n1 : GS; + } else { + unsigned c = 0; + i -= 3; + if (i < 0) + break; + if (!((d >> i) & 0x7)) { + scheme = SCH_NUM; + continue; + } + i -= 2; + if (i < 0) + break; + n = (d >> i) & 0x1f; + if (n == 0x04) { + scheme ^= 0x3; + dbprintf(2, ">%d", scheme); + } else if (n == 0x0f) + c = GS; + else if (n < 0x0f) + c = 43 + n; + else if (scheme == SCH_ALNUM) { + i--; + if (i < 0) + return (-1); + n = (d >> i) & 0x1f; + if (n < 0x1a) + c = 'A' + n; + else if (n == 0x1a) + c = '*'; + else if (n < 0x1f) + c = ',' + n - 0x1b; + else + return (-1); + } else if (scheme == SCH_ISO646 && n < 0x1d) { + i -= 2; + if (i < 0) + return (-1); + n = (d >> i) & 0x3f; + if (n < 0x1a) + c = 'A' + n; + else if (n < 0x34) + c = 'a' + n - 0x1a; + else + return (-1); + } else if (scheme == SCH_ISO646) { + i -= 3; + if (i < 0) + return (-1); + n = ((d >> i) & 0x1f); + dbprintf(2, "(%02x)", n); + if (n < 0xa) + c = '!' + n - 8; + else if (n < 0x15) + c = '%' + n - 0xa; + else if (n < 0x1b) + c = ':' + n - 0x15; + else if (n == 0x1b) + c = '_'; + else if (n == 0x1c) + c = ' '; + else + return (-1); + } else + return (-1); + + if (c) { + dbprintf(2, "%d%c", scheme, c); + *(buf++) = c; + } + } + } + /* FIXME check pad? */ + } + + i = buf - dcode->buf; + zassert(i < dcode->buf_alloc, -1, "i=%02x %s\n", i, + _zbar_decoder_buf_dump(dcode->buf, i)); + + *buf = 0; + dcode->buflen = i; + if (i && *--buf == GS) { + *buf = 0; + dcode->buflen--; + } + + dbprintf(2, "\n %s", _zbar_decoder_buf_dump(dcode->buf, dcode->buflen)); + return (0); +} +#undef FEED_BITS + +/* convert from heterogeneous base {1597,2841} + * to base 10 character representation + */ +static inline void databar_postprocess(zbar_decoder_t *dcode, unsigned d[4]) +{ + unsigned long r; + databar_decoder_t *db = &dcode->databar; + int i; + unsigned c, chk = 0; + unsigned char *buf = dcode->buf; + *(buf++) = '0'; + *(buf++) = '1'; + buf += 15; + *--buf = '\0'; + *--buf = '\0'; + + dbprintf(2, "\n d={%d,%d,%d,%d}", d[0], d[1], d[2], d[3]); + r = d[0] * 1597 + d[1]; + d[1] = r / 10000; + r %= 10000; + r = r * 2841 + d[2]; + d[2] = r / 10000; + r %= 10000; + r = r * 1597 + d[3]; + d[3] = r / 10000; + dbprintf(2, " r=%ld", r); + + for (i = 4; --i >= 0;) { + c = r % 10; + chk += c; + if (i & 1) + chk += c << 1; + *--buf = c + '0'; + if (i) + r /= 10; + } + + dbprintf(2, " d={%d,%d,%d}", d[1], d[2], d[3]); + r = d[1] * 2841 + d[2]; + d[2] = r / 10000; + r %= 10000; + r = r * 1597 + d[3]; + d[3] = r / 10000; + dbprintf(2, " r=%ld", r); + + for (i = 4; --i >= 0;) { + c = r % 10; + chk += c; + if (i & 1) + chk += c << 1; + *--buf = c + '0'; + if (i) + r /= 10; + } + + r = d[2] * 1597 + d[3]; + dbprintf(2, " d={%d,%d} r=%ld", d[2], d[3], r); + + for (i = 5; --i >= 0;) { + c = r % 10; + chk += c; + if (!(i & 1)) + chk += c << 1; + *--buf = c + '0'; + if (i) + r /= 10; + } + + /* NB linkage flag not supported */ + if (TEST_CFG(db->config, ZBAR_CFG_EMIT_CHECK)) { + chk %= 10; + if (chk) + chk = 10 - chk; + buf[13] = chk + '0'; + dcode->buflen = buf - dcode->buf + 14; + } else + dcode->buflen = buf - dcode->buf + 13; + + dbprintf(2, "\n %s", _zbar_decoder_buf_dump(dcode->buf, 16)); +} + +static inline int check_width(unsigned wf, unsigned wd, unsigned n) +{ + unsigned dwf = wf * 3; + wd *= 14; + wf *= n; + return (wf - dwf <= wd && wd <= wf + dwf); +} + +static inline void merge_segment(databar_decoder_t *db, databar_segment_t *seg) +{ + unsigned csegs = db->csegs; + int i; + for (i = 0; i < csegs; i++) { + databar_segment_t *s = db->segs + i; + if (s != seg && s->finder == seg->finder && s->exp == seg->exp && + s->color == seg->color && s->side == seg->side && + s->data == seg->data && s->check == seg->check && + check_width(seg->width, s->width, 14)) { + /* merge with existing segment */ + unsigned cnt = s->count; + if (cnt < 0x7f) + cnt++; + seg->count = cnt; + seg->partial &= s->partial; + seg->width = (3 * seg->width + s->width + 2) / 4; + s->finder = -1; + dbprintf(2, " dup@%d(%d,%d)", i, cnt, + (db->epoch - seg->epoch) & 0xff); + } else if (s->finder >= 0) { + unsigned age = (db->epoch - s->epoch) & 0xff; + if (age >= 248 || (age >= 128 && s->count < 2)) + s->finder = -1; + } + } +} + +static inline zbar_symbol_type_t match_segment(zbar_decoder_t *dcode, + databar_segment_t *seg) +{ + databar_decoder_t *db = &dcode->databar; + unsigned csegs = db->csegs, maxage = 0xfff; + int i0, i1, i2, maxcnt = 0; + databar_segment_t *smax[3] = { + NULL, + }; + unsigned d[4]; + + if (seg->partial && seg->count < 4) + return (ZBAR_PARTIAL); + + for (i0 = 0; i0 < csegs; i0++) { + databar_segment_t *s0 = db->segs + i0; + if (s0 == seg || s0->finder != seg->finder || s0->exp || + s0->color != seg->color || s0->side == seg->side || + (s0->partial && s0->count < 4) || + !check_width(seg->width, s0->width, 14)) + continue; + + for (i1 = 0; i1 < csegs; i1++) { + databar_segment_t *s1 = db->segs + i1; + int chkf, chks, chk; + unsigned age1; + if (i1 == i0 || s1->finder < 0 || s1->exp || + s1->color == seg->color || (s1->partial && s1->count < 4) || + !check_width(seg->width, s1->width, 14)) + continue; + dbprintf(2, "\n\t[%d,%d] f=%d(0%xx)/%d(%x%x%x)", i0, i1, + seg->finder, seg->color, s1->finder, s1->exp, s1->color, + s1->side); + + if (seg->color) + chkf = seg->finder + s1->finder * 9; + else + chkf = s1->finder + seg->finder * 9; + if (chkf > 72) + chkf--; + if (chkf > 8) + chkf--; + + chks = (seg->check + s0->check + s1->check) % 79; + + if (chkf >= chks) + chk = chkf - chks; + else + chk = 79 + chkf - chks; + + dbprintf(2, " chk=(%d,%d) => %d", chkf, chks, chk); + age1 = (((db->epoch - s0->epoch) & 0xff) + + ((db->epoch - s1->epoch) & 0xff)); + + for (i2 = i1 + 1; i2 < csegs; i2++) { + databar_segment_t *s2 = db->segs + i2; + unsigned cnt, age2, age; + if (i2 == i0 || s2->finder != s1->finder || s2->exp || + s2->color != s1->color || s2->side == s1->side || + s2->check != chk || (s2->partial && s2->count < 4) || + !check_width(seg->width, s2->width, 14)) + continue; + age2 = (db->epoch - s2->epoch) & 0xff; + age = age1 + age2; + cnt = s0->count + s1->count + s2->count; + dbprintf(2, " [%d] MATCH cnt=%d age=%d", i2, cnt, age); + if (maxcnt < cnt || (maxcnt == cnt && maxage > age)) { + maxcnt = cnt; + maxage = age; + smax[0] = s0; + smax[1] = s1; + smax[2] = s2; + } + } + } + } + + if (!smax[0]) + return (ZBAR_PARTIAL); + + d[(seg->color << 1) | seg->side] = seg->data; + for (i0 = 0; i0 < 3; i0++) { + d[(smax[i0]->color << 1) | smax[i0]->side] = smax[i0]->data; + if (!--(smax[i0]->count)) + smax[i0]->finder = -1; + } + seg->finder = -1; + + if (size_buf(dcode, 18)) + return (ZBAR_PARTIAL); + + if (acquire_lock(dcode, ZBAR_DATABAR)) + return (ZBAR_PARTIAL); + + databar_postprocess(dcode, d); + dcode->modifiers = MOD(ZBAR_MOD_GS1); + dcode->direction = 1 - 2 * (seg->side ^ seg->color ^ 1); + return (ZBAR_DATABAR); +} + +static inline signed lookup_sequence(databar_segment_t *seg, int fixed, + int seq[22], const size_t maxsize) +{ + unsigned n = seg->data / 211, i; + const unsigned char *p; + i = (n + 1) / 2 + 1; + n += 4; + i = (i * i) / 4; + dbprintf(2, " {%d,%d:", i, n); + p = exp_sequences + i; + + if (n >= maxsize-1) { + // The loop below checks i<n and increments i by one within the loop + // when accessing seq[22]. For this to be safe, n needs to be < 21. + // See CVE-2023-40890. + return -1; + } + + fixed >>= 1; + seq[0] = 0; + seq[1] = 1; + for (i = 2; i < n;) { + int s = *p; + if (!(i & 2)) { + p++; + s >>= 4; + } else + s &= 0xf; + if (s == fixed) + fixed = -1; + s <<= 1; + dbprintf(2, "%x", s); + seq[i++] = s++; + seq[i++] = s; + } + dbprintf(2, "}"); + seq[n] = -1; + return (fixed < 1); +} + +#define IDX(s) \ + (((s)->finder << 2) | ((s)->color << 1) | ((s)->color ^ (s)->side)) + +static inline zbar_symbol_type_t +match_segment_exp(zbar_decoder_t *dcode, databar_segment_t *seg, int dir) +{ + databar_decoder_t *db = &dcode->databar; + int bestsegs[22], i = 0, segs[22], seq[22]; + int ifixed = seg - db->segs, fixed = IDX(seg), maxcnt = 0; + int iseg[DATABAR_MAX_SEGMENTS]; + unsigned csegs = db->csegs, width = seg->width, maxage = 0x7fff; + + bestsegs[0] = segs[0] = seq[1] = -1; + seq[0] = 0; + + dbprintf(2, "\n fixed=%d@%d: ", fixed, ifixed); + for (i = csegs, seg = db->segs + csegs - 1; --i >= 0; seg--) { + if (seg->exp && seg->finder >= 0 && (!seg->partial || seg->count >= 4)) + iseg[i] = IDX(seg); + else + iseg[i] = -1; + dbprintf(2, " %d", iseg[i]); + } + + for (i = 0;; i--) { + unsigned cnt, chk, age; + unsigned data0, chk0; + if (!i) + dbprintf(2, "\n "); + for (; i >= 0 && seq[i] >= 0; i--) { + int j; + dbprintf(2, " [%d]%d", i, seq[i]); + + if (seq[i] == fixed) { + seg = db->segs + ifixed; + if (segs[i] < 0 && check_width(width, seg->width, 14)) { + dbprintf(2, "*"); + j = ifixed; + } else + continue; + } else { + for (j = segs[i] + 1; j < csegs; j++) { + if (iseg[j] == seq[i] && + (!i || check_width(width, db->segs[j].width, 14))) { + seg = db->segs + j; + break; + } + } + if (j == csegs) + continue; + } + + if (!i) { + signed int lu = lookup_sequence(seg, fixed, seq, sizeof(seq)/sizeof(seq[0])); + if(!lu) { + dbprintf(2, "[nf]"); + continue; + } + if(lu < 0) { + dbprintf(1, " [aborted]\n"); + goto abort; + } + width = seg->width; + dbprintf(2, " A00@%d", j); + } else { + width = (width + seg->width) / 2; + dbprintf(2, " %c%x%x@%d", 'A' + seg->finder, seg->color, + seg->side, j); + } + segs[i++] = j; + segs[i++] = -1; + } + if (i < 0) + break; + + seg = db->segs + segs[0]; + cnt = 0, chk = 0, age = (db->epoch - seg->epoch) & 0xff; + for (i = 1; segs[i] >= 0; i++) { + seg = db->segs + segs[i]; + chk += seg->check; + cnt += seg->count; + age += (db->epoch - seg->epoch) & 0xff; + } + + data0 = db->segs[segs[0]].data; + chk0 = data0 % 211; + chk %= 211; + + dbprintf(2, " chk=%d ?= %d", chk, chk0); + if (chk != chk0) + continue; + + dbprintf(2, " cnt=%d age=%d", cnt, age); + if (maxcnt > cnt || (maxcnt == cnt && maxage <= age)) + continue; + + dbprintf(2, " !"); + maxcnt = cnt; + maxage = age; + for (i = 0; segs[i] >= 0; i++) + bestsegs[i] = segs[i]; + bestsegs[i] = -1; + } + + if (bestsegs[0] < 0) + return (ZBAR_PARTIAL); + + if (acquire_lock(dcode, ZBAR_DATABAR_EXP)) + return (ZBAR_PARTIAL); + + for (i = 0; bestsegs[i] >= 0; i++) + segs[i] = db->segs[bestsegs[i]].data; + + if (databar_postprocess_exp(dcode, segs)) { + release_lock(dcode, ZBAR_DATABAR_EXP); + return (ZBAR_PARTIAL); + } + + for (i = 0; bestsegs[i] >= 0; i++) + if (bestsegs[i] != ifixed) { + seg = db->segs + bestsegs[i]; + if (!--seg->count) + seg->finder = -1; + } + + /* FIXME stacked rows are frequently reversed, + * so direction is impossible to determine at this level + */ + dcode->direction = (1 - 2 * (seg->side ^ seg->color)) * dir; + dcode->modifiers = MOD(ZBAR_MOD_GS1); + return (ZBAR_DATABAR_EXP); +abort: + return (ZBAR_NONE); +} +#undef IDX + +static inline unsigned calc_check(unsigned sig0, unsigned sig1, unsigned side, + unsigned mod) +{ + unsigned chk = 0; + int i; + for (i = 4; --i >= 0;) { + chk = (chk * 3 + (sig1 & 0xf) + 1) * 3 + (sig0 & 0xf) + 1; + sig1 >>= 4; + sig0 >>= 4; + if (!(i & 1)) + chk %= mod; + } + dbprintf(2, " chk=%d", chk); + + if (side) + chk = (chk * (6561 % mod)) % mod; + return (chk); +} + +static inline int calc_value4(unsigned sig, unsigned n, unsigned wmax, + unsigned nonarrow) +{ + unsigned w0, w1, w2, w3; + unsigned v = 0; + n--; + + w0 = (sig >> 12) & 0xf; + if (w0 > 1) { + unsigned n0, sk20, sk21; + if (w0 > wmax) + return (-1); + n0 = n - w0; + sk20 = (n - 1) * n * (2 * n - 1); + sk21 = n0 * (n0 + 1) * (2 * n0 + 1); + v = sk20 - sk21 - 3 * (w0 - 1) * (2 * n - w0); + + if (!nonarrow && w0 > 2 && n > 4) { + unsigned k = (n - 2) * (n - 1) * (2 * n - 3) - sk21; + k -= 3 * (w0 - 2) * (14 * n - 7 * w0 - 31); + v -= k; + } + + if (n - 2 > wmax) { + unsigned wm20 = 2 * wmax * (wmax + 1); + unsigned wm21 = (2 * wmax + 1); + unsigned k = sk20; + if (n0 > wmax) { + k -= sk21; + k += 3 * (w0 - 1) * (wm20 - wm21 * (2 * n - w0)); + } else { + k -= (wmax + 1) * (wmax + 2) * (2 * wmax + 3); + k += 3 * (n - wmax - 2) * (wm20 - wm21 * (n + wmax + 1)); + } + k *= 3; + v -= k; + } + v /= 12; + } else + nonarrow = 1; + n -= w0; + + w1 = (sig >> 8) & 0xf; + if (w1 > 1) { + if (w1 > wmax) + return (-1); + v += (2 * n - w1) * (w1 - 1) / 2; + if (!nonarrow && w1 > 2 && n > 3) + v -= (2 * n - w1 - 5) * (w1 - 2) / 2; + if (n - 1 > wmax) { + if (n - w1 > wmax) + v -= (w1 - 1) * (2 * n - w1 - 2 * wmax); + else + v -= (n - wmax) * (n - wmax - 1); + } + } else + nonarrow = 1; + n -= w1; + + w2 = (sig >> 4) & 0xf; + if (w2 > 1) { + if (w2 > wmax) + return (-1); + v += w2 - 1; + if (!nonarrow && w2 > 2 && n > 2) + v -= n - 2; + if (n > wmax) + v -= n - wmax; + } else + nonarrow = 1; + + w3 = sig & 0xf; + if (w3 == 1) + nonarrow = 1; + else if (w3 > wmax) + return (-1); + + if (!nonarrow) + return (-1); + + return (v); +} + +static inline zbar_symbol_type_t +decode_char(zbar_decoder_t *dcode, databar_segment_t *seg, int off, int dir) +{ + databar_decoder_t *db = &dcode->databar; + unsigned s = calc_s(dcode, (dir > 0) ? off : off - 6, 8); + int n, i, emin[2] = { 0, }, sum = 0; + unsigned sig0 = 0, sig1 = 0; + int diff, vodd, veven, v; + unsigned sum0, sum1, chk; + struct group_s *g; + + if (seg->exp) + n = 17; + else if (seg->side) + n = 15; + else + n = 16; + emin[1] = -n; + + dbprintf(2, + "\n char[%c%d]: n=%d s=%d w=%d sig=", (dir < 0) ? '>' : '<', + off, n, s, seg->width); + if (s < 13 || !check_width(seg->width, s, n)) + return (ZBAR_NONE); + + for (i = 4; --i >= 0;) { + int e = decode_e(pair_width(dcode, off), s, n); + if (e < 0) + return (ZBAR_NONE); + dbprintf(2, "%d", e); + sum = e - sum; + off += dir; + sig1 <<= 4; + if (emin[1] < -sum) + emin[1] = -sum; + sig1 += sum; + if (!i) + break; + + e = decode_e(pair_width(dcode, off), s, n); + if (e < 0) + return (ZBAR_NONE); + dbprintf(2, "%d", e); + sum = e - sum; + off += dir; + sig0 <<= 4; + if (emin[0] > sum) + emin[0] = sum; + sig0 += sum; + } + + diff = emin[~n & 1]; + diff = diff + (diff << 4); + diff = diff + (diff << 8); + + sig0 -= diff; + sig1 += diff; + + dbprintf(2, " emin=%d,%d el=%04x/%04x", emin[0], emin[1], sig0, sig1); + + sum0 = sig0 + (sig0 >> 8); + sum1 = sig1 + (sig1 >> 8); + sum0 += sum0 >> 4; + sum1 += sum1 >> 4; + sum0 &= 0xf; + sum1 &= 0xf; + + dbprintf(2, " sum=%d/%d", sum0, sum1); + + if (sum0 + sum1 + 8 != n) { + dbprintf(2, " [SUM]"); + return (ZBAR_NONE); + } + + if (((sum0 ^ (n >> 1)) | (sum1 ^ (n >> 1) ^ n)) & 1) { + dbprintf(2, " [ODD]"); + return (ZBAR_NONE); + } + + i = ((n & 0x3) ^ 1) * 5 + (sum1 >> 1); + zassert(i < sizeof(groups) / sizeof(*groups), -1, + "n=%d sum=%d/%d sig=%04x/%04x g=%d", n, sum0, sum1, sig0, sig1, i); + g = groups + i; + dbprintf(2, "\n g=%d(%d,%d,%d/%d)", i, g->sum, g->wmax, g->todd, + g->teven); + + vodd = calc_value4(sig0 + 0x1111, sum0 + 4, g->wmax, ~n & 1); + dbprintf(2, " v=%d", vodd); + if (vodd < 0 || vodd > g->todd) + return (ZBAR_NONE); + + veven = calc_value4(sig1 + 0x1111, sum1 + 4, 9 - g->wmax, n & 1); + dbprintf(2, "/%d", veven); + if (veven < 0 || veven > g->teven) + return (ZBAR_NONE); + + v = g->sum; + if (n & 2) + v += vodd + veven * g->todd; + else + v += veven + vodd * g->teven; + + dbprintf(2, " f=%d(%x%x%x)", seg->finder, seg->exp, seg->color, seg->side); + + chk = 0; + if (seg->exp) { + unsigned side = seg->color ^ seg->side ^ 1; + if (v >= 4096) + return (ZBAR_NONE); + /* skip A1 left */ + chk = calc_check(sig0, sig1, side, 211); + if (seg->finder || seg->color || seg->side) { + i = (seg->finder << 1) - side + seg->color; + zassert(i >= 0 && i < 12, ZBAR_NONE, "f=%d(%x%x%x) side=%d i=%d\n", + seg->finder, seg->exp, seg->color, seg->side, side, i); + chk = (chk * exp_checksums[i]) % 211; + } else if (v >= 4009) + return (ZBAR_NONE); + else + chk = 0; + } else { + chk = calc_check(sig0, sig1, seg->side, 79); + if (seg->color) + chk = (chk * 16) % 79; + } + dbprintf(2, " => %d val=%d", chk, v); + + seg->check = chk; + seg->data = v; + + merge_segment(db, seg); + + if (seg->exp) + return (match_segment_exp(dcode, seg, dir)); + else if (dir > 0) + return (match_segment(dcode, seg)); + return (ZBAR_PARTIAL); +} + +static inline int alloc_segment(databar_decoder_t *db) +{ + unsigned maxage = 0, csegs = db->csegs; + int i, old = -1; + for (i = 0; i < csegs; i++) { + databar_segment_t *seg = db->segs + i; + unsigned age; + if (seg->finder < 0) { + dbprintf(2, " free@%d", i); + return (i); + } + age = (db->epoch - seg->epoch) & 0xff; + if (age >= 128 && seg->count < 2) { + seg->finder = -1; + dbprintf(2, " stale@%d (%d - %d = %d)", i, db->epoch, seg->epoch, + age); + return (i); + } + + /* score based on both age and count */ + if (age > seg->count) + age = age - seg->count + 1; + else + age = 1; + + if (maxage < age) { + maxage = age; + old = i; + dbprintf(2, " old@%d(%u)", i, age); + } + } + + if (csegs < DATABAR_MAX_SEGMENTS) { + dbprintf(2, " new@%d", i); + i = csegs; + csegs *= 2; + if (csegs > DATABAR_MAX_SEGMENTS) + csegs = DATABAR_MAX_SEGMENTS; + if (csegs != db->csegs) { + databar_segment_t *seg; + db->segs = realloc(db->segs, csegs * sizeof(*db->segs)); + db->csegs = csegs; + seg = db->segs + csegs; + while (--seg, --csegs >= i) { + seg->finder = -1; + seg->exp = 0; + seg->color = 0; + seg->side = 0; + seg->partial = 0; + seg->count = 0; + seg->epoch = 0; + seg->check = 0; + } + return (i); + } + } + zassert(old >= 0, -1, "\n"); + + db->segs[old].finder = -1; + return (old); +} + +static inline zbar_symbol_type_t decode_finder(zbar_decoder_t *dcode) +{ + databar_decoder_t *db = &dcode->databar; + databar_segment_t *seg; + unsigned e0 = pair_width(dcode, 1); + unsigned e2 = pair_width(dcode, 3); + unsigned e1, e3, s, finder, dir; + int sig, iseg; + int rc, i; + + dbprintf(2, " databar: e0=%d e2=%d", e0, e2); + if (e0 < e2) { + unsigned e = e2 * 4; + if (e < 15 * e0 || e > 34 * e0) + return (ZBAR_NONE); + dir = 0; + e3 = pair_width(dcode, 4); + } else { + unsigned e = e0 * 4; + if (e < 15 * e2 || e > 34 * e2) + return (ZBAR_NONE); + dir = 1; + e2 = e0; + e3 = pair_width(dcode, 0); + } + e1 = pair_width(dcode, 2); + + s = e1 + e3; + dbprintf(2, " e1=%d e3=%d dir=%d s=%d", e1, e3, dir, s); + if (s < 12) + return (ZBAR_NONE); + + sig = ((decode_e(e3, s, 14) << 8) | (decode_e(e2, s, 14) << 4) | + decode_e(e1, s, 14)); + dbprintf(2, " sig=%04x", sig & 0xfff); + if (sig < 0 || ((sig >> 4) & 0xf) < 8 || ((sig >> 4) & 0xf) > 10 || + (sig & 0xf) >= 10 || ((sig >> 8) & 0xf) >= 10 || + (((sig >> 8) + sig) & 0xf) != 10) + return (ZBAR_NONE); + + finder = (finder_hash[(sig - (sig >> 5)) & 0x1f] + + finder_hash[(sig >> 1) & 0x1f]) & + 0x1f; + dbprintf(2, " finder=%d", finder); + if (finder == 0x1f || + !TEST_CFG((finder < 9) ? db->config : db->config_exp, ZBAR_CFG_ENABLE)) + return (ZBAR_NONE); + + zassert(finder >= 0, ZBAR_NONE, "dir=%d sig=%04x f=%d\n", dir, sig & 0xfff, + finder); + + iseg = alloc_segment(db); + if (iseg < 0) + return (ZBAR_NONE); + + seg = db->segs + iseg; + seg->finder = (finder >= 9) ? finder - 9 : finder; + seg->exp = (finder >= 9); + seg->color = get_color(dcode) ^ dir ^ 1; + seg->side = dir; + seg->partial = 0; + seg->count = 1; + seg->width = s; + seg->epoch = db->epoch; + + rc = decode_char(dcode, seg, 12 - dir, -1); + if (!rc) + seg->partial = 1; + else + db->epoch++; + + i = (dcode->idx + 8 + dir) & 0xf; + zassert(db->chars[i] == -1, ZBAR_NONE, "\n"); + db->chars[i] = iseg; + return (rc); +} + +zbar_symbol_type_t _zbar_decode_databar(zbar_decoder_t *dcode) +{ + databar_decoder_t *db = &dcode->databar; + databar_segment_t *seg, *pair; + zbar_symbol_type_t sym; + int iseg, i = dcode->idx & 0xf; + + sym = decode_finder(dcode); + dbprintf(2, "\n"); + + iseg = db->chars[i]; + if (iseg < 0) + return (sym); + + db->chars[i] = -1; + seg = db->segs + iseg; + dbprintf(2, " databar: i=%d part=%d f=%d(%x%x%x)", iseg, + seg->partial, seg->finder, seg->exp, seg->color, seg->side); + zassert(seg->finder >= 0, ZBAR_NONE, "i=%d f=%d(%x%x%x) part=%x\n", iseg, + seg->finder, seg->exp, seg->color, seg->side, seg->partial); + + if (seg->partial) { + pair = NULL; + seg->side = !seg->side; + } else { + int jseg = alloc_segment(db); + pair = db->segs + iseg; + seg = db->segs + jseg; + seg->finder = pair->finder; + seg->exp = pair->exp; + seg->color = pair->color; + seg->side = !pair->side; + seg->partial = 0; + seg->count = 1; + seg->width = pair->width; + seg->epoch = db->epoch; + } + + sym = decode_char(dcode, seg, 1, 1); + if (!sym) { + seg->finder = -1; + if (pair) + pair->partial = 1; + } else + db->epoch++; + dbprintf(2, "\n"); + + return (sym); +} diff --git a/zbar/decoder/databar.h b/zbar/decoder/databar.h new file mode 100644 index 0000000..2ed8820 --- /dev/null +++ b/zbar/decoder/databar.h @@ -0,0 +1,80 @@ +/*------------------------------------------------------------------------ + * Copyright 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 + *------------------------------------------------------------------------*/ +#ifndef _DATABAR_H_ +#define _DATABAR_H_ + +#define DATABAR_MAX_SEGMENTS 32 + +/* active DataBar (partial) segment entry */ +typedef struct databar_segment_s { + signed finder : 5; /* finder pattern */ + unsigned exp : 1; /* DataBar expanded finder */ + unsigned color : 1; /* finder coloring */ + unsigned side : 1; /* data character side of finder */ + + unsigned partial : 1; /* unpaired partial segment */ + unsigned count : 7; /* times encountered */ + unsigned epoch : 8; /* age, in characters scanned */ + unsigned check : 8; /* bar checksum */ + signed short data; /* decoded character data */ + unsigned short width; /* measured width of finder (14 modules) */ +} databar_segment_t; + +/* DataBar specific decode state */ +typedef struct databar_decoder_s { + unsigned config; /* decoder configuration flags */ + unsigned config_exp; + + unsigned csegs : 8; /* allocated segments */ + unsigned epoch : 8; /* current scan */ + + databar_segment_t *segs; /* active segment list */ + signed char chars[16]; /* outstanding character indices */ +} databar_decoder_t; + +/* reset DataBar segment decode state */ +static inline void databar_new_scan(databar_decoder_t *db) +{ + int i; + for (i = 0; i < 16; i++) + if (db->chars[i] >= 0) { + databar_segment_t *seg = db->segs + db->chars[i]; + if (seg->partial) + seg->finder = -1; + db->chars[i] = -1; + } +} + +/* reset DataBar accumulated segments */ +static inline void databar_reset(databar_decoder_t *db) +{ + int i, n = db->csegs; + databar_new_scan(db); + for (i = 0; i < n; i++) + db->segs[i].finder = -1; +} + +/* decode DataBar symbols */ +zbar_symbol_type_t _zbar_decode_databar(zbar_decoder_t *dcode); + +#endif diff --git a/zbar/decoder/ean.c b/zbar/decoder/ean.c new file mode 100644 index 0000000..024d7f8 --- /dev/null +++ b/zbar/decoder/ean.c @@ -0,0 +1,735 @@ +/*------------------------------------------------------------------------ + * 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" +#include <zbar.h> + +#ifdef DEBUG_EAN +#define DEBUG_LEVEL (DEBUG_EAN) +#endif +#include "debug.h" +#include "decoder.h" + +/* partial decode symbol location */ +typedef enum symbol_partial_e +{ + EAN_LEFT = 0x0000, + EAN_RIGHT = 0x1000, +} symbol_partial_t; + +/* convert compact encoded D2E1E2 to character (bit4 is parity) */ +static const unsigned char digits[] = { + /* E1 E2 */ + 0x06, 0x10, 0x04, 0x13, /* 2 2-5 */ + 0x19, 0x08, 0x11, 0x05, /* 3 2-5 (d2 <= thr) */ + 0x09, 0x12, 0x07, 0x15, /* 4 2-5 (d2 <= thr) */ + 0x16, 0x00, 0x14, 0x03, /* 5 2-5 */ + 0x18, 0x01, 0x02, 0x17, /* E1E2=43,44,33,34 (d2 > thr) */ +}; + +static const unsigned char parity_decode[] = { + 0xf0, /* [xx] BBBBBB = RIGHT half EAN-13 */ + + /* UPC-E check digit encoding */ + 0xff, 0xff, 0x0f, /* [07] BBBAAA = 0 */ + 0xff, 0x1f, /* [0b] BBABAA = 1 */ + 0x2f, /* [0d] BBAABA = 2 */ + 0xf3, /* [0e] BBAAAB = 3 */ + 0xff, 0x4f, /* [13] BABBAA = 4 */ + 0x7f, /* [15] BABABA = 7 */ + 0xf8, /* [16] BABAAB = 8 */ + 0x5f, /* [19] BAABBA = 5 */ + 0xf9, /* [1a] BAABAB = 9 */ + 0xf6, /* [1c] BAAABB = 6 */ + 0xff, + + /* LEFT half EAN-13 leading digit */ + 0xff, 0x6f, /* [23] ABBBAA = 6 */ + 0x9f, /* [25] ABBABA = 9 */ + 0xf5, /* [26] ABBAAB = 5 */ + 0x8f, /* [29] ABABBA = 8 */ + 0xf7, /* [2a] ABABAB = 7 */ + 0xf4, /* [2c] ABAABB = 4 */ + 0xff, 0x3f, /* [31] AABBBA = 3 */ + 0xf2, /* [32] AABBAB = 2 */ + 0xf1, /* [34] AABABB = 1 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, /* [3f] AAAAAA = 0 */ +}; + +#ifdef DEBUG_EAN +static unsigned char debug_buf[0x18]; + +static inline const unsigned char *dsprintbuf(ean_decoder_t *ean) +{ + int i; + for (i = 0; i < 7; i++) + debug_buf[i] = + ((ean->buf[0] < 0 || ean->buf[i] < 0) ? '-' : ean->buf[i] + '0'); + debug_buf[i] = ' '; + for (; i < 13; i++) + debug_buf[i + 1] = + ((ean->buf[7] < 0 || ean->buf[i] < 0) ? '-' : ean->buf[i] + '0'); + debug_buf[i + 1] = ' '; + for (; i < 18; i++) + debug_buf[i + 2] = + ((ean->buf[13] < 0 || ean->buf[i] < 0) ? '-' : ean->buf[i] + '0'); + debug_buf[i + 2] = '\0'; + return (debug_buf); +} +#endif + +static inline int check_width(unsigned w0, unsigned w1) +{ + unsigned dw0 = w0; + w0 *= 8; + w1 *= 8; + return (w0 - dw0 <= w1 && w1 <= w0 + dw0); +} + +/* evaluate previous N (>= 2) widths as auxiliary pattern, + * using preceding 4 as character width + */ +static inline signed char aux_end(zbar_decoder_t *dcode, unsigned char fwd) +{ + signed char code, i; + + /* reference width from previous character */ + unsigned s = calc_s(dcode, 4 + fwd, 4); + + /* check quiet zone */ + unsigned qz = get_width(dcode, 0); + if (!fwd && qz && qz <= s * 3 / 4) { + dbprintf(2, " [invalid quiet]"); + return (-1); + } + + dbprintf(2, " ("); + code = 0; + for (i = 1 - fwd; i < 3 + fwd; i++) { + unsigned e = get_width(dcode, i) + get_width(dcode, i + 1); + dbprintf(2, " %d", e); + code = (code << 2) | decode_e(e, s, 7); + if (code < 0) { + dbprintf(2, " [invalid end guard]"); + return (-1); + } + } + dbprintf(2, ") s=%d aux=%x", s, code); + return (code); +} + +/* determine possible auxiliary pattern + * using current 4 as possible character + */ +static inline signed char aux_start(zbar_decoder_t *dcode) +{ + /* FIXME NB add-on has no guard in reverse */ + unsigned e1, e2 = get_width(dcode, 5) + get_width(dcode, 6); + unsigned char E1; + if (dcode->ean.s4 < 6) + return (-1); + + if (decode_e(e2, dcode->ean.s4, 7)) { + dbprintf(2, " [invalid any]"); + return (-1); + } + + e1 = get_width(dcode, 4) + get_width(dcode, 5); + E1 = decode_e(e1, dcode->ean.s4, 7); + + if (get_color(dcode) == ZBAR_BAR) { + /* check for quiet-zone */ + unsigned qz = get_width(dcode, 7); + if (!qz || qz > dcode->ean.s4 * 3 / 4) { + if (!E1) { + dbprintf(2, " [valid normal]"); + return (0); /* normal symbol start */ + } else if (E1 == 1) { + dbprintf(2, " [valid add-on]"); + return (STATE_ADDON); /* add-on symbol start */ + } + } + dbprintf(2, " [invalid start]"); + return (-1); + } + + if (!E1) { + /* attempting decode from SPACE => validate center guard */ + unsigned e3 = get_width(dcode, 6) + get_width(dcode, 7); + unsigned e4 = get_width(dcode, 7) + get_width(dcode, 8); + if (!decode_e(e3, dcode->ean.s4, 7) && + !decode_e(e4, dcode->ean.s4, 7)) { + dbprintf(2, " [valid center]"); + return (0); /* start after center guard */ + } + } + dbprintf(2, " [invalid center]"); + return (-1); +} + +/* check addon delimiter using current 4 as character + */ +static inline signed char aux_mid(zbar_decoder_t *dcode) +{ + unsigned e = get_width(dcode, 4) + get_width(dcode, 5); + return (decode_e(e, dcode->ean.s4, 7)); +} + +/* attempt to decode previous 4 widths (2 bars and 2 spaces) as a character */ +static inline signed char decode4(zbar_decoder_t *dcode) +{ + signed char code; + + /* calculate similar edge measurements */ + unsigned e1 = ((get_color(dcode) == ZBAR_BAR) ? + get_width(dcode, 0) + get_width(dcode, 1) : + get_width(dcode, 2) + get_width(dcode, 3)); + unsigned e2 = get_width(dcode, 1) + get_width(dcode, 2); + dbprintf(2, "\n e1=%d e2=%d", e1, e2); + + if (dcode->ean.s4 < 6) + return (-1); + + /* create compacted encoding for direct lookup */ + code = ((decode_e(e1, dcode->ean.s4, 7) << 2) | + decode_e(e2, dcode->ean.s4, 7)); + if (code < 0) + return (-1); + dbprintf(2, " code=%x", code); + + /* 4 combinations require additional determinant (D2) + E1E2 == 34 (0110) + E1E2 == 43 (1001) + E1E2 == 33 (0101) + E1E2 == 44 (1010) + */ + if ((1 << code) & 0x0660) { + unsigned char mid, alt; + /* use sum of bar widths */ + unsigned d2 = ((get_color(dcode) == ZBAR_BAR) ? + get_width(dcode, 0) + get_width(dcode, 2) : + get_width(dcode, 1) + get_width(dcode, 3)); + d2 *= 7; + mid = (((1 << code) & 0x0420) ? 3 /* E1E2 in 33,44 */ + : + 4); /* E1E2 in 34,43 */ + alt = d2 > (mid * dcode->ean.s4); + if (alt) + code = ((code >> 1) & 3) | 0x10; /* compress code space */ + dbprintf(2, " (d2=%d(%d) alt=%d)", d2, mid * dcode->ean.s4, alt); + } + dbprintf(2, " char=%02x", digits[(unsigned char)code]); + zassert(code < 0x14, -1, "code=%02x e1=%x e2=%x s4=%x color=%x\n", code, e1, + e2, dcode->ean.s4, get_color(dcode)); + return (code); +} + +static inline char ean_part_end2(ean_decoder_t *ean, ean_pass_t *pass) +{ + unsigned char par, chk; + if (!TEST_CFG(ean->ean2_config, ZBAR_CFG_ENABLE)) + return (ZBAR_NONE); + + /* extract parity bits */ + par = ((pass->raw[1] & 0x10) >> 3 | (pass->raw[2] & 0x10) >> 4); + /* calculate "checksum" */ + chk = ~((pass->raw[1] & 0xf) * 10 + (pass->raw[2] & 0xf)) & 0x3; + dbprintf(2, " par=%x chk=%x", par, chk); + if (par != chk) + return (ZBAR_NONE); + + dbprintf(2, "\n"); + dbprintf(1, "decode2=%x%x\n", pass->raw[1] & 0xf, pass->raw[2] & 0xf); + return (ZBAR_EAN2); +} + +static inline zbar_symbol_type_t ean_part_end4(ean_pass_t *pass, + unsigned char fwd) +{ + /* extract parity bits */ + unsigned char par = + ((pass->raw[1] & 0x10) >> 1 | (pass->raw[2] & 0x10) >> 2 | + (pass->raw[3] & 0x10) >> 3 | (pass->raw[4] & 0x10) >> 4); + + dbprintf(2, " par=%x", par); + if (par && par != 0xf) + /* invalid parity combination */ + return (ZBAR_NONE); + + if ((!par) == fwd) { + /* reverse sampled digits */ + unsigned char tmp = pass->raw[1]; + pass->state |= STATE_REV; + pass->raw[1] = pass->raw[4]; + pass->raw[4] = tmp; + tmp = pass->raw[2]; + pass->raw[2] = pass->raw[3]; + pass->raw[3] = tmp; + } + + dbprintf(2, "\n"); + dbprintf(1, "decode4=%x%x%x%x\n", pass->raw[1] & 0xf, pass->raw[2] & 0xf, + pass->raw[3] & 0xf, pass->raw[4] & 0xf); + if (!par) + return (ZBAR_EAN8 | EAN_RIGHT); + return (ZBAR_EAN8 | EAN_LEFT); +} + +static inline char ean_part_end5(ean_decoder_t *ean, ean_pass_t *pass) +{ + unsigned char par, chk, parchk; + if (!TEST_CFG(ean->ean5_config, ZBAR_CFG_ENABLE)) + return (ZBAR_NONE); + + /* extract parity bits */ + par = ((pass->raw[1] & 0x10) | (pass->raw[2] & 0x10) >> 1 | + (pass->raw[3] & 0x10) >> 2 | (pass->raw[4] & 0x10) >> 3 | + (pass->raw[5] & 0x10) >> 4); + /* calculate checksum */ + chk = (((pass->raw[1] & 0x0f) + (pass->raw[2] & 0x0f) * 3 + + (pass->raw[3] & 0x0f) + (pass->raw[4] & 0x0f) * 3 + + (pass->raw[5] & 0x0f)) * + 3) % + 10; + + parchk = parity_decode[par >> 1]; + if (par & 1) + parchk >>= 4; + parchk &= 0xf; + dbprintf(2, " par=%x(%d) chk=%d", par, parchk, chk); + if (parchk != chk) + return (ZBAR_NONE); + + dbprintf(2, "\n"); + dbprintf(1, "decode5=%x%x%x%x%x\n", pass->raw[1] & 0xf, pass->raw[2] & 0xf, + pass->raw[3] & 0xf, pass->raw[4] & 0xf, pass->raw[5] & 0xf); + + return (ZBAR_EAN5); +} + +static inline zbar_symbol_type_t +ean_part_end7(ean_decoder_t *ean, ean_pass_t *pass, unsigned char fwd) +{ + /* calculate parity index */ + unsigned char par = + ((fwd) ? ((pass->raw[1] & 0x10) << 1 | (pass->raw[2] & 0x10) | + (pass->raw[3] & 0x10) >> 1 | (pass->raw[4] & 0x10) >> 2 | + (pass->raw[5] & 0x10) >> 3 | (pass->raw[6] & 0x10) >> 4) : + ((pass->raw[1] & 0x10) >> 4 | (pass->raw[2] & 0x10) >> 3 | + (pass->raw[3] & 0x10) >> 2 | (pass->raw[4] & 0x10) >> 1 | + (pass->raw[5] & 0x10) | (pass->raw[6] & 0x10) << 1)); + + /* lookup parity combination */ + pass->raw[0] = parity_decode[par >> 1]; + if (par & 1) + pass->raw[0] >>= 4; + pass->raw[0] &= 0xf; + dbprintf(2, " par=%02x(%x)", par, pass->raw[0]); + + if (pass->raw[0] == 0xf) + /* invalid parity combination */ + return (ZBAR_NONE); + + if ((!par) == fwd) { + unsigned char i; + pass->state |= STATE_REV; + /* reverse sampled digits */ + for (i = 1; i < 4; i++) { + unsigned char tmp = pass->raw[i]; + pass->raw[i] = pass->raw[7 - i]; + pass->raw[7 - i] = tmp; + } + } + + dbprintf(2, "\n"); + dbprintf(1, "decode=%x%x%x%x%x%x%x(%02x)\n", pass->raw[0] & 0xf, + pass->raw[1] & 0xf, pass->raw[2] & 0xf, pass->raw[3] & 0xf, + pass->raw[4] & 0xf, pass->raw[5] & 0xf, pass->raw[6] & 0xf, par); + + if (TEST_CFG(ean->ean13_config, ZBAR_CFG_ENABLE)) { + if (!par) + return (ZBAR_EAN13 | EAN_RIGHT); + if (par & 0x20) + return (ZBAR_EAN13 | EAN_LEFT); + } + if (par && !(par & 0x20)) + return (ZBAR_UPCE); + + return (ZBAR_NONE); +} + +/* update state for one of 4 parallel passes */ +static inline zbar_symbol_type_t decode_pass(zbar_decoder_t *dcode, + ean_pass_t *pass) +{ + unsigned char idx, fwd; + pass->state++; + idx = pass->state & STATE_IDX; + fwd = pass->state & 1; + + if (get_color(dcode) == ZBAR_SPACE) { + if (pass->state & STATE_ADDON) { + dbprintf(2, " i=%d", idx); + if (idx == 0x09 || idx == 0x21) { + unsigned qz = get_width(dcode, 0); + unsigned s = calc_s(dcode, 1, 4); + zbar_symbol_type_t part = !qz || (qz >= s * 3 / 4); + if (part && idx == 0x09) + part = ean_part_end2(&dcode->ean, pass); + else if (part) + part = ean_part_end5(&dcode->ean, pass); + + if (part || idx == 0x21) { + dcode->ean.direction = 0; + pass->state = -1; + return (part); + } + } + if ((idx & 7) == 1) { + dbprintf(2, " +"); + pass->state += 2; + idx += 2; + } + } else if ((idx == 0x10 || idx == 0x11) && + TEST_CFG(dcode->ean.ean8_config, ZBAR_CFG_ENABLE) && + !aux_end(dcode, fwd)) { + zbar_symbol_type_t part; + dbprintf(2, " fwd=%x", fwd); + part = ean_part_end4(pass, fwd); + if (part) + dcode->ean.direction = (pass->state & STATE_REV) != 0; + pass->state = -1; + return (part); + } else if ((idx == 0x18 || idx == 0x19)) { + zbar_symbol_type_t part = ZBAR_NONE; + dbprintf(2, " fwd=%x", fwd); + if (!aux_end(dcode, fwd) && pass->raw[5] != 0xff) + part = ean_part_end7(&dcode->ean, pass, fwd); + if (part) + dcode->ean.direction = (pass->state & STATE_REV) != 0; + pass->state = -1; + return (part); + } + } + + if (pass->state & STATE_ADDON) + idx >>= 1; + + if (!(idx & 0x03) && idx <= 0x14) { + signed char code = -1; + unsigned w = pass->width; + if (!dcode->ean.s4) + return (0); + /* validate guard bars before decoding first char of symbol */ + if (!pass->state) { + pass->state = aux_start(dcode); + pass->width = dcode->ean.s4; + if (pass->state < 0) + return (0); + idx = pass->state & STATE_IDX; + } else { + w = check_width(w, dcode->ean.s4); + if (w) + pass->width = (pass->width + dcode->ean.s4 * 3) / 4; + } + + if (w) + code = decode4(dcode); + else + dbprintf(2, " [bad width]"); + + if ((code < 0 && idx != 0x10) || + (idx > 0 && (pass->state & STATE_ADDON) && aux_mid(dcode))) + pass->state = -1; + else if (code < 0) + pass->raw[5] = 0xff; + else { + dbprintf(2, "\n raw[%x]=%02x =>", idx >> 2, + digits[(unsigned char)code]); + pass->raw[(idx >> 2) + 1] = digits[(unsigned char)code]; + dbprintf(2, " raw=%d%d%d%d%d%d%d", pass->raw[0] & 0xf, + pass->raw[1] & 0xf, pass->raw[2] & 0xf, pass->raw[3] & 0xf, + pass->raw[4] & 0xf, pass->raw[5] & 0xf, + pass->raw[6] & 0xf); + } + } + return (0); +} + +static inline signed char ean_verify_checksum(ean_decoder_t *ean, int n) +{ + unsigned char chk = 0; + unsigned char i, d; + for (i = 0; i < n; i++) { + unsigned char d = ean->buf[i]; + zassert(d < 10, -1, "i=%x d=%x chk=%x %s\n", i, d, chk, + _zbar_decoder_buf_dump((void *)ean->buf, 18)); + chk += d; + if ((i ^ n) & 1) { + chk += d << 1; + if (chk >= 20) + chk -= 20; + } + if (chk >= 10) + chk -= 10; + } + zassert(chk < 10, -1, "chk=%x n=%x %s", chk, n, + _zbar_decoder_buf_dump((void *)ean->buf, 18)); + if (chk) + chk = 10 - chk; + d = ean->buf[n]; + zassert(d < 10, -1, "n=%x d=%x chk=%x %s\n", n, d, chk, + _zbar_decoder_buf_dump((void *)ean->buf, 18)); + if (chk != d) { + dbprintf(1, "\nchecksum mismatch %d != %d (%s)\n", chk, d, + dsprintbuf(ean)); + return (-1); + } + return (0); +} + +static inline unsigned char isbn10_calc_checksum(ean_decoder_t *ean) +{ + unsigned int chk = 0; + unsigned char w; + for (w = 10; w > 1; w--) { + unsigned char d = ean->buf[13 - w]; + zassert(d < 10, '?', "w=%x d=%x chk=%x %s\n", w, d, chk, + _zbar_decoder_buf_dump((void *)ean->buf, 18)); + chk += d * w; + } + chk = chk % 11; + if (!chk) + return ('0'); + chk = 11 - chk; + if (chk < 10) + return (chk + '0'); + return ('X'); +} + +static inline void ean_expand_upce(ean_decoder_t *ean, ean_pass_t *pass) +{ + int i = 0; + unsigned char decode; + /* parity encoded digit is checksum */ + ean->buf[12] = pass->raw[i++]; + + decode = pass->raw[6] & 0xf; + ean->buf[0] = 0; + ean->buf[1] = 0; + ean->buf[2] = pass->raw[i++] & 0xf; + ean->buf[3] = pass->raw[i++] & 0xf; + ean->buf[4] = (decode < 3) ? decode : pass->raw[i++] & 0xf; + ean->buf[5] = (decode < 4) ? 0 : pass->raw[i++] & 0xf; + ean->buf[6] = (decode < 5) ? 0 : pass->raw[i++] & 0xf; + ean->buf[7] = 0; + ean->buf[8] = 0; + ean->buf[9] = (decode < 3) ? pass->raw[i++] & 0xf : 0; + ean->buf[10] = (decode < 4) ? pass->raw[i++] & 0xf : 0; + ean->buf[11] = (decode < 5) ? pass->raw[i] & 0xf : decode; +} + +static inline zbar_symbol_type_t +integrate_partial(ean_decoder_t *ean, ean_pass_t *pass, zbar_symbol_type_t part) +{ + signed char i, j; + /* copy raw data into holding buffer */ + /* if same partial is not consistent, reset others */ + dbprintf(2, " integrate part=%x (%s)", part, dsprintbuf(ean)); + + if ((ean->left && ((part & ZBAR_SYMBOL) != ean->left)) || + (ean->right && ((part & ZBAR_SYMBOL) != ean->right))) { + /* partial mismatch - reset collected parts */ + dbprintf(2, " rst(type %x %x)", ean->left, ean->right); + ean->left = ean->right = ZBAR_NONE; + } + + if ((ean->left || ean->right) && !check_width(ean->width, pass->width)) { + dbprintf(2, " rst(width %d)", pass->width); + ean->left = ean->right = ZBAR_NONE; + } + + if (part & EAN_RIGHT) { + part &= ZBAR_SYMBOL; + j = part - 1; + for (i = part >> 1; i; i--, j--) { + unsigned char digit = pass->raw[i] & 0xf; + if (ean->right && ean->buf[j] != digit) { + /* partial mismatch - reset collected parts */ + dbprintf(2, " rst(right)"); + ean->left = ean->right = ZBAR_NONE; + } + ean->buf[j] = digit; + } + ean->right = part; + part &= ean->left; /* FIXME!? */ + } else if (part == ZBAR_EAN13 || part == ZBAR_EAN8) /* EAN_LEFT */ { + j = (part - 1) >> 1; + for (i = part >> 1; j >= 0; i--, j--) { + unsigned char digit = pass->raw[i] & 0xf; + if (ean->left && ean->buf[j] != digit) { + /* partial mismatch - reset collected parts */ + dbprintf(2, " rst(left)"); + ean->left = ean->right = ZBAR_NONE; + } + ean->buf[j] = digit; + } + ean->left = part; + part &= ean->right; /* FIXME!? */ + } else if (part != ZBAR_UPCE) /* add-ons */ { + for (i = part; i > 0; i--) + ean->buf[i - 1] = pass->raw[i] & 0xf; + ean->left = part; + } else + ean_expand_upce(ean, pass); + + ean->width = pass->width; + + if (!part) + part = ZBAR_PARTIAL; + + if (((part == ZBAR_EAN13 || part == ZBAR_UPCE) && + ean_verify_checksum(ean, 12)) || + (part == ZBAR_EAN8 && ean_verify_checksum(ean, 7))) { + /* invalid checksum */ + if (ean->right) + ean->left = ZBAR_NONE; + else + ean->right = ZBAR_NONE; + part = ZBAR_NONE; + } + + if (part == ZBAR_EAN13) { + /* special case EAN-13 subsets */ + if (!ean->buf[0] && TEST_CFG(ean->upca_config, ZBAR_CFG_ENABLE)) + part = ZBAR_UPCA; + else if (ean->buf[0] == 9 && ean->buf[1] == 7) { + if ((ean->buf[2] == 8 || ean->buf[2] == 9) && + TEST_CFG(ean->isbn13_config, ZBAR_CFG_ENABLE)) { + part = ZBAR_ISBN13; + } else if (ean->buf[2] == 8 && + TEST_CFG(ean->isbn10_config, ZBAR_CFG_ENABLE)) { + part = ZBAR_ISBN10; + } + } + } else if (part == ZBAR_UPCE) { + if (TEST_CFG(ean->upce_config, ZBAR_CFG_ENABLE)) { + /* UPC-E was decompressed for checksum verification, + * but user requested compressed result + */ + ean->buf[0] = ean->buf[1] = 0; + for (i = 2; i < 8; i++) + ean->buf[i] = pass->raw[i - 1] & 0xf; + ean->buf[i] = pass->raw[0] & 0xf; + } else if (TEST_CFG(ean->upca_config, ZBAR_CFG_ENABLE)) + /* UPC-E reported as UPC-A has priority over EAN-13 */ + part = ZBAR_UPCA; + else if (TEST_CFG(ean->ean13_config, ZBAR_CFG_ENABLE)) + part = ZBAR_EAN13; + else + part = ZBAR_NONE; + } + + dbprintf(2, " dir=%d %x/%x=%x", ean->direction, ean->left, ean->right, + part); + return (part); +} + +/* copy result to output buffer */ +static inline void postprocess(zbar_decoder_t *dcode, zbar_symbol_type_t sym) +{ + ean_decoder_t *ean = &dcode->ean; + zbar_symbol_type_t base = sym; + int i = 0, j = 0; + if (base > ZBAR_PARTIAL) { + if (base == ZBAR_UPCA) + i = 1; + else if (base == ZBAR_UPCE) { + i = 1; + base--; + } else if (base == ZBAR_ISBN13) + base = ZBAR_EAN13; + else if (base == ZBAR_ISBN10) + i = 3; + + if (base == ZBAR_ISBN10 || + (base > ZBAR_EAN5 && + !TEST_CFG(ean_get_config(ean, sym), ZBAR_CFG_EMIT_CHECK))) + base--; + + for (; j < base && ean->buf[i] >= 0; i++, j++) + dcode->buf[j] = ean->buf[i] + '0'; + + if (sym == ZBAR_ISBN10 && j == 9 && + TEST_CFG(ean->isbn10_config, ZBAR_CFG_EMIT_CHECK)) + /* recalculate ISBN-10 check digit */ + dcode->buf[j++] = isbn10_calc_checksum(ean); + } + dcode->buflen = j; + dcode->buf[j] = '\0'; + dcode->direction = 1 - 2 * ean->direction; + dcode->modifiers = 0; + dbprintf(2, " base=%d j=%d (%s)", base, j, dcode->buf); +} + +zbar_symbol_type_t _zbar_decode_ean(zbar_decoder_t *dcode) +{ + /* process upto 4 separate passes */ + zbar_symbol_type_t sym = ZBAR_NONE; + unsigned char pass_idx = dcode->idx & 3; + unsigned char i; + + /* update latest character width */ + dcode->ean.s4 -= get_width(dcode, 4); + dcode->ean.s4 += get_width(dcode, 0); + + for (i = 0; i < 4; i++) { + ean_pass_t *pass = &dcode->ean.pass[i]; + if (pass->state >= 0 || i == pass_idx) { + zbar_symbol_type_t part; + dbprintf(2, " ean[%x/%x]: idx=%x st=%d s=%d", i, pass_idx, + dcode->idx, pass->state, dcode->ean.s4); + part = decode_pass(dcode, pass); + if (part) { + /* update accumulated data from new partial decode */ + sym = integrate_partial(&dcode->ean, pass, part); + if (sym) { + /* this pass valid => _reset_ all passes */ + dbprintf(2, " sym=%x", sym); + dcode->ean.pass[0].state = dcode->ean.pass[1].state = -1; + dcode->ean.pass[2].state = dcode->ean.pass[3].state = -1; + if (sym > ZBAR_PARTIAL) { + if (!acquire_lock(dcode, sym)) + postprocess(dcode, sym); + else { + dbprintf(1, " [locked %d]", dcode->lock); + sym = ZBAR_PARTIAL; + } + } + } + } + dbprintf(2, "\n"); + } + } + return (sym); +} diff --git a/zbar/decoder/ean.h b/zbar/decoder/ean.h new file mode 100644 index 0000000..351245b --- /dev/null +++ b/zbar/decoder/ean.h @@ -0,0 +1,99 @@ +/*------------------------------------------------------------------------ + * 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 + *------------------------------------------------------------------------*/ +#ifndef _EAN_H_ +#define _EAN_H_ + +/* state of each parallel decode attempt */ +typedef struct ean_pass_s { + signed char state; /* module position of w[idx] in symbol */ +#define STATE_REV 0x80 /* scan direction reversed */ +#define STATE_ADDON 0x40 /* scanning add-on */ +#define STATE_IDX 0x3f /* element offset into symbol */ + unsigned width; /* width of last character */ + unsigned char raw[7]; /* decode in process */ +} ean_pass_t; + +/* EAN/UPC specific decode state */ +typedef struct ean_decoder_s { + ean_pass_t pass[4]; /* state of each parallel decode attempt */ + zbar_symbol_type_t left; /* current holding buffer contents */ + zbar_symbol_type_t right; + int direction; /* scan direction */ + unsigned s4, width; /* character width */ + signed char buf[18]; /* holding buffer */ + + signed char enable; + unsigned ean13_config; + unsigned ean8_config; + unsigned upca_config; + unsigned upce_config; + unsigned isbn10_config; + unsigned isbn13_config; + unsigned ean5_config; + unsigned ean2_config; +} ean_decoder_t; + +/* reset EAN/UPC pass specific state */ +static inline void ean_new_scan(ean_decoder_t *ean) +{ + ean->pass[0].state = ean->pass[1].state = -1; + ean->pass[2].state = ean->pass[3].state = -1; + ean->s4 = 0; +} + +/* reset all EAN/UPC state */ +static inline void ean_reset(ean_decoder_t *ean) +{ + ean_new_scan(ean); + ean->left = ean->right = ZBAR_NONE; +} + +static inline unsigned ean_get_config(ean_decoder_t *ean, + zbar_symbol_type_t sym) +{ + switch (sym) { + case ZBAR_EAN2: + return (ean->ean2_config); + case ZBAR_EAN5: + return (ean->ean5_config); + case ZBAR_EAN8: + return (ean->ean8_config); + case ZBAR_UPCE: + return (ean->upce_config); + case ZBAR_ISBN10: + return (ean->isbn10_config); + case ZBAR_UPCA: + return (ean->upca_config); + case ZBAR_EAN13: + return (ean->ean13_config); + case ZBAR_ISBN13: + return (ean->isbn13_config); + default: + return (0); + } +} + +/* decode EAN/UPC symbols */ +zbar_symbol_type_t _zbar_decode_ean(zbar_decoder_t *dcode); + +#endif diff --git a/zbar/decoder/i25.c b/zbar/decoder/i25.c new file mode 100644 index 0000000..8fdea25 --- /dev/null +++ b/zbar/decoder/i25.c @@ -0,0 +1,265 @@ +/*------------------------------------------------------------------------ + * Copyright 2008-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" +#include <string.h> /* memmove */ + +#include <zbar.h> + +#ifdef DEBUG_I25 +#define DEBUG_LEVEL (DEBUG_I25) +#endif +#include "debug.h" +#include "decoder.h" + +static inline unsigned char i25_decode1(unsigned char enc, unsigned e, + unsigned s) +{ + unsigned char E = decode_e(e, s, 45); + if (E > 7) + return (0xff); + enc <<= 1; + if (E > 2) + enc |= 1; + return (enc); +} + +static inline unsigned char i25_decode10(zbar_decoder_t *dcode, + unsigned char offset) +{ + unsigned char enc = 0, par = 0; + signed char i; + + i25_decoder_t *dcode25 = &dcode->i25; + dbprintf(2, " s=%d", dcode25->s10); + if (dcode25->s10 < 10) + return (0xff); + + /* threshold bar width ratios */ + for (i = 8; i >= 0; i -= 2) { + unsigned char j = offset + ((dcode25->direction) ? i : 8 - i); + enc = i25_decode1(enc, get_width(dcode, j), dcode25->s10); + if (enc == 0xff) + return (0xff); + if (enc & 1) + par++; + } + + dbprintf(2, " enc=%02x par=%x", enc, par); + + /* parity check */ + if (par != 2) { + dbprintf(2, " [bad parity]"); + return (0xff); + } + + /* decode binary weights */ + enc &= 0xf; + if (enc & 8) { + if (enc == 12) + enc = 0; + else if (--enc > 9) { + dbprintf(2, " [invalid encoding]"); + return (0xff); + } + } + + dbprintf(2, " => %x", enc); + return (enc); +} + +static inline signed char i25_decode_start(zbar_decoder_t *dcode) +{ + unsigned char enc = 0; + unsigned char i = 10; + unsigned quiet; + + i25_decoder_t *dcode25 = &dcode->i25; + if (dcode25->s10 < 10) + return (ZBAR_NONE); + + enc = i25_decode1(enc, get_width(dcode, i++), dcode25->s10); + enc = i25_decode1(enc, get_width(dcode, i++), dcode25->s10); + enc = i25_decode1(enc, get_width(dcode, i++), dcode25->s10); + + if ((get_color(dcode) == ZBAR_BAR) ? + enc != 4 : + (enc = i25_decode1(enc, get_width(dcode, i++), dcode25->s10))) { + dbprintf(4, " i25: s=%d enc=%x [invalid]\n", dcode25->s10, enc); + return (ZBAR_NONE); + } + + /* check leading quiet zone - spec is 10n(?) + * we require 5.25n for w=2n to 6.75n for w=3n + * (FIXME should really factor in w:n ratio) + */ + quiet = get_width(dcode, i); + if (quiet && quiet < dcode25->s10 * 3 / 8) { + dbprintf(3, " i25: s=%d enc=%x q=%d [invalid qz]\n", dcode25->s10, + enc, quiet); + return (ZBAR_NONE); + } + + dcode25->direction = get_color(dcode); + dcode25->element = 1; + dcode25->character = 0; + return (ZBAR_PARTIAL); +} + +static inline int i25_acquire_lock(zbar_decoder_t *dcode) +{ + int i; + /* lock shared resources */ + if (acquire_lock(dcode, ZBAR_I25)) { + dcode->i25.character = -1; + return (1); + } + + /* copy holding buffer */ + for (i = 4; --i >= 0;) + dcode->buf[i] = dcode->i25.buf[i]; + return (0); +} + +static inline signed char i25_decode_end(zbar_decoder_t *dcode) +{ + unsigned char E; + i25_decoder_t *dcode25 = &dcode->i25; + + /* check trailing quiet zone */ + unsigned quiet = get_width(dcode, 0); + if ((quiet && quiet < dcode25->width * 3 / 8) || + decode_e(get_width(dcode, 1), dcode25->width, 45) > 2 || + decode_e(get_width(dcode, 2), dcode25->width, 45) > 2) { + dbprintf(3, " i25: s=%d q=%d [invalid qz]\n", dcode25->width, + quiet); + return (ZBAR_NONE); + } + + /* check exit condition */ + E = decode_e(get_width(dcode, 3), dcode25->width, 45); + if ((!dcode25->direction) ? + E - 3 > 4 : + (E > 2 || decode_e(get_width(dcode, 4), dcode25->width, 45) > 2)) + return (ZBAR_NONE); + + if (dcode25->character <= 4 && i25_acquire_lock(dcode)) + return (ZBAR_PARTIAL); + + dcode->direction = 1 - 2 * dcode25->direction; + if (dcode25->direction) { + int i; + /* reverse buffer */ + dbprintf(2, " (rev)"); + for (i = 0; i < dcode25->character / 2; i++) { + unsigned j = dcode25->character - 1 - i; + char c = dcode->buf[i]; + dcode->buf[i] = dcode->buf[j]; + dcode->buf[j] = c; + } + } + + if (dcode25->character < CFG(*dcode25, ZBAR_CFG_MIN_LEN) || + (CFG(*dcode25, ZBAR_CFG_MAX_LEN) > 0 && + dcode25->character > CFG(*dcode25, ZBAR_CFG_MAX_LEN))) { + dbprintf(2, " [invalid len]\n"); + release_lock(dcode, ZBAR_I25); + dcode25->character = -1; + return (ZBAR_NONE); + } + + zassert(dcode25->character < dcode->buf_alloc, ZBAR_NONE, "i=%02x %s\n", + dcode25->character, + _zbar_decoder_buf_dump(dcode->buf, dcode25->character)); + dcode->buflen = dcode25->character; + dcode->buf[dcode25->character] = '\0'; + dcode->modifiers = 0; + dbprintf(2, " [valid end]\n"); + dcode25->character = -1; + return (ZBAR_I25); +} + +zbar_symbol_type_t _zbar_decode_i25(zbar_decoder_t *dcode) +{ + unsigned char c; + unsigned char *buf; + + i25_decoder_t *dcode25 = &dcode->i25; + + /* update latest character width */ + dcode25->s10 -= get_width(dcode, 10); + dcode25->s10 += get_width(dcode, 0); + + if (dcode25->character < 0 && !i25_decode_start(dcode)) + return (ZBAR_NONE); + + if (--dcode25->element == 6 - dcode25->direction) + return (i25_decode_end(dcode)); + else if (dcode25->element) + return (ZBAR_NONE); + + /* FIXME check current character width against previous */ + dcode25->width = dcode25->s10; + + dbprintf(2, " i25[%c%02d+%x]", (dcode25->direction) ? '<' : '>', + dcode25->character, dcode25->element); + + if (dcode25->character == 4 && i25_acquire_lock(dcode)) + return (ZBAR_PARTIAL); + + c = i25_decode10(dcode, 1); + dbprintf(2, " c=%x", c); + if (c > 9) { + dbprintf(2, " [aborted]\n"); + goto reset; + } + + if (size_buf(dcode, dcode25->character + 3)) { + dbprintf(2, " [overflow]\n"); + goto reset; + } + + if (dcode25->character >= 4) + buf = dcode->buf; + else + buf = dcode25->buf; + buf[dcode25->character++] = c + '0'; + + c = i25_decode10(dcode, 0); + dbprintf(2, " c=%x", c); + if (c > 9) { + dbprintf(2, " [aborted]\n"); + goto reset; + } else + dbprintf(2, "\n"); + + buf[dcode25->character++] = c + '0'; + dcode25->element = 10; + return ((dcode25->character == 2) ? ZBAR_PARTIAL : ZBAR_NONE); + +reset: + if (dcode25->character >= 4) + release_lock(dcode, ZBAR_I25); + dcode25->character = -1; + return (ZBAR_NONE); +} diff --git a/zbar/decoder/i25.h b/zbar/decoder/i25.h new file mode 100644 index 0000000..57403e8 --- /dev/null +++ b/zbar/decoder/i25.h @@ -0,0 +1,51 @@ +/*------------------------------------------------------------------------ + * Copyright 2008-2009 (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 + *------------------------------------------------------------------------*/ +#ifndef _I25_H_ +#define _I25_H_ + +/* interleaved 2 of 5 specific decode state */ +typedef struct i25_decoder_s { + unsigned direction : 1; /* scan direction: 0=fwd/space, 1=rev/bar */ + unsigned element : 4; /* element offset 0-8 */ + int character : 12; /* character position in symbol */ + unsigned s10; /* current character width */ + unsigned width; /* last character width */ + unsigned char buf[4]; /* initial scan buffer */ + + unsigned config; + int configs[NUM_CFGS]; /* int valued configurations */ +} i25_decoder_t; + +/* reset interleaved 2 of 5 specific state */ +static inline void i25_reset(i25_decoder_t *i25) +{ + i25->direction = 0; + i25->element = 0; + i25->character = -1; + i25->s10 = 0; +} + +/* decode interleaved 2 of 5 symbols */ +zbar_symbol_type_t _zbar_decode_i25(zbar_decoder_t *dcode); + +#endif diff --git a/zbar/decoder/pdf417.c b/zbar/decoder/pdf417.c new file mode 100644 index 0000000..436a7bd --- /dev/null +++ b/zbar/decoder/pdf417.c @@ -0,0 +1,224 @@ +/*------------------------------------------------------------------------ + * Copyright 2008-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" + +#include <zbar.h> + +#ifdef DEBUG_PDF417 +#define DEBUG_LEVEL (DEBUG_PDF417) +#endif +#include "debug.h" +#include "decoder.h" + +#include "pdf417_hash.h" + +#define PDF417_STOP 0xbff + +static inline signed short pdf417_decode8(zbar_decoder_t *dcode) +{ + long sig = 0; + signed char e; + unsigned char i; + int clst; + signed short g[3]; + unsigned short c; + /* build edge signature of character + * from similar edge measurements + */ + unsigned s = dcode->pdf417.s8; + dbprintf(2, " s=%d ", s); + if (s < 8) + return (-1); + + for (i = 0; i < 7; i++) { + if (get_color(dcode) == ZBAR_SPACE) + e = decode_e(get_width(dcode, i) + get_width(dcode, i + 1), s, 17); + else + e = decode_e(get_width(dcode, 7 - i) + get_width(dcode, 6 - i), s, + 17); + dbprintf(4, "%x", e); + if (e < 0 || e > 8) + return (-1); + sig = (sig << 3) ^ e; + } + dbprintf(2, " sig=%06lx", sig); + + /* determine cluster number */ + clst = + ((sig & 7) - ((sig >> 3) & 7) + ((sig >> 12) & 7) - ((sig >> 15) & 7)); + if (clst < 0) + clst += 9; + dbprintf(2, " k=%d", clst); + zassert(clst >= 0 && clst < 9, -1, "dir=%x sig=%lx k=%x %s\n", + dcode->pdf417.direction, sig, clst, + _zbar_decoder_buf_dump(dcode->buf, dcode->pdf417.character)); + + if (clst != 0 && clst != 3 && clst != 6) { + if (get_color(dcode) && clst == 7 && sig == 0x080007) + return (PDF417_STOP); + return (-1); + } + + sig &= 0x3ffff; + g[0] = pdf417_hash[(sig - (sig >> 10)) & PDF417_HASH_MASK]; + g[1] = pdf417_hash[((sig >> 8) - sig) & PDF417_HASH_MASK]; + g[2] = pdf417_hash[((sig >> 14) - (sig >> 1)) & PDF417_HASH_MASK]; + zassert(g[0] >= 0 && g[1] >= 0 && g[2] >= 0, -1, + "dir=%x sig=%lx k=%x g0=%03x g1=%03x g2=%03x %s\n", + dcode->pdf417.direction, sig, clst, g[0], g[1], g[2], + _zbar_decoder_buf_dump(dcode->buf, dcode->pdf417.character)); + + c = (g[0] + g[1] + g[2]) & PDF417_HASH_MASK; + dbprintf(2, " g0=%x g1=%x g2=%x c=%03d(%d)", g[0], g[1], g[2], c & 0x3ff, + c >> 10); + return (c); +} + +static inline signed char pdf417_decode_start(zbar_decoder_t *dcode) +{ + int ei, ex; + pdf417_decoder_t *dcode417; + unsigned s = dcode->pdf417.s8; + if (s < 8) + return (0); + + ei = decode_e(get_width(dcode, 0) + get_width(dcode, 1), s, 17); + ex = (get_color(dcode) == ZBAR_SPACE) ? 2 : 6; + if (ei != ex) + return (0); + + ei = decode_e(get_width(dcode, 1) + get_width(dcode, 2), s, 17); + if (ei) + return (0); + + ei = decode_e(get_width(dcode, 2) + get_width(dcode, 3), s, 17); + ex = (get_color(dcode) == ZBAR_SPACE) ? 0 : 2; + if (ei != ex) + return (0); + + ei = decode_e(get_width(dcode, 3) + get_width(dcode, 4), s, 17); + ex = (get_color(dcode) == ZBAR_SPACE) ? 0 : 2; + if (ei != ex) + return (0); + + ei = decode_e(get_width(dcode, 4) + get_width(dcode, 5), s, 17); + if (ei) + return (0); + + ei = decode_e(get_width(dcode, 5) + get_width(dcode, 6), s, 17); + if (ei) + return (0); + + ei = decode_e(get_width(dcode, 6) + get_width(dcode, 7), s, 17); + ex = (get_color(dcode) == ZBAR_SPACE) ? 7 : 1; + if (ei != ex) + return (0); + + ei = decode_e(get_width(dcode, 7) + get_width(dcode, 8), s, 17); + ex = (get_color(dcode) == ZBAR_SPACE) ? 8 : 1; + + if (get_color(dcode) == ZBAR_BAR) { + /* stop character has extra bar */ + if (ei != 1) + return (0); + ei = decode_e(get_width(dcode, 8) + get_width(dcode, 9), s, 17); + } + + dbprintf(2, " pdf417[%c]: s=%d", (get_color(dcode)) ? '<' : '>', s); + + /* check quiet zone */ + if (ei >= 0 && ei < ex) { + dbprintf(2, " [invalid quiet]\n"); + return (0); + } + + /* lock shared resources */ + if (acquire_lock(dcode, ZBAR_PDF417)) { + dbprintf(2, " [locked %d]\n", dcode->lock); + return (0); + } + + dcode417 = &dcode->pdf417; + dcode417->direction = get_color(dcode); + dcode417->element = 0; + dcode417->character = 0; + + dbprintf(2, " [valid start]\n"); + return (ZBAR_PARTIAL); +} + +zbar_symbol_type_t _zbar_decode_pdf417(zbar_decoder_t *dcode) +{ + signed short c; + pdf417_decoder_t *dcode417 = &dcode->pdf417; + + /* update latest character width */ + dcode417->s8 -= get_width(dcode, 8); + dcode417->s8 += get_width(dcode, 0); + + if (dcode417->character < 0) { + pdf417_decode_start(dcode); + dbprintf(4, "\n"); + return (0); + } + + /* process every 8th element of active symbol */ + if (++dcode417->element) + return (0); + dcode417->element = 0; + + dbprintf(2, " pdf417[%c%02d]:", (dcode417->direction) ? '<' : '>', + dcode417->character); + + if (get_color(dcode) != dcode417->direction) { + int c = dcode417->character; + release_lock(dcode, ZBAR_PDF417); + dcode417->character = -1; + zassert(get_color(dcode) == dcode417->direction, ZBAR_NONE, + "color=%x dir=%x char=%d elem=0 %s\n", get_color(dcode), + dcode417->direction, c, _zbar_decoder_buf_dump(dcode->buf, c)); + } + + c = pdf417_decode8(dcode); + if (c < 0 || size_buf(dcode, dcode417->character + 1)) { + dbprintf(1, (c < 0) ? " [aborted]\n" : " [overflow]\n"); + release_lock(dcode, ZBAR_PDF417); + dcode417->character = -1; + return (0); + } + + /* FIXME TBD infer dimensions, save codewords */ + + if (c == PDF417_STOP) { + dbprintf(1, " [valid stop]"); + /* FIXME check trailing bar and qz */ + dcode->direction = 1 - 2 * dcode417->direction; + dcode->modifiers = 0; + release_lock(dcode, ZBAR_PDF417); + dcode417->character = -1; + } + + dbprintf(2, "\n"); + return (0); +} diff --git a/zbar/decoder/pdf417.h b/zbar/decoder/pdf417.h new file mode 100644 index 0000000..6c5d3aa --- /dev/null +++ b/zbar/decoder/pdf417.h @@ -0,0 +1,49 @@ +/*------------------------------------------------------------------------ + * Copyright 2008-2009 (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 + *------------------------------------------------------------------------*/ +#ifndef _PDF417_H_ +#define _PDF417_H_ + +/* PDF417 specific decode state */ +typedef struct pdf417_decoder_s { + unsigned direction : 1; /* scan direction: 0=fwd/space, 1=rev/bar */ + unsigned element : 3; /* element offset 0-7 */ + int character : 12; /* character position in symbol */ + unsigned s8; /* character width */ + + unsigned config; + int configs[NUM_CFGS]; /* int valued configurations */ +} pdf417_decoder_t; + +/* reset PDF417 specific state */ +static inline void pdf417_reset(pdf417_decoder_t *pdf417) +{ + pdf417->direction = 0; + pdf417->element = 0; + pdf417->character = -1; + pdf417->s8 = 0; +} + +/* decode PDF417 symbols */ +zbar_symbol_type_t _zbar_decode_pdf417(zbar_decoder_t *dcode); + +#endif diff --git a/zbar/decoder/pdf417_hash.h b/zbar/decoder/pdf417_hash.h new file mode 100644 index 0000000..6c2c5e8 --- /dev/null +++ b/zbar/decoder/pdf417_hash.h @@ -0,0 +1,406 @@ +/*------------------------------------------------------------------------ + * Copyright 2008-2009 (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 + *------------------------------------------------------------------------*/ +#ifndef _PDF417_HASH_H_ +#define _PDF417_HASH_H_ + +/* PDF417 bar to codeword decode table */ + +#define PDF417_HASH_MASK 0xfff + +static const signed short pdf417_hash[PDF417_HASH_MASK + 1] = { + 0x170, 0xd8e, 0x02e, 0x000, 0xa21, 0xc99, 0x000, 0xf06, 0xdaa, 0x7a1, 0xc5f, + 0x7ff, 0xbcf, 0xac8, 0x000, 0xc51, 0x49a, 0x5c7, 0x000, 0xef2, 0x000, 0x7dd, + 0x9ee, 0xe32, 0x1b7, 0x489, 0x3b7, 0xe70, 0x9c8, 0xe5e, 0xdf4, 0x599, 0x4e0, + 0x608, 0x639, 0xead, 0x0ac, 0x57c, 0x000, 0x20d, 0x61b, 0x000, 0x7d1, 0x80f, + 0x803, 0x000, 0x946, 0x093, 0x79c, 0xf9c, 0xb34, 0x6d8, 0x4f1, 0x975, 0x886, + 0x313, 0xe8a, 0xf20, 0x3c9, 0xa92, 0xb90, 0xa1d, 0x091, 0x0ac, 0xb50, 0x3af, + 0x90a, 0x45a, 0x815, 0xf29, 0xb20, 0xb6d, 0xc5c, 0x1cd, 0x1e2, 0x1bf, 0x963, + 0x80b, 0xa7c, 0x9b7, 0xb65, 0x6b7, 0x117, 0xc04, 0x000, 0x18e, 0x000, 0x77f, + 0xe0e, 0xf48, 0x370, 0x818, 0x379, 0x000, 0x090, 0xe77, 0xd99, 0x8b8, 0xb95, + 0x8a9, 0x94c, 0xc48, 0x679, 0x000, 0x41a, 0x9ea, 0xb0e, 0x9c1, 0x1b4, 0x000, + 0x630, 0x811, 0x4b1, 0xc05, 0x98f, 0xa68, 0x485, 0x706, 0xfff, 0x0d9, 0xddc, + 0x000, 0x83f, 0x54e, 0x290, 0xfe7, 0x64f, 0xf36, 0x000, 0x151, 0xb9b, 0x5cd, + 0x961, 0x690, -1, 0xa7a, 0x328, 0x707, 0xe6d, 0xe1f, -1, 0x6a0, 0xf3e, + 0xb27, 0x315, 0xc8c, 0x6de, 0x996, 0x2f9, 0xc4c, 0x90f, -1, 0xaa7, 0x9e9, + 0xfff, 0x0bb, 0x33b, 0xbc6, 0xe17, 0x000, 0x85d, 0x912, 0x5f7, 0x000, 0xff1, + 0xba1, 0x086, 0xa1e, 0x85a, 0x4cf, 0xd47, 0x5a9, 0x5dc, 0x0bc, -1, 0x544, + 0x522, 0x1ff, 0xfa6, 0xa83, 0xc7d, 0x545, 0xd75, 0xb6f, 0x284, 0xf11, 0xe46, + -1, 0x900, 0x0f3, 0xe31, 0x705, 0x06d, 0xd59, 0x67b, 0xe56, -1, 0xde2, + 0x000, 0xd42, -1, 0x24b, 0x000, 0xf87, 0x842, -1, 0xbb9, 0x065, 0x626, + 0x86a, 0x9f8, -1, 0x7ac, 0xe20, 0xbe9, 0x357, 0xfff, 0xf82, 0x219, 0x9d4, + 0x269, 0x8a6, 0x251, 0x0af, 0xd02, 0x09a, 0x803, 0x0a5, 0xfed, 0x278, -1, + 0x338, 0x1e5, 0xcad, 0xf9e, 0x73e, 0xb39, 0xe48, 0x754, -1, 0x680, 0xd99, + 0x4d4, 0x80b, 0x4be, 0xb0d, 0x5f2, -1, 0x4b1, 0x38a, 0xff5, 0x000, 0xa1b, + 0xece, 0xa06, 0x8e6, 0xdcb, 0xcb8, 0xc63, 0x98c, 0x346, 0x69c, 0x299, 0xa52, + 0xfff, 0x000, -1, 0x7b2, 0xbf8, 0x2d1, 0xaff, 0x2f2, 0xd69, 0xf20, -1, + 0xdcf, 0x9fb, 0x68f, 0x24e, 0xfd7, 0xfdb, 0x894, 0xc8f, 0x615, 0xa25, 0x36d, + 0x1bb, 0x064, 0xb80, 0x280, 0xd7a, -1, 0xd75, 0xc90, 0xdce, 0xdce, 0x011, + 0x869, 0xb2f, 0xd24, 0xe26, 0x492, 0xe0a, 0xcae, -1, 0x2ac, 0x38c, 0x0b9, + 0xc4f, -1, 0x32b, 0x415, 0x49c, 0x11c, 0x816, 0xd08, 0xf5c, 0x356, 0x2b3, + 0xfbf, 0x7ff, 0x35d, 0x276, 0x292, 0x4f5, 0x0e2, 0xc68, 0x4c4, 0x000, 0xb5e, + 0xd0b, 0xca7, 0x624, 0x247, 0xf0d, 0x017, 0x7ec, 0x2a6, 0x62c, 0x192, 0x610, + 0xd98, 0x7a4, 0xfa3, 0x80b, 0x043, 0xd7b, 0x301, 0x69d, 0x7e4, 0x10c, 0xacb, + 0x6eb, 0xea7, 0xe65, 0x75d, 0x4f5, 0x5b0, 0xa50, 0x7b6, 0x0ec, -1, 0xcf9, + 0x4b4, 0x639, 0x111, 0xbdf, 0xe89, 0x9fa, 0x76b, 0xdf6, 0x2d0, 0x857, 0x3a3, + 0x000, 0xa3e, 0x8cb, 0x35f, 0x4f0, 0x022, 0xb38, 0xc12, 0x93c, 0x2fc, 0x546, + 0xe6e, 0x91f, 0x145, 0xfff, 0x1af, 0x957, 0xbde, 0x09d, 0xfd2, 0x9df, 0x2dc, + 0x07f, 0x115, 0x7bf, 0xa35, 0x061, 0x9bf, 0xc85, 0x918, 0x0c8, 0x317, 0xce5, + 0xf28, 0x108, 0x51b, 0x621, 0x188, 0x000, 0x28c, 0xf67, 0x6ef, 0x000, 0xd72, + 0xce2, 0x1be, 0x000, 0x000, 0x282, 0x357, -1, 0x4e5, 0x246, 0x859, 0x66c, + 0x5d3, 0x9fd, 0x000, 0x000, 0x82f, 0xc29, 0x331, 0xa93, 0x000, 0xae4, 0x48a, + 0x254, 0x000, 0x0ba, 0xe83, 0x7c7, 0xb6e, 0x88e, 0x774, 0xf6f, 0x85d, 0x47f, + 0xcd6, 0xe41, 0xdb6, 0x000, 0x0f4, 0xb4d, 0x77f, 0x000, 0x901, 0x1a2, 0x44a, + 0x482, 0x000, 0xe99, 0xa75, 0x000, 0x7ab, 0x000, 0x0b6, 0x35c, 0x306, 0x11c, + 0x08e, 0x6eb, 0x11c, 0x771, 0xff9, 0x1c8, 0x63b, 0x58b, 0x9d2, 0x250, 0x198, + 0xfe7, 0xebc, 0x000, 0xa97, 0xacc, 0xd4b, 0x28b, 0x892, 0x150, 0xcf4, 0xbc1, + 0x000, 0x662, 0xdd8, 0x61f, 0x903, 0x083, 0x000, 0xc55, 0x02f, 0xc29, 0x4f5, + 0xbcf, 0xe27, 0x9e3, 0xb13, 0xadc, 0x845, 0x415, 0x0ae, 0x000, 0xe30, 0x931, + 0x84a, 0xb09, 0x250, 0x631, 0x7aa, 0x026, 0xdc9, 0x486, 0x3a7, 0xab0, 0xe04, + 0xe1a, 0xe17, 0x611, 0x556, 0xfac, 0x3c6, 0x5ab, 0x002, 0xc16, 0xe60, -1, + 0xc51, 0x772, 0x67f, 0xfa9, 0x83c, 0x974, 0x96a, 0xe94, 0x250, 0xa20, 0xc95, + 0x65b, 0x479, 0xe48, 0xa35, 0x23f, 0x5cf, 0x40a, 0xcf0, 0xe82, 0x1da, 0x390, + 0xc86, 0xa92, 0x433, 0xbed, 0x4a7, 0x09a, 0x15a, 0xb8d, 0x9c7, 0x5fb, 0x8a0, + 0x000, 0xf9a, 0xf3c, 0x11c, 0x20c, 0xf23, 0x79d, 0xc79, 0xb71, 0x7af, 0xc5b, + 0x771, 0x629, 0x834, 0xb34, 0x20c, 0x940, 0x2ca, 0x60b, 0x000, 0x4cb, 0x70b, + 0x000, 0x000, 0x9e8, 0x000, 0xdca, 0x000, 0x1ae, 0xb21, 0xfe3, 0x191, 0x9e1, + 0x7f6, 0x04f, 0x64a, 0xba2, 0x59e, 0x1ae, 0x000, 0x728, 0x000, 0x081, 0xecd, + 0x946, 0x000, 0xdee, 0x3ff, 0xdf9, 0x1bf, 0x01a, 0x1a9, 0xc58, 0xe05, 0x3bf, + 0x5e8, 0x39d, 0xbfa, 0x23f, 0xb8d, -1, 0x000, 0x779, 0x540, 0xf2c, 0x7cc, + 0x340, 0x77a, 0xa8e, 0xe8d, 0x2fd, 0xfed, 0x5d1, 0x308, 0x00f, 0xf4a, 0x39b, + 0xbe2, 0x0e5, -1, 0xf4d, 0x1fe, 0xf00, 0x867, 0x195, 0x2de, 0x712, 0x000, + 0x00c, 0x0a3, 0x1f3, 0x4ee, 0x317, 0x665, 0x000, 0x5d8, 0x291, 0x6c4, 0xa46, + 0x492, 0x8d4, 0x647, 0x57f, 0x000, 0x259, 0xd87, 0x5c2, 0x1d8, 0xfad, -1, + -1, 0x79f, 0x43a, 0xfd1, 0x164, 0x6e1, 0x350, 0xf00, 0x0e9, 0xac4, 0xe35, + 0x307, 0xfff, 0xabb, 0xc1a, 0x768, 0x000, 0x372, 0x839, 0xf4b, 0x1c3, 0xab0, + 0xcb6, 0x943, 0xbe9, 0x20f, 0xddc, 0xe18, 0x4eb, 0x21d, 0x530, 0x24c, 0x000, + 0xf79, -1, 0x1bd, -1, 0x155, 0x435, -1, 0x132, 0x5c2, 0xb3d, 0x802, + 0x733, -1, 0x336, 0xf19, 0xfea, 0xd2a, 0x07f, 0x8e9, 0x000, 0xdab, -1, + 0x088, 0x4b1, 0x7ac, 0x000, 0xe66, 0xde0, 0x73c, 0xfff, 0x02f, -1, 0x000, + -1, 0x000, 0x562, 0x389, 0xb20, 0x9ea, -1, 0x3f8, 0x567, 0x035, 0xa55, + 0x255, 0xc98, 0x65f, -1, 0x1ac, 0x571, 0x13d, 0xf57, 0x32a, 0xbdb, 0x0ec, + 0x47d, 0x43a, -1, 0x1aa, 0x9d6, 0x843, -1, 0x244, 0xb03, 0xd0d, 0x579, + 0x1b1, 0xea7, 0x000, 0x062, -1, 0x533, 0x1db, 0xf1f, 0x2f7, 0x2df, 0x3e5, + 0xdec, 0xc5c, 0x55a, 0xf6c, 0x4c1, 0x5a8, 0xcd4, 0x6fd, 0x1a6, 0x4b8, 0x98a, + 0xe17, 0xeb9, 0xfd1, -1, 0x175, 0x4d6, 0xba2, 0x000, 0x614, 0x147, 0x429, + 0xfee, -1, 0x0d8, -1, 0x98a, 0xdd2, 0xedd, 0x255, 0xef3, 0x345, 0x000, + 0xf3e, -1, -1, 0x210, 0x88a, 0x699, -1, 0x02c, 0xfee, 0x1c1, 0xb38, + 0x000, 0x7cc, 0x165, 0x536, -1, 0x1ae, 0xefb, 0x734, -1, 0x1a4, 0x984, + 0x804, 0x487, -1, -1, 0x31e, 0x9f2, 0x966, 0x000, 0xcb0, 0x552, 0x0c9, + -1, 0x750, 0x650, 0x064, 0xffe, 0xe84, 0x537, 0xee7, 0x834, -1, 0x998, + 0xa03, -1, 0xcdf, 0x4be, 0x310, 0x051, 0xf3f, 0x040, 0x973, 0x925, 0x000, + 0x000, 0xe51, 0x8b1, 0x468, 0xe11, 0xd4f, 0x374, 0x33a, 0x126, 0x88b, 0x43a, + 0xc9b, 0xdb9, 0x3c2, 0x3bd, 0x1ae, 0x000, 0xc4a, 0x000, 0x4c4, 0x859, 0xe5a, + 0x000, 0xeb4, 0xd40, 0x87d, 0xc79, 0xe13, 0x50b, -1, 0x724, 0x000, 0x7be, + 0x062, 0xe7f, 0xad0, 0x5f3, 0x69e, 0x381, 0x272, 0x50f, 0xac8, 0x053, 0x55e, + 0xf19, 0xd71, 0x75b, 0xbf2, 0x000, 0x3ac, 0xdf0, 0xd75, 0x7e3, 0xe75, 0xa13, + 0xfd8, 0xbdc, 0x1d9, 0x15f, 0x8cc, 0xba4, 0xb79, 0xb7f, 0x812, 0xfe6, 0x000, + 0x2d3, 0xd7b, 0x5d4, 0xad2, 0x316, 0x908, 0x323, 0x758, 0xb0b, 0x965, 0x1a9, + 0xdce, 0x660, 0x625, 0xeff, 0x0ed, 0x000, 0x323, 0x986, 0x831, 0x5c5, 0x22f, + 0xd49, 0xec6, 0x90e, 0x234, 0x000, 0x80f, 0x16c, 0x528, 0x1f8, 0x2bd, 0x97d, + 0xe20, 0xf29, 0x97d, 0x3a0, 0x7fc, 0x086, 0x720, 0x1f9, 0x3eb, 0xf67, 0x423, + 0xa55, 0x69e, 0xede, 0x206, 0x7fa, 0x809, 0xfa8, 0xe22, 0x15e, 0x2a0, 0x04a, + 0xf7b, 0x4ea, 0xd9a, -1, 0x1d8, 0x0b4, 0xb87, 0x406, -1, 0xcdf, 0x187, + 0xf6d, 0x914, 0x4b1, 0x000, 0x104, 0x67e, 0xc74, 0x6da, 0xe67, 0x7d2, 0xd1f, + 0x64c, 0x19d, 0x000, 0xa17, 0xfd5, 0x000, 0x8ad, 0xf38, 0xd65, 0xabd, 0x75e, + 0x667, 0x632, 0x346, 0xc48, 0xa77, 0x45e, 0x2b5, 0xded, 0x7da, 0x160, 0x560, + -1, 0xf4e, 0xb0c, 0xdb0, 0x287, 0x34a, 0x065, 0x439, 0x2ec, 0x679, 0xefa, + 0x208, 0xeb1, 0x1b0, 0x8c8, 0xca6, 0x62c, 0xa10, 0x673, 0x000, 0x000, 0xc6a, + 0x7b2, 0xbd7, 0xb2b, 0x17a, 0x6f3, 0x1ab, 0xffa, 0x5e0, 0x1fa, 0xb8f, 0xe5c, + 0xcab, 0xdbc, 0x10f, 0x000, 0x000, 0xefe, 0x34b, 0x1d9, 0x834, 0x52f, 0xb58, + 0x82b, 0x6e8, 0x1f3, 0x719, 0x64e, 0xf55, 0xccd, 0x531, 0x0de, 0x3aa, 0x150, + 0x89a, 0x3b9, 0x26e, 0xebc, 0x7ae, 0x670, 0x315, 0x8a9, 0x03b, 0x896, 0x247, + 0x2f4, 0x450, 0xd10, 0xb79, 0x0ed, 0x041, -1, 0x707, 0x9e1, 0xed6, 0x6d2, + 0x000, 0xfff, 0xb1a, 0x084, 0xaf3, 0x47f, 0x02f, 0xac3, 0x751, 0x8c4, 0x291, + 0xadd, 0x000, 0xea1, 0x8ec, 0xf9f, 0x5c2, 0x000, 0xd6b, 0x71e, 0x000, 0xcea, + 0x971, 0x5f8, 0x4b9, 0x7c6, 0xb7e, 0x353, 0xd25, 0x423, 0x6ec, 0xb71, 0xf93, + 0x000, 0x795, 0xc43, 0xaa2, 0x96a, 0xcbd, 0xb55, 0x184, 0xdf0, 0x3d9, 0xbfe, + 0xf79, 0x8f0, 0x22c, 0xeeb, 0x000, 0xa4b, 0xe07, 0xf34, 0xc9d, 0x4be, 0x95b, + 0x371, 0x78c, 0x9e9, 0xde6, 0x072, 0xf0d, 0x60b, 0x5a5, 0xab1, 0x000, 0x260, + 0x000, 0xd2a, 0xd90, 0x154, 0x4c6, 0x438, 0x5d9, 0x736, 0x062, 0x000, 0x000, + 0xb84, 0x72e, 0x0b7, 0x000, 0x050, 0x063, 0xa95, 0x89b, 0x917, 0x049, 0xb14, + 0x9a0, 0x734, 0x0c3, 0xd50, 0x917, 0xb02, 0x8cf, 0x453, 0x0af, 0x8e5, 0x000, + 0x7aa, 0x5d5, 0x81b, 0x788, 0xb9c, 0x01a, 0x974, 0x000, 0x000, 0x37f, 0xd9f, + 0x000, 0xec4, 0x4f4, 0xbff, 0x4fe, 0x860, 0x11c, 0x74e, 0x34a, 0x281, 0x52f, + 0xb05, 0xa89, 0xbee, 0x6ad, 0x9fc, 0x9ba, 0xb0b, 0x515, 0x1c7, 0x330, 0xfde, + 0x97e, 0x6e7, 0xc45, -1, 0x658, 0x710, 0x28a, 0x921, 0x1de, 0x4a1, 0x9d7, + 0xe32, 0xa2d, 0xb0f, 0x545, 0xd6f, 0x329, 0x9b8, 0xb4d, 0x9a0, 0x938, 0x783, + 0xfa7, 0xd0a, 0xdc9, 0x0fe, 0x000, 0x249, 0x000, 0x8cd, 0x922, 0x7cd, 0x021, + 0xa89, 0x3d5, 0xcee, 0x0a1, 0x6d6, 0x000, -1, 0x48b, 0x000, 0x87a, 0x8bb, + 0x9ed, 0x01f, 0xe20, 0xb7f, -1, 0xe95, 0x593, 0x1da, 0x57a, -1, 0xf3a, + 0x000, 0x000, -1, -1, 0x160, 0x501, 0x7a3, 0xb59, -1, -1, 0xc7f, + -1, 0xf79, -1, -1, 0x48d, 0x781, -1, -1, 0xb74, -1, 0x3c4, + 0xbe9, -1, -1, 0x9a4, 0x9ae, 0xa75, -1, -1, 0x9cd, 0x000, -1, + -1, -1, 0xc3c, 0x2d4, -1, 0x173, 0xf38, 0x000, -1, 0xee9, -1, + 0xb91, 0xcc1, 0x86d, 0x8ab, 0xeb0, 0xec7, 0x687, 0xd98, 0xa95, 0x744, 0xe7c, + 0x826, 0x80e, 0x599, 0x3d9, 0xf2f, -1, 0x96a, 0xfd1, 0x174, -1, 0x000, + 0x1aa, 0x50e, -1, 0x5a2, 0xbcd, 0x000, -1, 0x019, 0x588, 0x18d, 0x470, + 0x812, 0xeec, 0xf63, 0x05c, -1, 0x000, 0xb7f, 0x357, 0x436, 0xbb4, 0x1fb, + 0x425, 0x1ed, 0xe13, 0x66c, 0x555, 0xb11, 0x7b5, 0x48d, 0x38d, 0xf72, 0x000, + 0x000, 0xa66, 0x4fa, 0xf36, 0x1eb, 0x000, 0x95f, 0x000, 0xd9a, 0x82f, 0x07f, + 0x253, 0x70f, 0x915, -1, 0x12d, 0x040, 0x2ca, 0x446, 0x90a, 0x7a8, 0x687, + 0x000, 0x04e, 0x74f, 0x1ca, 0x793, 0x3c7, 0x3f0, 0x4c7, 0x000, 0xc30, 0x533, + 0x889, 0x9ef, 0xebd, 0x984, 0x18f, 0xfe1, 0x8ea, 0x185, 0x410, 0x107, 0x000, + 0x73e, 0xd4b, 0x8fc, 0xd34, 0x1e6, 0x4bf, 0xbac, 0x7c3, 0x000, 0x7c8, 0xb2f, + 0x02c, 0xa46, 0x000, 0x0f9, 0x680, 0x94d, 0x6ad, 0x767, 0xfeb, 0x6c7, 0x2d5, + 0x43f, 0x9af, 0x261, 0xe83, 0xfa7, 0xb7b, 0xf2d, 0x2f5, 0x4d7, 0x494, 0xbc2, + 0x45b, 0x000, 0x17d, 0x5c6, 0xe2b, 0xb20, 0x19e, 0x6ba, 0x973, 0xedd, 0xea8, + 0x000, 0x9f3, 0xd9a, 0x7fa, 0xb78, 0x556, 0xbb6, 0xc58, 0x210, 0x000, 0xf9a, + 0x56d, 0x48b, 0xf12, 0x000, 0x54d, 0x5f4, 0x1ad, 0x86e, 0xe16, 0x6ff, 0xa35, + 0x47e, 0x4c7, 0x93c, -1, -1, 0xc98, 0xd3f, 0x000, 0x788, 0x6ef, 0x959, + 0xec2, 0x45e, 0xa4d, 0xa90, 0x000, 0x768, 0x8bb, 0x6ee, 0x7f5, 0x770, 0xfa8, + 0xba4, 0xf49, 0x7b8, 0x616, 0x2bd, 0x23f, 0xe8c, 0x9fa, 0xa49, 0x213, 0x98a, + 0x2c1, 0x595, 0x885, 0x6de, 0x057, 0x1bc, 0x000, 0xc58, 0x7a8, 0x5c1, 0x3d0, + 0xa78, 0xb80, 0x000, 0xc06, -1, 0x428, 0xe92, 0xfa3, 0x341, -1, 0x000, + 0x000, 0x1ca, 0x27c, 0xdeb, 0x835, 0x4c8, 0xdb3, 0x000, 0xf9d, 0x000, 0xe81, + 0xc22, 0xfce, -1, 0xe6e, 0x96e, 0x161, -1, 0x3b9, 0x945, 0xa95, 0x13d, + 0x748, 0x184, 0x588, 0x636, 0xf7e, 0xb44, 0x2b7, 0x217, 0xee5, 0x65a, 0xc47, + -1, 0xca3, 0x83e, 0x431, 0xc64, 0x636, 0x06e, 0x404, 0x993, -1, 0xeb3, + 0x134, 0x8a3, 0xca9, -1, -1, 0x2ab, 0x000, 0x8ed, 0x877, 0x1a8, 0xc89, + 0x000, 0x000, 0xf94, 0x000, 0x709, 0x249, 0x9ac, 0x22a, 0x605, 0x000, 0x000, + 0x6b4, 0x00c, 0xc53, 0xf23, 0x005, 0x29f, 0x865, 0xf79, 0x000, 0x5fa, 0x764, + 0xe51, 0xbdc, 0xb64, 0x0f3, 0xf29, 0x2f7, 0x5da, 0x000, 0x16f, 0xb8b, 0x255, + 0x9cc, 0xe43, 0x279, 0x2c2, 0x483, -1, 0xf7d, 0x7bb, 0x000, 0x9e3, 0xd84, + 0xe36, 0x6e6, 0x000, -1, 0x33f, 0x41d, 0x5b5, 0x83e, 0x2f4, 0xf5b, 0x9fc, + 0xb1e, -1, 0x8f4, 0xb26, 0x856, 0x3b6, 0x126, 0x4c2, 0x274, 0x0c1, 0xfa9, + 0x57d, 0x000, 0x100, 0x7af, 0xc62, 0x000, 0xa55, 0x416, 0x93f, 0x78c, 0xfba, + 0x5a2, 0x0c2, 0x4d4, 0xa3e, 0xcc3, 0xe73, 0xd02, 0x8df, 0x3e9, 0xe9a, 0x0f6, + 0x32c, 0x23d, 0xdab, 0xf50, 0xfc2, 0x000, 0x065, 0xc23, 0xd3d, 0xc84, 0x35e, + 0x000, 0xa24, 0x634, 0x4b4, 0xa52, 0x098, 0xb39, 0x9a4, 0xe71, 0x8aa, 0x741, + 0x000, 0xb16, 0x5c2, 0xea1, 0xc01, 0x5c1, 0x30d, 0xca4, 0x201, 0xc9c, 0x717, + 0x000, 0xba0, 0x537, 0x619, 0x000, 0xfd9, 0x6dc, 0xdaa, 0x1da, 0xe51, 0xd39, + 0xb4c, 0x8a1, 0x098, 0x2f8, 0x191, 0x9dc, 0xdb0, 0x5e1, 0x000, 0xe97, 0xef1, + 0x8d3, 0xb0d, 0xfce, 0x336, 0xee1, 0x7a2, 0xbc8, 0x494, 0x580, 0xba7, 0x000, + 0x62a, 0x96a, 0x527, 0x859, 0x811, 0xef0, 0x429, 0xef4, 0xf3d, 0x000, 0x9d6, + 0xb71, 0x000, 0x14b, 0xf3d, 0xb16, 0x204, 0x0c1, 0xcd4, 0x339, 0x39d, 0xfe3, + 0x837, 0x8c7, 0x955, 0x69a, 0x5f6, 0x4c6, -1, 0x3d5, 0x000, 0x0e7, 0x4b1, + -1, 0xa3e, 0xb03, 0x1ea, 0xac8, -1, 0x000, 0xed8, -1, 0x4e0, 0x9f7, + 0xc91, 0x6b3, -1, -1, 0xa53, 0x290, 0xa64, 0x0e3, 0x3dc, 0xed3, 0xf2f, + 0x000, 0xd7c, 0xf44, -1, 0x205, 0x900, 0x864, -1, -1, 0xed3, 0x7d2, + 0x000, -1, 0xdd2, 0x79b, 0x000, -1, 0xae6, 0x5cf, 0xde8, 0x000, 0x1f2, + -1, 0x2f3, 0x000, -1, 0x2ce, 0xcf2, 0x8f4, 0xee8, 0x165, 0x309, 0x15f, + -1, 0x714, 0xbfc, 0x532, 0xad0, 0x151, 0x2d5, 0x0a4, 0x391, -1, 0x0dc, + 0x0c1, 0x451, -1, -1, 0x6a0, 0x250, -1, 0xab8, 0x977, 0xa86, 0x407, + 0x72f, -1, 0x05f, 0x000, 0xefe, 0x950, 0x4f4, 0x957, -1, 0xd68, 0x26c, + 0xa30, 0x4f1, 0x279, 0x584, 0xb34, -1, 0x4d7, 0x258, 0x000, 0x518, 0x685, + 0x91c, 0x3ac, 0x0fa, -1, 0x979, 0x40c, 0x506, 0x000, -1, 0x7bd, 0xb97, + 0x87f, 0xc06, 0x050, 0x7bf, 0xe3e, 0xc81, 0x000, 0x65e, 0x000, -1, 0xb76, + 0xc37, 0x4c4, 0xfc9, 0x336, 0x9fa, 0xaa2, 0x32c, 0xb8b, 0xaa9, 0xc95, 0x85a, + 0xa9a, 0x260, 0x4cd, 0x8fe, 0xd3c, 0x982, 0x0d7, 0xbc1, 0xdcf, 0xe62, 0xe0d, + 0xf8f, 0xd7b, 0x91a, 0x3e0, 0x33a, 0x1c5, 0xf00, 0xde5, 0xad1, 0xebc, 0xebc, + 0x942, 0xd86, 0x3bf, 0x8ce, 0xb8c, 0x000, 0x8d6, 0x784, 0xb74, -1, 0x818, + 0x000, 0xfff, 0x07e, 0x029, 0xf48, 0xb65, 0xd81, 0x220, 0x095, 0x21f, 0xac4, + 0xb31, -1, 0x864, 0x000, 0x3bd, 0xf85, 0x237, 0x369, 0x2d9, 0xfdf, 0x25a, + 0x782, 0x7b8, 0xabd, 0x5e3, 0x438, 0x230, 0xbc4, 0x7ad, 0x00a, 0x441, 0x6dc, + 0x2c4, 0xf16, 0x0b3, 0x04c, 0xfd2, 0x8aa, 0xad8, 0x3e4, 0x142, 0x585, 0xc8f, + 0x9bf, 0x29b, 0xac9, 0x743, 0xfb5, 0x7fc, 0x05e, 0xd38, 0x002, -1, 0xb4e, + 0xd0c, 0x84c, 0xf93, 0x91f, 0xcd2, 0x04f, 0x569, 0xd1b, 0xfc6, 0x630, 0x6f6, + 0x1d8, 0x91a, 0x4da, 0x9f5, 0x07a, 0xcf5, 0x634, 0x42f, 0xfff, 0x951, 0x0f9, + 0xc01, 0x491, 0xbd6, 0x730, 0xfea, 0x9f4, 0xbfc, 0xf1a, 0x413, 0xa2a, 0xdc6, + 0xc87, 0x9db, 0xc2c, 0x30f, 0xdb5, 0x785, 0xbaa, 0x000, 0x000, 0xa49, 0x000, + 0x61d, 0xf6f, -1, 0x031, -1, 0x441, 0x7bf, 0x53e, -1, 0x6fd, 0x0f6, + -1, 0xadb, -1, 0x000, 0x432, 0x187, 0xd37, 0x154, 0x539, 0xc08, 0xe51, + 0x219, 0x1e9, 0x897, 0xa0e, 0x201, 0x447, 0x89f, 0x000, 0x463, 0x726, 0xa05, + 0xab9, 0xd01, 0x1e4, 0xfea, 0x895, 0x816, 0x313, 0xae3, 0x3a4, -1, 0x70f, + -1, 0xa42, 0x5e9, 0x78e, -1, 0x317, 0x6c8, 0x000, 0xbf7, 0xefd, -1, + 0xb17, 0x382, 0xd26, 0x5ff, 0xf81, 0x20b, 0x373, 0x774, 0x081, 0xaae, 0xfdb, + 0xe5d, -1, -1, 0xcb7, 0x738, 0x919, 0x933, 0x398, 0x000, 0x14e, -1, + 0xe14, 0xbf8, 0x11c, 0x94b, 0x031, -1, 0x000, 0x2d4, 0xd41, 0xdc6, 0x9f6, + 0xea7, 0x9e8, 0x2ec, 0x10a, 0x50d, 0xeae, 0xdb0, 0xef0, 0x9c8, 0x000, -1, + 0x82e, 0x9d3, 0xdb7, 0x46d, -1, 0x230, 0x73b, 0x45b, -1, -1, 0x000, + 0x4a7, -1, -1, 0x47c, 0x10e, 0x4b4, -1, -1, -1, 0x1d7, 0xa5d, + 0x233, 0x6b2, 0x6bd, 0x387, 0x7ca, 0xb1a, 0xf75, 0xea4, 0xdc9, 0x98b, 0x80c, + 0x702, 0xe22, 0xa6e, 0x6f8, 0x05b, 0x17c, -1, 0x000, 0xebe, 0xc8e, 0xaec, + -1, 0x42b, 0xdce, -1, -1, -1, 0xef3, 0xc52, 0x31b, -1, 0xdff, + 0xbd0, 0x000, 0xa72, 0x525, 0x9cf, 0x2ff, 0xfc8, 0x37c, 0xbce, 0xd8c, 0xd88, + 0x3a6, 0xed8, 0x4ab, 0x000, 0x449, 0x9d7, -1, -1, 0x9be, 0x59f, 0x000, + 0x882, -1, 0x742, 0x000, -1, -1, -1, 0xe8b, 0x0f3, 0x771, -1, + 0x3ea, 0x8f9, 0xcbb, 0x548, 0x46d, 0x000, -1, 0xf74, 0xa23, 0x15b, -1, + 0xaeb, 0x7f8, 0xbe2, 0x000, -1, 0x023, 0x61e, 0x95d, 0x7ac, 0x024, 0x141, + 0x561, 0x9fe, 0xb10, -1, 0x623, 0xc47, 0x413, 0x0e7, 0x663, 0xcdf, 0xebe, + 0x5c9, 0x573, 0x21d, 0xb28, 0x280, 0xb9f, 0xd1a, 0xecf, 0xff0, 0x000, 0xfc0, + 0x085, 0x9c4, 0x48c, 0x000, 0xb0b, 0x43d, -1, 0x73b, 0x802, 0x633, 0x6ef, + -1, -1, 0x5c1, 0xea6, 0x0a9, 0xab4, 0xacd, 0xb81, 0xa32, -1, -1, + 0xa26, 0x9d5, 0xf7c, -1, 0xf69, 0xdbb, 0x6d5, 0x405, -1, 0xd0a, 0xfe0, + 0xf5e, 0xbd7, -1, 0x89a, 0x868, 0xeb2, 0x792, 0x7fe, 0x115, 0x000, 0x8bb, + 0xdd1, 0xc40, 0x453, 0xbb3, 0x7cc, 0x3e6, 0x071, 0x0f1, 0xbae, 0xf67, 0x896, + 0x38e, 0x86e, 0xfaa, 0xccc, -1, 0x411, 0x8e5, 0x699, 0x2ef, 0x785, 0x9d4, + 0xe30, 0xb2e, 0x976, 0x203, 0x035, 0x75d, 0x8f1, 0x144, 0x092, 0x1a5, -1, + 0x55f, 0x000, 0xa43, 0x5be, 0x68d, 0x852, 0xb87, 0x9af, 0x0c0, -1, 0xa50, + 0x9ca, 0x15f, 0xf06, 0x869, 0x0f3, -1, 0x000, -1, 0x9a9, -1, -1, + -1, -1, 0xf05, 0x000, -1, 0x000, 0x4a9, 0xf9d, -1, 0x000, 0xab1, + 0x04c, -1, 0xd17, 0x893, 0x763, 0x332, -1, 0xc41, 0x5bd, 0xa72, 0x67c, + 0xb78, 0x973, 0x6c7, 0x569, -1, 0x96a, 0xc68, 0x48c, -1, 0x6fa, -1, + 0xa2a, 0x44f, -1, 0x73f, 0x28f, 0x536, 0xd91, 0xc86, 0xef8, 0x1f5, 0xfb4, + 0x060, 0x230, 0xe10, -1, 0x000, 0x305, 0x0e6, 0xb19, 0x1e2, 0x7fc, 0xf35, + -1, 0x7d9, 0x000, 0x000, 0xd2f, 0xb3a, 0x0a2, 0x7c9, 0xda6, 0x37c, 0xe43, + -1, 0x7da, 0x0d6, 0x000, -1, 0xd40, -1, 0x156, 0xee9, -1, 0x239, + 0x10f, 0x60c, 0x9d4, 0x663, 0x565, 0x603, 0x38b, -1, 0x606, 0x13c, 0x681, + 0x436, 0xc29, 0x9c7, 0x1d9, 0x000, 0x0a6, 0x996, 0x231, 0x055, 0x01f, 0x0a3, + 0xd96, 0x7c8, 0x0f3, 0xaa7, 0xd99, -1, 0x3be, 0x476, 0x25f, 0x38c, 0xdf3, + 0x6d5, 0xcb5, 0xadd, 0x000, 0x136, 0x64d, 0xc0d, 0xe61, 0xd0b, -1, 0x000, + 0x535, 0x9c3, 0x279, 0x00c, 0xa87, 0xa31, 0xc4a, 0x167, 0x423, 0xec8, -1, + 0x926, 0xa4d, 0x5ba, -1, -1, 0x9bf, 0x000, 0x47f, 0x8f3, 0xd5b, 0xc3b, + 0xa18, -1, 0x548, 0x8f7, 0x8cf, 0x000, 0x9bd, 0xaa2, 0x7ec, 0x000, 0xfb8, + 0xafd, 0xe68, -1, 0xfa7, 0x31c, 0xef3, 0x288, 0xdf0, 0x1bc, 0xfe9, 0x1ea, + 0xa9f, 0x000, 0x53f, 0x000, 0xda6, 0x09c, 0x1bf, 0x09c, 0x31c, 0x0c8, 0x31c, + -1, 0x689, 0x211, -1, 0x77f, 0x723, 0xb8f, 0x683, 0x351, -1, 0xb33, + 0xce0, -1, 0x61c, 0x073, 0x783, 0x6b2, 0x6a8, 0x729, 0x81b, 0xabf, 0xd15, + 0x563, 0x433, -1, 0x823, 0xf99, 0x2c5, -1, 0x367, 0xcc4, 0x934, 0x6f2, + 0xdf0, 0xa1f, 0x579, 0x012, -1, 0x508, 0x070, -1, 0x018, 0x270, 0xa6f, + -1, 0x7a7, -1, 0x826, 0x4b5, -1, 0x7d8, 0xb20, -1, 0xc28, 0x463, + -1, 0xdf9, 0x22c, 0xe17, 0x4f2, 0xe13, 0x4ff, 0x40a, 0xdcb, 0x9ed, 0x34a, + 0xeb8, 0xa0e, 0x5f2, 0x594, 0x60d, 0x4b6, 0xd3c, 0x675, 0x1c4, 0xbb5, 0xc73, + 0xfad, 0xead, -1, 0xfb6, -1, 0x146, 0xd40, 0x02f, 0x000, 0x302, -1, + -1, 0x6e5, 0x000, 0xed7, 0xd8c, 0x7a3, 0x0fc, 0x259, 0x34b, 0xa1b, 0x882, + -1, 0x211, 0x000, 0xd30, 0xe02, 0x5cd, 0x53e, 0x11b, 0xa16, -1, 0x24e, + -1, 0xace, 0xe9a, -1, 0x5c6, 0x9be, 0x000, 0x169, 0x982, -1, 0x3fd, + 0x457, 0x06f, 0x7e7, 0xed1, 0x5ee, 0xcef, 0x62b, 0x26c, 0xc9f, 0xe68, 0x59f, + 0x0b5, 0x000, 0x0bc, 0x086, 0x890, 0x005, 0xc42, 0x939, 0xaca, 0xdd9, -1, + -1, 0x6e5, 0x0dd, 0x434, 0x297, 0xe21, 0x0f5, -1, 0xa6c, 0x4ad, 0x978, + 0x433, 0xa41, 0xd6f, 0x8bf, 0xfb8, -1, 0x928, 0x85e, 0xfb6, 0x5c7, 0x99a, + 0x8ec, 0xebc, 0x226, 0x7d4, 0xdcd, 0xc0b, 0x000, 0x7f4, 0xc6f, 0x000, 0x3ad, + 0x5b2, -1, 0x67b, -1, 0x568, 0x6e2, -1, -1, -1, 0x3f3, 0xaf5, + 0x33f, -1, 0x022, 0x1bd, 0xae5, -1, 0x9c3, 0x000, 0x92b, 0xee5, 0x29c, + 0x000, 0x15e, 0xe71, 0xacb, 0x9d2, 0x1a3, 0xb7f, 0xa5b, 0x095, -1, 0xb6e, + 0x79f, 0x3d1, 0x7d0, 0x131, 0xcd7, -1, 0x2c3, -1, 0x396, 0x4d2, 0x297, + 0x405, 0x634, -1, -1, -1, 0x928, 0xbca, 0xb6c, 0x011, 0xfc0, 0xbaf, + 0xbd2, -1, 0x585, 0x000, 0xb8a, 0x7f9, 0xd6b, 0x4eb, 0x9a3, 0x000, -1, + 0xaeb, 0xa47, 0xcef, 0x9c6, -1, 0x000, -1, 0x2a9, 0x371, 0xca6, -1, + 0xb7d, 0x15f, 0x2a9, -1, 0xe80, 0x7a7, 0x23a, 0x000, 0x000, 0xcc9, 0x60e, + -1, 0x130, 0x9cd, 0x498, 0xe25, 0x366, 0x34b, 0x899, 0xe49, 0x1a8, 0xc93, + 0x94d, 0x05e, -1, 0x0c2, 0x757, 0xb9d, 0xaa3, 0x086, 0x395, 0x3c3, 0xa2e, + 0xf77, 0xcb1, 0x45e, 0x169, 0xbba, 0x367, 0x8cb, 0x260, 0x5a0, 0x8cb, 0x737, + 0xa1f, 0xaaf, 0xf92, 0x430, 0x97d, 0x542, 0xb09, -1, 0x774, 0x084, 0x4c0, + 0x2b3, 0xaf6, 0x93c, 0x32d, 0xee2, -1, 0x605, 0xece, 0x8eb, 0xc62, 0x01d, + 0x000, 0xaba, 0xfc5, 0xb8e, 0xc07, 0xfb6, 0xbca, 0x8f0, 0xd33, -1, 0x283, + 0x6d6, 0x6ad, 0x678, 0x51a, 0xc95, 0xda4, 0x962, 0x9ed, 0x307, 0x94a, 0x052, + 0xf4e, 0x3bd, 0x210, 0x71a, 0x3c7, 0x5a4, 0x6e7, 0x23a, 0x523, 0x1dc, 0x6b2, + 0x5e0, 0xbb0, 0xae4, 0xdb1, 0xd40, 0xc0d, 0x59e, 0x21b, 0x4e6, 0x8be, 0x3b1, + 0xc71, 0x5e4, 0x4aa, 0xaf3, 0xa27, 0x43c, 0x9ea, 0x2ee, 0x6b2, 0xd51, 0x59d, + 0xd3a, 0xd43, 0x59d, 0x405, 0x2d4, 0x05b, 0x1b9, 0x68b, 0xbfa, 0xbb9, 0x77a, + 0xf91, 0xfcb, -1, 0x949, 0x177, 0x68d, 0xcc3, 0xcf2, 0x000, 0xa87, 0x2e6, + 0xd2f, 0x111, 0x168, 0x94c, 0x54c, 0x000, 0x0c5, 0x829, 0xcbc, 0xc0b, 0x1ed, + 0x836, 0x9d8, 0xbdc, 0xc5e, 0x4e5, 0xb94, 0x6f2, 0x74f, 0x878, 0x3b2, 0x48d, + 0xc72, 0xcff, 0xccb, 0x8f9, 0x7ee, 0x869, 0x228, 0x035, 0x81e, 0xcf9, 0x309, + 0xdf2, -1, 0x047, 0xdd3, 0xcab, 0x11d, 0xe76, 0xb52, 0xbbd, 0x12d, 0xf37, + 0x552, 0x61d, 0xdd8, 0x2cd, 0x298, 0x9e2, 0xf2c, 0x9f7, 0xf41, 0xcb4, 0x277, + 0xfde, 0xe7e, 0x82a, 0x86b, 0x9cc, 0x580, 0xfcc, 0x33a, 0x438, 0xd6e, 0x000, + 0xc04, 0xd50, 0x681, 0x1b3, 0x322, 0x86c, 0x4a6, 0x000, 0xa17, 0xd53, 0xdc0, + 0xb61, 0x323, 0x3d1, 0x3fb, 0x929, 0xa6e, 0x919, 0xae0, 0x283, 0xe6a, 0xed3, + 0x371, 0xd51, 0x309, 0x510, 0xd50, 0x6f4, 0xc84, 0x566, 0xba7, 0x75b, 0xbeb, + 0x793, 0x58f, 0x974, 0xe77, 0x52c, 0xcef, 0x942, 0xa7c, 0x56a, 0xaa0, 0x784, + 0x0ec, 0xad3, 0xccf, 0xecf, 0xc3f, 0x846, 0x1b2, 0x112, 0x4ee, 0x18b, 0xa76, + 0xe14, -1, 0xfb1, 0xb4c, -1, 0xd54, 0x0e0, 0x72d, 0xdf0, 0xf0c, 0x881, + 0xc60, 0xe1b, 0x000, 0x453, 0xe21, 0xb2a, 0x6df, 0x84f, 0x000, 0x12a, 0x991, + 0x587, 0xa4e, 0x522, 0x000, 0xe17, 0xc64, 0x994, 0x4cc, 0x0e5, 0xc2b, 0x8cf, + 0x458, 0x992, 0xec0, 0xc80, 0xefb, 0x7b7, 0x863, 0xc0a, 0x250, 0x338, 0xa44, + 0x8ab, 0x873, 0x134, 0x23c, 0x4f6, 0x066, 0xd0f, 0xdd6, 0x93d, 0xf20, 0x000, + 0x9bb, 0x255, 0xe7b, 0x916, 0x4f3, 0x68e, 0xb82, 0x2b4, 0x9d7, 0xfd1, 0x0fb, + 0x11e, 0x204, 0x241, 0x67f, 0x1c4, 0xe91, 0xf41, 0xb4a, -1, 0x6d2, 0xce6, + 0xdfb, -1, 0xdd0, 0xb8d, 0x8db, 0x86c, 0x224, 0xdeb, 0x7bb, 0x50e, 0x000, + 0xb79, 0x11e, 0x486, 0xd4c, 0x000, 0xa54, 0x946, 0x83a, 0x537, 0x875, 0x000, + 0x8e4, 0x95a, 0xdd5, 0x9d5, 0x910, 0x95b, 0x8c8, 0xd22, 0x07c, 0xac0, 0x000, + 0x048, 0x170, 0x9b2, 0xcea, 0xb0f, 0x958, 0x0f9, 0xa9e, 0x87a, 0x166, 0x69c, + 0x112, 0xde0, 0x487, 0xeca, 0x639, 0x4ee, 0x7fa, 0x2cc, 0x709, 0x87a, 0x5bb, + 0xf64, 0x173, 0xdc6, 0xaaf, 0xbff, 0xf2a, 0x8fb, 0xd44, 0x0ca, 0xf34, 0xb3a, + 0xeb3, 0xfc5, 0x61d, 0x92f, 0x6fb, 0x1a1, 0xd85, 0x8fe, 0xb6a, 0x0a1, 0x44f, + 0x89a, 0xc5d, 0x13b, 0x5cc, 0x000, 0x9ac, 0x9e6, 0xf95, 0x511, 0xf2e, 0xf3c, + 0x707, 0xec5, 0xaec, 0xc72, 0xeb1, 0x105, 0xda3, 0xbcb, 0x1d6, 0xf16, 0xd50, + 0x306, 0x03f, 0xe6a, 0x25c, 0x9fe, 0xd3f, 0x8a4, 0x7bc, 0x0bc, 0x532, 0x62e, + 0x111, 0x797, 0xc2c, 0x9d0, 0x338, 0xbc7, 0xd64, 0xb09, 0x4d6, 0xcba, 0x62c, + 0xef2, 0x32b, 0x9c5, 0xc06, 0x38b, 0xbb2, 0x8b7, 0x6f2, 0x7ec, 0xa77, -1, + 0x7a3, 0xc95, 0xa91, 0x5d3, 0xffc, -1, 0xe27, 0xa0a, 0x071, 0x9da, 0x000, + 0xba3, 0x3fd, 0x120, 0x845, 0x151, 0xb5f, 0x193, 0xe99, 0x0f6, 0x640, 0xef4, + 0xe17, 0x46b, 0x432, 0x8a4, 0x415, 0x252, 0x79c, 0xbe5, 0x3f0, 0xb64, 0x984, + 0x5a7, 0x1be, 0xedc, -1, 0xd7e, 0x5b4, -1, 0xd27, 0x03e, 0x136, 0xb30, + 0xfff, 0x1dc, 0xc32, 0x84a, 0x392, 0xf4f, 0x911, 0x501, 0x836, 0x700, 0x9ac, + 0x000, 0xdb5, 0xfa3, 0x5d3, 0x1f8, 0x888, -1, 0xfa4, 0xfe7, 0xd87, 0x9fe, + 0x6af, 0x9a5, 0xfd5, 0xf49, 0xded, 0x416, 0x2fc, 0x078, 0xd2e, 0xbf1, 0xcd9, + 0x733, -1, 0xb50, 0xd57, 0xaa1, -1, 0x9b0, 0x874, 0x18f, -1, -1, + 0x2f9, 0xfbb, 0xf73, 0x646, 0x868, 0x000, 0x000, 0xba2, 0xd74, 0xc8f, 0xe36, + 0xcfd, 0x5b8, 0x850, 0x48a, 0x689, 0x7ad, 0xefd, 0x7e1, 0xf45, 0xd9e, 0x0f0, + 0x7c0, 0x675, -1, 0xf26, 0x3c0, 0xe2d, 0xb62, 0x276, -1, 0xf90, 0x837, + 0xc7c, 0x8ed, 0x08d, 0x1d6, 0x506, 0xac9, 0xd81, 0x1be, 0xf7e, 0x1a3, 0xb2a, + 0x5e2, 0x217, 0x1df, 0x5a3, 0x498, 0xfb1, 0x432, 0x2ca, 0x5a1, 0x5d5, 0x135, + 0x6f0, -1, 0xf70, 0x000, 0xd4c, 0x634, -1, 0x8ca, 0x198, -1, 0x7b9, + 0x629, 0xc16, 0x68c, 0xea2, -1, 0xba0, 0x6d9, 0xce9, 0x7b2, 0x000, 0xf59, + 0x252, 0x575, 0x0a8, 0x894, 0x000, 0x824, 0xf63, 0xd70, 0xdd8, 0x856, 0xc77, + 0x334, 0xeb2, 0x2b8, 0x307, 0xc34, 0x2e7, 0xa74, 0x6b9, 0x0e1, 0x20f, 0x3ee, + 0xf80, 0xa1f, 0x6e6, 0xa03, 0x3c7, 0x72f, 0xfff, 0x156, 0x273, 0x1b7, 0xd90, + 0x998, 0x474, 0xf1b, 0x80f, 0xe4c, 0x0ba, 0xfff, 0x85e, 0x3af, 0x58f, 0x000, + 0xf6b, 0x71c, 0x4ed, 0xec3, 0x4cb, 0x000, 0xa68, 0x6ca, 0x086, 0x000, 0x6e4, + 0xab3, 0xff5, 0x281, 0xf0a, 0xc92, 0x8d5, 0x486, 0xdd1, 0x903, 0x8e3, 0x9df, + 0x2ab, 0xd08, 0x144, 0xdcd, 0x281, 0x046, 0x161, 0xe83, 0xf24, 0xce7, 0x30a, + 0xf89, 0xf01, 0x308, 0x142, 0x9df, 0x44d, 0x9dd, 0x3ed, 0x81b, 0xd9d, 0x000, + 0x8c2, 0xe01, 0xfe6, 0xa20, 0x167, 0xedd, 0xdb1, 0x470, 0xe70, 0x3aa, 0x0ff, + 0x4d1, 0xd30, 0x67a, 0xc56, 0x3d8, 0xf2e, 0x86a, 0x18b, 0x3f5, 0x1a7, 0xd97, + 0x652, 0x000, 0x00d, 0xbc7, 0xed3, 0x39e, 0xb0d, 0xd8d, 0xc49, 0x2db, 0x44e, + 0x820, 0x189, 0xd51, 0x523, 0x161, 0x2eb, 0x41c, 0x951, -1, 0xbb7, -1, + -1, 0x0a7, 0x3ed, 0xfaa, 0x18e, 0xa34, 0x820, 0x2b4, -1, 0x8c2, 0x3ee, + 0x59d, 0x97b, 0x209, 0x3a2, 0x102, 0x351, 0x6bf, 0xd3f, 0x4fc, 0x55f, 0x4b5, + 0xe22, 0xf13, 0x53a, 0x08a, 0x38d, 0xf4b, 0x424, 0xea6, 0x48e, 0x11c, 0x339, + 0x5bd, 0xf7c, 0x3bd, 0x15a, 0x35c, 0x854, 0x71b, 0x30f, 0x065, 0x97e, 0x354, + 0x28e, 0x344, 0x926, 0xc0b, 0xae0, 0x5db, 0xb3e, 0x661, 0x432, 0x3c8, 0xf5e, + 0x368, 0xc85, 0xfff, 0x7f5, 0x0b6, 0x98b, -1, 0x28c, 0x784, 0xb78, 0x50a, + 0x696, 0x47c, 0x40d, -1, 0xe4d, 0x5fc, -1, -1, 0xadb, 0x1db, 0x830, + 0xd48, -1, 0xf3a, 0xee4, 0xed4, 0xb1a, 0xa14, 0x36d, 0xf1c, 0x774, 0x000, + 0x942, 0x278, 0x7ee, 0x000, 0x550, 0x57c, 0x343, 0x22b, 0x324, 0xa34, 0x0ea, + 0x230, 0x51b, 0x2d1, 0x500, 0x59f, 0xd56, 0x540, 0x2f4, 0x87d, 0x9e5, 0x9c5, + 0x5ea, 0x771, 0x491, 0x206, 0xa4b, 0x4bf, 0xdaf, 0x308, 0xb25, 0x261, 0x991, + 0x000, 0x88e, 0x7e8, 0x3d6, 0x15d, 0xebc, 0x6c2, 0xd45, 0x000, 0x3c6, 0x48d, + 0x622, 0x758, 0xfa9, 0x3cf, 0x401, 0xcdb, 0xe3f, 0x544, 0xf1f, 0x000, -1, + 0x4d4, 0x000, 0x7f1, 0xba4, 0x81c, 0x92f, 0x7d1, 0xa83, 0xa31, 0xe74, 0xa20, + 0x475, 0x489, 0xf20, 0x3d1, 0xac1, 0xb2d, 0x6b2, 0x1b6, 0x063, 0xd00, 0xfeb, + 0x5ca, 0xb2c, 0xcb2, 0x1cb, 0x251, 0x82b, 0x8ba, 0x40b, 0xf1e, 0xa8a, 0xd24, + 0x880, 0x84e, 0x8cb, 0x0a3, 0x000, 0xaf7, 0xf99, 0x6a1, 0x156, 0x382, 0x0a0, + 0x000, 0xed1, 0xd07, 0xbf5, 0x000, 0x295, 0xe48, 0x760, 0x019, 0x97f, 0xb46, + 0xff5, 0x7c9, 0x1cf, 0xba4, 0x630, 0xe58, 0xda6, 0xd4b, 0xc02, 0xf9f, 0x11c, + 0x000, 0xb99, 0x51f, 0x43e, 0x199, 0xdfb, 0x72f, 0x913, 0x509, 0xac5, 0xa2e, + 0xcdb, 0x348, 0xb15, 0x472, 0x95d, 0x67f, 0x000, 0x4b9, 0xd78, 0xc87, 0x0f6, + 0x281, 0x0bd, 0x924, 0x35e, 0x129, 0xffd, 0xe24, 0x000, 0x640, 0x09b, 0xd10, + 0xa0d, 0x000, 0x474, 0x189, 0x49f, 0x62d, 0xcba, 0x561, 0x000, 0x58a, 0xaa1, + 0x603, 0x5ab, 0x0c7, 0x00a, 0x784, 0x5e4, 0x7e4, 0xe4d, -1, 0x276, 0x465, + 0xee9, 0xe51, 0xdae, 0xbb1, 0x51f, 0xcba, 0x1c3, 0xd70, 0x000, 0x5be, 0x4ea, + 0x3cc, 0xf13, 0x811, 0x813, 0x234, 0x7e4, 0xbae, 0xd97, 0xb74, 0x000, 0x76a, + 0xda1, 0x000, 0xd8c, 0x53a, 0xc5a, 0x000, 0x000, 0x61b, 0xd87, 0x141, 0x383, + 0xe06, 0x6a3, 0x6c3, 0xbcc, 0xc44, 0xf63, 0xd8b, 0x58d, 0x000, 0x839, 0x77a, + 0x000, 0x8e4, 0x000, 0xdbe, 0x483, 0xd5b, 0xa9d, 0xca5, 0x431, 0x491, 0x29a, + 0x27d, 0x2d2, 0x691, 0x000, 0x19a, 0xa0d, 0xb0b, 0xf32, 0xe49, 0xfbf, 0x399, + 0xd20, 0x000, 0x66a, 0x000, 0x447, 0xb20, 0x894, 0x038, 0xc9c, 0xff0, 0x000, + 0x0d4, 0xad4, 0x768, 0x65c, 0x000, 0x27b, 0x6c6, 0x9be, 0xd35, 0xc6a, 0xdd3, + 0x000, 0x2a7, 0x158, 0x38d, 0x8ef, 0x7b6, 0xd49, 0x30c, 0xec3, 0x211, 0x17c, + 0xcd0, 0x61f, 0x000, 0xe6e, 0x1d4, 0x6e9, 0x000, 0xc2d, 0x5c3, 0xcd4, 0x760, + 0x532, 0xc94, 0x590, 0x000, 0x4a3, 0xc33, 0x000, 0x426, 0x604, 0xa06, 0xa99, + 0x917, 0x0c4, 0xc8d, 0x9e5, 0xcc7, 0x415, 0xf79, 0x000, 0xaf4, 0x622, 0x756, + 0x9c2, 0xa51, 0xb0f, 0x4ef, 0xbc4, 0xe15, 0x29e, 0x055, 0x6c9, 0x695, 0x94f, + 0x9d6, 0x000, 0xb9f, 0xd46, 0x1d4, 0x000, 0xcb2, 0x9e8, 0x000, 0xa5e, 0xce0, + 0x000, 0x098, 0xa98, 0x6d9, 0x5e2, 0x95f, 0x791, 0xeb8, 0x5fa, 0x60a, 0xacc, + 0x3d3, 0x4df, 0x0df, 0x9ca, 0x972, 0x3cc, 0x583, 0xca5, 0xe1a, 0x000, 0x2d3, + 0x266, 0x000, 0x06c, 0xfff, 0x62d, 0x64e, 0x40c, 0x599, 0x475, 0xaa9, 0xba6, + 0x96f, 0xe32, 0x059, 0x342, 0x36d, 0xfd1, 0x09b, 0x878, 0x9f8, 0x000, 0x3ad, + 0xdba, 0x000, 0x544, 0xc1a, 0x000, 0xee8, 0x492, 0xa6b, 0x447, 0xd2a, 0xb4e, + 0x02c, 0xadb, 0xde2, 0x904, 0x62d, 0xf01, 0xbb8, 0x255, 0x382, 0xfff, 0x29e, + 0x000, 0x000, 0x011, 0xfff, +}; + +#endif diff --git a/zbar/decoder/qr_finder.c b/zbar/decoder/qr_finder.c new file mode 100644 index 0000000..c932c03 --- /dev/null +++ b/zbar/decoder/qr_finder.c @@ -0,0 +1,105 @@ +/*------------------------------------------------------------------------ + * Copyright 2009-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" +#include <assert.h> + +#include <zbar.h> + +#ifdef DEBUG_QR_FINDER +#define DEBUG_LEVEL (DEBUG_QR_FINDER) +#endif +#include "debug.h" +#include "decoder.h" + +/* at this point lengths are all decode unit offsets from the decode edge + * NB owned by finder + */ +qr_finder_line *_zbar_decoder_get_qr_finder_line(zbar_decoder_t *dcode) +{ + return (&dcode->qrf.line); +} + +zbar_symbol_type_t _zbar_find_qr(zbar_decoder_t *dcode) +{ + qr_finder_t *qrf = &dcode->qrf; + unsigned s, qz, w; + int ei; + + /* update latest finder pattern width */ + qrf->s5 -= get_width(dcode, 6); + qrf->s5 += get_width(dcode, 1); + s = qrf->s5; + + /*TODO: The 2005 standard allows reflectance-reversed codes (light on dark + instead of dark on light). + If we find finder patterns with the opposite polarity, we should invert + the final binarized image and use them to search for QR codes in that.*/ + if (get_color(dcode) != ZBAR_SPACE || s < 7) + return (0); + + dbprintf(2, " qrf: s=%d", s); + + ei = decode_e(pair_width(dcode, 1), s, 7); + dbprintf(2, " %d", ei); + if (ei) + goto invalid; + + ei = decode_e(pair_width(dcode, 2), s, 7); + dbprintf(2, "%d", ei); + if (ei != 2) + goto invalid; + + ei = decode_e(pair_width(dcode, 3), s, 7); + dbprintf(2, "%d", ei); + if (ei != 2) + goto invalid; + + ei = decode_e(pair_width(dcode, 4), s, 7); + dbprintf(2, "%d", ei); + if (ei) + goto invalid; + + /* valid QR finder symbol + * mark positions needed by decoder + */ + qz = get_width(dcode, 0); + w = get_width(dcode, 1); + qrf->line.eoffs = qz + (w + 1) / 2; + qrf->line.len = qz + w + get_width(dcode, 2); + qrf->line.pos[0] = qrf->line.len + get_width(dcode, 3); + qrf->line.pos[1] = qrf->line.pos[0]; + w = get_width(dcode, 5); + qrf->line.boffs = qrf->line.pos[0] + get_width(dcode, 4) + (w + 1) / 2; + + dbprintf(2, " boff=%d pos=%d len=%d eoff=%d [valid]\n", qrf->line.boffs, + qrf->line.pos[0], qrf->line.len, qrf->line.eoffs); + + dcode->direction = 0; + dcode->buflen = 0; + return (ZBAR_QRCODE); + +invalid: + dbprintf(2, " [invalid]\n"); + return (0); +} diff --git a/zbar/decoder/qr_finder.h b/zbar/decoder/qr_finder.h new file mode 100644 index 0000000..552ad4d --- /dev/null +++ b/zbar/decoder/qr_finder.h @@ -0,0 +1,23 @@ +#ifndef _DECODER_QR_FINDER_H_ +#define _DECODER_QR_FINDER_H_ + +#include "qrcode.h" + +/* QR Code symbol finder state */ +typedef struct qr_finder_s { + unsigned s5; /* finder pattern width */ + qr_finder_line line; /* position info needed by decoder */ + + unsigned config; +} qr_finder_t; + +/* reset QR finder specific state */ +static inline void qr_finder_reset(qr_finder_t *qrf) +{ + qrf->s5 = 0; +} + +/* find QR Code symbols */ +zbar_symbol_type_t _zbar_find_qr(zbar_decoder_t *dcode); + +#endif diff --git a/zbar/decoder/sq_finder.c b/zbar/decoder/sq_finder.c new file mode 100644 index 0000000..ee75e56 --- /dev/null +++ b/zbar/decoder/sq_finder.c @@ -0,0 +1,7 @@ +#include "sq_finder.h" +#include "decoder.h" + +unsigned _zbar_decoder_get_sq_finder_config(zbar_decoder_t *dcode) +{ + return dcode->sqf.config; +} diff --git a/zbar/decoder/sq_finder.h b/zbar/decoder/sq_finder.h new file mode 100644 index 0000000..97232dd --- /dev/null +++ b/zbar/decoder/sq_finder.h @@ -0,0 +1,9 @@ +#ifndef _DECODER_SQ_FINDER_H_ +#define _DECODER_SQ_FINDER_H_ + +/* SQ Code symbol finder state */ +typedef struct sq_finder_s { + unsigned config; +} sq_finder_t; + +#endif diff --git a/zbar/error.c b/zbar/error.c new file mode 100644 index 0000000..1becc1a --- /dev/null +++ b/zbar/error.c @@ -0,0 +1,175 @@ +/*------------------------------------------------------------------------ + * 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 "error.h" +#include <string.h> + +int _zbar_verbosity = 0; + +static const char *const sev_str[] = { "FATAL ERROR", "ERROR", "OK", "WARNING", + "NOTE" }; +#define SEV_MAX (strlen(sev_str[0])) + +static const char *const mod_str[] = { "processor", "video", "window", + "image scanner", "<unknown>" }; +#define MOD_MAX (strlen(mod_str[ZBAR_MOD_IMAGE_SCANNER])) + +static const char *const err_str[] = { + "no error", /* OK */ + "out of memory", /* NOMEM */ + "internal library error", /* INTERNAL */ + "unsupported request", /* UNSUPPORTED */ + "invalid request", /* INVALID */ + "system error", /* SYSTEM */ + "locking error", /* LOCKING */ + "all resources busy", /* BUSY */ + "X11 display error", /* XDISPLAY */ + "X11 protocol error", /* XPROTO */ + "output window is closed", /* CLOSED */ + "windows system error", /* WINAPI */ + "unknown error" /* NUM */ +}; +#define ERR_MAX (strlen(err_str[ZBAR_ERR_CLOSED])) + +int zbar_version(unsigned *major, unsigned *minor, unsigned *patch) +{ + if (major) + *major = ZBAR_VERSION_MAJOR; + if (minor) + *minor = ZBAR_VERSION_MINOR; + if (patch) + *patch = ZBAR_VERSION_PATCH; + return (0); +} + +void zbar_set_verbosity(int level) +{ + _zbar_verbosity = level; +} + +void zbar_increase_verbosity() +{ + if (!_zbar_verbosity) + _zbar_verbosity++; + else + _zbar_verbosity <<= 1; +} + +int _zbar_error_spew(const void *container, int verbosity) +{ + const errinfo_t *err = container; + assert(err->magic == ERRINFO_MAGIC); + fprintf(stderr, "%s", _zbar_error_string(err, verbosity)); + return (-err->sev); +} + +zbar_error_t _zbar_get_error_code(const void *container) +{ + const errinfo_t *err = container; + assert(err->magic == ERRINFO_MAGIC); + return (err->type); +} + +/* ERROR: zbar video in v4l1_set_format(): + * system error: blah[: blah] + */ + +const char *_zbar_error_string(const void *container, int verbosity) +{ + static const char basefmt[] = "%s: zbar %s in %s():\n %s: "; + errinfo_t *err = (errinfo_t *)container; + const char *sev, *mod, *func, *type; + int len; + + assert(err->magic == ERRINFO_MAGIC); + + if (err->sev >= SEV_FATAL && err->sev <= SEV_NOTE) + sev = sev_str[err->sev + 2]; + else + sev = sev_str[1]; + + if (err->module >= ZBAR_MOD_PROCESSOR && err->module < ZBAR_MOD_UNKNOWN) + mod = mod_str[err->module]; + else + mod = mod_str[ZBAR_MOD_UNKNOWN]; + + func = (err->func) ? err->func : "<unknown>"; + + if (err->type >= 0 && err->type < ZBAR_ERR_NUM) + type = err_str[err->type]; + else + type = err_str[ZBAR_ERR_NUM]; + + len = SEV_MAX + MOD_MAX + ERR_MAX + strlen(func) + sizeof(basefmt); + err->buf = realloc(err->buf, len); + len = sprintf(err->buf, basefmt, sev, mod, func, type); + if (len <= 0) + return ("<unknown>"); + + if (err->detail) { + int newlen = len + strlen(err->detail) + 1; + if (strstr(err->detail, "%s")) { + if (!err->arg_str) + err->arg_str = strdup("<?>"); + err->buf = realloc(err->buf, newlen + strlen(err->arg_str)); + len += sprintf(err->buf + len, err->detail, err->arg_str); + } else if (strstr(err->detail, "%d") || strstr(err->detail, "%x")) { + err->buf = realloc(err->buf, newlen + 32); + len += sprintf(err->buf + len, err->detail, err->arg_int); + } else { + err->buf = realloc(err->buf, newlen); + len += sprintf(err->buf + len, "%s", err->detail); + } + if (len <= 0) + return ("<unknown>"); + } + +#ifdef HAVE_ERRNO_H + if (err->type == ZBAR_ERR_SYSTEM) { + static const char sysfmt[] = ": %s (%d)\n"; + const char *syserr = strerror(err->errnum); + err->buf = realloc(err->buf, len + strlen(sysfmt) + strlen(syserr)); + len += sprintf(err->buf + len, sysfmt, syserr, err->errnum); + } +#endif +#ifdef _WIN32 + else if (err->type == ZBAR_ERR_WINAPI) { + char *syserr = NULL; + if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, err->errnum, 0, (LPTSTR)&syserr, 1, NULL) && + syserr) { + char sysfmt[] = ": %s (%d)\n"; + err->buf = realloc(err->buf, len + strlen(sysfmt) + strlen(syserr)); + len += sprintf(err->buf + len, sysfmt, syserr, err->errnum); + LocalFree(syserr); + } + } +#endif + else { + err->buf = realloc(err->buf, len + 2); + len += sprintf(err->buf + len, "\n"); + } + return (err->buf); +} diff --git a/zbar/error.h b/zbar/error.h new file mode 100644 index 0000000..baf759c --- /dev/null +++ b/zbar/error.h @@ -0,0 +1,236 @@ +/*------------------------------------------------------------------------ + * 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 + *------------------------------------------------------------------------*/ +#ifndef _ERROR_H_ +#define _ERROR_H_ + +#include "config.h" +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#include <assert.h> + +#include <zbar.h> + +#ifdef _WIN32 +#include <windows.h> +#endif + +#if __STDC_VERSION__ < 199901L +#if __GNUC__ >= 2 +#define __func__ __FUNCTION__ +#else +#define __func__ "<unknown>" +#endif +#endif + +#define ERRINFO_MAGIC (0x5252457a) /* "zERR" (LE) */ + +typedef enum errsev_e +{ + SEV_FATAL = -2, /* application must terminate */ + SEV_ERROR = -1, /* might be able to recover and continue */ + SEV_OK = 0, + SEV_WARNING = 1, /* unexpected condition */ + SEV_NOTE = 2, /* fyi */ +} errsev_t; + +typedef enum errmodule_e +{ + ZBAR_MOD_PROCESSOR, + ZBAR_MOD_VIDEO, + ZBAR_MOD_WINDOW, + ZBAR_MOD_IMAGE_SCANNER, + ZBAR_MOD_UNKNOWN, +} errmodule_t; + +typedef struct errinfo_s { + uint32_t magic; /* just in case */ + errmodule_t module; /* reporting module */ + char *buf; /* formatted and passed to application */ + int errnum; /* errno for system errors */ + + errsev_t sev; + zbar_error_t type; + const char *func; /* reporting function */ + const char *detail; /* description */ + char *arg_str; /* single string argument */ + int arg_int; /* single integer argument */ +} errinfo_t; + +extern int _zbar_verbosity; + +/* FIXME don't we need varargs hacks here? */ + +#ifdef _WIN32 +#define ZFLUSH fflush(stderr); +#else +#define ZFLUSH +#endif + +#ifdef ZNO_MESSAGES + +#ifdef __GNUC__ +/* older versions of gcc (< 2.95) require a named varargs parameter */ +#define zprintf(args...) +#else +/* unfortunately named vararg parameter is a gcc-specific extension */ +#define zprintf(...) +#endif + +#else + +#ifdef __GNUC__ +#define zprintf(level, format, args...) \ + do { \ + if (_zbar_verbosity >= level) { \ + fprintf(stderr, "%s: " format, __func__, ##args); \ + ZFLUSH \ + } \ + } while (0) +#define zwprintf(level, format, args...) \ + do { \ + if (_zbar_verbosity >= level) { \ + fprintf(stderr, "%s: ", __func__); \ + fwprintf(stderr, format, ##args); \ + ZFLUSH \ + } \ + } while (0) +#else +#define zprintf(level, format, ...) \ + do { \ + if (_zbar_verbosity >= level) { \ + fprintf(stderr, "%s: " format, __func__, ##__VA_ARGS__); \ + ZFLUSH \ + } \ + } while (0) +#define zwprintf(level, format, ...) \ + do { \ + if (_zbar_verbosity >= level) { \ + fprintf(stderr, "%s: ", __func__); \ + fwprintf(stderr, format, ##__VA_ARGS__); \ + ZFLUSH \ + } \ + } while (0) +#endif + +#endif + +static inline int err_copy(void *dst_c, void *src_c) +{ + errinfo_t *dst = dst_c; + errinfo_t *src = src_c; + assert(dst->magic == ERRINFO_MAGIC); + assert(src->magic == ERRINFO_MAGIC); + + dst->errnum = src->errnum; + dst->sev = src->sev; + dst->type = src->type; + dst->func = src->func; + dst->detail = src->detail; + dst->arg_str = src->arg_str; + src->arg_str = NULL; /* unused at src, avoid double free */ + dst->arg_int = src->arg_int; + return (-1); +} + +static inline int err_capture(const void *container, errsev_t sev, + zbar_error_t type, const char *func, + const char *detail) +{ + errinfo_t *err = (errinfo_t *)container; + assert(err->magic == ERRINFO_MAGIC); +#ifdef HAVE_ERRNO_H + if (type == ZBAR_ERR_SYSTEM) + err->errnum = errno; +#endif +#ifdef _WIN32 + if (type == ZBAR_ERR_WINAPI) + err->errnum = GetLastError(); +#endif + err->sev = sev; + err->type = type; + err->func = func; + err->detail = detail; + if (_zbar_verbosity >= 1) + _zbar_error_spew(err, 0); + return (-1); +} + +static inline int err_capture_str(const void *container, errsev_t sev, + zbar_error_t type, const char *func, + const char *detail, const char *arg) +{ + errinfo_t *err = (errinfo_t *)container; + assert(err->magic == ERRINFO_MAGIC); + if (err->arg_str) + free(err->arg_str); + err->arg_str = strdup(arg); + return (err_capture(container, sev, type, func, detail)); +} + +static inline int err_capture_int(const void *container, errsev_t sev, + zbar_error_t type, const char *func, + const char *detail, int arg) +{ + errinfo_t *err = (errinfo_t *)container; + assert(err->magic == ERRINFO_MAGIC); + err->arg_int = arg; + return (err_capture(container, sev, type, func, detail)); +} + +static inline int err_capture_num(const void *container, errsev_t sev, + zbar_error_t type, const char *func, + const char *detail, int num) +{ + errinfo_t *err = (errinfo_t *)container; + assert(err->magic == ERRINFO_MAGIC); + err->errnum = num; + return (err_capture(container, sev, type, func, detail)); +} + +static inline void err_init(errinfo_t *err, errmodule_t module) +{ + err->magic = ERRINFO_MAGIC; + err->module = module; +} + +static inline void err_cleanup(errinfo_t *err) +{ + assert(err->magic == ERRINFO_MAGIC); + if (err->buf) { + free(err->buf); + err->buf = NULL; + } + if (err->arg_str) { + free(err->arg_str); + err->arg_str = NULL; + } +} + +#endif diff --git a/zbar/event.h b/zbar/event.h new file mode 100644 index 0000000..aef5a07 --- /dev/null +++ b/zbar/event.h @@ -0,0 +1,60 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (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 + *------------------------------------------------------------------------*/ +#ifndef _ZBAR_EVENT_H_ +#define _ZBAR_EVENT_H_ + +#include "config.h" +#include "mutex.h" +#include "timer.h" + +/* platform synchronization "event" abstraction + */ + +#if defined(_WIN32) + +#include <windows.h> + +typedef HANDLE zbar_event_t; + +#else + +#ifdef HAVE_LIBPTHREAD +#include <pthread.h> +#endif + +typedef struct zbar_event_s { + int state; +#ifdef HAVE_LIBPTHREAD + pthread_cond_t cond; +#endif + int pollfd; +} zbar_event_t; + +#endif + +extern int _zbar_event_init(zbar_event_t *); +extern void _zbar_event_destroy(zbar_event_t *); +extern void _zbar_event_trigger(zbar_event_t *); +extern int _zbar_event_wait(zbar_event_t *, zbar_mutex_t *, zbar_timer_t *); + +#endif diff --git a/zbar/image.c b/zbar/image.c new file mode 100644 index 0000000..82aeeb8 --- /dev/null +++ b/zbar/image.c @@ -0,0 +1,342 @@ +/*------------------------------------------------------------------------ + * 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" +#include <stdio.h> +#include <stdlib.h> + +#include "error.h" +#include "image.h" +#include "refcnt.h" + +zbar_image_t *zbar_image_create() +{ + zbar_image_t *img = calloc(1, sizeof(zbar_image_t)); + _zbar_refcnt_init(); + _zbar_image_refcnt(img, 1); + img->srcidx = -1; + return (img); +} + +void _zbar_image_free(zbar_image_t *img) +{ + if (img->syms) { + zbar_symbol_set_ref(img->syms, -1); + img->syms = NULL; + } + free(img); +} + +void zbar_image_destroy(zbar_image_t *img) +{ + _zbar_image_refcnt(img, -1); +} + +void zbar_image_ref(zbar_image_t *img, int refs) +{ + _zbar_image_refcnt(img, refs); +} + +unsigned long zbar_image_get_format(const zbar_image_t *img) +{ + return (img->format); +} + +unsigned zbar_image_get_sequence(const zbar_image_t *img) +{ + return (img->seq); +} + +unsigned zbar_image_get_width(const zbar_image_t *img) +{ + return (img->width); +} + +unsigned zbar_image_get_height(const zbar_image_t *img) +{ + return (img->height); +} + +void zbar_image_get_size(const zbar_image_t *img, unsigned *w, unsigned *h) +{ + if (w) + *w = img->width; + if (h) + *h = img->height; +} + +void zbar_image_get_crop(const zbar_image_t *img, unsigned *x, unsigned *y, + unsigned *w, unsigned *h) +{ + if (x) + *x = img->crop_x; + if (y) + *y = img->crop_y; + if (w) + *w = img->crop_w; + if (h) + *h = img->crop_h; +} + +const void *zbar_image_get_data(const zbar_image_t *img) +{ + return (img->data); +} + +unsigned long zbar_image_get_data_length(const zbar_image_t *img) +{ + return (img->datalen); +} + +void zbar_image_set_format(zbar_image_t *img, unsigned long fmt) +{ + img->format = fmt; +} + +void zbar_image_set_sequence(zbar_image_t *img, unsigned seq) +{ + img->seq = seq; +} + +void zbar_image_set_size(zbar_image_t *img, unsigned w, unsigned h) +{ + img->crop_x = img->crop_y = 0; + img->width = img->crop_w = w; + img->height = img->crop_h = h; +} + +void zbar_image_set_crop(zbar_image_t *img, unsigned x, unsigned y, unsigned w, + unsigned h) +{ + unsigned img_h; + unsigned img_w = img->width; + if (x > img_w) + x = img_w; + if (x + w > img_w) + w = img_w - x; + img->crop_x = x; + img->crop_w = w; + + img_h = img->height; + if (y > img_h) + y = img_h; + if (y + h > img_h) + h = img_h - y; + img->crop_y = y; + img->crop_h = h; +} + +inline void zbar_image_free_data(zbar_image_t *img) +{ + if (!img) + return; + if (img->src) { + zbar_image_t *newimg; + /* replace video image w/new copy */ + assert(img->refcnt); /* FIXME needs lock */ + newimg = zbar_image_create(); + memcpy(newimg, img, sizeof(zbar_image_t)); + /* recycle video image */ + newimg->cleanup(newimg); + /* detach old image from src */ + img->cleanup = NULL; + img->src = NULL; + img->srcidx = -1; + } else if (img->cleanup && img->data) { + if (img->cleanup != zbar_image_free_data) { + /* using function address to detect this case is a bad idea; + * windows link libraries add an extra layer of indirection... + * this works around that problem (bug #2796277) + */ + zbar_image_cleanup_handler_t *cleanup = img->cleanup; + img->cleanup = zbar_image_free_data; + cleanup(img); + } else + free((void *)img->data); + } + img->data = NULL; +} + +void zbar_image_set_data(zbar_image_t *img, const void *data, unsigned long len, + zbar_image_cleanup_handler_t *cleanup) +{ + zbar_image_free_data(img); + img->data = data; + img->datalen = len; + img->cleanup = cleanup; +} + +void zbar_image_set_userdata(zbar_image_t *img, void *userdata) +{ + img->userdata = userdata; +} + +void *zbar_image_get_userdata(const zbar_image_t *img) +{ + return (img->userdata); +} + +zbar_image_t *zbar_image_copy(const zbar_image_t *src) +{ + return _zbar_image_copy(src, 0); +} + +const zbar_symbol_set_t *zbar_image_get_symbols(const zbar_image_t *img) +{ + return (img->syms); +} + +void zbar_image_set_symbols(zbar_image_t *img, const zbar_symbol_set_t *syms) +{ + if (syms) + zbar_symbol_set_ref(syms, 1); + if (img->syms) + zbar_symbol_set_ref(img->syms, -1); + img->syms = (zbar_symbol_set_t *)syms; +} + +const zbar_symbol_t *zbar_image_first_symbol(const zbar_image_t *img) +{ + return ((img->syms) ? img->syms->head : NULL); +} + +typedef struct zimg_hdr_s { + uint32_t magic, format; + uint16_t width, height; + uint32_t size; +} zimg_hdr_t; + +int zbar_image_write(const zbar_image_t *img, const char *filebase) +{ + int len = strlen(filebase) + 16; + char *filename = malloc(len); + int n = 0, rc = 0; + FILE *f; + zimg_hdr_t hdr; + strcpy(filename, filebase); + if ((img->format & 0xff) >= ' ') + n = snprintf(filename, len, "%s.%.4s.zimg", filebase, + (char *)&img->format); + else + n = snprintf(filename, len, "%s.%08" PRIx32 ".zimg", filebase, + img->format); + assert(n < len - 1); + filename[len - 1] = '\0'; + + zprintf(1, "dumping %.4s(%08" PRIx32 ") image to %s\n", + (char *)&img->format, img->format, filename); + + f = fopen(filename, "w"); + if (!f) { +#ifdef HAVE_ERRNO_H + rc = errno; + zprintf(1, "ERROR opening %s: %s\n", filename, strerror(rc)); +#else + rc = 1; +#endif + goto error; + } + + hdr.magic = 0x676d697a; + hdr.format = img->format; + hdr.width = img->width; + hdr.height = img->height; + hdr.size = img->datalen; + + if (fwrite(&hdr, sizeof(hdr), 1, f) != 1 || + fwrite(img->data, 1, img->datalen, f) != img->datalen) { +#ifdef HAVE_ERRNO_H + rc = errno; + zprintf(1, "ERROR writing %s: %s\n", filename, strerror(rc)); +#else + rc = 1; +#endif + fclose(f); + goto error; + } + + rc = fclose(f); + +error: + free(filename); + return (rc); +} + +#ifdef DEBUG_SVG +#include <png.h> + +int zbar_image_write_png(const zbar_image_t *img, const char *filename) +{ + int rc = -1; + FILE *file = NULL; + png_struct *png = NULL; + png_info *info = NULL; + const uint8_t **rows = NULL; + + rows = malloc(img->height * sizeof(*rows)); + if (!rows) + goto done; + + rows[0] = img->data; + int y; + for (y = 1; y < img->height; y++) + rows[y] = rows[y - 1] + img->width; + + file = fopen(filename, "wb"); + if (!file) + goto done; + + png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png) + goto done; + + info = png_create_info_struct(png); + if (!info) + goto done; + + if (setjmp(png_jmpbuf(png))) + goto done; + + png_init_io(png, file); + png_set_compression_level(png, 9); + png_set_IHDR(png, info, img->width, img->height, 8, PNG_COLOR_TYPE_GRAY, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + png_set_rows(png, info, (void *)rows); + png_write_png(png, info, PNG_TRANSFORM_IDENTITY, NULL); + + png_write_end(png, info); + rc = 0; + +done: + if (png) + png_destroy_write_struct(&png, &info); + if (rows) + free(rows); + if (file) + fclose(file); + return (rc); +} + +#endif diff --git a/zbar/image.h b/zbar/image.h new file mode 100644 index 0000000..8d85fba --- /dev/null +++ b/zbar/image.h @@ -0,0 +1,177 @@ +/*------------------------------------------------------------------------ + * 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 + *------------------------------------------------------------------------*/ +#ifndef _IMAGE_H_ +#define _IMAGE_H_ + +#include "config.h" +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif +#include <assert.h> +#include <stdlib.h> + +#include <zbar.h> +#include "error.h" +#include "refcnt.h" +#include "symbol.h" + +#define fourcc zbar_fourcc + +/* unpack size/location of component */ +#define RGB_SIZE(c) ((c) >> 5) +#define RGB_OFFSET(c) ((c)&0x1f) + +/* coarse image format categorization. + * to limit conversion variations + */ +typedef enum zbar_format_group_e +{ + ZBAR_FMT_GRAY, + ZBAR_FMT_YUV_PLANAR, + ZBAR_FMT_YUV_PACKED, + ZBAR_FMT_RGB_PACKED, + ZBAR_FMT_YUV_NV, + ZBAR_FMT_JPEG, + + /* enum size */ + ZBAR_FMT_NUM +} zbar_format_group_t; + +struct zbar_image_s { + uint32_t format; /* fourcc image format code */ + unsigned width, height; /* image size */ + const void *data; /* image sample data */ + unsigned long datalen; /* allocated/mapped size of data */ + unsigned crop_x, crop_y; /* crop rectangle */ + unsigned crop_w, crop_h; + void *userdata; /* user specified data associated w/image */ + + /* cleanup handler */ + zbar_image_cleanup_handler_t *cleanup; + refcnt_t refcnt; /* reference count */ + zbar_video_t *src; /* originator */ + int srcidx; /* index used by originator */ + zbar_image_t *next; /* internal image lists */ + + unsigned seq; /* page/frame sequence number */ + zbar_symbol_set_t *syms; /* decoded result set */ +}; + +/* description of an image format */ +typedef struct zbar_format_def_s { + uint32_t format; /* fourcc */ + zbar_format_group_t group; /* coarse categorization */ + union { + uint8_t gen[4]; /* raw bytes */ + struct { + uint8_t bpp; /* bits per pixel */ + uint8_t red, green, blue; /* size/location a la RGB_BITS() */ + } rgb; + struct { + uint8_t xsub2, ysub2; /* chroma subsampling in each axis */ + uint8_t packorder; /* channel ordering flags + * bit0: 0=UV, 1=VU + * bit1: 0=Y/chroma, 1=chroma/Y + */ + } yuv; + uint32_t cmp; /* quick compare equivalent formats */ + } p; +} zbar_format_def_t; + +extern int _zbar_best_format(uint32_t, uint32_t *, const uint32_t *); +extern const zbar_format_def_t *_zbar_format_lookup(uint32_t); +extern void _zbar_image_free(zbar_image_t *); + +#ifdef DEBUG_SVG +extern int zbar_image_write_png(const zbar_image_t *, const char *); +#else +#define zbar_image_write_png(...) +#endif + +static inline void _zbar_image_refcnt(zbar_image_t *img, int delta) +{ + if (!_zbar_refcnt(&img->refcnt, delta) && delta <= 0) { + if (img->cleanup) + img->cleanup(img); + if (!img->src) + _zbar_image_free(img); + } +} + +static inline void _zbar_image_swap_symbols(zbar_image_t *a, zbar_image_t *b) +{ + zbar_symbol_set_t *tmp = a->syms; + a->syms = b->syms; + b->syms = tmp; +} + +static inline void _zbar_image_copy_size(zbar_image_t *dst, + const zbar_image_t *src) +{ + dst->width = src->width; + dst->height = src->height; + dst->crop_x = src->crop_x; + dst->crop_y = src->crop_y; + dst->crop_w = src->crop_w; + dst->crop_h = src->crop_h; +} + +static inline zbar_image_t *_zbar_image_copy(const zbar_image_t *src, + int inverted) +{ + zbar_image_t *dst; + + if (inverted && (src->format != fourcc('Y', '8', '0', '0')) && + (src->format != fourcc('G', 'R', 'E', 'Y'))) + return NULL; + + dst = zbar_image_create(); + dst->format = src->format; + _zbar_image_copy_size(dst, src); + dst->datalen = src->datalen; + dst->data = malloc(src->datalen); + assert(dst->data); + + if (!inverted) { + memcpy((void *)dst->data, src->data, src->datalen); + } else { + int i, len = src->datalen; + long *sp = (void *)src->data, *dp = (void *)dst->data; + char *spc, *dpc; + + /* Do it word per word, in order to speedup */ + for (i = 0; i < len; i += sizeof(long)) + *dp++ = ~(*sp++); + + /* Deal with non-aligned remains, if any */ + len -= i; + spc = (char *)sp; + dpc = (char *)dp; + for (i = 0; i < len; i++) + *dpc++ = ~(*spc++); + } + dst->cleanup = zbar_image_free_data; + return (dst); +} + +#endif 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 diff --git a/zbar/img_scanner.h b/zbar/img_scanner.h new file mode 100644 index 0000000..2f7f9f1 --- /dev/null +++ b/zbar/img_scanner.h @@ -0,0 +1,37 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (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 + *------------------------------------------------------------------------*/ +#ifndef _IMG_SCANNER_H_ +#define _IMG_SCANNER_H_ + +#include <zbar.h> + +/* internal image scanner APIs for 2D readers */ + +extern zbar_symbol_t *_zbar_image_scanner_alloc_sym(zbar_image_scanner_t *, + zbar_symbol_type_t, int); +extern void _zbar_image_scanner_add_sym(zbar_image_scanner_t *, + zbar_symbol_t *); +extern void _zbar_image_scanner_recycle_syms(zbar_image_scanner_t *, + zbar_symbol_t *); + +#endif diff --git a/zbar/jpeg.c b/zbar/jpeg.c new file mode 100644 index 0000000..d6b2e59 --- /dev/null +++ b/zbar/jpeg.c @@ -0,0 +1,245 @@ +/*------------------------------------------------------------------------ + * Copyright 2009 (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 <assert.h> /* FIXME tmp debug */ +#include <jerror.h> +#include <setjmp.h> +#include <stdio.h> + +#include <jpeglib.h> + +#undef HAVE_STDLIB_H +#include <zbar.h> +#include "image.h" +#include "video.h" + +#define HAVE_LONGJMP +#ifdef HAVE_LONGJMP + +typedef struct errenv_s { + struct jpeg_error_mgr err; + int valid; + jmp_buf env; +} errenv_t; + +void zbar_jpeg_error(j_common_ptr cinfo) +{ + errenv_t *jerr = (errenv_t *)cinfo->err; + assert(jerr->valid); + jerr->valid = 0; + longjmp(jerr->env, 1); + assert(0); +} + +#endif + +typedef struct zbar_src_mgr_s { + struct jpeg_source_mgr src; + const zbar_image_t *img; +} zbar_src_mgr_t; + +static const JOCTET fake_eoi[2] = { 0xff, JPEG_EOI }; + +void init_source(j_decompress_ptr cinfo) +{ + /* buffer/length refer to compressed data */ + /* FIXME find img */ + const zbar_image_t *img = ((zbar_src_mgr_t *)cinfo->src)->img; + cinfo->src->next_input_byte = img->data; + cinfo->src->bytes_in_buffer = img->datalen; +} + +boolean fill_input_buffer(j_decompress_ptr cinfo) +{ + /* buffer underrun error case */ + cinfo->src->next_input_byte = fake_eoi; + cinfo->src->bytes_in_buffer = 2; + return (1); +} + +void skip_input_data(j_decompress_ptr cinfo, long num_bytes) +{ + if (num_bytes > 0) { + if (num_bytes < cinfo->src->bytes_in_buffer) { + cinfo->src->next_input_byte += num_bytes; + cinfo->src->bytes_in_buffer -= num_bytes; + } else { + fill_input_buffer(cinfo); + } + } +} + +void term_source(j_decompress_ptr cinfo) +{ + /* nothing todo */ +} + +struct jpeg_decompress_struct *_zbar_jpeg_decomp_create(void) +{ + j_decompress_ptr cinfo = calloc(1, sizeof(struct jpeg_decompress_struct)); + if (!cinfo) + return (NULL); + + errenv_t *jerr = calloc(1, sizeof(errenv_t)); + if (!jerr) { + free(cinfo); + return (NULL); + } + + cinfo->err = jpeg_std_error(&jerr->err); + jerr->err.error_exit = zbar_jpeg_error; + + jerr->valid = 1; + if (setjmp(jerr->env)) { + jpeg_destroy_decompress(cinfo); + + /* FIXME TBD save error to errinfo_t */ + (*cinfo->err->output_message)((j_common_ptr)cinfo); + + free(jerr); + free(cinfo); + return (NULL); + } + + jpeg_create_decompress(cinfo); + + jerr->valid = 0; + return (cinfo); +} + +void _zbar_jpeg_decomp_destroy(struct jpeg_decompress_struct *cinfo) +{ + if (cinfo->err) { + free(cinfo->err); + cinfo->err = NULL; + } + if (cinfo->src) { + free(cinfo->src); + cinfo->src = NULL; + } + /* FIXME can this error? */ + jpeg_destroy_decompress(cinfo); + free(cinfo); +} + +/* invoke libjpeg to decompress JPEG format to luminance plane */ +void _zbar_convert_jpeg_to_y(zbar_image_t *dst, const zbar_format_def_t *dstfmt, + const zbar_image_t *src, + const zbar_format_def_t *srcfmt) +{ + /* create decompressor, or use cached video stream decompressor */ + errenv_t *jerr = NULL; + j_decompress_ptr cinfo; + if (!src->src) + cinfo = _zbar_jpeg_decomp_create(); + else { + cinfo = src->src->jpeg; + assert(cinfo); + } + if (!cinfo) + goto error; + + jerr = (errenv_t *)cinfo->err; + jerr->valid = 1; + if (setjmp(jerr->env)) { + /* FIXME TBD save error to src->src->err */ + (*cinfo->err->output_message)((j_common_ptr)cinfo); + if (dst->data) { + free((void *)dst->data); + dst->data = NULL; + } + dst->datalen = 0; + goto error; + } + + /* setup input image */ + if (!cinfo->src) { + cinfo->src = calloc(1, sizeof(zbar_src_mgr_t)); + cinfo->src->init_source = init_source; + cinfo->src->fill_input_buffer = fill_input_buffer; + cinfo->src->skip_input_data = skip_input_data; + cinfo->src->resync_to_restart = jpeg_resync_to_restart; + cinfo->src->term_source = term_source; + } + cinfo->src->next_input_byte = NULL; + cinfo->src->bytes_in_buffer = 0; + ((zbar_src_mgr_t *)cinfo->src)->img = src; + + int rc = jpeg_read_header(cinfo, TRUE); + zprintf(30, "header: %s\n", (rc == 2) ? "tables-only" : "normal"); + + /* supporting color with jpeg became...complicated, + * so we skip that for now + */ + cinfo->out_color_space = JCS_GRAYSCALE; + + /* FIXME set scaling based on dst->{width,height} + * then pass bigger buffer... + */ + + jpeg_start_decompress(cinfo); + + /* adjust dst image parameters to match(?) decompressor */ + if (dst->width < cinfo->output_width) { + dst->width = cinfo->output_width; + if (dst->crop_x + dst->crop_w > dst->width) + dst->crop_w = dst->width - dst->crop_x; + } + if (dst->height < cinfo->output_height) { + dst->height = cinfo->output_height; + if (dst->crop_y + dst->crop_h > dst->height) + dst->crop_h = dst->height - dst->crop_y; + } + unsigned long datalen = (cinfo->output_width * cinfo->output_height * + cinfo->out_color_components); + + zprintf(24, "dst=%dx%d %lx src=%dx%d %lx dct=%x\n", dst->width, dst->height, + dst->datalen, src->width, src->height, src->datalen, + cinfo->dct_method); + if (!dst->data) { + dst->datalen = datalen; + dst->data = malloc(dst->datalen); + dst->cleanup = zbar_image_free_data; + } else + assert(datalen <= dst->datalen); + if (!dst->data) + return; + + unsigned bpl = dst->width * cinfo->output_components; + JSAMPROW buf = (void *)dst->data; + JSAMPARRAY line = &buf; + for (; cinfo->output_scanline < cinfo->output_height; buf += bpl) { + jpeg_read_scanlines(cinfo, line, 1); + /* FIXME pad out to dst->width */ + } + + /* FIXME always do this? */ + jpeg_finish_decompress(cinfo); + +error: + if (jerr) + jerr->valid = 0; + if (!src->src && cinfo) + /* cleanup only if we allocated locally */ + _zbar_jpeg_decomp_destroy(cinfo); +} diff --git a/zbar/libzbar.rc b/zbar/libzbar.rc new file mode 100644 index 0000000..c241f82 --- /dev/null +++ b/zbar/libzbar.rc @@ -0,0 +1,31 @@ +#include <config.h> +#include <winver.h> + +#define STR(s) #s +#define XSTR(s) STR(s) + +VS_VERSION_INFO VERSIONINFO + FILEVERSION LIB_VERSION_MAJOR, LIB_VERSION_MINOR, LIB_VERSION_REVISION, 0 + PRODUCTVERSION ZBAR_VERSION_MAJOR, ZBAR_VERSION_MINOR, ZBAR_VERSION_PATCH, 0 + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL +{ + BLOCK "StringFileInfo" { + BLOCK "040904E4" { + VALUE "ProductName", "ZBar Bar Code Reader" + VALUE "Company Name", "ZBar Bar Code Reader" + VALUE "InternalName", "libzbar" + VALUE "OriginalFilename", "libzbar-" XSTR(LIB_VERSION_MAJOR) ".dll" + + VALUE "FileVersion", XSTR(LIB_VERSION_MAJOR) "." XSTR(LIB_VERSION_MINOR) "." XSTR(LIB_VERSION_REVISION) + VALUE "ProductVersion", PACKAGE_VERSION + + VALUE "FileDescription", "Bar code reader library" + + VALUE "LegalCopyright", "Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net>" + } + } + BLOCK "VarFileInfo" { + VALUE "Translation", 0x0409, 0x04e4 + } +} diff --git a/zbar/misc.c b/zbar/misc.c new file mode 100644 index 0000000..d4add31 --- /dev/null +++ b/zbar/misc.c @@ -0,0 +1,105 @@ +/*------------------------------------------------------------------------ + * Copyright 2012 (c) Jarek Czekalski <jarekczek@poczta.onet.pl> + * + * 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 "misc.h" +#include "error.h" + +static int module_initialized = 0; +static errinfo_t err; + +static void module_init() +{ + if (module_initialized) + return; + err_init(&err, ZBAR_MOD_UNKNOWN); + module_initialized = 1; +} + +void resolution_list_init(resolution_list_t *list) +{ + module_init(); + list->cnt = 0; + // an empty list consists of 1 zeroed element + list->resolutions = calloc(1, sizeof(resolution_t)); + if (!list->resolutions) { + err_capture(&err, SEV_FATAL, ZBAR_ERR_NOMEM, __func__, + "allocating resources"); + } +} + +void resolution_list_cleanup(resolution_list_t *list) +{ + free(list->resolutions); +} + +void resolution_list_add(resolution_list_t *list, resolution_t *resolution) +{ + list->cnt++; + list->resolutions = + realloc(list->resolutions, (list->cnt + 1) * sizeof(resolution_t)); + if (!list->resolutions) { + err_capture(&err, SEV_FATAL, ZBAR_ERR_NOMEM, __func__, + "allocating resources"); + } + list->resolutions[list->cnt - 1] = *resolution; + memset(&list->resolutions[list->cnt], 0, sizeof(resolution_t)); +} + +void get_closest_resolution(resolution_t *resolution, resolution_list_t *list) +{ + resolution_t *test_res; + long min_diff = 0; + long idx_best = -1; // the index of best resolution in resolutions + int i = 0; + for (test_res = list->resolutions; !is_struct_null(test_res); test_res++) { + long diff; + if (resolution->cx) { + diff = test_res->cx - resolution->cx; + if (diff < 0) + diff = -diff; + } else { + // empty resolution, looking for the biggest + diff = -test_res->cx; + } + if (idx_best < 0 || diff < min_diff) { + idx_best = i; + min_diff = diff; + } + i++; + } + + if (idx_best >= 0) { + *resolution = list->resolutions[idx_best]; + } +} + +int is_struct_null_fun(const void *pdata, const int len) +{ + int i; + for (i = 0; i < len; i++) { + if (((char *)pdata)[i] != 0) { + return 0; + } + } + return 1; +} diff --git a/zbar/misc.h b/zbar/misc.h new file mode 100644 index 0000000..d6e7d96 --- /dev/null +++ b/zbar/misc.h @@ -0,0 +1,54 @@ +/*------------------------------------------------------------------------ + * Copyright 2012 (c) Jarek Czekalski <jarekczek@poczta.onet.pl> + * + * 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 + *------------------------------------------------------------------------*/ +#ifndef _MISC_H_ +#define _MISC_H_ + +struct resolution_s { + long cx; + long cy; +}; +typedef struct resolution_s resolution_t; + +struct resolution_list_s { + resolution_t *resolutions; + long cnt; +}; +typedef struct resolution_list_s resolution_list_t; + +void resolution_list_init(resolution_list_t *list); +void resolution_list_cleanup(resolution_list_t *list); +void resolution_list_add(resolution_list_t *list, resolution_t *resolution); +/// Fill <code>resolution</code> with the closest resolution found in +/// <code>list</code>. +/** If <code>list</code> is empty, + * the <code>resolution</code> is unchanged. + * If <code>resolution</code> is empty, + * the biggest resolution is chosen. */ +void get_closest_resolution(resolution_t *resolution, resolution_list_t *list); + +/// Returns 1 if the struct is null, otherwise 0 +int is_struct_null_fun(const void *pdata, const int len); +/// Returns 1 if the struct is null, otherwise 0 +#define is_struct_null(pdata) is_struct_null_fun(pdata, sizeof(*pdata)) + +#endif diff --git a/zbar/mutex.h b/zbar/mutex.h new file mode 100644 index 0000000..4514b88 --- /dev/null +++ b/zbar/mutex.h @@ -0,0 +1,160 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (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 + *------------------------------------------------------------------------*/ +#ifndef _ZBAR_MUTEX_H_ +#define _ZBAR_MUTEX_H_ + +#include "config.h" + +#include <assert.h> + +/* simple platform mutex abstraction + */ + +#if defined(_WIN32) + +#include <windows.h> + +#define DEBUG_LOCKS +#ifdef DEBUG_LOCKS + +typedef struct zbar_mutex_s { + int count; + CRITICAL_SECTION mutex; +} zbar_mutex_t; + +static inline int _zbar_mutex_init(zbar_mutex_t *lock) +{ + lock->count = 1; + InitializeCriticalSection(&lock->mutex); + return (0); +} + +static inline void _zbar_mutex_destroy(zbar_mutex_t *lock) +{ + DeleteCriticalSection(&lock->mutex); +} + +static inline int _zbar_mutex_lock(zbar_mutex_t *lock) +{ + EnterCriticalSection(&lock->mutex); + if (lock->count++ < 1) + assert(0); + return (0); +} + +static inline int _zbar_mutex_unlock(zbar_mutex_t *lock) +{ + if (lock->count-- <= 1) + assert(0); + LeaveCriticalSection(&lock->mutex); + return (0); +} + +#else + +typedef CRITICAL_SECTION zbar_mutex_t; + +static inline int _zbar_mutex_init(zbar_mutex_t *lock) +{ + InitializeCriticalSection(lock); + return (0); +} + +static inline void _zbar_mutex_destroy(zbar_mutex_t *lock) +{ + DeleteCriticalSection(lock); +} + +static inline int _zbar_mutex_lock(zbar_mutex_t *lock) +{ + EnterCriticalSection(lock); + return (0); +} + +static inline int _zbar_mutex_unlock(zbar_mutex_t *lock) +{ + LeaveCriticalSection(lock); + return (0); +} + +#endif + +#elif defined(HAVE_LIBPTHREAD) + +#include <pthread.h> + +typedef pthread_mutex_t zbar_mutex_t; + +static inline int _zbar_mutex_init(zbar_mutex_t *lock) +{ +#ifdef DEBUG_LOCKS + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); + int rc = pthread_mutex_init(lock, &attr); + pthread_mutexattr_destroy(&attr); + return (rc); +#else + return (pthread_mutex_init(lock, NULL)); +#endif +} + +static inline void _zbar_mutex_destroy(zbar_mutex_t *lock) +{ + pthread_mutex_destroy(lock); +} + +static inline int _zbar_mutex_lock(zbar_mutex_t *lock) +{ + int rc = pthread_mutex_lock(lock); +#ifdef DEBUG_LOCKS + assert(!rc); +#endif + /* FIXME save system code */ + /*rc = err_capture(proc, SEV_ERROR, ZBAR_ERR_LOCKING, __func__, + "unable to lock processor");*/ + return (rc); +} + +static inline int _zbar_mutex_unlock(zbar_mutex_t *lock) +{ + int rc = pthread_mutex_unlock(lock); +#ifdef DEBUG_LOCKS + assert(!rc); +#endif + /* FIXME save system code */ + return (rc); +} + +#else + +typedef int zbar_mutex_t[0]; + +#define _zbar_mutex_init(l) -1 +#define _zbar_mutex_destroy(l) +#define _zbar_mutex_lock(l) 0 +#define _zbar_mutex_unlock(l) 0 + +#endif + +#endif diff --git a/zbar/processor.c b/zbar/processor.c new file mode 100644 index 0000000..cecd182 --- /dev/null +++ b/zbar/processor.c @@ -0,0 +1,727 @@ +/*------------------------------------------------------------------------ + * 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 "processor.h" +#include "image.h" +#include "img_scanner.h" +#include "window.h" + +static inline int proc_enter(zbar_processor_t *proc) +{ + _zbar_mutex_lock(&proc->mutex); + return (_zbar_processor_lock(proc)); +} + +static inline int proc_leave(zbar_processor_t *proc) +{ + int rc = _zbar_processor_unlock(proc, 0); + _zbar_mutex_unlock(&proc->mutex); + return (rc); +} + +static inline int proc_open(zbar_processor_t *proc) +{ + /* arbitrary default */ + int width = 640, height = 480; + if (proc->video) { + width = zbar_video_get_width(proc->video); + height = zbar_video_get_height(proc->video); + } + return (_zbar_processor_open(proc, "zbar barcode reader", width, height)); +} + +/* API lock is already held */ +int _zbar_process_image(zbar_processor_t *proc, zbar_image_t *img) +{ + int rc; + uint32_t force_fmt = proc->force_output; + if (img) { + uint32_t format; + zbar_image_t *tmp; + int nsyms; + if (proc->dumping) { + zbar_image_write(proc->window->image, "zbar"); + proc->dumping = 0; + } + + format = zbar_image_get_format(img); + zprintf(16, "processing: %.4s(%08" PRIx32 ") %dx%d @%p\n", + (char *)&format, format, zbar_image_get_width(img), + zbar_image_get_height(img), zbar_image_get_data(img)); + + /* FIXME locking all other interfaces while processing is conservative + * but easier for now and we don't expect this to take long... + */ + tmp = zbar_image_convert(img, fourcc('Y', '8', '0', '0')); + if (!tmp) + goto error; + + if (proc->syms) { + zbar_symbol_set_ref(proc->syms, -1); + proc->syms = NULL; + } + zbar_image_scanner_recycle_image(proc->scanner, img); + nsyms = zbar_scan_image(proc->scanner, tmp); + _zbar_image_swap_symbols(img, tmp); + + zbar_image_destroy(tmp); + tmp = NULL; + if (nsyms < 0) + goto error; + + proc->syms = zbar_image_scanner_get_results(proc->scanner); + if (proc->syms) + zbar_symbol_set_ref(proc->syms, 1); + + if (_zbar_verbosity >= 8) { + const zbar_symbol_t *sym = zbar_image_first_symbol(img); + while (sym) { + zbar_symbol_type_t type = zbar_symbol_get_type(sym); + int count = zbar_symbol_get_count(sym); + zprintf(8, "%s: %s (%d pts) (dir=%d) (q=%d) (%s)\n", + zbar_get_symbol_name(type), zbar_symbol_get_data(sym), + zbar_symbol_get_loc_size(sym), + zbar_symbol_get_orientation(sym), + zbar_symbol_get_quality(sym), + (count < 0) ? "uncertain" : + (count > 0) ? "duplicate" : + "new"); + sym = zbar_symbol_next(sym); + } + } + + if (nsyms) { + /* FIXME only call after filtering */ + _zbar_mutex_lock(&proc->mutex); + _zbar_processor_notify(proc, EVENT_OUTPUT); + _zbar_mutex_unlock(&proc->mutex); + if (proc->handler) + proc->handler(img, proc->userdata); + } + + if (force_fmt) { + zbar_symbol_set_t *syms = img->syms; + img = zbar_image_convert(img, force_fmt); + if (!img) + goto error; + img->syms = syms; + zbar_symbol_set_ref(syms, 1); + } + } + + /* display to window if enabled */ + rc = 0; + if (proc->window) { + if ((rc = zbar_window_draw(proc->window, img))) + err_copy(proc, proc->window); + _zbar_processor_invalidate(proc); + } + + if (force_fmt && img) + zbar_image_destroy(img); + return (rc); + +error: + return (err_capture(proc, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, __func__, + "unknown image format")); +} + +int _zbar_processor_handle_input(zbar_processor_t *proc, int input) +{ + int event = EVENT_INPUT; + switch (input) { + case -1: + event |= EVENT_CANCELED; + _zbar_processor_set_visible(proc, 0); + err_capture(proc, SEV_WARNING, ZBAR_ERR_CLOSED, __func__, + "user closed display window"); + break; + + case 'd': + proc->dumping = 1; + return (0); + + case '+': + case '=': + if (proc->window) { + int ovl = zbar_window_get_overlay(proc->window); + zbar_window_set_overlay(proc->window, ovl + 1); + } + break; + + case '-': + if (proc->window) { + int ovl = zbar_window_get_overlay(proc->window); + zbar_window_set_overlay(proc->window, ovl - 1); + } + break; + } + + _zbar_mutex_lock(&proc->mutex); + proc->input = input; + if (input == -1 && proc->visible && proc->streaming) + /* also cancel outstanding output waiters */ + event |= EVENT_OUTPUT; + _zbar_processor_notify(proc, event); + _zbar_mutex_unlock(&proc->mutex); + return (input); +} + +#ifdef ZTHREAD + +static ZTHREAD proc_video_thread(void *arg) +{ + zbar_processor_t *proc = arg; + zbar_thread_t *thread = &proc->video_thread; + + _zbar_mutex_lock(&proc->mutex); + _zbar_thread_init(thread); + zprintf(4, "spawned video thread\n"); + + while (thread->started) { + zbar_image_t *img; + /* wait for video stream to be active */ + while (thread->started && !proc->streaming) + _zbar_event_wait(&thread->notify, &proc->mutex, NULL); + if (!thread->started) + break; + + /* blocking capture image from video */ + _zbar_mutex_unlock(&proc->mutex); + img = zbar_video_next_image(proc->video); + _zbar_mutex_lock(&proc->mutex); + + if (!img && !proc->streaming) + continue; + else if (!img) + /* FIXME could abort streaming and keep running? */ + break; + + /* acquire API lock */ + _zbar_processor_lock(proc); + _zbar_mutex_unlock(&proc->mutex); + + if (thread->started && proc->streaming) + _zbar_process_image(proc, img); + + zbar_image_destroy(img); + + _zbar_mutex_lock(&proc->mutex); + /* release API lock */ + _zbar_processor_unlock(proc, 0); + } + + thread->running = 0; + _zbar_event_trigger(&thread->activity); + _zbar_mutex_unlock(&proc->mutex); + return (0); +} + +static ZTHREAD proc_input_thread(void *arg) +{ + int rc = 0; + zbar_processor_t *proc = arg; + zbar_thread_t *thread = &proc->input_thread; + if (proc->window && proc_open(proc)) + goto done; + + _zbar_mutex_lock(&proc->mutex); + thread->running = 1; + _zbar_event_trigger(&thread->activity); + zprintf(4, "spawned input thread\n"); + + rc = 0; + while (thread->started && rc >= 0) { + _zbar_mutex_unlock(&proc->mutex); + rc = _zbar_processor_input_wait(proc, &thread->notify, -1); + _zbar_mutex_lock(&proc->mutex); + } + + _zbar_mutex_unlock(&proc->mutex); + _zbar_processor_close(proc); + _zbar_mutex_lock(&proc->mutex); + +done: + thread->running = 0; + _zbar_event_trigger(&thread->activity); + _zbar_mutex_unlock(&proc->mutex); + return (0); +} + +#endif + +zbar_processor_t *zbar_processor_create(int threaded) +{ + zbar_processor_t *proc = calloc(1, sizeof(zbar_processor_t)); + if (!proc) + return (NULL); + err_init(&proc->err, ZBAR_MOD_PROCESSOR); + + proc->scanner = zbar_image_scanner_create(); + if (!proc->scanner) { + free(proc); + return (NULL); + } + + proc->threaded = !_zbar_mutex_init(&proc->mutex) && threaded; + _zbar_processor_init(proc); + return (proc); +} + +void zbar_processor_destroy(zbar_processor_t *proc) +{ + proc_waiter_t *w, *next; + + zbar_processor_init(proc, NULL, 0); + + if (proc->syms) { + zbar_symbol_set_ref(proc->syms, -1); + proc->syms = NULL; + } + if (proc->scanner) { + zbar_image_scanner_destroy(proc->scanner); + proc->scanner = NULL; + } + + _zbar_mutex_destroy(&proc->mutex); + _zbar_processor_cleanup(proc); + + assert(!proc->wait_head); + assert(!proc->wait_tail); + assert(!proc->wait_next); + + for (w = proc->free_waiter; w; w = next) { + next = w->next; + _zbar_event_destroy(&w->notify); + free(w); + } + + err_cleanup(&proc->err); + free(proc); +} + +int zbar_processor_init(zbar_processor_t *proc, const char *dev, + int enable_display) +{ + int rc = 0; + int video_threaded, input_threaded; + + if (proc->video) + zbar_processor_set_active(proc, 0); + + if (proc->window && !proc->input_thread.started) + _zbar_processor_close(proc); + + _zbar_mutex_lock(&proc->mutex); + _zbar_thread_stop(&proc->input_thread, &proc->mutex); + _zbar_thread_stop(&proc->video_thread, &proc->mutex); + + _zbar_processor_lock(proc); + _zbar_mutex_unlock(&proc->mutex); + + if (proc->window) { + zbar_window_destroy(proc->window); + proc->window = NULL; + } + + rc = 0; + if (proc->video) { + zbar_video_destroy(proc->video); + proc->video = NULL; + } + + if (!dev && !enable_display) + /* nothing to do */ + goto done; + + if (enable_display) { + proc->window = zbar_window_create(); + if (!proc->window) { + rc = err_capture(proc, SEV_FATAL, ZBAR_ERR_NOMEM, __func__, + "allocating window resources"); + goto done; + } + } + + if (dev) { + proc->video = zbar_video_create(); + if (!proc->video) { + rc = err_capture(proc, SEV_FATAL, ZBAR_ERR_NOMEM, __func__, + "allocating video resources"); + goto done; + } + if (proc->req_width || proc->req_height) + zbar_video_request_size(proc->video, proc->req_width, + proc->req_height); + if (proc->req_intf) + zbar_video_request_interface(proc->video, proc->req_intf); + if ((proc->req_iomode && + zbar_video_request_iomode(proc->video, proc->req_iomode)) || + zbar_video_open(proc->video, dev)) { + rc = err_copy(proc, proc->video); + goto done; + } + } + + /* spawn blocking video thread */ + video_threaded = + (proc->threaded && proc->video && zbar_video_get_fd(proc->video) < 0); + if (video_threaded && + _zbar_thread_start(&proc->video_thread, proc_video_thread, proc, + &proc->mutex)) { + rc = err_capture(proc, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "spawning video thread"); + goto done; + } + + /* spawn input monitor thread */ + input_threaded = + (proc->threaded && (proc->window || (proc->video && !video_threaded))); + if (input_threaded && + _zbar_thread_start(&proc->input_thread, proc_input_thread, proc, + &proc->mutex)) { + rc = err_capture(proc, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "spawning input thread"); + goto done; + } + + if (proc->window && !input_threaded && (rc = proc_open(proc))) + goto done; + + if (proc->video && proc->force_input) { + if (zbar_video_init(proc->video, proc->force_input)) + rc = err_copy(proc, proc->video); + } else if (proc->video) { + int retry = -1; + if (proc->window) { + retry = zbar_negotiate_format(proc->video, proc->window); + if (retry) + fprintf(stderr, + "WARNING: no compatible input to output format\n" + "...trying again with output disabled\n"); + } + if (retry) + retry = zbar_negotiate_format(proc->video, NULL); + + if (retry) { + zprintf(1, "ERROR: no compatible %s format\n", + (proc->video) ? "video input" : "window output"); + rc = err_capture(proc, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, __func__, + "no compatible image format"); + } + } + +done: + _zbar_mutex_lock(&proc->mutex); + proc_leave(proc); + return (rc); +} + +zbar_image_data_handler_t * +zbar_processor_set_data_handler(zbar_processor_t *proc, + zbar_image_data_handler_t *handler, + const void *userdata) +{ + zbar_image_data_handler_t *result = NULL; + proc_enter(proc); + + result = proc->handler; + proc->handler = handler; + proc->userdata = userdata; + + proc_leave(proc); + return (result); +} + +void zbar_processor_set_userdata(zbar_processor_t *proc, void *userdata) +{ + _zbar_mutex_lock(&proc->mutex); + proc->userdata = userdata; + _zbar_mutex_unlock(&proc->mutex); +} + +void *zbar_processor_get_userdata(const zbar_processor_t *proc) +{ + void *userdata; + zbar_processor_t *ncproc = (zbar_processor_t *)proc; + _zbar_mutex_lock(&ncproc->mutex); + userdata = (void *)ncproc->userdata; + _zbar_mutex_unlock(&ncproc->mutex); + return (userdata); +} + +int zbar_processor_set_config(zbar_processor_t *proc, zbar_symbol_type_t sym, + zbar_config_t cfg, int val) +{ + int rc; + proc_enter(proc); + rc = zbar_image_scanner_set_config(proc->scanner, sym, cfg, val); + proc_leave(proc); + return (rc); +} + +int zbar_processor_set_control(zbar_processor_t *proc, const char *control_name, + int value) +{ + int rc; + int value_before, value_after; + proc_enter(proc); + if (_zbar_verbosity >= 4) + if (zbar_video_get_control(proc->video, control_name, &value_before) == + 0) + zprintf(0, "value of %s before a set: %d\n", control_name, + value_before); + rc = zbar_video_set_control(proc->video, control_name, value); + if (_zbar_verbosity >= 4) + if (zbar_video_get_control(proc->video, control_name, &value_after) == + 0) + zprintf(0, "value of %s after a set: %d\n", control_name, + value_after); + proc_leave(proc); + return (rc); +} + +int zbar_processor_get_control(zbar_processor_t *proc, const char *control_name, + int *value) +{ + int rc; + proc_enter(proc); + rc = zbar_video_get_control(proc->video, control_name, value); + proc_leave(proc); + return (rc); +} + +int zbar_processor_request_size(zbar_processor_t *proc, unsigned width, + unsigned height) +{ + proc_enter(proc); + proc->req_width = width; + proc->req_height = height; + proc_leave(proc); + return (0); +} + +int zbar_processor_request_interface(zbar_processor_t *proc, int ver) +{ + proc_enter(proc); + proc->req_intf = ver; + proc_leave(proc); + return (0); +} + +int zbar_processor_request_iomode(zbar_processor_t *proc, int iomode) +{ + proc_enter(proc); + proc->req_iomode = iomode; + proc_leave(proc); + return (0); +} + +int zbar_processor_force_format(zbar_processor_t *proc, unsigned long input, + unsigned long output) +{ + proc_enter(proc); + proc->force_input = input; + proc->force_output = output; + proc_leave(proc); + return (0); +} + +int zbar_processor_is_visible(zbar_processor_t *proc) +{ + int visible; + proc_enter(proc); + visible = proc->window && proc->visible; + proc_leave(proc); + return (visible); +} + +int zbar_processor_set_visible(zbar_processor_t *proc, int visible) +{ + int rc = 0; + proc_enter(proc); + _zbar_mutex_unlock(&proc->mutex); + + if (proc->window) { + if (proc->video) + rc = _zbar_processor_set_size(proc, + zbar_video_get_width(proc->video), + zbar_video_get_height(proc->video)); + if (!rc) + rc = _zbar_processor_set_visible(proc, visible); + + if (!rc) + proc->visible = (visible != 0); + } else if (visible) + rc = err_capture(proc, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "processor display window not initialized"); + + _zbar_mutex_lock(&proc->mutex); + proc_leave(proc); + return (rc); +} + +const zbar_symbol_set_t * +zbar_processor_get_results(const zbar_processor_t *proc) +{ + const zbar_symbol_set_t *syms; + zbar_processor_t *ncproc = (zbar_processor_t *)proc; + proc_enter(ncproc); + syms = proc->syms; + if (syms) + zbar_symbol_set_ref(syms, 1); + proc_leave(ncproc); + return (syms); +} + +int zbar_processor_user_wait(zbar_processor_t *proc, int timeout) +{ + int rc = -1; + + proc_enter(proc); + _zbar_mutex_unlock(&proc->mutex); + + if (proc->visible || proc->streaming || timeout >= 0) { + zbar_timer_t timer; + rc = _zbar_processor_wait(proc, EVENT_INPUT, + _zbar_timer_init(&timer, timeout)); + } + + if (!proc->visible) + rc = err_capture(proc, SEV_WARNING, ZBAR_ERR_CLOSED, __func__, + "display window not available for input"); + + if (rc > 0) + rc = proc->input; + + _zbar_mutex_lock(&proc->mutex); + proc_leave(proc); + return (rc); +} + +int zbar_processor_set_active(zbar_processor_t *proc, int active) +{ + int rc; + proc_enter(proc); + + if (!proc->video) { + rc = err_capture(proc, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "video input not initialized"); + goto done; + } + _zbar_mutex_unlock(&proc->mutex); + + zbar_image_scanner_enable_cache(proc->scanner, active); + + rc = zbar_video_enable(proc->video, active); + if (!rc) { + _zbar_mutex_lock(&proc->mutex); + proc->streaming = active; + _zbar_mutex_unlock(&proc->mutex); + rc = _zbar_processor_enable(proc); + } else + err_copy(proc, proc->video); + + if (!proc->streaming && proc->window) { + if (zbar_window_draw(proc->window, NULL) && !rc) + rc = err_copy(proc, proc->window); + _zbar_processor_invalidate(proc); + } + + _zbar_mutex_lock(&proc->mutex); + if (proc->video_thread.started) + _zbar_event_trigger(&proc->video_thread.notify); + +done: + proc_leave(proc); + return (rc); +} + +int zbar_process_one(zbar_processor_t *proc, int timeout) +{ + int streaming, rc; + zbar_timer_t timer; + + proc_enter(proc); + streaming = proc->streaming; + _zbar_mutex_unlock(&proc->mutex); + + rc = 0; + if (!proc->video) { + rc = err_capture(proc, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "video input not initialized"); + goto done; + } + + if (!streaming) { + rc = zbar_processor_set_active(proc, 1); + if (rc) + goto done; + } + + rc = _zbar_processor_wait(proc, EVENT_OUTPUT, + _zbar_timer_init(&timer, timeout)); + + if (!streaming && zbar_processor_set_active(proc, 0)) + rc = -1; + +done: + _zbar_mutex_lock(&proc->mutex); + proc_leave(proc); + return (rc); +} + +int zbar_process_image(zbar_processor_t *proc, zbar_image_t *img) +{ + int rc = 0; + + proc_enter(proc); + _zbar_mutex_unlock(&proc->mutex); + + if (img && proc->window) + rc = _zbar_processor_set_size(proc, zbar_image_get_width(img), + zbar_image_get_height(img)); + if (!rc) { + zbar_image_scanner_enable_cache(proc->scanner, 0); + zbar_image_scanner_request_dbus(proc->scanner, proc->is_dbus_enabled); + rc = _zbar_process_image(proc, img); + if (proc->streaming) + zbar_image_scanner_enable_cache(proc->scanner, 1); + } + + _zbar_mutex_lock(&proc->mutex); + proc_leave(proc); + return (rc); +} + +int zbar_processor_request_dbus(zbar_processor_t *proc, int req_dbus_enabled) +{ +#ifdef HAVE_DBUS + proc_enter(proc); + proc->is_dbus_enabled = req_dbus_enabled; + proc_leave(proc); + return (0); +#else + return (1); +#endif +} diff --git a/zbar/processor.h b/zbar/processor.h new file mode 100644 index 0000000..a8289e0 --- /dev/null +++ b/zbar/processor.h @@ -0,0 +1,128 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (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 + *------------------------------------------------------------------------*/ +#ifndef _PROCESSOR_H_ +#define _PROCESSOR_H_ + +#include "config.h" +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <zbar.h> +#include "error.h" +#include "event.h" +#include "thread.h" + +/* max time to wait for input before looking for the next frame. + * only used in unthreaded mode with blocking (non-pollable) video. + * NB subject to precision of whatever timer is in use + */ +#define MAX_INPUT_BLOCK 15 /*ms*/ + +/* platform specific state wrapper */ +typedef struct processor_state_s processor_state_t; + +/* specific notification tracking */ +typedef struct proc_waiter_s { + struct proc_waiter_s *next; + zbar_event_t notify; + zbar_thread_id_t requester; + unsigned events; +} proc_waiter_t; + +/* high-level API events */ +#define EVENT_INPUT 0x01 /* user input */ +#define EVENT_OUTPUT 0x02 /* decoded output data available */ +#define EVENT_CANCELED 0x80 /* cancellation flag */ +#define EVENTS_PENDING (EVENT_INPUT | EVENT_OUTPUT) + +struct zbar_processor_s { + errinfo_t err; /* error reporting */ + const void *userdata; /* application data */ + + zbar_video_t *video; /* input video device abstraction */ + zbar_window_t *window; /* output window abstraction */ + zbar_image_scanner_t *scanner; /* barcode scanner */ + + zbar_image_data_handler_t *handler; /* application data handler */ + + unsigned req_width, req_height; /* application requested video size */ + int req_intf, req_iomode; /* application requested interface */ + uint32_t force_input; /* force input format (debug) */ + uint32_t force_output; /* force format conversion (debug) */ + + int input; /* user input status */ + + /* state flags */ + int threaded; + int visible; /* output window mapped to display */ + int streaming; /* video enabled */ + int dumping; /* debug image dump */ + + void *display; /* X display connection */ + unsigned long xwin; /* toplevel window */ + + zbar_thread_t input_thread; /* video input handler */ + zbar_thread_t video_thread; /* window event handler */ + + const zbar_symbol_set_t *syms; /* previous decode results */ + + zbar_mutex_t mutex; /* shared data mutex */ + + /* API serialization lock */ + int lock_level; + zbar_thread_id_t lock_owner; + proc_waiter_t *wait_head, *wait_tail, *wait_next; + proc_waiter_t *free_waiter; + + processor_state_t *state; + + int is_dbus_enabled; /* dbus enabled flag */ +}; + +/* processor lock API */ +extern int _zbar_processor_lock(zbar_processor_t *); +extern int _zbar_processor_unlock(zbar_processor_t *, int); +extern void _zbar_processor_notify(zbar_processor_t *, unsigned); +extern int _zbar_processor_wait(zbar_processor_t *, unsigned, zbar_timer_t *); + +/* platform API */ +extern int _zbar_processor_init(zbar_processor_t *); +extern int _zbar_processor_cleanup(zbar_processor_t *); +extern int _zbar_processor_input_wait(zbar_processor_t *, zbar_event_t *, int); +extern int _zbar_processor_enable(zbar_processor_t *); + +extern int _zbar_process_image(zbar_processor_t *, zbar_image_t *); +extern int _zbar_processor_handle_input(zbar_processor_t *, int); + +/* windowing platform API */ +extern int _zbar_processor_open(zbar_processor_t *, char *, unsigned, unsigned); +extern int _zbar_processor_close(zbar_processor_t *); +extern int _zbar_processor_set_visible(zbar_processor_t *, int); +extern int _zbar_processor_set_size(zbar_processor_t *, unsigned, unsigned); +extern int _zbar_processor_invalidate(zbar_processor_t *); + +#endif diff --git a/zbar/processor/lock.c b/zbar/processor/lock.c new file mode 100644 index 0000000..2668b4e --- /dev/null +++ b/zbar/processor/lock.c @@ -0,0 +1,228 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (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 <assert.h> +#include "processor.h" + +/* the processor api lock is a recursive mutex with added capabilities + * to completely drop all lock levels before blocking and atomically + * unblock a waiting set. the lock is implemented using a variation + * of the "specific notification pattern" [cargill], which makes it + * easy to provide these features across platforms with consistent, + * predictable semantics. probably overkill, but additional overhead + * associated with this mechanism should fall in the noise, as locks + * are only exchanged O(frame/image) + * + * [cargill] + * http://www.profcon.com/profcon/cargill/jgf/9809/SpecificNotification.html + */ + +static inline proc_waiter_t *proc_waiter_queue(zbar_processor_t *proc) +{ + proc_waiter_t *waiter = proc->free_waiter; + if (waiter) { + proc->free_waiter = waiter->next; + waiter->events = 0; + } else { + waiter = calloc(1, sizeof(proc_waiter_t)); + _zbar_event_init(&waiter->notify); + } + + waiter->next = NULL; + waiter->requester = _zbar_thread_self(); + + if (proc->wait_head) + proc->wait_tail->next = waiter; + else + proc->wait_head = waiter; + proc->wait_tail = waiter; + return (waiter); +} + +static inline proc_waiter_t *proc_waiter_dequeue(zbar_processor_t *proc) +{ + proc_waiter_t *prev = proc->wait_next, *waiter; + if (prev) + waiter = prev->next; + else + waiter = proc->wait_head; + while (waiter && (waiter->events & EVENTS_PENDING)) { + prev = waiter; + proc->wait_next = waiter; + waiter = waiter->next; + } + + if (waiter) { + if (prev) + prev->next = waiter->next; + else + proc->wait_head = waiter->next; + if (!waiter->next) + proc->wait_tail = prev; + waiter->next = NULL; + + proc->lock_level = 1; + proc->lock_owner = waiter->requester; + } + return (waiter); +} + +static inline void proc_waiter_release(zbar_processor_t *proc, + proc_waiter_t *waiter) +{ + if (waiter) { + waiter->next = proc->free_waiter; + proc->free_waiter = waiter; + } +} + +int _zbar_processor_lock(zbar_processor_t *proc) +{ + proc_waiter_t *waiter; + if (!proc->lock_level) { + proc->lock_owner = _zbar_thread_self(); + proc->lock_level = 1; + return (0); + } + + if (_zbar_thread_is_self(proc->lock_owner)) { + proc->lock_level++; + return (0); + } + + waiter = proc_waiter_queue(proc); + _zbar_event_wait(&waiter->notify, &proc->mutex, NULL); + + assert(proc->lock_level == 1); + assert(_zbar_thread_is_self(proc->lock_owner)); + + proc_waiter_release(proc, waiter); + return (0); +} + +int _zbar_processor_unlock(zbar_processor_t *proc, int all) +{ + assert(proc->lock_level > 0); + assert(_zbar_thread_is_self(proc->lock_owner)); + + if (all) + proc->lock_level = 0; + else + proc->lock_level--; + + if (!proc->lock_level) { + proc_waiter_t *waiter = proc_waiter_dequeue(proc); + if (waiter) + _zbar_event_trigger(&waiter->notify); + } + return (0); +} + +void _zbar_processor_notify(zbar_processor_t *proc, unsigned events) +{ + proc_waiter_t *waiter; + proc->wait_next = NULL; + for (waiter = proc->wait_head; waiter; waiter = waiter->next) + waiter->events = + ((waiter->events & ~events) | (events & EVENT_CANCELED)); + + if (!proc->lock_level) { + waiter = proc_waiter_dequeue(proc); + if (waiter) + _zbar_event_trigger(&waiter->notify); + } +} + +static inline int proc_wait_unthreaded(zbar_processor_t *proc, + proc_waiter_t *waiter, + zbar_timer_t *timeout) +{ + int rc; + int blocking = proc->streaming && zbar_video_get_fd(proc->video) < 0; + _zbar_mutex_unlock(&proc->mutex); + + rc = 1; + while (rc > 0 && (waiter->events & EVENTS_PENDING)) { + int reltime; + /* FIXME lax w/the locking (though shouldn't matter...) */ + if (blocking) { + zbar_image_t *img = zbar_video_next_image(proc->video); + if (!img) { + rc = -1; + break; + } + + /* FIXME reacquire API lock! (refactor w/video thread?) */ + _zbar_mutex_lock(&proc->mutex); + _zbar_process_image(proc, img); + zbar_image_destroy(img); + _zbar_mutex_unlock(&proc->mutex); + } + reltime = _zbar_timer_check(timeout); + if (blocking && (reltime < 0 || reltime > MAX_INPUT_BLOCK)) + reltime = MAX_INPUT_BLOCK; + rc = _zbar_processor_input_wait(proc, NULL, reltime); + } + _zbar_mutex_lock(&proc->mutex); + return (rc); +} + +int _zbar_processor_wait(zbar_processor_t *proc, unsigned events, + zbar_timer_t *timeout) +{ + int save_level; + proc_waiter_t *waiter; + int rc; + + _zbar_mutex_lock(&proc->mutex); + save_level = proc->lock_level; + waiter = proc_waiter_queue(proc); + waiter->events = events & EVENTS_PENDING; + + _zbar_processor_unlock(proc, 1); + if (proc->threaded) + rc = _zbar_event_wait(&waiter->notify, &proc->mutex, timeout); + else + rc = proc_wait_unthreaded(proc, waiter, timeout); + + if (rc <= 0 || !proc->threaded) { + /* reacquire api lock */ + waiter->events &= EVENT_CANCELED; + proc->wait_next = NULL; + if (!proc->lock_level) { + proc_waiter_t *w = proc_waiter_dequeue(proc); + assert(w == waiter); + } else + _zbar_event_wait(&waiter->notify, &proc->mutex, NULL); + } + if (rc > 0 && (waiter->events & EVENT_CANCELED)) + rc = -1; + + assert(proc->lock_level == 1); + assert(_zbar_thread_is_self(proc->lock_owner)); + + proc->lock_level = save_level; + proc_waiter_release(proc, waiter); + _zbar_mutex_unlock(&proc->mutex); + return (rc); +} diff --git a/zbar/processor/null.c b/zbar/processor/null.c new file mode 100644 index 0000000..5ca8d42 --- /dev/null +++ b/zbar/processor/null.c @@ -0,0 +1,57 @@ +/*------------------------------------------------------------------------ + * Copyright 2008-2009 (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 "processor.h" + +static inline int null_error(void *m, const char *func) +{ + return (err_capture(m, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, func, + "not compiled with output window support")); +} + +int _zbar_processor_open(zbar_processor_t *proc, char *name, unsigned w, + unsigned h) +{ + return (null_error(proc, __func__)); +} + +int _zbar_processor_close(zbar_processor_t *proc) +{ + return (null_error(proc, __func__)); +} + +int _zbar_processor_set_visible(zbar_processor_t *proc, int vis) +{ + return (null_error(proc, __func__)); +} + +int _zbar_processor_set_size(zbar_processor_t *proc, unsigned width, + unsigned height) +{ + return (null_error(proc, __func__)); +} + +int _zbar_processor_invalidate(zbar_processor_t *proc) +{ + return (null_error(proc, __func__)); +} diff --git a/zbar/processor/posix.c b/zbar/processor/posix.c new file mode 100644 index 0000000..06b8d9a --- /dev/null +++ b/zbar/processor/posix.c @@ -0,0 +1,325 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (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 "posix.h" +#include <assert.h> +#include <errno.h> +#include <unistd.h> +#include "processor.h" + +static inline int proc_sleep(int timeout) +{ + assert(timeout > 0); + struct timespec sleepns, remns; + sleepns.tv_sec = timeout / 1000; + sleepns.tv_nsec = (timeout % 1000) * 1000000; + while (nanosleep(&sleepns, &remns) && errno == EINTR) + sleepns = remns; + return (1); +} + +int _zbar_event_init(zbar_event_t *event) +{ + event->state = 0; + event->pollfd = -1; +#ifdef HAVE_LIBPTHREAD + pthread_cond_init(&event->cond, NULL); +#endif + return (0); +} + +void _zbar_event_destroy(zbar_event_t *event) +{ + event->state = -1; + event->pollfd = -1; +#ifdef HAVE_LIBPTHREAD + pthread_cond_destroy(&event->cond); +#endif +} + +/* lock must be held */ +void _zbar_event_trigger(zbar_event_t *event) +{ + event->state = 1; +#ifdef HAVE_LIBPTHREAD + pthread_cond_broadcast(&event->cond); +#endif + if (event->pollfd >= 0) { + unsigned i = 0; /* unused */ + if (write(event->pollfd, &i, sizeof(unsigned)) < 0) + perror(""); + event->pollfd = -1; + } +} + +#ifdef HAVE_LIBPTHREAD + +/* lock must be held */ +int _zbar_event_wait(zbar_event_t *event, zbar_mutex_t *lock, + zbar_timer_t *timeout) +{ + int rc = 0; + while (!rc && !event->state) { + if (!timeout) + rc = pthread_cond_wait(&event->cond, lock); + else { + struct timespec *timer; +#if _POSIX_TIMERS > 0 + timer = timeout; +#else + struct timespec tmp; + tmp.tv_sec = timeout->tv_sec; + tmp.tv_nsec = timeout->tv_usec * 1000; + timer = &tmp; +#endif + rc = pthread_cond_timedwait(&event->cond, lock, timer); + } + } + + /* consume/reset event */ + event->state = 0; + + if (!rc) + return (1); /* got event */ + if (rc == ETIMEDOUT) + return (0); /* timed out */ + return (-1); /* error (FIXME save info) */ +} + +int _zbar_thread_start(zbar_thread_t *thr, zbar_thread_proc_t *proc, void *arg, + zbar_mutex_t *lock) +{ + if (thr->started || thr->running) + return (-1 /*FIXME*/); + thr->started = 1; + _zbar_event_init(&thr->notify); + _zbar_event_init(&thr->activity); + + int rc = 0; + _zbar_mutex_lock(lock); + if (pthread_create(&thr->tid, NULL, proc, arg) || + _zbar_event_wait(&thr->activity, lock, NULL) < 0 || !thr->running) { + thr->started = 0; + _zbar_event_destroy(&thr->notify); + _zbar_event_destroy(&thr->activity); + /*rc = err_capture_num(proc, SEV_ERROR, ZBAR_ERR_SYSTEM, + __func__, "spawning thread", rc);*/ + rc = -1 /*FIXME*/; + } + _zbar_mutex_unlock(lock); + return (rc); +} + +int _zbar_thread_stop(zbar_thread_t *thr, zbar_mutex_t *lock) +{ + if (thr->started) { + thr->started = 0; + _zbar_event_trigger(&thr->notify); + while (thr->running) + /* FIXME time out and abandon? */ + _zbar_event_wait(&thr->activity, lock, NULL); + pthread_join(thr->tid, NULL); + _zbar_event_destroy(&thr->notify); + _zbar_event_destroy(&thr->activity); + } + return (0); +} + +#else + +int _zbar_event_wait(zbar_event_t *event, zbar_mutex_t *lock, + zbar_timer_t *timeout) +{ + int rc = !event->state; + if (rc) { + if (!timeout) + /* FIXME was that error or hang? */ + return (-1); + + int sleep = _zbar_timer_check(timeout); + if (sleep) + proc_sleep(sleep); + } + + rc = !event->state; + + /* consume/reset event */ + event->state = 0; + + return (rc); +} + +#endif + +/* used by poll interface. lock is already held */ +static int proc_video_handler(zbar_processor_t *proc, int i) +{ + _zbar_mutex_lock(&proc->mutex); + _zbar_processor_lock(proc); + _zbar_mutex_unlock(&proc->mutex); + + zbar_image_t *img = NULL; + if (proc->streaming) { + /* not expected to block */ + img = zbar_video_next_image(proc->video); + if (img) + _zbar_process_image(proc, img); + } + + _zbar_mutex_lock(&proc->mutex); + _zbar_processor_unlock(proc, 0); + _zbar_mutex_unlock(&proc->mutex); + if (img) + zbar_image_destroy(img); + return (0); +} + +static inline void proc_cache_polling(processor_state_t *state) +{ + /* make a thread-local copy of polling data */ + int n = state->thr_polling.num = state->polling.num; + alloc_polls(&state->thr_polling); + memcpy(state->thr_polling.fds, state->polling.fds, + n * sizeof(struct pollfd)); + memcpy(state->thr_polling.handlers, state->polling.handlers, + n * sizeof(poll_handler_t *)); +} + +static int proc_kick_handler(zbar_processor_t *proc, int i) +{ + processor_state_t *state = proc->state; + zprintf(5, "kicking %d fds\n", state->polling.num); + + unsigned junk[2]; + int rc = read(state->kick_fds[0], junk, 2 * sizeof(unsigned)); + + assert(proc->threaded); + _zbar_mutex_lock(&proc->mutex); + proc_cache_polling(proc->state); + _zbar_mutex_unlock(&proc->mutex); + return (rc); +} + +static inline int proc_poll_inputs(zbar_processor_t *proc, int timeout) +{ + processor_state_t *state = proc->state; + if (state->pre_poll_handler) + state->pre_poll_handler(proc, -1); + + poll_desc_t *p = &state->thr_polling; + assert(p->num); + int rc = poll(p->fds, p->num, timeout); + if (rc <= 0) + /* FIXME detect and handle fatal errors (somehow) */ + return (rc); + int i; + for (i = p->num - 1; i >= 0; i--) + if (p->fds[i].revents) { + if (p->handlers[i]) + p->handlers[i](proc, i); + p->fds[i].revents = 0; /* debug */ + rc--; + } + assert(!rc); + return (1); +} + +int _zbar_processor_input_wait(zbar_processor_t *proc, zbar_event_t *event, + int timeout) +{ + processor_state_t *state = proc->state; + if (state->thr_polling.num) { + if (event) { + _zbar_mutex_lock(&proc->mutex); + event->pollfd = state->kick_fds[1]; + _zbar_mutex_unlock(&proc->mutex); + } + return (proc_poll_inputs(proc, timeout)); + } else if (timeout) + return (proc_sleep(timeout)); + return (-1); +} + +int _zbar_processor_init(zbar_processor_t *proc) +{ + processor_state_t *state = proc->state = + calloc(1, sizeof(processor_state_t)); + state->kick_fds[0] = state->kick_fds[1] = -1; + + if (proc->threaded) { + /* FIXME check errors */ + if (pipe(state->kick_fds)) + return (err_capture(proc, SEV_FATAL, ZBAR_ERR_SYSTEM, __func__, + "failed to open pipe")); + add_poll(proc, state->kick_fds[0], proc_kick_handler); + proc_cache_polling(proc->state); + } + return (0); +} + +int _zbar_processor_cleanup(zbar_processor_t *proc) +{ + processor_state_t *state = proc->state; + if (proc->threaded) { + close(state->kick_fds[0]); + close(state->kick_fds[1]); + state->kick_fds[0] = state->kick_fds[1] = -1; + } + if (state->polling.fds) { + free(state->polling.fds); + state->polling.fds = NULL; + if (!proc->threaded) + state->thr_polling.fds = NULL; + } + if (state->polling.handlers) { + free(state->polling.handlers); + state->polling.handlers = NULL; + if (!proc->threaded) + state->thr_polling.handlers = NULL; + } + if (state->thr_polling.fds) { + free(state->thr_polling.fds); + state->thr_polling.fds = NULL; + } + if (state->thr_polling.handlers) { + free(state->thr_polling.handlers); + state->thr_polling.handlers = NULL; + } + free(proc->state); + proc->state = NULL; + return (0); +} + +int _zbar_processor_enable(zbar_processor_t *proc) +{ + int vid_fd = zbar_video_get_fd(proc->video); + if (vid_fd < 0) + return (0); + + if (proc->streaming) + add_poll(proc, vid_fd, proc_video_handler); + else + remove_poll(proc, vid_fd); + /* FIXME failure recovery? */ + return (0); +} diff --git a/zbar/processor/posix.h b/zbar/processor/posix.h new file mode 100644 index 0000000..7ed05cc --- /dev/null +++ b/zbar/processor/posix.h @@ -0,0 +1,132 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (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 + *------------------------------------------------------------------------*/ +#ifndef _PROCESSOR_POSIX_H_ +#define _PROCESSOR_POSIX_H_ + +#include "processor.h" + +#ifdef HAVE_POLL_H +#include <poll.h> +#endif + +#ifdef HAVE_POLL_H +typedef int(poll_handler_t)(zbar_processor_t *, int); + +/* poll information */ +typedef struct poll_desc_s { + int num; /* number of descriptors */ + struct pollfd *fds; /* poll descriptors */ + poll_handler_t **handlers; /* poll handlers */ +} poll_desc_t; +#endif + +struct processor_state_s { +#ifdef HAVE_POLL_H + poll_desc_t polling; /* polling registration */ + poll_desc_t thr_polling; /* thread copy */ +#endif + int kick_fds[2]; /* poll kicker */ + poll_handler_t *pre_poll_handler; /* special case */ +}; + +#ifdef HAVE_POLL_H +static inline int alloc_polls(volatile poll_desc_t *p) +{ + p->fds = realloc(p->fds, p->num * sizeof(struct pollfd)); + p->handlers = realloc(p->handlers, p->num * sizeof(poll_handler_t *)); + /* FIXME should check for ENOMEM */ + return (0); +} + +static inline int add_poll(zbar_processor_t *proc, int fd, + poll_handler_t *handler) +{ + processor_state_t *state = proc->state; + + _zbar_mutex_lock(&proc->mutex); + + poll_desc_t *polling = &state->polling; + unsigned i = polling->num++; + zprintf(5, "[%d] fd=%d handler=%p\n", i, fd, handler); + if (!alloc_polls(polling)) { + memset(&polling->fds[i], 0, sizeof(struct pollfd)); + polling->fds[i].fd = fd; + polling->fds[i].events = POLLIN; + polling->handlers[i] = handler; + } else + i = -1; + + _zbar_mutex_unlock(&proc->mutex); + + if (proc->input_thread.started) { + assert(state->kick_fds[1] >= 0); + if (write(state->kick_fds[1], &i /* unused */, sizeof(unsigned)) < 0) + return (-1); + } else if (!proc->threaded) { + state->thr_polling.num = polling->num; + state->thr_polling.fds = polling->fds; + state->thr_polling.handlers = polling->handlers; + } + return (i); +} + +static inline int remove_poll(zbar_processor_t *proc, int fd) +{ + processor_state_t *state = proc->state; + + _zbar_mutex_lock(&proc->mutex); + + poll_desc_t *polling = &state->polling; + int i; + for (i = polling->num - 1; i >= 0; i--) + if (polling->fds[i].fd == fd) + break; + zprintf(5, "[%d] fd=%d n=%d\n", i, fd, polling->num); + + if (i >= 0) { + if (i + 1 < polling->num) { + int n = polling->num - i - 1; + memmove(&polling->fds[i], &polling->fds[i + 1], + n * sizeof(struct pollfd)); + memmove(&polling->handlers[i], &polling->handlers[i + 1], + n * sizeof(poll_handler_t)); + } + polling->num--; + i = alloc_polls(polling); + } + + _zbar_mutex_unlock(&proc->mutex); + + if (proc->input_thread.started) { + if (write(state->kick_fds[1], &i /* unused */, sizeof(unsigned)) < 0) + return (-1); + } else if (!proc->threaded) { + state->thr_polling.num = polling->num; + state->thr_polling.fds = polling->fds; + state->thr_polling.handlers = polling->handlers; + } + return (i); +} +#endif + +#endif diff --git a/zbar/processor/win.c b/zbar/processor/win.c new file mode 100644 index 0000000..48fa9ac --- /dev/null +++ b/zbar/processor/win.c @@ -0,0 +1,332 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (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 <assert.h> +#if defined(_MSC_VER) +#include <WinSock2.h> +#endif +#include <windows.h> +#include "processor.h" + +#define WIN_STYLE \ + (WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX) +#define EXT_STYLE (WS_EX_APPWINDOW | WS_EX_OVERLAPPEDWINDOW) + +struct processor_state_s { + ATOM registeredClass; +}; + +int _zbar_event_init(zbar_event_t *event) +{ + *event = CreateEvent(NULL, 0, 0, NULL); + return ((*event) ? 0 : -1); +} + +void _zbar_event_destroy(zbar_event_t *event) +{ + if (*event) { + CloseHandle(*event); + *event = NULL; + } +} + +void _zbar_event_trigger(zbar_event_t *event) +{ + SetEvent(*event); +} + +/* lock must be held */ +int _zbar_event_wait(zbar_event_t *event, zbar_mutex_t *lock, + zbar_timer_t *timeout) +{ + int rc; + if (lock) + _zbar_mutex_unlock(lock); + rc = WaitForSingleObject(*event, _zbar_timer_check(timeout)); + if (lock) + _zbar_mutex_lock(lock); + + if (!rc) + return (1); /* got event */ + if (rc == WAIT_TIMEOUT) + return (0); /* timed out */ + return (-1); /* error (FIXME save info) */ +} + +int _zbar_thread_start(zbar_thread_t *thr, zbar_thread_proc_t *proc, void *arg, + zbar_mutex_t *lock) +{ + HANDLE hthr; + int rc; + if (thr->started || thr->running) + return (-1 /*FIXME*/); + thr->started = 1; + _zbar_event_init(&thr->notify); + _zbar_event_init(&thr->activity); + + hthr = CreateThread(NULL, 0, proc, arg, 0, NULL); + rc = (!hthr || _zbar_event_wait(&thr->activity, NULL, NULL) < 0 || + !thr->running); + CloseHandle(hthr); + if (rc) { + thr->started = 0; + _zbar_event_destroy(&thr->notify); + _zbar_event_destroy(&thr->activity); + return (-1 /*FIXME*/); + } + return (0); +} + +int _zbar_thread_stop(zbar_thread_t *thr, zbar_mutex_t *lock) +{ + if (thr->started) { + thr->started = 0; + _zbar_event_trigger(&thr->notify); + while (thr->running) + /* FIXME time out and abandon? */ + _zbar_event_wait(&thr->activity, lock, NULL); + _zbar_event_destroy(&thr->notify); + _zbar_event_destroy(&thr->activity); + } + return (0); +} + +static LRESULT CALLBACK win_handle_event(HWND hwnd, UINT message, WPARAM wparam, + LPARAM lparam) +{ + zbar_processor_t *proc = + (zbar_processor_t *)GetWindowLongPtr(hwnd, GWLP_USERDATA); + /* initialized during window creation */ + if (message == WM_NCCREATE) { + proc = ((LPCREATESTRUCT)lparam)->lpCreateParams; + assert(proc); + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)proc); + proc->display = hwnd; + + zbar_window_attach(proc->window, proc->display, proc->xwin); + } else if (!proc) + return (DefWindowProc(hwnd, message, wparam, lparam)); + + switch (message) { + case WM_SIZE: { + RECT r; + GetClientRect(hwnd, &r); + zprintf(3, "WM_SIZE %ldx%ld\n", r.right, r.bottom); + assert(proc); + zbar_window_resize(proc->window, r.right, r.bottom); + InvalidateRect(hwnd, NULL, 0); + return (0); + } + + case WM_PAINT: { + PAINTSTRUCT ps; + BeginPaint(hwnd, &ps); + if (zbar_window_redraw(proc->window)) { + HDC hdc = GetDC(hwnd); + RECT r; + GetClientRect(hwnd, &r); + FillRect(hdc, &r, GetStockObject(BLACK_BRUSH)); + ReleaseDC(hwnd, hdc); + } + EndPaint(hwnd, &ps); + return (0); + } + + case WM_CHAR: { + _zbar_processor_handle_input(proc, wparam); + return (0); + } + + case WM_LBUTTONDOWN: { + _zbar_processor_handle_input(proc, 1); + return (0); + } + + case WM_MBUTTONDOWN: { + _zbar_processor_handle_input(proc, 2); + return (0); + } + + case WM_RBUTTONDOWN: { + _zbar_processor_handle_input(proc, 3); + return (0); + } + + case WM_CLOSE: { + zprintf(3, "WM_CLOSE\n"); + _zbar_processor_handle_input(proc, -1); + return (1); + } + + case WM_DESTROY: { + zprintf(3, "WM_DESTROY\n"); + proc->display = NULL; + zbar_window_attach(proc->window, NULL, 0); + return (0); + } + } + return (DefWindowProc(hwnd, message, wparam, lparam)); +} + +static inline int win_handle_events(zbar_processor_t *proc) +{ + int rc = 0; + while (1) { + MSG msg; + rc = PeekMessage(&msg, proc->display, 0, 0, PM_NOYIELD | PM_REMOVE); + if (!rc) + return (0); + if (rc < 0) + return (err_capture(proc, SEV_ERROR, ZBAR_ERR_WINAPI, __func__, + "failed to obtain event")); + + TranslateMessage(&msg); + DispatchMessage(&msg); + } +} + +int _zbar_processor_init(zbar_processor_t *proc) +{ + proc->state = calloc(1, sizeof(processor_state_t)); + return (0); +} + +int _zbar_processor_cleanup(zbar_processor_t *proc) +{ + free(proc->state); + proc->state = 0; + return (0); +} + +int _zbar_processor_input_wait(zbar_processor_t *proc, zbar_event_t *event, + int timeout) +{ + int n = (event) ? 1 : 0; + int rc = MsgWaitForMultipleObjects(n, event, 0, timeout, QS_ALLINPUT); + + if (rc == n) { + if (win_handle_events(proc) < 0) + return (-1); + return (1); + } + if (!rc) + return (1); + if (rc == WAIT_TIMEOUT) + return (0); + return (-1); +} + +int _zbar_processor_enable(zbar_processor_t *proc) +{ + return (0); +} + +static inline ATOM win_register_class(HINSTANCE hmod) +{ + BYTE and_mask[1] = { 0xff }; + BYTE xor_mask[1] = { 0x00 }; + + WNDCLASSEX wc = { + sizeof(WNDCLASSEX), + 0, + }; + wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wc.hInstance = hmod; + wc.lpfnWndProc = win_handle_event; + wc.lpszClassName = "_ZBar Class"; + wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + wc.hCursor = CreateCursor(hmod, 0, 0, 1, 1, and_mask, xor_mask); + + return (RegisterClassEx(&wc)); +} + +int _zbar_processor_open(zbar_processor_t *proc, char *title, unsigned width, + unsigned height) +{ + ATOM wca; + RECT r = { 0, 0, width, height }; + HMODULE hmod = NULL; + if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (void *)_zbar_processor_open, (HINSTANCE *)&hmod)) + return (err_capture(proc, SEV_ERROR, ZBAR_ERR_WINAPI, __func__, + "failed to obtain module handle")); + + wca = win_register_class(hmod); + if (!wca) + return (err_capture(proc, SEV_ERROR, ZBAR_ERR_WINAPI, __func__, + "failed to register window class")); + + proc->state->registeredClass = wca; + AdjustWindowRectEx(&r, WIN_STYLE, 0, EXT_STYLE); + proc->display = CreateWindowEx(EXT_STYLE, (LPCTSTR)(long)wca, "ZBar", + WIN_STYLE, CW_USEDEFAULT, CW_USEDEFAULT, + r.right - r.left, r.bottom - r.top, NULL, + NULL, hmod, proc); + + if (!proc->display) + return (err_capture(proc, SEV_ERROR, ZBAR_ERR_WINAPI, __func__, + "failed to open window")); + return (0); +} + +int _zbar_processor_close(zbar_processor_t *proc) +{ + if (proc->display) { + DestroyWindow(proc->display); + UnregisterClass((LPCTSTR)(long)proc->state->registeredClass, 0); + proc->display = NULL; + } + return (0); +} + +int _zbar_processor_set_visible(zbar_processor_t *proc, int visible) +{ + ShowWindow(proc->display, (visible) ? SW_SHOWNORMAL : SW_HIDE); + if (visible) + InvalidateRect(proc->display, NULL, 0); + /* no error conditions */ + return (0); +} + +int _zbar_processor_set_size(zbar_processor_t *proc, unsigned width, + unsigned height) +{ + RECT r = { 0, 0, width, height }; + AdjustWindowRectEx(&r, GetWindowLong(proc->display, GWL_STYLE), 0, + GetWindowLong(proc->display, GWL_EXSTYLE)); + if (!SetWindowPos( + proc->display, NULL, 0, 0, r.right - r.left, r.bottom - r.top, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOREPOSITION)) + return (-1 /*FIXME*/); + + return (0); +} + +int _zbar_processor_invalidate(zbar_processor_t *proc) +{ + if (!InvalidateRect(proc->display, NULL, 0)) + return (-1 /*FIXME*/); + + return (0); +} diff --git a/zbar/processor/x.c b/zbar/processor/x.c new file mode 100644 index 0000000..7cb1a6e --- /dev/null +++ b/zbar/processor/x.c @@ -0,0 +1,266 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (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 "posix.h" +#include "processor.h" +#include "window.h" + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/keysym.h> + +static inline int x_handle_event(zbar_processor_t *proc) +{ + XEvent ev; + XNextEvent(proc->display, &ev); + + switch (ev.type) { + case Expose: { + /* FIXME ignore when running(?) */ + XExposeEvent *exp = (XExposeEvent *)&ev; + while (1) { + assert(ev.type == Expose); + _zbar_window_expose(proc->window, exp->x, exp->y, exp->width, + exp->height); + if (!exp->count) + break; + XNextEvent(proc->display, &ev); + } + zbar_window_redraw(proc->window); + break; + } + + case ConfigureNotify: + zprintf(3, "resized to %d x %d\n", ev.xconfigure.width, + ev.xconfigure.height); + zbar_window_resize(proc->window, ev.xconfigure.width, + ev.xconfigure.height); + _zbar_processor_invalidate(proc); + break; + + case ClientMessage: + if ((ev.xclient.message_type == + XInternAtom(proc->display, "WM_PROTOCOLS", 0)) && + ev.xclient.format == 32 && + (ev.xclient.data.l[0] == + XInternAtom(proc->display, "WM_DELETE_WINDOW", 0))) { + zprintf(3, "WM_DELETE_WINDOW\n"); + return (_zbar_processor_handle_input(proc, -1)); + } + break; + + case KeyPress: { + KeySym key = XLookupKeysym(&ev.xkey, 0); + if (IsModifierKey(key)) + break; + if ((key & 0xff00) == 0xff00) + key &= 0x00ff; + zprintf(16, "KeyPress(%04lx)\n", key); + /* FIXME this doesn't really work... */ + return (_zbar_processor_handle_input(proc, key & 0xffff)); + } + case ButtonPress: { + zprintf(16, "ButtonPress(%d)\n", ev.xbutton.button); + int idx = 1; + switch (ev.xbutton.button) { + case Button2: + idx = 2; + break; + case Button3: + idx = 3; + break; + case Button4: + idx = 4; + break; + case Button5: + idx = 5; + break; + } + return (_zbar_processor_handle_input(proc, idx)); + } + + case DestroyNotify: + zprintf(16, "DestroyNotify\n"); + zbar_window_attach(proc->window, NULL, 0); + proc->xwin = 0; + return (0); + + default: + /* ignored */; + } + return (0); +} + +static int x_handle_events(zbar_processor_t *proc) +{ + int rc = 0; + while (rc >= 0 && XPending(proc->display)) + rc = x_handle_event(proc); + return (rc); +} + +static int x_connection_handler(zbar_processor_t *proc, int i) +{ + _zbar_mutex_lock(&proc->mutex); + _zbar_processor_lock(proc); + _zbar_mutex_unlock(&proc->mutex); + + x_handle_events(proc); + + _zbar_mutex_lock(&proc->mutex); + _zbar_processor_unlock(proc, 0); + _zbar_mutex_unlock(&proc->mutex); + return (0); +} + +static int x_internal_handler(zbar_processor_t *proc, int i) +{ + XProcessInternalConnection(proc->display, proc->state->polling.fds[i].fd); + x_connection_handler(proc, i); + return (0); +} + +static void x_internal_watcher(Display *display, XPointer client_arg, int fd, + Bool opening, XPointer *watch_arg) +{ + zbar_processor_t *proc = (void *)client_arg; + if (opening) + add_poll(proc, fd, x_internal_handler); + else + remove_poll(proc, fd); +} + +int _zbar_processor_open(zbar_processor_t *proc, char *title, unsigned width, + unsigned height) +{ + proc->display = XOpenDisplay(NULL); + if (!proc->display) + return (err_capture_str(proc, SEV_ERROR, ZBAR_ERR_XDISPLAY, __func__, + "unable to open X display", + XDisplayName(NULL))); + + add_poll(proc, ConnectionNumber(proc->display), x_connection_handler); + XAddConnectionWatch(proc->display, x_internal_watcher, (void *)proc); + /* must also flush X event queue before polling */ + proc->state->pre_poll_handler = x_connection_handler; + + int screen = DefaultScreen(proc->display); + XSetWindowAttributes attr; + attr.event_mask = + (ExposureMask | StructureNotifyMask | KeyPressMask | ButtonPressMask); + + proc->xwin = XCreateWindow(proc->display, RootWindow(proc->display, screen), + 0, 0, width, height, 0, CopyFromParent, + InputOutput, CopyFromParent, CWEventMask, &attr); + if (!proc->xwin) { + XCloseDisplay(proc->display); + return (err_capture(proc, SEV_ERROR, ZBAR_ERR_XPROTO, __func__, + "creating window")); + } + + XStoreName(proc->display, proc->xwin, title); + + XClassHint *class_hint = XAllocClassHint(); + class_hint->res_name = "zbar"; + class_hint->res_class = "zbar"; + XSetClassHint(proc->display, proc->xwin, class_hint); + XFree(class_hint); + class_hint = NULL; + + Atom wm_delete_window = XInternAtom(proc->display, "WM_DELETE_WINDOW", 0); + if (wm_delete_window) + XSetWMProtocols(proc->display, proc->xwin, &wm_delete_window, 1); + + if (zbar_window_attach(proc->window, proc->display, proc->xwin)) + return (err_copy(proc, proc->window)); + return (0); +} + +int _zbar_processor_close(zbar_processor_t *proc) +{ + if (proc->window) + zbar_window_attach(proc->window, NULL, 0); + + if (proc->display) { + if (proc->xwin) { + XDestroyWindow(proc->display, proc->xwin); + proc->xwin = 0; + } + proc->state->pre_poll_handler = NULL; + remove_poll(proc, ConnectionNumber(proc->display)); + XCloseDisplay(proc->display); + proc->display = NULL; + } + return (0); +} + +int _zbar_processor_invalidate(zbar_processor_t *proc) +{ + if (!proc->display || !proc->xwin) + return (0); + XClearArea(proc->display, proc->xwin, 0, 0, 0, 0, 1); + XFlush(proc->display); + return (0); +} + +int _zbar_processor_set_size(zbar_processor_t *proc, unsigned width, + unsigned height) +{ + if (!proc->display || !proc->xwin) + return (0); + + /* refuse to resize greater than (default) screen size */ + XWindowAttributes attr; + XGetWindowAttributes(proc->display, proc->xwin, &attr); + + int maxw = WidthOfScreen(attr.screen); + int maxh = HeightOfScreen(attr.screen); + int w, h; + if (width > maxw) { + h = (maxw * height + width - 1) / width; + w = maxw; + } else { + w = width; + h = height; + } + if (h > maxh) { + w = (maxh * width + height - 1) / height; + h = maxh; + } + assert(w <= maxw); + assert(h <= maxh); + + XResizeWindow(proc->display, proc->xwin, w, h); + XFlush(proc->display); + return (0); +} + +int _zbar_processor_set_visible(zbar_processor_t *proc, int visible) +{ + if (visible) + XMapRaised(proc->display, proc->xwin); + else + XUnmapWindow(proc->display, proc->xwin); + XFlush(proc->display); + return (0); +} diff --git a/zbar/qrcode.h b/zbar/qrcode.h new file mode 100644 index 0000000..01a2fa9 --- /dev/null +++ b/zbar/qrcode.h @@ -0,0 +1,64 @@ +/*Copyright (C) 2008-2009 Timothy B. Terriberry (tterribe@xiph.org) + You can redistribute this library and/or modify it under the terms of the + GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version.*/ +#ifndef _QRCODE_H_ +#define _QRCODE_H_ + +#include <zbar.h> + +typedef struct qr_reader qr_reader; + +typedef int qr_point[2]; +typedef struct qr_finder_line qr_finder_line; + +/*The number of bits of subpel precision to store image coordinates in. + This helps when estimating positions in low-resolution images, which may have + a module pitch only a pixel or two wide, making rounding errors matter a + great deal.*/ +#define QR_FINDER_SUBPREC (2) + +/*A line crossing a finder pattern. + Whether the line is horizontal or vertical is determined by context. + The offsts to various parts of the finder pattern are as follows: + |*****| |*****|*****|*****| |*****| + |*****| |*****|*****|*****| |*****| + ^ ^ ^ ^ + | | | | + | | | pos[v]+len+eoffs + | | pos[v]+len + | pos[v] + pos[v]-boffs + Here v is 0 for horizontal and 1 for vertical lines.*/ +struct qr_finder_line { + /*The location of the upper/left endpoint of the line. + The left/upper edge of the center section is used, since other lines must + cross in this region.*/ + qr_point pos; + /*The length of the center section. + This extends to the right/bottom of the center section, since other lines + must cross in this region.*/ + int len; + /*The offset to the midpoint of the upper/left section (part of the outside + ring), or 0 if we couldn't identify the edge of the beginning section. + We use the midpoint instead of the edge because it can be located more + reliably.*/ + int boffs; + /*The offset to the midpoint of the end section (part of the outside ring), + or 0 if we couldn't identify the edge of the end section. + We use the midpoint instead of the edge because it can be located more + reliably.*/ + int eoffs; +}; + +qr_reader *_zbar_qr_create(void); +void _zbar_qr_destroy(qr_reader *reader); +void _zbar_qr_reset(qr_reader *reader); + +int _zbar_qr_found_line(qr_reader *reader, int direction, + const qr_finder_line *line); +int _zbar_qr_decode(qr_reader *reader, zbar_image_scanner_t *iscn, + zbar_image_t *img); + +#endif diff --git a/zbar/qrcode/bch15_5.c b/zbar/qrcode/bch15_5.c new file mode 100644 index 0000000..2295ad9 --- /dev/null +++ b/zbar/qrcode/bch15_5.c @@ -0,0 +1,207 @@ +/*Copyright (C) 2008-2009 Timothy B. Terriberry (tterribe@xiph.org) + You can redistribute this library and/or modify it under the terms of the + GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version.*/ +#include "bch15_5.h" + +/*A cycle in GF(2**4) generated by alpha=(x**4+x+1). + It is extended an extra 16 entries to avoid some expensive mod operations.*/ +static const unsigned char gf16_exp[31] = { 1, 2, 4, 8, 3, 6, 12, 11, + 5, 10, 7, 14, 15, 13, 9, 1, + 2, 4, 8, 3, 6, 12, 11, 5, + 10, 7, 14, 15, 13, 9, 1 }; + +/*The location of each integer 1...16 in the cycle.*/ +static const signed char gf16_log[16] = { -1, 0, 1, 4, 2, 8, 5, 10, + 3, 14, 9, 7, 6, 13, 11, 12 }; + +/*Multiplication in GF(2**4) using logarithms.*/ +static unsigned gf16_mul(unsigned _a, unsigned _b) +{ + return _a == 0 || _b == 0 ? 0 : gf16_exp[gf16_log[_a] + gf16_log[_b]]; +} + +/*Division in GF(2**4) using logarithms. + The result when dividing by zero is undefined.*/ +static unsigned gf16_div(unsigned _a, unsigned _b) +{ + return _a == 0 ? 0 : gf16_exp[gf16_log[_a] + 15 - gf16_log[_b]]; +} + +/*Multiplication in GF(2**4) when the second argument is known to be non-zero + (proven by representing it by its logarithm).*/ +static unsigned gf16_hmul(unsigned _a, unsigned _logb) +{ + return _a == 0 ? 0 : gf16_exp[gf16_log[_a] + _logb]; +} + +/*The syndrome normally has five values, S_1 ... S_5. + We only calculate and store the odd ones in _s, since S_2=S_1**2 and + S_4=S_2**2. + Returns zero iff all the syndrome values are zero.*/ +static int bch15_5_calc_syndrome(unsigned _s[3], unsigned _y) +{ + unsigned p; + int i; + int j; + p = 0; + for (i = 0; i < 15; i++) + if (_y & 1 << i) + p ^= gf16_exp[i]; + _s[0] = p; + p = 0; + for (i = 0; i < 3; i++) + for (j = 0; j < 5; j++) + if (_y & 1 << 5 * i + j) + p ^= gf16_exp[j * 3]; + _s[1] = p; + p = 0; + for (i = 0; i < 5; i++) + for (j = 0; j < 3; j++) + if (_y & 1 << 3 * i + j) + p ^= gf16_exp[j * 5]; + _s[2] = p; + return _s[0] != 0 || _s[1] != 0 || _s[2] != 0; +} + +/*Compute the coefficients of the error-locator polynomial. + Returns the number of errors (the degree of the polynomial).*/ +static int bch15_5_calc_omega(unsigned _o[3], unsigned _s[3]) +{ + unsigned s02; + unsigned tt; + unsigned dd; + int d; + _o[0] = _s[0]; + s02 = gf16_mul(_s[0], _s[0]); + dd = _s[1] ^ gf16_mul(_s[0], s02); + tt = _s[2] ^ gf16_mul(s02, _s[1]); + _o[1] = dd ? gf16_div(tt, dd) : 0; + _o[2] = dd ^ gf16_mul(_s[0], _o[1]); + for (d = 3; d > 0 && !_o[d - 1]; d--) + ; + return d; +} + +/*Find the roots of the error polynomial. + Returns the number of roots found, or a negative value if the polynomial did + not have enough roots, indicating a decoding error.*/ +static int bch15_5_calc_epos(unsigned _epos[3], unsigned _s[3]) +{ + unsigned o[3]; + int nerrors; + int d; + int i; + d = bch15_5_calc_omega(o, _s); + nerrors = 0; + if (d == 1) + _epos[nerrors++] = gf16_log[o[0]]; + else if (d > 0) { + for (i = 0; i < 15; i++) { + int i2; + i2 = gf16_log[gf16_exp[i << 1]]; + if (!(gf16_exp[i + i2] ^ gf16_hmul(o[0], i2) ^ gf16_hmul(o[1], i) ^ + o[2])) { + _epos[nerrors++] = i; + } + } + if (nerrors < d) + return -1; + } + return nerrors; +} + +int bch15_5_correct(unsigned *_y) +{ + unsigned s[3]; + unsigned epos[3]; + unsigned y; + int nerrors; + int i; + y = *_y; + if (!bch15_5_calc_syndrome(s, y)) + return 0; + nerrors = bch15_5_calc_epos(epos, s); + if (nerrors > 0) { + /*If we had a non-zero syndrome value, we should always find at least one + error location, or we've got a decoding error.*/ + for (i = 0; i < nerrors; i++) + y ^= 1 << epos[i]; + /*If there were too many errors, we may not find enough roots to reduce the + syndrome to zero. + We could recompute it to check, but it's much faster just to check that + we have a valid codeword.*/ + if (bch15_5_encode(y >> 10) == y) { + /*Decoding succeeded.*/ + *_y = y; + return nerrors; + } + } + /*Decoding failed due to too many bit errors.*/ + return -1; +} + +unsigned bch15_5_encode(unsigned _x) +{ + return (-(_x & 1) & 0x0537) ^ (-(_x >> 1 & 1) & 0x0A6E) ^ + (-(_x >> 2 & 1) & 0x11EB) ^ (-(_x >> 3 & 1) & 0x23D6) ^ + (-(_x >> 4 & 1) & 0x429B); +} + +#if 0 +#include <stdio.h> + +static unsigned codes[32]; + +static int hamming(int _a,int _b){ + int d; + int n; + d=_a^_b; + for(n=0;d;n++)d&=d-1; + return n; +} + +static int closest(int _y){ + int min_i; + int min_d; + int i; + int d; + min_i=0; + min_d=hamming(_y,codes[0]); + for(i=1;i<32;i++){ + d=hamming(_y,codes[i]); + if(d<min_d){ + min_d=d; + min_i=i; + } + } + return codes[min_i]; +} + +int main(void){ + int i; + /*Print a list of the valid (uncorrupt) codewords.*/ + for(i=0;i<32;i++)codes[i]=bch15_5_encode(i); + for(i=0;i<32;i++)printf("0x%04X%s",codes[i],i+1<32?" ":"\n"); + /*Try to decode all receivable (possibly corrupt) codewords.*/ + for(i=0;i<0x8000;i++){ + unsigned y; + unsigned z; + int nerrors; + int j; + y=i; + nerrors=bch15_5_correct(&y); + z=closest(i); + if(nerrors<0){ + printf("0x%04X->Failed\n",i); + if(hamming(i,z)<=3)printf("Error: 0x%04X should map to 0x%04X\n",i,z); + } + else{ + printf("0x%04X->0x%04X\n",i,y); + if(z!=y)printf("Error: 0x%04X should map to 0x%04X\n",i,z); + } + } + return 0; +} +#endif diff --git a/zbar/qrcode/bch15_5.h b/zbar/qrcode/bch15_5.h new file mode 100644 index 0000000..bf95e12 --- /dev/null +++ b/zbar/qrcode/bch15_5.h @@ -0,0 +1,20 @@ +/*Copyright (C) 2008-2009 Timothy B. Terriberry (tterribe@xiph.org) + You can redistribute this library and/or modify it under the terms of the + GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version.*/ +#if !defined(_bch15_5_H) +#define _bch15_5_H (1) + +/*Encodes a raw 5-bit value _x into a 15-bit BCH(15,5) code. + This is capable of correcting up to 3 bit errors, and detecting as many as + 5 bit errors in some cases.*/ +unsigned bch15_5_encode(unsigned _x); + +/*Corrects the received code *_y, if possible. + The original data is located in the top five bits. + Returns the number of errors corrected, or a negative value if decoding + failed due to too many bit errors, in which case *_y is left unchanged.*/ +int bch15_5_correct(unsigned *_y); + +#endif diff --git a/zbar/qrcode/binarize.c b/zbar/qrcode/binarize.c new file mode 100644 index 0000000..33a6fe4 --- /dev/null +++ b/zbar/qrcode/binarize.c @@ -0,0 +1,646 @@ +/*Copyright (C) 2008-2009 Timothy B. Terriberry (tterribe@xiph.org) + You can redistribute this library and/or modify it under the terms of the + GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version.*/ +#include "binarize.h" +#include <math.h> +#include <stdlib.h> +#include <string.h> +#include "image.h" +#include "util.h" + +#if 0 +/*Binarization based on~\cite{GPP06}. + @ARTICLE{GPP06, + author="Basilios Gatos and Ioannis E. Pratikakis and Stavros J. Perantonis", + title="Adaptive Degraded Document Image Binarization", + journal="Pattern Recognition", + volume=39, + number=3, + pages="317-327", + month=Mar, + year=2006 + }*/ + +#if 0 +/*Applies a 5x5 Wiener filter to the image, in-place, emphasizing differences + where the local variance is small, and de-emphasizing them where it is + large.*/ +void qr_wiener_filter(unsigned char *_img,int _width,int _height){ + unsigned *m_buf[8]; + unsigned *sn2_buf[8]; + unsigned char g; + int x; + int y; + if(_width<=0||_height<=0)return; + m_buf[0]=(unsigned *)malloc((_width+4<<3)*sizeof(*m_buf)); + sn2_buf[0]=(unsigned *)malloc((_width+4<<3)*sizeof(*sn2_buf)); + for(y=1;y<8;y++){ + m_buf[y]=m_buf[y-1]+_width+4; + sn2_buf[y]=sn2_buf[y-1]+_width+4; + } + for(y=-4;y<_height;y++){ + unsigned *pm; + unsigned *psn2; + int i; + int j; + pm=m_buf[y+2&7]; + psn2=sn2_buf[y+2&7]; + for(x=-4;x<_width;x++){ + unsigned m; + unsigned m2; + m=m2=0; + if(y>=0&&y<_height-4&&x>=0&&x<_width-4)for(i=0;i<5;i++)for(j=0;j<5;j++){ + g=_img[(y+i)*_width+x+j]; + m+=g; + m2+=g*g; + } + else for(i=0;i<5;i++)for(j=0;j<5;j++){ + g=_img[QR_CLAMPI(0,y+i,_height-1)*_width+QR_CLAMPI(0,x+j,_width-1)]; + m+=g; + m2+=g*g; + } + pm[x+4]=m; + psn2[x+4]=(m2*25-m*m); + } + pm=m_buf[y&7]; + if(y>=0)for(x=0;x<_width;x++){ + int sn2; + sn2=sn2_buf[y&7][x+2]; + if(sn2){ + int vn3; + int m; + /*Gatos et al. give the expression + mu+(s2-v2)*(g-mu)/s2 , + which we reduce to + mu+(s2-v2)*g/s2-(s2-v2)*mu/s2 , + g-(v2/s2)*g+(v2/s2)*mu , + g+(mu-g)*(v2/s2) . + However, s2 is much noisier than v2, and dividing by it often gives + extremely large adjustments, causing speckle near edges. + Therefore we limit the ratio (v2/s2) to lie between 0 and 1.*/ + vn3=0; + for(i=-2;i<3;i++){ + psn2=sn2_buf[y+i&7]; + for(j=0;j<5;j++)vn3+=psn2[x+j]; + } + m=m_buf[y&7][x+2]; + vn3=vn3+1023>>10; + sn2=25*sn2+1023>>10; + if(vn3<sn2){ + int a; + g=_img[y*_width+x]; + a=(m-25*g)*vn3; + sn2*=25; + _img[y*_width+x]=QR_CLAMP255(g+QR_DIVROUND(a,sn2)); + } + else _img[y*_width+x]=(unsigned char)(((m<<1)+25)/50); + } + } + } + free(sn2_buf[0]); + free(m_buf[0]); +} + +#else +/*Applies a 3x3 Wiener filter to the image, in-place, emphasizing differences + where the local variance is small, and de-emphasizing them where it is + large.*/ +void qr_wiener_filter(unsigned char *_img,int _width,int _height){ + unsigned *m_buf[4]; + unsigned *sn2_buf[4]; + unsigned char g; + int x; + int y; + if(_width<=0||_height<=0)return; + m_buf[0]=(unsigned *)malloc((_width+2<<2)*sizeof(*m_buf)); + sn2_buf[0]=(unsigned *)malloc((_width+2<<2)*sizeof(*sn2_buf)); + for(y=1;y<4;y++){ + m_buf[y]=m_buf[y-1]+_width+2; + sn2_buf[y]=sn2_buf[y-1]+_width+2; + } + for(y=-2;y<_height;y++){ + unsigned *pm; + unsigned *psn2; + int i; + int j; + pm=m_buf[y+1&3]; + psn2=sn2_buf[y+1&3]; + for(x=-2;x<_width;x++){ + unsigned m; + unsigned m2; + m=m2=0; + if(y>=0&&y<_height-2&&x>=0&&x<_width-2)for(i=0;i<3;i++)for(j=0;j<3;j++){ + g=_img[(y+i)*_width+x+j]; + m+=g; + m2+=g*g; + } + else for(i=0;i<3;i++)for(j=0;j<3;j++){ + g=_img[QR_CLAMPI(0,y+i,_height-1)*_width+QR_CLAMPI(0,x+j,_width-1)]; + m+=g; + m2+=g*g; + } + pm[x+2]=m; + psn2[x+2]=(m2*9-m*m); + } + pm=m_buf[y&3]; + if(y>=0)for(x=0;x<_width;x++){ + int sn2; + sn2=sn2_buf[y&3][x+1]; + if(sn2){ + int m; + int vn3; + /*Gatos et al. give the expression + mu+(s2-v2)*(g-mu)/s2 , + which we reduce to + mu+(s2-v2)*g/s2-(s2-v2)*mu/s2 , + g-(v2/s2)*g+(v2/s2)*mu , + g+(mu-g)*(v2/s2) . + However, s2 is much noisier than v2, and dividing by it often gives + extremely large adjustments, causing speckle near edges. + Therefore we limit the ratio (v2/s2) to lie between 0 and 1.*/ + vn3=0; + for(i=-1;i<2;i++){ + psn2=sn2_buf[y+i&3]; + for(j=0;j<3;j++)vn3+=psn2[x+j]; + } + m=m_buf[y&3][x+1]; + vn3=vn3+31>>5; + sn2=9*sn2+31>>5; + if(vn3<sn2){ + int a; + g=_img[y*_width+x]; + a=m-9*g; + sn2*=9; + _img[y*_width+x]=QR_CLAMP255(g+QR_DIVROUND(a,sn2)); + } + else _img[y*_width+x]=(unsigned char)(((m<<1)+9)/18); + } + } + } + free(sn2_buf[0]); + free(m_buf[0]); +} +#endif + +/*Computes a (conservative) foreground mask using the adaptive binarization + threshold given in~\cite{SP00}, but knocking the threshold parameter down to + k=0.2. + Note on dynamic range: we assume _width*_height<=0x1000000 (24 bits). + Returns the average background value. + @ARTICLE{SP00, + author="Jaakko J. Sauvola and Matti Pietik\"{a}inen", + title="Adaptive Document Image Binarization", + volume=33, + number=2, + pages="225--236", + month=Feb, + year=2000 + }*/ +static void qr_sauvola_mask(unsigned char *_mask,unsigned *_b,int *_nb, + const unsigned char *_img,int _width,int _height){ + unsigned b; + int nb; + b=0; + nb=0; + if(_width>0&&_height>0){ + unsigned *col_sums; + unsigned *col2_sums; + int logwindw; + int logwindh; + int windw; + int windh; + int y0offs; + int y1offs; + unsigned g; + unsigned g2; + int x; + int y; + /*We keep the window size fairly large to ensure it doesn't fit completely + inside the center of a finder pattern of a version 1 QR code at full + resolution.*/ + for(logwindw=4;logwindw<8&&(1<<logwindw)<(_width+7>>3);logwindw++); + for(logwindh=4;logwindh<8&&(1<<logwindh)<(_height+7>>3);logwindh++); + windw=1<<logwindw; + windh=1<<logwindh; + col_sums=(unsigned *)malloc(_width*sizeof(*col_sums)); + col2_sums=(unsigned *)malloc(_width*sizeof(*col2_sums)); + /*Initialize sums down each column.*/ + for(x=0;x<_width;x++){ + g=_img[x]; + g2=g*g; + col_sums[x]=(g<<logwindh-1)+g; + col2_sums[x]=(g2<<logwindh-1)+g2; + } + for(y=1;y<(windh>>1);y++){ + y1offs=QR_MINI(y,_height-1)*_width; + for(x=0;x<_width;x++){ + g=_img[y1offs+x]; + col_sums[x]+=g; + col2_sums[x]+=g*g; + } + } + for(y=0;y<_height;y++){ + unsigned m; + unsigned m2; + int x0; + int x1; + /*Initialize the sums over the window.*/ + m=(col_sums[0]<<logwindw-1)+col_sums[0]; + m2=(col2_sums[0]<<logwindw-1)+col2_sums[0]; + for(x=1;x<(windw>>1);x++){ + x1=QR_MINI(x,_width-1); + m+=col_sums[x1]; + m2+=col2_sums[x1]; + } + for(x=0;x<_width;x++){ + int d; + /*Perform the test against the threshold T = (m/n)*(1+k*(s/R-1)), + where n=windw*windh, s=sqrt((m2-(m*m)/n)/n), and R=128. + We don't actually compute the threshold directly, as that would + require a square root. + Instead we perform the equivalent test: + (m/n)*(m/n)*(m2/n-(m/n)*(m/n))/16 > (((1/k)*g-((1-k)/k)*(m/n))*32)**2 + R is split up across each side of the inequality to maximize the + dynamic range available for the right hand side, which requires + 31 bits in the worst case.*/ + /*(m/n)*(1+(1/5)*(sqrt((m2-m*m/n)/n)/128-1)) > g + m*(1+(1/5)*(sqrt((m2-m*m/n)/n)/128-1)) > g*n + m*sqrt((m2-m*m/n)/n) > 5*g*n-4*m<<7 + m*m*(m2*n-m*m) > (5*g*n-4*m<<7)**2*n*n || 5*g*n-4*m < 0 */ + g=_img[y*_width+x]; + d=(5*g<<logwindw+logwindh)-4*m; + if(d>=0){ + unsigned mm; + unsigned mms2; + unsigned d2; + mm=(m>>logwindw)*(m>>logwindh); + mms2=(m2-mm>>logwindw+logwindh)*(mm>>logwindw+logwindh)+15>>4; + d2=d>>logwindw+logwindh-5; + d2*=d2; + if(d2>=mms2){ + /*Update the background average.*/ + b+=g; + nb++; + _mask[y*_width+x]=0; + } + else _mask[y*_width+x]=0xFF; + } + else _mask[y*_width+x]=0xFF; + /*Update the window sums.*/ + if(x+1<_width){ + x0=QR_MAXI(0,x-(windw>>1)); + x1=QR_MINI(x+(windw>>1),_width-1); + m+=col_sums[x1]-col_sums[x0]; + m2+=col2_sums[x1]-col2_sums[x0]; + } + } + /*Update the column sums.*/ + if(y+1<_height){ + y0offs=QR_MAXI(0,y-(windh>>1))*_width; + y1offs=QR_MINI(y+(windh>>1),_height-1)*_width; + for(x=0;x<_width;x++){ + g=_img[y0offs+x]; + col_sums[x]-=g; + col2_sums[x]-=g*g; + g=_img[y1offs+x]; + col_sums[x]+=g; + col2_sums[x]+=g*g; + } + } + } + free(col2_sums); + free(col_sums); + } + *_b=b; + *_nb=nb; +} + +/*Interpolates a background image given the source and a conservative + foreground mask. + If the current window contains no foreground pixels, the average background + value over the whole image is used. + Note on dynamic range: we assume _width*_height<=0x8000000 (23 bits). + Returns the average difference between the foreground and the interpolated + background.*/ +static void qr_interpolate_background(unsigned char *_dst, + int *_delta,int *_ndelta,const unsigned char *_img,const unsigned char *_mask, + int _width,int _height,unsigned _b,int _nb){ + int delta; + int ndelta; + delta=ndelta=0; + if(_width>0&&_height>0){ + unsigned *col_sums; + unsigned *ncol_sums; + int logwindw; + int logwindh; + int windw; + int windh; + int y0offs; + int y1offs; + unsigned b; + unsigned g; + int x; + int y; + b=_nb>0?((_b<<1)+_nb)/(_nb<<1):0xFF; + for(logwindw=4;logwindw<8&&(1<<logwindw)<(_width+15>>4);logwindw++); + for(logwindh=4;logwindh<8&&(1<<logwindh)<(_height+15>>4);logwindh++); + windw=1<<logwindw; + windh=1<<logwindh; + col_sums=(unsigned *)malloc(_width*sizeof(*col_sums)); + ncol_sums=(unsigned *)malloc(_width*sizeof(*ncol_sums)); + /*Initialize sums down each column.*/ + for(x=0;x<_width;x++){ + if(!_mask[x]){ + g=_img[x]; + col_sums[x]=(g<<logwindh-1)+g; + ncol_sums[x]=(1<<logwindh-1)+1; + } + else col_sums[x]=ncol_sums[x]=0; + } + for(y=1;y<(windh>>1);y++){ + y1offs=QR_MINI(y,_height-1)*_width; + for(x=0;x<_width;x++)if(!_mask[y1offs+x]){ + col_sums[x]+=_img[y1offs+x]; + ncol_sums[x]++; + } + } + for(y=0;y<_height;y++){ + unsigned n; + unsigned m; + int x0; + int x1; + /*Initialize the sums over the window.*/ + m=(col_sums[0]<<logwindw-1)+col_sums[0]; + n=(ncol_sums[0]<<logwindw-1)+ncol_sums[0]; + for(x=1;x<(windw>>1);x++){ + x1=QR_MINI(x,_width-1); + m+=col_sums[x1]; + n+=ncol_sums[x1]; + } + for(x=0;x<_width;x++){ + if(!_mask[y*_width+x])g=_img[y*_width+x]; + else{ + g=n>0?((m<<1)+n)/(n<<1):b; + delta+=(int)g-_img[y*_width+x]; + ndelta++; + } + _dst[y*_width+x]=(unsigned char)g; + /*Update the window sums.*/ + if(x+1<_width){ + x0=QR_MAXI(0,x-(windw>>1)); + x1=QR_MINI(x+(windw>>1),_width-1); + m+=col_sums[x1]-col_sums[x0]; + n+=ncol_sums[x1]-ncol_sums[x0]; + } + } + /*Update the column sums.*/ + if(y+1<_height){ + y0offs=QR_MAXI(0,y-(windh>>1))*_width; + y1offs=QR_MINI(y+(windh>>1),_height-1)*_width; + for(x=0;x<_width;x++){ + if(!_mask[y0offs+x]){ + col_sums[x]-=_img[y0offs+x]; + ncol_sums[x]--; + } + if(!_mask[y1offs+x]){ + col_sums[x]+=_img[y1offs+x]; + ncol_sums[x]++; + } + } + } + } + free(ncol_sums); + free(col_sums); + } + *_delta=delta; + *_ndelta=ndelta; +} + +/*Parameters of the logistic sigmoid function that defines the threshold based + on the background intensity. + They should all be between 0 and 1.*/ +#define QR_GATOS_Q (0.7) +#define QR_GATOS_P1 (0.5) +#define QR_GATOS_P2 (0.8) + +/*Compute the final binarization mask according to Gatos et al.'s + method~\cite{GPP06}.*/ +static void qr_gatos_mask(unsigned char *_mask,const unsigned char *_img, + const unsigned char *_background,int _width,int _height, + unsigned _b,int _nb,int _delta,int _ndelta){ + unsigned thresh[256]; + unsigned g; + double delta; + double b; + int x; + int y; + /*Construct a lookup table for the thresholds. + This bit uses floating point, but doesn't need to do much calculation, so + emulation should be fine.*/ + b=_nb>0?(_b+0.5)/_nb:0xFF; + delta=_ndelta>0?(_delta+0.5)/_ndelta:0xFF; + for(g=0;g<256;g++){ + double d; + d=QR_GATOS_Q*delta*(QR_GATOS_P2+(1-QR_GATOS_P2)/ + (1+exp(2*(1+QR_GATOS_P1)/(1-QR_GATOS_P1)-4*g/(b*(1-QR_GATOS_P1))))); + if(d<1)d=1; + else if(d>0xFF)d=0xFF; + thresh[g]=(unsigned)floor(d); + } + /*Apply the adaptive threshold.*/ + for(y=0;y<_height;y++)for(x=0;x<_width;x++){ + g=_background[y*_width+x]; + /*_background[y*_width+x]=thresh[g];*/ + _mask[y*_width+x]=(unsigned char)(-(g-_img[y*_width+x]>thresh[g])&0xFF); + } + /*{ + FILE *fout; + fout=fopen("thresh.png","wb"); + image_write_png(_background,_width,_height,fout); + fclose(fout); + }*/ +} + +/*Binarizes a grayscale image.*/ +void qr_binarize(unsigned char *_img,int _width,int _height){ + unsigned char *mask; + unsigned char *background; + unsigned b; + int nb; + int delta; + int ndelta; + /*qr_wiener_filter(_img,_width,_height); + { + FILE *fout; + fout=fopen("wiener.png","wb"); + image_write_png(_img,_width,_height,fout); + fclose(fout); + }*/ + mask=(unsigned char *)malloc(_width*_height*sizeof(*mask)); + qr_sauvola_mask(mask,&b,&nb,_img,_width,_height); + /*{ + FILE *fout; + fout=fopen("foreground.png","wb"); + image_write_png(mask,_width,_height,fout); + fclose(fout); + }*/ + background=(unsigned char *)malloc(_width*_height*sizeof(*mask)); + qr_interpolate_background(background,&delta,&ndelta, + _img,mask,_width,_height,b,nb); + /*{ + FILE *fout; + fout=fopen("background.png","wb"); + image_write_png(background,_width,_height,fout); + fclose(fout); + }*/ + qr_gatos_mask(_img,_img,background,_width,_height,b,nb,delta,ndelta); + free(background); + free(mask); +} + +#else +/*The above algorithms are computationally expensive, and do not work as well + as the simple algorithm below. + Sauvola by itself does an excellent job of classifying regions outside the + QR code as background, which greatly reduces the chance of false alarms. + However, it also tends to over-shrink isolated black dots inside the code, + making them easy to miss with even slight mis-alignment. + Since the Gatos method uses Sauvola as input to its background interpolation + method, it cannot possibly mark any pixels as foreground which Sauvola + classified as background, and thus suffers from the same problem. + The following simple adaptive threshold method does not have this problem, + though it produces essentially random noise outside the QR code region. + QR codes are structured well enough that this does not seem to lead to any + actual false alarms in practice, and it allows many more codes to be + detected and decoded successfully than the Sauvola or Gatos binarization + methods.*/ + +/*A simplified adaptive thresholder. + This compares the current pixel value to the mean value of a (large) window + surrounding it.*/ +unsigned char *qr_binarize(const unsigned char *_img, int _width, int _height) +{ + unsigned char *mask = NULL; + if (_width > 0 && _height > 0) { + unsigned *col_sums; + int logwindw; + int logwindh; + int windw; + int windh; + int y0offs; + int y1offs; + unsigned g; + int x; + int y; + mask = (unsigned char *)malloc(_width * _height * sizeof(*mask)); + /*We keep the window size fairly large to ensure it doesn't fit completely + inside the center of a finder pattern of a version 1 QR code at full + resolution.*/ + for (logwindw = 4; logwindw < 8 && (1 << logwindw) < (_width + 7 >> 3); + logwindw++) + ; + for (logwindh = 4; logwindh < 8 && (1 << logwindh) < (_height + 7 >> 3); + logwindh++) + ; + windw = 1 << logwindw; + windh = 1 << logwindh; + col_sums = (unsigned *)malloc(_width * sizeof(*col_sums)); + /*Initialize sums down each column.*/ + for (x = 0; x < _width; x++) { + g = _img[x]; + col_sums[x] = (g << logwindh - 1) + g; + } + for (y = 1; y < (windh >> 1); y++) { + y1offs = QR_MINI(y, _height - 1) * _width; + for (x = 0; x < _width; x++) { + g = _img[y1offs + x]; + col_sums[x] += g; + } + } + for (y = 0; y < _height; y++) { + unsigned m; + int x0; + int x1; + /*Initialize the sum over the window.*/ + m = (col_sums[0] << logwindw - 1) + col_sums[0]; + for (x = 1; x < (windw >> 1); x++) { + x1 = QR_MINI(x, _width - 1); + m += col_sums[x1]; + } + for (x = 0; x < _width; x++) { + /*Perform the test against the threshold T = (m/n)-D, + where n=windw*windh and D=3.*/ + g = _img[y * _width + x]; + mask[y * _width + x] = -(g + 3 << logwindw + logwindh < m) & + 0xFF; + /*Update the window sum.*/ + if (x + 1 < _width) { + x0 = QR_MAXI(0, x - (windw >> 1)); + x1 = QR_MINI(x + (windw >> 1), _width - 1); + m += col_sums[x1] - col_sums[x0]; + } + } + /*Update the column sums.*/ + if (y + 1 < _height) { + y0offs = QR_MAXI(0, y - (windh >> 1)) * _width; + y1offs = QR_MINI(y + (windh >> 1), _height - 1) * _width; + for (x = 0; x < _width; x++) { + col_sums[x] -= _img[y0offs + x]; + col_sums[x] += _img[y1offs + x]; + } + } + } + free(col_sums); + } +#if defined(QR_DEBUG) + { + FILE *fout; + fout = fopen("binary.png", "wb"); + image_write_png(_img, _width, _height, fout); + fclose(fout); + } +#endif + return (mask); +} +#endif + +#if defined(TEST_BINARIZE) +#include <stdio.h> +#include "image.c" + +int main(int _argc, char **_argv) +{ + unsigned char *img; + int width; + int height; + int x; + int y; + if (_argc < 2) { + fprintf(stderr, "usage: %s <image>.png\n", _argv[0]); + return EXIT_FAILURE; + } + /*width=1182; + height=1181; + img=(unsigned char *)malloc(width*height*sizeof(*img)); + for(y=0;y<height;y++)for(x=0;x<width;x++){ + img[y*width+x]=(unsigned char)(-((x&1)^(y&1))&0xFF); + }*/ + { + FILE *fin; + fin = fopen(_argv[1], "rb"); + image_read_png(&img, &width, &height, fin); + fclose(fin); + } + qr_binarize(img, width, height); + /*{ + FILE *fout; + fout=fopen("binary.png","wb"); + image_write_png(img,width,height,fout); + fclose(fout); + }*/ + free(img); + return EXIT_SUCCESS; +} +#endif diff --git a/zbar/qrcode/binarize.h b/zbar/qrcode/binarize.h new file mode 100644 index 0000000..3ec5bc4 --- /dev/null +++ b/zbar/qrcode/binarize.h @@ -0,0 +1,17 @@ +/*Copyright (C) 2008-2009 Timothy B. Terriberry (tterribe@xiph.org) + You can redistribute this library and/or modify it under the terms of the + GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version.*/ +#if !defined(_qrcode_binarize_H) +#define _qrcode_binarize_H (1) + +void qr_image_cross_masking_median_filter(unsigned char *_img, int _width, + int _height); + +void qr_wiener_filter(unsigned char *_img, int _width, int _height); + +/*Binarizes a grayscale image.*/ +unsigned char *qr_binarize(const unsigned char *_img, int _width, int _height); + +#endif diff --git a/zbar/qrcode/isaac.c b/zbar/qrcode/isaac.c new file mode 100644 index 0000000..88889f3 --- /dev/null +++ b/zbar/qrcode/isaac.c @@ -0,0 +1,145 @@ +/*Written by Timothy B. Terriberry (tterribe@xiph.org) 1999-2009 public domain. + Based on the public domain implementation by Robert J. Jenkins Jr.*/ +#include "isaac.h" +#include <float.h> +#include <math.h> +#include <string.h> + +#define ISAAC_MASK (0xFFFFFFFFU) + +static void isaac_update(isaac_ctx *_ctx) +{ + unsigned *m; + unsigned *r; + unsigned a; + unsigned b; + unsigned x; + unsigned y; + int i; + m = _ctx->m; + r = _ctx->r; + a = _ctx->a; + b = _ctx->b + (++_ctx->c) & ISAAC_MASK; + for (i = 0; i < ISAAC_SZ / 2; i++) { + x = m[i]; + a = (a ^ a << 13) + m[i + ISAAC_SZ / 2] & ISAAC_MASK; + m[i] = y = m[(x & ISAAC_SZ - 1 << 2) >> 2] + a + b & ISAAC_MASK; + r[i] = b = m[y >> ISAAC_SZ_LOG + 2 & ISAAC_SZ - 1] + x & ISAAC_MASK; + x = m[++i]; + a = (a ^ a >> 6) + m[i + ISAAC_SZ / 2] & ISAAC_MASK; + m[i] = y = m[(x & ISAAC_SZ - 1 << 2) >> 2] + a + b & ISAAC_MASK; + r[i] = b = m[y >> ISAAC_SZ_LOG + 2 & ISAAC_SZ - 1] + x & ISAAC_MASK; + x = m[++i]; + a = (a ^ a << 2) + m[i + ISAAC_SZ / 2] & ISAAC_MASK; + m[i] = y = m[(x & ISAAC_SZ - 1 << 2) >> 2] + a + b & ISAAC_MASK; + r[i] = b = m[y >> ISAAC_SZ_LOG + 2 & ISAAC_SZ - 1] + x & ISAAC_MASK; + x = m[++i]; + a = (a ^ a >> 16) + m[i + ISAAC_SZ / 2] & ISAAC_MASK; + m[i] = y = m[(x & ISAAC_SZ - 1 << 2) >> 2] + a + b & ISAAC_MASK; + r[i] = b = m[y >> ISAAC_SZ_LOG + 2 & ISAAC_SZ - 1] + x & ISAAC_MASK; + } + for (i = ISAAC_SZ / 2; i < ISAAC_SZ; i++) { + x = m[i]; + a = (a ^ a << 13) + m[i - ISAAC_SZ / 2] & ISAAC_MASK; + m[i] = y = m[(x & ISAAC_SZ - 1 << 2) >> 2] + a + b & ISAAC_MASK; + r[i] = b = m[y >> ISAAC_SZ_LOG + 2 & ISAAC_SZ - 1] + x & ISAAC_MASK; + x = m[++i]; + a = (a ^ a >> 6) + m[i - ISAAC_SZ / 2] & ISAAC_MASK; + m[i] = y = m[(x & ISAAC_SZ - 1 << 2) >> 2] + a + b & ISAAC_MASK; + r[i] = b = m[y >> ISAAC_SZ_LOG + 2 & ISAAC_SZ - 1] + x & ISAAC_MASK; + x = m[++i]; + a = (a ^ a << 2) + m[i - ISAAC_SZ / 2] & ISAAC_MASK; + m[i] = y = m[(x & ISAAC_SZ - 1 << 2) >> 2] + a + b & ISAAC_MASK; + r[i] = b = m[y >> ISAAC_SZ_LOG + 2 & ISAAC_SZ - 1] + x & ISAAC_MASK; + x = m[++i]; + a = (a ^ a >> 16) + m[i - ISAAC_SZ / 2] & ISAAC_MASK; + m[i] = y = m[(x & ISAAC_SZ - 1 << 2) >> 2] + a + b & ISAAC_MASK; + r[i] = b = m[y >> ISAAC_SZ_LOG + 2 & ISAAC_SZ - 1] + x & ISAAC_MASK; + } + _ctx->b = b; + _ctx->a = a; + _ctx->n = ISAAC_SZ; +} + +static void isaac_mix(unsigned _x[8]) +{ + static const unsigned char SHIFT[8] = { 11, 2, 8, 16, 10, 4, 8, 9 }; + int i; + for (i = 0; i < 8; i++) { + _x[i] ^= _x[i + 1 & 7] << SHIFT[i]; + _x[i + 3 & 7] += _x[i]; + _x[i + 1 & 7] += _x[i + 2 & 7]; + i++; + _x[i] ^= _x[i + 1 & 7] >> SHIFT[i]; + _x[i + 3 & 7] += _x[i]; + _x[i + 1 & 7] += _x[i + 2 & 7]; + } +} + +void isaac_init(isaac_ctx *_ctx, const void *_seed, int _nseed) +{ + const unsigned char *seed; + unsigned *m; + unsigned *r; + unsigned x[8]; + int i; + int j; + _ctx->a = _ctx->b = _ctx->c = 0; + m = _ctx->m; + r = _ctx->r; + x[0] = x[1] = x[2] = x[3] = x[4] = x[5] = x[6] = x[7] = 0x9E3779B9; + for (i = 0; i < 4; i++) + isaac_mix(x); + if (_nseed > ISAAC_SEED_SZ_MAX) + _nseed = ISAAC_SEED_SZ_MAX; + seed = (const unsigned char *)_seed; + for (i = 0; i < (_nseed >> 2); i++) { + r[i] = seed[i << 2 | 3] << 24 | seed[i << 2 | 2] << 16 | + seed[i << 2 | 1] << 8 | seed[i << 2]; + } + if (_nseed & 3) { + r[i] = seed[i << 2]; + for (j = 1; j < (_nseed & 3); j++) + r[i] += seed[i << 2 | j] << (j << 3); + i++; + } + memset(r + i, 0, (ISAAC_SZ - i) * sizeof(*r)); + for (i = 0; i < ISAAC_SZ; i += 8) { + for (j = 0; j < 8; j++) + x[j] += r[i + j]; + isaac_mix(x); + memcpy(m + i, x, sizeof(x)); + } + for (i = 0; i < ISAAC_SZ; i += 8) { + for (j = 0; j < 8; j++) + x[j] += m[i + j]; + isaac_mix(x); + memcpy(m + i, x, sizeof(x)); + } + isaac_update(_ctx); +} + +unsigned isaac_next_uint32(isaac_ctx *_ctx) +{ + if (!_ctx->n) + isaac_update(_ctx); + return _ctx->r[--_ctx->n]; +} + +/*Returns a uniform random integer less than the given maximum value. + _n: The upper bound on the range of numbers returned (not inclusive). + This must be strictly less than 2**32. + Return: An integer uniformly distributed between 0 (inclusive) and _n + (exclusive).*/ +unsigned isaac_next_uint(isaac_ctx *_ctx, unsigned _n) +{ + unsigned r; + unsigned v; + unsigned d; + do { + r = isaac_next_uint32(_ctx); + v = r % _n; + d = r - v; + } while ((d + _n - 1 & ISAAC_MASK) < d); + return v; +} diff --git a/zbar/qrcode/isaac.h b/zbar/qrcode/isaac.h new file mode 100644 index 0000000..e0b10e7 --- /dev/null +++ b/zbar/qrcode/isaac.h @@ -0,0 +1,34 @@ +/*Written by Timothy B. Terriberry (tterribe@xiph.org) 1999-2009 public domain. + Based on the public domain implementation by Robert J. Jenkins Jr.*/ +#if !defined(_isaac_H) +#define _isaac_H (1) + +typedef struct isaac_ctx isaac_ctx; + +#define ISAAC_SZ_LOG (8) +#define ISAAC_SZ (1 << ISAAC_SZ_LOG) +#define ISAAC_SEED_SZ_MAX (ISAAC_SZ << 2) + +/*ISAAC is the most advanced of a series of Pseudo-Random Number Generators + designed by Robert J. Jenkins Jr. in 1996. + http://www.burtleburtle.net/bob/rand/isaac.html + To quote: + No efficient method is known for deducing their internal states. + ISAAC requires an amortized 18.75 instructions to produce a 32-bit value. + There are no cycles in ISAAC shorter than 2**40 values. + The expected cycle length is 2**8295 values.*/ +struct isaac_ctx { + unsigned n; + unsigned r[ISAAC_SZ]; + unsigned m[ISAAC_SZ]; + unsigned a; + unsigned b; + unsigned c; +}; + +void isaac_init(isaac_ctx *_ctx, const void *_seed, int _nseed); + +unsigned isaac_next_uint32(isaac_ctx *_ctx); +unsigned isaac_next_uint(isaac_ctx *_ctx, unsigned _n); + +#endif diff --git a/zbar/qrcode/qrdec.c b/zbar/qrcode/qrdec.c new file mode 100644 index 0000000..e4c922c --- /dev/null +++ b/zbar/qrcode/qrdec.c @@ -0,0 +1,4395 @@ +/*Copyright (C) 2008-2009 Timothy B. Terriberry (tterribe@xiph.org) + You can redistribute this library and/or modify it under the terms of the + GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version.*/ + +#include "config.h" + +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include "bch15_5.h" +#include "binarize.h" +#include "error.h" +#include "image.h" +#include "isaac.h" +#include "qrcode.h" +#include "rs.h" +#include "svg.h" +#include "util.h" + +#include "qrdec.h" + +typedef int qr_line[3]; + +typedef struct qr_finder_cluster qr_finder_cluster; +typedef struct qr_finder_edge_pt qr_finder_edge_pt; +typedef struct qr_finder_center qr_finder_center; + +typedef struct qr_aff qr_aff; +typedef struct qr_hom qr_hom; + +typedef struct qr_finder qr_finder; + +typedef struct qr_hom_cell qr_hom_cell; +typedef struct qr_sampling_grid qr_sampling_grid; +typedef struct qr_pack_buf qr_pack_buf; + +/*The number of bits in an int. + Note the cast to (int): this prevents this value from "promoting" whole + expressions to an (unsigned) size_t.*/ +#define QR_INT_BITS ((int)sizeof(int) * CHAR_BIT) +#define QR_INT_LOGBITS (QR_ILOG(QR_INT_BITS)) + +/*A 14 bit resolution for a homography ensures that the ideal module size for a + version 40 code differs from that of a version 39 code by at least 2.*/ +#define QR_HOM_BITS (14) + +/*The number of bits of sub-module precision to use when searching for + alignment patterns. + Two bits allows an alignment pattern to be found even if the modules have + been eroded by up to 50% (due to blurring, etc.). + This must be at least one, since it affects the dynamic range of the + transforms, and we sample at half-module resolution to compute a bounding + quadrilateral for the code.*/ +#define QR_ALIGN_SUBPREC (2) + +/* collection of finder lines */ +typedef struct qr_finder_lines { + qr_finder_line *lines; + int nlines, clines; +} qr_finder_lines; + +struct qr_reader { + /*The GF(256) representation used in Reed-Solomon decoding.*/ + rs_gf256 gf; + /*The random number generator used by RANSAC.*/ + isaac_ctx isaac; + /* current finder state, horizontal and vertical lines */ + qr_finder_lines finder_lines[2]; +}; + +/*Initializes a client reader handle.*/ +static void qr_reader_init(qr_reader *reader) +{ + /*time_t now; + now=time(NULL); + isaac_init(&_reader->isaac,&now,sizeof(now));*/ + isaac_init(&reader->isaac, NULL, 0); + rs_gf256_init(&reader->gf, QR_PPOLY); +} + +/*Allocates a client reader handle.*/ +qr_reader *_zbar_qr_create(void) +{ + qr_reader *reader = (qr_reader *)calloc(1, sizeof(*reader)); + qr_reader_init(reader); + return (reader); +} + +/*Frees a client reader handle.*/ +void _zbar_qr_destroy(qr_reader *reader) +{ + zprintf(1, "max finder lines = %dx%d\n", reader->finder_lines[0].clines, + reader->finder_lines[1].clines); + if (reader->finder_lines[0].lines) + free(reader->finder_lines[0].lines); + if (reader->finder_lines[1].lines) + free(reader->finder_lines[1].lines); + free(reader); +} + +/* reset finder state between scans */ +void _zbar_qr_reset(qr_reader *reader) +{ + reader->finder_lines[0].nlines = 0; + reader->finder_lines[1].nlines = 0; +} + +/*A cluster of lines crossing a finder pattern (all in the same direction).*/ +struct qr_finder_cluster { + /*Pointers to the lines crossing the pattern.*/ + qr_finder_line **lines; + /*The number of lines in the cluster.*/ + int nlines; +}; + +/*A point on the edge of a finder pattern. + These are obtained from the endpoints of the lines crossing this particular + pattern.*/ +struct qr_finder_edge_pt { + /*The location of the edge point.*/ + qr_point pos; + /*A label classifying which edge this belongs to: + 0: negative u edge (left) + 1: positive u edge (right) + 2: negative v edge (top) + 3: positive v edge (bottom)*/ + int edge; + /*The (signed) perpendicular distance of the edge point from a line parallel + to the edge passing through the finder center, in (u,v) coordinates. + This is also re-used by RANSAC to store inlier flags.*/ + int extent; +}; + +/*The center of a finder pattern obtained from the crossing of one or more + clusters of horizontal finder lines with one or more clusters of vertical + finder lines.*/ +struct qr_finder_center { + /*The estimated location of the finder center.*/ + qr_point pos; + /*The list of edge points from the crossing lines.*/ + qr_finder_edge_pt *edge_pts; + /*The number of edge points from the crossing lines.*/ + int nedge_pts; +}; + +static int qr_finder_vline_cmp(const void *_a, const void *_b) +{ + const qr_finder_line *a; + const qr_finder_line *b; + a = (const qr_finder_line *)_a; + b = (const qr_finder_line *)_b; + return ((a->pos[0] > b->pos[0]) - (a->pos[0] < b->pos[0]) << 1) + + (a->pos[1] > b->pos[1]) - (a->pos[1] < b->pos[1]); +} + +/*Clusters adjacent lines into groups that are large enough to be crossing a + finder pattern (relative to their length). + _clusters: The buffer in which to store the clusters found. + _neighbors: The buffer used to store the lists of lines in each cluster. + _lines: The list of lines to cluster. + Horizontal lines must be sorted in ascending order by Y + coordinate, with ties broken by X coordinate. + Vertical lines must be sorted in ascending order by X coordinate, + with ties broken by Y coordinate. + _nlines: The number of lines in the set of lines to cluster. + _v: 0 for horizontal lines, or 1 for vertical lines. + Return: The number of clusters.*/ +static int qr_finder_cluster_lines(qr_finder_cluster *_clusters, + qr_finder_line **_neighbors, + qr_finder_line *_lines, int _nlines, int _v) +{ + unsigned char *mark; + qr_finder_line **neighbors; + int nneighbors; + int nclusters; + int i; + /*TODO: Kalman filters!*/ + mark = (unsigned char *)calloc(_nlines, sizeof(*mark)); + neighbors = _neighbors; + nclusters = 0; + for (i = 0; i < _nlines - 1; i++) + if (!mark[i]) { + int len; + int j; + nneighbors = 1; + neighbors[0] = _lines + i; + len = _lines[i].len; + for (j = i + 1; j < _nlines; j++) + if (!mark[j]) { + const qr_finder_line *a; + const qr_finder_line *b; + int thresh; + a = neighbors[nneighbors - 1]; + b = _lines + j; + /*The clustering threshold is proportional to the size of the lines, + since minor noise in large areas can interrupt patterns more easily + at high resolutions.*/ + thresh = a->len + 7 >> 2; + if (abs(a->pos[1 - _v] - b->pos[1 - _v]) > thresh) + break; + if (abs(a->pos[_v] - b->pos[_v]) > thresh) + continue; + if (abs(a->pos[_v] + a->len - b->pos[_v] - b->len) > thresh) + continue; + if (a->boffs > 0 && b->boffs > 0 && + abs(a->pos[_v] - a->boffs - b->pos[_v] + b->boffs) > + thresh) { + continue; + } + if (a->eoffs > 0 && b->eoffs > 0 && + abs(a->pos[_v] + a->len + a->eoffs - b->pos[_v] - + b->len - b->eoffs) > thresh) { + continue; + } + neighbors[nneighbors++] = _lines + j; + len += b->len; + } + /*We require at least three lines to form a cluster, which eliminates a + large number of false positives, saving considerable decoding time. + This should still be sufficient for 1-pixel codes with no noise.*/ + if (nneighbors < 3) + continue; + /*The expected number of lines crossing a finder pattern is equal to their + average length. + We accept the cluster if size is at least 1/3 their average length (this + is a very small threshold, but was needed for some test images).*/ + len = ((len << 1) + nneighbors) / (nneighbors << 1); + if (nneighbors * (5 << QR_FINDER_SUBPREC) >= len) { + _clusters[nclusters].lines = neighbors; + _clusters[nclusters].nlines = nneighbors; + for (j = 0; j < nneighbors; j++) + mark[neighbors[j] - _lines] = 1; + neighbors += nneighbors; + nclusters++; + } + } + free(mark); + return nclusters; +} + +/*Adds the coordinates of the edge points from the lines contained in the + given list of clusters to the list of edge points for a finder center. + Only the edge point position is initialized. + The edge label and extent are set by qr_finder_edge_pts_aff_classify() + or qr_finder_edge_pts_hom_classify(). + _edge_pts: The buffer in which to store the edge points. + _nedge_pts: The current number of edge points in the buffer. + _neighbors: The list of lines in the cluster. + _nneighbors: The number of lines in the list of lines in the cluster. + _v: 0 for horizontal lines and 1 for vertical lines. + Return: The new total number of edge points.*/ +static int qr_finder_edge_pts_fill(qr_finder_edge_pt *_edge_pts, int _nedge_pts, + qr_finder_cluster **_neighbors, + int _nneighbors, int _v) +{ + int i; + for (i = 0; i < _nneighbors; i++) { + qr_finder_cluster *c; + int j; + c = _neighbors[i]; + for (j = 0; j < c->nlines; j++) { + qr_finder_line *l; + l = c->lines[j]; + if (l->boffs > 0) { + _edge_pts[_nedge_pts].pos[0] = l->pos[0]; + _edge_pts[_nedge_pts].pos[1] = l->pos[1]; + _edge_pts[_nedge_pts].pos[_v] -= l->boffs; + _nedge_pts++; + } + if (l->eoffs > 0) { + _edge_pts[_nedge_pts].pos[0] = l->pos[0]; + _edge_pts[_nedge_pts].pos[1] = l->pos[1]; + _edge_pts[_nedge_pts].pos[_v] += l->len + l->eoffs; + _nedge_pts++; + } + } + } + return _nedge_pts; +} + +static int qr_finder_center_cmp(const void *_a, const void *_b) +{ + const qr_finder_center *a; + const qr_finder_center *b; + a = (const qr_finder_center *)_a; + b = (const qr_finder_center *)_b; + return ((b->nedge_pts > a->nedge_pts) - (b->nedge_pts < a->nedge_pts) + << 2) + + ((a->pos[1] > b->pos[1]) - (a->pos[1] < b->pos[1]) << 1) + + (a->pos[0] > b->pos[0]) - (a->pos[0] < b->pos[0]); +} + +/*Determine if a horizontal line crosses a vertical line. + _hline: The horizontal line. + _vline: The vertical line. + Return: A non-zero value if the lines cross, or zero if they do not.*/ +static int qr_finder_lines_are_crossing(const qr_finder_line *_hline, + const qr_finder_line *_vline) +{ + return _hline->pos[0] <= _vline->pos[0] && + _vline->pos[0] < _hline->pos[0] + _hline->len && + _vline->pos[1] <= _hline->pos[1] && + _hline->pos[1] < _vline->pos[1] + _vline->len; +} + +/*Finds horizontal clusters that cross corresponding vertical clusters, + presumably corresponding to a finder center. + _center: The buffer in which to store putative finder centers. + _edge_pts: The buffer to use for the edge point lists for each finder + center. + _hclusters: The clusters of horizontal lines crossing finder patterns. + _nhclusters: The number of horizontal line clusters. + _vclusters: The clusters of vertical lines crossing finder patterns. + _nvclusters: The number of vertical line clusters. + Return: The number of putative finder centers.*/ +static int qr_finder_find_crossings(qr_finder_center *_centers, + qr_finder_edge_pt *_edge_pts, + qr_finder_cluster *_hclusters, + int _nhclusters, + qr_finder_cluster *_vclusters, + int _nvclusters) +{ + qr_finder_cluster **hneighbors; + qr_finder_cluster **vneighbors; + unsigned char *hmark; + unsigned char *vmark; + int ncenters; + int i; + int j; + hneighbors = + (qr_finder_cluster **)malloc(_nhclusters * sizeof(*hneighbors)); + vneighbors = + (qr_finder_cluster **)malloc(_nvclusters * sizeof(*vneighbors)); + hmark = (unsigned char *)calloc(_nhclusters, sizeof(*hmark)); + vmark = (unsigned char *)calloc(_nvclusters, sizeof(*vmark)); + ncenters = 0; + /*TODO: This may need some re-working. + We should be finding groups of clusters such that _all_ horizontal lines in + _all_ horizontal clusters in the group cross _all_ vertical lines in _all_ + vertical clusters in the group. + This is equivalent to finding the maximum bipartite clique in the + connectivity graph, which requires linear progamming to solve efficiently. + In principle, that is easy to do, but a realistic implementation without + floating point is a lot of work (and computationally expensive). + Right now we are relying on a sufficient border around the finder patterns + to prevent false positives.*/ + for (i = 0; i < _nhclusters; i++) + if (!hmark[i]) { + qr_finder_line *a; + qr_finder_line *b; + int nvneighbors; + int nedge_pts; + int y; + a = _hclusters[i].lines[_hclusters[i].nlines >> 1]; + y = nvneighbors = 0; + for (j = 0; j < _nvclusters; j++) + if (!vmark[j]) { + b = _vclusters[j].lines[_vclusters[j].nlines >> 1]; + if (qr_finder_lines_are_crossing(a, b)) { + vmark[j] = 1; + y += (b->pos[1] << 1) + b->len; + if (b->boffs > 0 && b->eoffs > 0) + y += b->eoffs - b->boffs; + vneighbors[nvneighbors++] = _vclusters + j; + } + } + if (nvneighbors > 0) { + qr_finder_center *c; + int nhneighbors; + int x; + x = (a->pos[0] << 1) + a->len; + if (a->boffs > 0 && a->eoffs > 0) + x += a->eoffs - a->boffs; + hneighbors[0] = _hclusters + i; + nhneighbors = 1; + j = nvneighbors >> 1; + b = vneighbors[j]->lines[vneighbors[j]->nlines >> 1]; + for (j = i + 1; j < _nhclusters; j++) + if (!hmark[j]) { + a = _hclusters[j].lines[_hclusters[j].nlines >> 1]; + if (qr_finder_lines_are_crossing(a, b)) { + hmark[j] = 1; + x += (a->pos[0] << 1) + a->len; + if (a->boffs > 0 && a->eoffs > 0) + x += a->eoffs - a->boffs; + hneighbors[nhneighbors++] = _hclusters + j; + } + } + c = _centers + ncenters++; + c->pos[0] = (x + nhneighbors) / (nhneighbors << 1); + c->pos[1] = (y + nvneighbors) / (nvneighbors << 1); + c->edge_pts = _edge_pts; + nedge_pts = qr_finder_edge_pts_fill(_edge_pts, 0, hneighbors, + nhneighbors, 0); + nedge_pts = qr_finder_edge_pts_fill(_edge_pts, nedge_pts, + vneighbors, nvneighbors, 1); + c->nedge_pts = nedge_pts; + _edge_pts += nedge_pts; + } + } + free(vmark); + free(hmark); + free(vneighbors); + free(hneighbors); + /*Sort the centers by decreasing numbers of edge points.*/ + qsort(_centers, ncenters, sizeof(*_centers), qr_finder_center_cmp); + return ncenters; +} + +/*Locates a set of putative finder centers in the image. + First we search for horizontal and vertical lines that have + (dark:light:dark:light:dark) runs with size ratios of roughly (1:1:3:1:1). + Then we cluster them into groups such that each subsequent pair of endpoints + is close to the line before it in the cluster. + This will locate many line clusters that don't cross a finder pattern, but + qr_finder_find_crossings() will filter most of them out. + Where horizontal and vertical clusters cross, a prospective finder center is + returned. + _centers: Returns a pointer to a freshly-allocated list of finder centers. + This must be freed by the caller. + _edge_pts: Returns a pointer to a freshly-allocated list of edge points + around those centers. + This must be freed by the caller. + _img: The binary image to search. + _width: The width of the image. + _height: The height of the image. + Return: The number of putative finder centers located.*/ +static int qr_finder_centers_locate(qr_finder_center **_centers, + qr_finder_edge_pt **_edge_pts, + qr_reader *reader, int _width, int _height) +{ + qr_finder_line *hlines = reader->finder_lines[0].lines; + int nhlines = reader->finder_lines[0].nlines; + qr_finder_line *vlines = reader->finder_lines[1].lines; + int nvlines = reader->finder_lines[1].nlines; + + qr_finder_line **hneighbors; + qr_finder_cluster *hclusters; + int nhclusters; + qr_finder_line **vneighbors; + qr_finder_cluster *vclusters; + int nvclusters; + int ncenters; + + /*Cluster the detected lines.*/ + hneighbors = (qr_finder_line **)malloc(nhlines * sizeof(*hneighbors)); + /*We require more than one line per cluster, so there are at most nhlines/2.*/ + hclusters = + (qr_finder_cluster *)malloc((nhlines >> 1) * sizeof(*hclusters)); + nhclusters = + qr_finder_cluster_lines(hclusters, hneighbors, hlines, nhlines, 0); + /*We need vertical lines to be sorted by X coordinate, with ties broken by Y + coordinate, for clustering purposes. + We scan the image in the opposite order for cache efficiency, so sort the + lines we found here.*/ + qsort(vlines, nvlines, sizeof(*vlines), qr_finder_vline_cmp); + vneighbors = (qr_finder_line **)malloc(nvlines * sizeof(*vneighbors)); + /*We require more than one line per cluster, so there are at most nvlines/2.*/ + vclusters = + (qr_finder_cluster *)malloc((nvlines >> 1) * sizeof(*vclusters)); + nvclusters = + qr_finder_cluster_lines(vclusters, vneighbors, vlines, nvlines, 1); + /*Find line crossings among the clusters.*/ + if (nhclusters >= 3 && nvclusters >= 3) { + qr_finder_edge_pt *edge_pts; + qr_finder_center *centers; + int nedge_pts; + int i; + nedge_pts = 0; + for (i = 0; i < nhclusters; i++) + nedge_pts += hclusters[i].nlines; + for (i = 0; i < nvclusters; i++) + nedge_pts += vclusters[i].nlines; + nedge_pts <<= 1; + edge_pts = (qr_finder_edge_pt *)malloc(nedge_pts * sizeof(*edge_pts)); + centers = (qr_finder_center *)malloc(QR_MINI(nhclusters, nvclusters) * + sizeof(*centers)); + ncenters = qr_finder_find_crossings(centers, edge_pts, hclusters, + nhclusters, vclusters, nvclusters); + *_centers = centers; + *_edge_pts = edge_pts; + } else + ncenters = 0; + free(vclusters); + free(vneighbors); + free(hclusters); + free(hneighbors); + return ncenters; +} + +static void qr_point_translate(qr_point _point, int _dx, int _dy) +{ + _point[0] += _dx; + _point[1] += _dy; +} + +static unsigned qr_point_distance2(const qr_point _p1, const qr_point _p2) +{ + return (_p1[0] - _p2[0]) * (_p1[0] - _p2[0]) + + (_p1[1] - _p2[1]) * (_p1[1] - _p2[1]); +} + +/*Returns the cross product of the three points, which is positive if they are + in CCW order (in a right-handed coordinate system), and 0 if they're + colinear.*/ +static int qr_point_ccw(const qr_point _p0, const qr_point _p1, + const qr_point _p2) +{ + return (_p1[0] - _p0[0]) * (_p2[1] - _p0[1]) - + (_p1[1] - _p0[1]) * (_p2[0] - _p0[0]); +} + +/*Evaluates a line equation at a point. + _line: The line to evaluate. + _x: The X coordinate of the point. + _y: The y coordinate of the point. + Return: The value of the line equation _line[0]*_x+_line[1]*_y+_line[2].*/ +static int qr_line_eval(qr_line _line, int _x, int _y) +{ + return _line[0] * _x + _line[1] * _y + _line[2]; +} + +/*Computes a line passing through the given point using the specified second + order statistics. + Given a line defined by the equation + A*x+B*y+C = 0 , + the least squares fit to n points (x_i,y_i) must satisfy the two equations + A^2 + (Syy - Sxx)/Sxy*A*B - B^2 = 0 , + C = -(xbar*A+ybar*B) , + where + xbar = sum(x_i)/n , + ybar = sum(y_i)/n , + Sxx = sum((x_i-xbar)**2) , + Sxy = sum((x_i-xbar)*(y_i-ybar)) , + Syy = sum((y_i-ybar)**2) . + The quadratic can be solved for the ratio (A/B) or (B/A): + A/B = (Syy + sqrt((Sxx-Syy)**2 + 4*Sxy**2) - Sxx)/(-2*Sxy) , + B/A = (Sxx + sqrt((Sxx-Syy)**2 + 4*Sxy**2) - Syy)/(-2*Sxy) . + We pick the one that leads to the larger ratio to avoid destructive + cancellation (and e.g., 0/0 for horizontal or vertical lines). + The above solutions correspond to the actual minimum. + The other solution of the quadratic corresponds to a saddle point of the + least squares objective function. + _l: Returns the fitted line values A, B, and C. + _x0: The X coordinate of the point the line is supposed to pass through. + _y0: The Y coordinate of the point the line is supposed to pass through. + _sxx: The sum Sxx. + _sxy: The sum Sxy. + _syy: The sum Syy. + _res: The maximum number of bits occupied by the product of any two of + _l[0] or _l[1]. + Smaller numbers give less angular resolution, but allow more overhead + room for computations.*/ +static void qr_line_fit(qr_line _l, int _x0, int _y0, int _sxx, int _sxy, + int _syy, int _res) +{ + int dshift; + int dround; + int u; + int v; + int w; + u = abs(_sxx - _syy); + v = -_sxy << 1; + w = qr_ihypot(u, v); + /*Computations in later stages can easily overflow with moderate sizes, so we + compute a shift factor to scale things down into a manageable range. + We ensure that the product of any two of _l[0] and _l[1] fits within _res + bits, which allows computation of line intersections without overflow.*/ + dshift = + QR_MAXI(0, QR_MAXI(qr_ilog(u), qr_ilog(abs(v))) + 1 - (_res + 1 >> 1)); + dround = (1 << dshift) >> 1; + if (_sxx > _syy) { + _l[0] = v + dround >> dshift; + _l[1] = u + w + dround >> dshift; + } else { + _l[0] = u + w + dround >> dshift; + _l[1] = v + dround >> dshift; + } + _l[2] = -(_x0 * _l[0] + _y0 * _l[1]); +} + +/*Perform a least-squares line fit to a list of points. + At least two points are required.*/ +static void qr_line_fit_points(qr_line _l, qr_point *_p, int _np, int _res) +{ + int sx; + int sy; + int xmin; + int xmax; + int ymin; + int ymax; + int xbar; + int ybar; + int dx; + int dy; + int sxx; + int sxy; + int syy; + int sshift; + int sround; + int i; + sx = sy = 0; + ymax = xmax = INT_MIN; + ymin = xmin = INT_MAX; + for (i = 0; i < _np; i++) { + sx += _p[i][0]; + xmin = QR_MINI(xmin, _p[i][0]); + xmax = QR_MAXI(xmax, _p[i][0]); + sy += _p[i][1]; + ymin = QR_MINI(ymin, _p[i][1]); + ymax = QR_MAXI(ymax, _p[i][1]); + } + xbar = (sx + (_np >> 1)) / _np; + ybar = (sy + (_np >> 1)) / _np; + sshift = + QR_MAXI(0, qr_ilog(_np * QR_MAXI(QR_MAXI(xmax - xbar, xbar - xmin), + QR_MAXI(ymax - ybar, ybar - ymin))) - + (QR_INT_BITS - 1 >> 1)); + sround = (1 << sshift) >> 1; + sxx = sxy = syy = 0; + for (i = 0; i < _np; i++) { + dx = _p[i][0] - xbar + sround >> sshift; + dy = _p[i][1] - ybar + sround >> sshift; + sxx += dx * dx; + sxy += dx * dy; + syy += dy * dy; + } + qr_line_fit(_l, xbar, ybar, sxx, sxy, syy, _res); +} + +static void qr_line_orient(qr_line _l, int _x, int _y) +{ + if (qr_line_eval(_l, _x, _y) < 0) { + _l[0] = -_l[0]; + _l[1] = -_l[1]; + _l[2] = -_l[2]; + } +} + +static int qr_line_isect(qr_point _p, const qr_line _l0, const qr_line _l1) +{ + int d; + int x; + int y; + d = _l0[0] * _l1[1] - _l0[1] * _l1[0]; + if (d == 0) + return -1; + x = _l0[1] * _l1[2] - _l1[1] * _l0[2]; + y = _l1[0] * _l0[2] - _l0[0] * _l1[2]; + if (d < 0) { + x = -x; + y = -y; + d = -d; + } + _p[0] = QR_DIVROUND(x, d); + _p[1] = QR_DIVROUND(y, d); + return 0; +} + +/*An affine homography. + This maps from the image (at subpel resolution) to a square domain with + power-of-two sides (of res bits) and back.*/ +struct qr_aff { + int fwd[2][2]; + int inv[2][2]; + int x0; + int y0; + int res; + int ires; +}; + +static void qr_aff_init(qr_aff *_aff, const qr_point _p0, const qr_point _p1, + const qr_point _p2, int _res) +{ + int det; + int ires; + int dx1; + int dy1; + int dx2; + int dy2; + /*det is ensured to be positive by our caller.*/ + dx1 = _p1[0] - _p0[0]; + dx2 = _p2[0] - _p0[0]; + dy1 = _p1[1] - _p0[1]; + dy2 = _p2[1] - _p0[1]; + det = dx1 * dy2 - dy1 * dx2; + ires = QR_MAXI((qr_ilog(abs(det)) >> 1) - 2, 0); + _aff->fwd[0][0] = dx1; + _aff->fwd[0][1] = dx2; + _aff->fwd[1][0] = dy1; + _aff->fwd[1][1] = dy2; + _aff->inv[0][0] = QR_DIVROUND(dy2 << _res, det >> ires); + _aff->inv[0][1] = QR_DIVROUND(-dx2 << _res, det >> ires); + _aff->inv[1][0] = QR_DIVROUND(-dy1 << _res, det >> ires); + _aff->inv[1][1] = QR_DIVROUND(dx1 << _res, det >> ires); + _aff->x0 = _p0[0]; + _aff->y0 = _p0[1]; + _aff->res = _res; + _aff->ires = ires; +} + +/*Map from the image (at subpel resolution) into the square domain.*/ +static void qr_aff_unproject(qr_point _q, const qr_aff *_aff, int _x, int _y) +{ + _q[0] = _aff->inv[0][0] * (_x - _aff->x0) + + _aff->inv[0][1] * (_y - _aff->y0) + (1 << _aff->ires >> 1) >> + _aff->ires; + _q[1] = _aff->inv[1][0] * (_x - _aff->x0) + + _aff->inv[1][1] * (_y - _aff->y0) + (1 << _aff->ires >> 1) >> + _aff->ires; +} + +/*Map from the square domain into the image (at subpel resolution).*/ +static void qr_aff_project(qr_point _p, const qr_aff *_aff, int _u, int _v) +{ + _p[0] = + (_aff->fwd[0][0] * _u + _aff->fwd[0][1] * _v + (1 << _aff->res - 1) >> + _aff->res) + + _aff->x0; + _p[1] = + (_aff->fwd[1][0] * _u + _aff->fwd[1][1] * _v + (1 << _aff->res - 1) >> + _aff->res) + + _aff->y0; +} + +/*A full homography. + Like the affine homography, this maps from the image (at subpel resolution) + to a square domain with power-of-two sides (of res bits) and back.*/ +struct qr_hom { + int fwd[3][2]; + int inv[3][2]; + int fwd22; + int inv22; + int x0; + int y0; + int res; +}; + +static void qr_hom_init(qr_hom *_hom, int _x0, int _y0, int _x1, int _y1, + int _x2, int _y2, int _x3, int _y3, int _res) +{ + int dx10; + int dx20; + int dx30; + int dx31; + int dx32; + int dy10; + int dy20; + int dy30; + int dy31; + int dy32; + int a20; + int a21; + int a22; + int b0; + int b1; + int b2; + int s1; + int s2; + int r1; + int r2; + dx10 = _x1 - _x0; + dx20 = _x2 - _x0; + dx30 = _x3 - _x0; + dx31 = _x3 - _x1; + dx32 = _x3 - _x2; + dy10 = _y1 - _y0; + dy20 = _y2 - _y0; + dy30 = _y3 - _y0; + dy31 = _y3 - _y1; + dy32 = _y3 - _y2; + a20 = dx32 * dy10 - dx10 * dy32; + a21 = dx20 * dy31 - dx31 * dy20; + a22 = dx32 * dy31 - dx31 * dy32; + /*Figure out if we need to downscale anything.*/ + b0 = qr_ilog(QR_MAXI(abs(dx10), abs(dy10))) + qr_ilog(abs(a20 + a22)); + b1 = qr_ilog(QR_MAXI(abs(dx20), abs(dy20))) + qr_ilog(abs(a21 + a22)); + b2 = qr_ilog(QR_MAXI(QR_MAXI(abs(a20), abs(a21)), abs(a22))); + s1 = QR_MAXI(0, _res + QR_MAXI(QR_MAXI(b0, b1), b2) - (QR_INT_BITS - 2)); + r1 = (1 << s1) >> 1; + /*Compute the final coefficients of the forward transform. + The 32x32->64 bit multiplies are really needed for accuracy with large + versions.*/ + _hom->fwd[0][0] = QR_FIXMUL(dx10, a20 + a22, r1, s1); + _hom->fwd[0][1] = QR_FIXMUL(dx20, a21 + a22, r1, s1); + _hom->x0 = _x0; + _hom->fwd[1][0] = QR_FIXMUL(dy10, a20 + a22, r1, s1); + _hom->fwd[1][1] = QR_FIXMUL(dy20, a21 + a22, r1, s1); + _hom->y0 = _y0; + _hom->fwd[2][0] = a20 + r1 >> s1; + _hom->fwd[2][1] = a21 + r1 >> s1; + _hom->fwd22 = s1 > _res ? a22 + (r1 >> _res) >> s1 - _res : + a22 << _res - s1; + /*Now compute the inverse transform.*/ + b0 = qr_ilog(QR_MAXI(QR_MAXI(abs(dx10), abs(dx20)), abs(dx30))) + + qr_ilog(QR_MAXI(abs(_hom->fwd[0][0]), abs(_hom->fwd[1][0]))); + b1 = qr_ilog(QR_MAXI(QR_MAXI(abs(dy10), abs(dy20)), abs(dy30))) + + qr_ilog(QR_MAXI(abs(_hom->fwd[0][1]), abs(_hom->fwd[1][1]))); + b2 = qr_ilog(abs(a22)) - s1; + s2 = QR_MAXI(0, QR_MAXI(b0, b1) + b2 - (QR_INT_BITS - 3)); + r2 = (1 << s2) >> 1; + s1 += s2; + r1 <<= s2; + /*The 32x32->64 bit multiplies are really needed for accuracy with large + versions.*/ + _hom->inv[0][0] = QR_FIXMUL(_hom->fwd[1][1], a22, r1, s1); + _hom->inv[0][1] = QR_FIXMUL(-_hom->fwd[0][1], a22, r1, s1); + _hom->inv[1][0] = QR_FIXMUL(-_hom->fwd[1][0], a22, r1, s1); + _hom->inv[1][1] = QR_FIXMUL(_hom->fwd[0][0], a22, r1, s1); + _hom->inv[2][0] = + QR_FIXMUL(_hom->fwd[1][0], _hom->fwd[2][1], + -QR_EXTMUL(_hom->fwd[1][1], _hom->fwd[2][0], r2), s2); + _hom->inv[2][1] = + QR_FIXMUL(_hom->fwd[0][1], _hom->fwd[2][0], + -QR_EXTMUL(_hom->fwd[0][0], _hom->fwd[2][1], r2), s2); + _hom->inv22 = QR_FIXMUL(_hom->fwd[0][0], _hom->fwd[1][1], + -QR_EXTMUL(_hom->fwd[0][1], _hom->fwd[1][0], r2), + s2); + _hom->res = _res; +} + +/*Map from the image (at subpel resolution) into the square domain. + Returns a negative value if the point went to infinity.*/ +static int qr_hom_unproject(qr_point _q, const qr_hom *_hom, int _x, int _y) +{ + int x; + int y; + int w; + _x -= _hom->x0; + _y -= _hom->y0; + x = _hom->inv[0][0] * _x + _hom->inv[0][1] * _y; + y = _hom->inv[1][0] * _x + _hom->inv[1][1] * _y; + w = _hom->inv[2][0] * _x + _hom->inv[2][1] * _y + _hom->inv22 + + (1 << _hom->res - 1) >> + _hom->res; + if (w == 0) { + _q[0] = x < 0 ? INT_MIN : INT_MAX; + _q[1] = y < 0 ? INT_MIN : INT_MAX; + return -1; + } else { + if (w < 0) { + x = -x; + y = -y; + w = -w; + } + _q[0] = QR_DIVROUND(x, w); + _q[1] = QR_DIVROUND(y, w); + } + return 0; +} + +/*Finish a partial projection, converting from homogeneous coordinates to the + normal 2-D representation. + In loops, we can avoid many multiplies by computing the homogeneous _x, _y, + and _w incrementally, but we cannot avoid the divisions, done here.*/ +static void qr_hom_fproject(qr_point _p, const qr_hom *_hom, int _x, int _y, + int _w) +{ + if (_w == 0) { + _p[0] = _x < 0 ? INT_MIN : INT_MAX; + _p[1] = _y < 0 ? INT_MIN : INT_MAX; + } else { + if (_w < 0) { + _x = -_x; + _y = -_y; + _w = -_w; + } + _p[0] = QR_DIVROUND(_x, _w) + _hom->x0; + _p[1] = QR_DIVROUND(_y, _w) + _hom->y0; + } +} + +#if defined(QR_DEBUG) +/*Map from the square domain into the image (at subpel resolution). + Currently only used directly by debug code.*/ +static void qr_hom_project(qr_point _p, const qr_hom *_hom, int _u, int _v) +{ + qr_hom_fproject(_p, _hom, _hom->fwd[0][0] * _u + _hom->fwd[0][1] * _v, + _hom->fwd[1][0] * _u + _hom->fwd[1][1] * _v, + _hom->fwd[2][0] * _u + _hom->fwd[2][1] * _v + _hom->fwd22); +} +#endif + +/*All the information we've collected about a finder pattern in the current + configuration.*/ +struct qr_finder { + /*The module size along each axis (in the square domain).*/ + int size[2]; + /*The version estimated from the module size along each axis.*/ + int eversion[2]; + /*The list of classified edge points for each edge.*/ + qr_finder_edge_pt *edge_pts[4]; + /*The number of edge points classified as belonging to each edge.*/ + int nedge_pts[4]; + /*The number of inliers found after running RANSAC on each edge.*/ + int ninliers[4]; + /*The center of the finder pattern (in the square domain).*/ + qr_point o; + /*The finder center information from the original image.*/ + qr_finder_center *c; +}; + +static int qr_cmp_edge_pt(const void *_a, const void *_b) +{ + const qr_finder_edge_pt *a; + const qr_finder_edge_pt *b; + a = (const qr_finder_edge_pt *)_a; + b = (const qr_finder_edge_pt *)_b; + return ((a->edge > b->edge) - (a->edge < b->edge) << 1) + + (a->extent > b->extent) - (a->extent < b->extent); +} + +/*Computes the index of the edge each edge point belongs to, and its (signed) + distance along the corresponding axis from the center of the finder pattern + (in the square domain). + The resulting list of edge points is sorted by edge index, with ties broken + by extent.*/ +static void qr_finder_edge_pts_aff_classify(qr_finder *_f, const qr_aff *_aff) +{ + qr_finder_center *c; + int i; + int e; + c = _f->c; + for (e = 0; e < 4; e++) + _f->nedge_pts[e] = 0; + for (i = 0; i < c->nedge_pts; i++) { + qr_point q; + int d; + qr_aff_unproject(q, _aff, c->edge_pts[i].pos[0], c->edge_pts[i].pos[1]); + qr_point_translate(q, -_f->o[0], -_f->o[1]); + d = abs(q[1]) > abs(q[0]); + e = d << 1 | (q[d] >= 0); + _f->nedge_pts[e]++; + c->edge_pts[i].edge = e; + c->edge_pts[i].extent = q[d]; + } + qsort(c->edge_pts, c->nedge_pts, sizeof(*c->edge_pts), qr_cmp_edge_pt); + _f->edge_pts[0] = c->edge_pts; + for (e = 1; e < 4; e++) + _f->edge_pts[e] = _f->edge_pts[e - 1] + _f->nedge_pts[e - 1]; +} + +/*Computes the index of the edge each edge point belongs to, and its (signed) + distance along the corresponding axis from the center of the finder pattern + (in the square domain). + The resulting list of edge points is sorted by edge index, with ties broken + by extent.*/ +static void qr_finder_edge_pts_hom_classify(qr_finder *_f, const qr_hom *_hom) +{ + qr_finder_center *c; + int i; + int e; + c = _f->c; + for (e = 0; e < 4; e++) + _f->nedge_pts[e] = 0; + for (i = 0; i < c->nedge_pts; i++) { + qr_point q; + int d; + if (qr_hom_unproject(q, _hom, c->edge_pts[i].pos[0], + c->edge_pts[i].pos[1]) >= 0) { + qr_point_translate(q, -_f->o[0], -_f->o[1]); + d = abs(q[1]) > abs(q[0]); + e = d << 1 | (q[d] >= 0); + _f->nedge_pts[e]++; + c->edge_pts[i].edge = e; + c->edge_pts[i].extent = q[d]; + } else { + c->edge_pts[i].edge = 4; + c->edge_pts[i].extent = q[0]; + } + } + qsort(c->edge_pts, c->nedge_pts, sizeof(*c->edge_pts), qr_cmp_edge_pt); + _f->edge_pts[0] = c->edge_pts; + for (e = 1; e < 4; e++) + _f->edge_pts[e] = _f->edge_pts[e - 1] + _f->nedge_pts[e - 1]; +} + +/*TODO: Perhaps these thresholds should be on the module size instead? + Unfortunately, I'd need real-world images of codes with larger versions to + see if these thresholds are still effective, but such versions aren't used + often.*/ + +/*The amount that the estimated version numbers are allowed to differ from the + real version number and still be considered valid.*/ +#define QR_SMALL_VERSION_SLACK (1) +/*Since cell phone cameras can have severe radial distortion, the estimated + version for larger versions can be off by larger amounts.*/ +#define QR_LARGE_VERSION_SLACK (3) + +/*Estimates the size of a module after classifying the edge points. + _width: The distance between UL and UR in the square domain. + _height: The distance between UL and DL in the square domain.*/ +static int qr_finder_estimate_module_size_and_version(qr_finder *_f, int _width, + int _height) +{ + qr_point offs; + int sums[4]; + int nsums[4]; + int usize; + int nusize; + int vsize; + int nvsize; + int uversion; + int vversion; + int e; + offs[0] = offs[1] = 0; + for (e = 0; e < 4; e++) + if (_f->nedge_pts[e] > 0) { + qr_finder_edge_pt *edge_pts; + int sum; + int mean; + int n; + int i; + /*Average the samples for this edge, dropping the top and bottom 25%.*/ + edge_pts = _f->edge_pts[e]; + n = _f->nedge_pts[e]; + sum = 0; + for (i = (n >> 2); i < n - (n >> 2); i++) + sum += edge_pts[i].extent; + n = n - ((n >> 2) << 1); + mean = QR_DIVROUND(sum, n); + offs[e >> 1] += mean; + sums[e] = sum; + nsums[e] = n; + } else + nsums[e] = sums[e] = 0; + /*If we have samples on both sides of an axis, refine our idea of where the + unprojected finder center is located.*/ + if (_f->nedge_pts[0] > 0 && _f->nedge_pts[1] > 0) { + _f->o[0] -= offs[0] >> 1; + sums[0] -= offs[0] * nsums[0] >> 1; + sums[1] -= offs[0] * nsums[1] >> 1; + } + if (_f->nedge_pts[2] > 0 && _f->nedge_pts[3] > 0) { + _f->o[1] -= offs[1] >> 1; + sums[2] -= offs[1] * nsums[2] >> 1; + sums[3] -= offs[1] * nsums[3] >> 1; + } + /*We must have _some_ samples along each axis... if we don't, our transform + must be pretty severely distorting the original square (e.g., with + coordinates so large as to cause overflow).*/ + nusize = nsums[0] + nsums[1]; + if (nusize <= 0) + return -1; + /*The module size is 1/3 the average edge extent.*/ + nusize *= 3; + usize = sums[1] - sums[0]; + usize = ((usize << 1) + nusize) / (nusize << 1); + if (usize <= 0) + return -1; + /*Now estimate the version directly from the module size and the distance + between the finder patterns. + This is done independently using the extents along each axis. + If either falls significantly outside the valid range (1 to 40), reject the + configuration.*/ + uversion = (_width - 8 * usize) / (usize << 2); + if (uversion < 1 || uversion > 40 + QR_LARGE_VERSION_SLACK) + return -1; + /*Now do the same for the other axis.*/ + nvsize = nsums[2] + nsums[3]; + if (nvsize <= 0) + return -1; + nvsize *= 3; + vsize = sums[3] - sums[2]; + vsize = ((vsize << 1) + nvsize) / (nvsize << 1); + if (vsize <= 0) + return -1; + vversion = (_height - 8 * vsize) / (vsize << 2); + if (vversion < 1 || vversion > 40 + QR_LARGE_VERSION_SLACK) + return -1; + /*If the estimated version using extents along one axis is significantly + different than the estimated version along the other axis, then the axes + have significantly different scalings (relative to the grid). + This can happen, e.g., when we have multiple adjacent QR codes, and we've + picked two finder patterns from one and the third finder pattern from + another, e.g.: + X---DL UL---X + |.... |.... + X.... UR.... + Such a configuration might even pass any other geometric checks if we + didn't reject it here.*/ + if (abs(uversion - vversion) > QR_LARGE_VERSION_SLACK) + return -1; + _f->size[0] = usize; + _f->size[1] = vsize; + /*We intentionally do not compute an average version from the sizes along + both axes. + In the presence of projective distortion, one of them will be much more + accurate than the other.*/ + _f->eversion[0] = uversion; + _f->eversion[1] = vversion; + return 0; +} + +/*Eliminate outliers from the classified edge points with RANSAC.*/ +static void qr_finder_ransac(qr_finder *_f, const qr_aff *_hom, + isaac_ctx *_isaac, int _e) +{ + qr_finder_edge_pt *edge_pts; + int best_ninliers; + int n; + edge_pts = _f->edge_pts[_e]; + n = _f->nedge_pts[_e]; + best_ninliers = 0; + if (n > 1) { + int max_iters; + int i; + int j; + /*17 iterations is enough to guarantee an outlier-free sample with more + than 99% probability given as many as 50% outliers.*/ + max_iters = 17; + for (i = 0; i < max_iters; i++) { + qr_point q0; + qr_point q1; + int ninliers; + int thresh; + int p0i; + int p1i; + int *p0; + int *p1; + int j; + /*Pick two random points on this edge.*/ + p0i = isaac_next_uint(_isaac, n); + p1i = isaac_next_uint(_isaac, n - 1); + if (p1i >= p0i) + p1i++; + p0 = edge_pts[p0i].pos; + p1 = edge_pts[p1i].pos; + /*If the corresponding line is not within 45 degrees of the proper + orientation in the square domain, reject it outright. + This can happen, e.g., when highly skewed orientations cause points to + be misclassified into the wrong edge. + The irony is that using such points might produce a line which _does_ + pass the corresponding validity checks.*/ + qr_aff_unproject(q0, _hom, p0[0], p0[1]); + qr_aff_unproject(q1, _hom, p1[0], p1[1]); + qr_point_translate(q0, -_f->o[0], -_f->o[1]); + qr_point_translate(q1, -_f->o[0], -_f->o[1]); + if (abs(q0[_e >> 1] - q1[_e >> 1]) > + abs(q0[1 - (_e >> 1)] - q1[1 - (_e >> 1)])) + continue; + /*Identify the other edge points which are inliers. + The squared distance should be distributed as a \Chi^2 distribution + with one degree of freedom, which means for a 95% confidence the + point should lie within a factor 3.8414588 ~= 4 times the expected + variance of the point locations. + We grossly approximate the standard deviation as 1 pixel in one + direction, and 0.5 pixels in the other (because we average two + coordinates).*/ + thresh = qr_isqrt(qr_point_distance2(p0, p1) + << 2 * QR_FINDER_SUBPREC + 1); + ninliers = 0; + for (j = 0; j < n; j++) { + if (abs(qr_point_ccw(p0, p1, edge_pts[j].pos)) <= thresh) { + edge_pts[j].extent |= 1; + ninliers++; + } else + edge_pts[j].extent &= ~1; + } + if (ninliers > best_ninliers) { + for (j = 0; j < n; j++) + edge_pts[j].extent <<= 1; + best_ninliers = ninliers; + /*The actual number of iterations required is + log(1-\alpha)/log(1-r*r), + where \alpha is the required probability of taking a sample with + no outliers (e.g., 0.99) and r is the estimated ratio of inliers + (e.g. ninliers/n). + This is just a rough (but conservative) approximation, but it + should be good enough to stop the iteration early when we find + a good set of inliers.*/ + if (ninliers > n >> 1) + max_iters = (67 * n - 63 * ninliers - 1) / (n << 1); + } + } + /*Now collect all the inliers at the beginning of the list.*/ + for (i = j = 0; j < best_ninliers; i++) + if (edge_pts[i].extent & 2) { + if (j < i) { + qr_finder_edge_pt tmp; + *&tmp = *(edge_pts + i); + *(edge_pts + j) = *(edge_pts + i); + *(edge_pts + i) = *&tmp; + } + j++; + } + } + _f->ninliers[_e] = best_ninliers; +} + +/*Perform a least-squares line fit to an edge of a finder pattern using the + inliers found by RANSAC.*/ +static int qr_line_fit_finder_edge(qr_line _l, const qr_finder *_f, int _e, + int _res) +{ + qr_finder_edge_pt *edge_pts; + qr_point *pts; + int npts; + int i; + npts = _f->ninliers[_e]; + if (npts < 2) + return -1; + /*We could write a custom version of qr_line_fit_points that accesses + edge_pts directly, but this saves on code size and doesn't measurably slow + things down.*/ + pts = (qr_point *)malloc(npts * sizeof(*pts)); + edge_pts = _f->edge_pts[_e]; + for (i = 0; i < npts; i++) { + pts[i][0] = edge_pts[i].pos[0]; + pts[i][1] = edge_pts[i].pos[1]; + } + qr_line_fit_points(_l, pts, npts, _res); + /*Make sure the center of the finder pattern lies in the positive halfspace + of the line.*/ + qr_line_orient(_l, _f->c->pos[0], _f->c->pos[1]); + free(pts); + return 0; +} + +/*Perform a least-squares line fit to a pair of common finder edges using the + inliers found by RANSAC. + Unlike a normal edge fit, we guarantee that this one succeeds by creating at + least one point on each edge using the estimated module size if it has no + inliers.*/ +static void qr_line_fit_finder_pair(qr_line _l, const qr_aff *_aff, + const qr_finder *_f0, const qr_finder *_f1, + int _e) +{ + qr_point *pts; + int npts; + qr_finder_edge_pt *edge_pts; + qr_point q; + int n0; + int n1; + int i; + n0 = _f0->ninliers[_e]; + n1 = _f1->ninliers[_e]; + /*We could write a custom version of qr_line_fit_points that accesses + edge_pts directly, but this saves on code size and doesn't measurably slow + things down.*/ + npts = QR_MAXI(n0, 1) + QR_MAXI(n1, 1); + pts = (qr_point *)malloc(npts * sizeof(*pts)); + if (n0 > 0) { + edge_pts = _f0->edge_pts[_e]; + for (i = 0; i < n0; i++) { + pts[i][0] = edge_pts[i].pos[0]; + pts[i][1] = edge_pts[i].pos[1]; + } + } else { + q[0] = _f0->o[0]; + q[1] = _f0->o[1]; + q[_e >> 1] += _f0->size[_e >> 1] * (2 * (_e & 1) - 1); + qr_aff_project(pts[0], _aff, q[0], q[1]); + n0++; + } + if (n1 > 0) { + edge_pts = _f1->edge_pts[_e]; + for (i = 0; i < n1; i++) { + pts[n0 + i][0] = edge_pts[i].pos[0]; + pts[n0 + i][1] = edge_pts[i].pos[1]; + } + } else { + q[0] = _f1->o[0]; + q[1] = _f1->o[1]; + q[_e >> 1] += _f1->size[_e >> 1] * (2 * (_e & 1) - 1); + qr_aff_project(pts[n0], _aff, q[0], q[1]); + n1++; + } + qr_line_fit_points(_l, pts, npts, _aff->res); + /*Make sure at least one finder center lies in the positive halfspace.*/ + qr_line_orient(_l, _f0->c->pos[0], _f0->c->pos[1]); + free(pts); +} + +static int qr_finder_quick_crossing_check(const unsigned char *_img, int _width, + int _height, int _x0, int _y0, + int _x1, int _y1, int _v) +{ + /*The points must be inside the image, and have a !_v:_v:!_v pattern. + We don't scan the whole line initially, but quickly reject if the endpoints + aren't !_v, or the midpoint isn't _v. + If either end point is out of the image, or we don't encounter a _v pixel, + we return a negative value, indicating the region should be considered + empty. + Otherwise, we return a positive value to indicate it is non-empty.*/ + if (_x0 < 0 || _x0 >= _width || _y0 < 0 || _y0 >= _height || _x1 < 0 || + _x1 >= _width || _y1 < 0 || _y1 >= _height) { + return -1; + } + if ((!_img[_y0 * _width + _x0]) != _v || (!_img[_y1 * _width + _x1]) != _v) + return 1; + if ((!_img[(_y0 + _y1 >> 1) * _width + (_x0 + _x1 >> 1)]) == _v) + return -1; + return 0; +} + +/*Locate the midpoint of a _v segment along a !_v:_v:!_v line from (_x0,_y0) to + (_x1,_y1). + All coordinates, which are NOT in subpel resolution, must lie inside the + image, and the endpoints are already assumed to have the value !_v. + The returned value is in subpel resolution.*/ +static int qr_finder_locate_crossing(const unsigned char *_img, int _width, + int _height, int _x0, int _y0, int _x1, + int _y1, int _v, qr_point _p) +{ + qr_point x0; + qr_point x1; + qr_point dx; + int step[2]; + int steep; + int err; + int derr; + /*Use Bresenham's algorithm to trace along the line and find the exact + transitions from !_v to _v and back.*/ + x0[0] = _x0; + x0[1] = _y0; + x1[0] = _x1; + x1[1] = _y1; + dx[0] = abs(_x1 - _x0); + dx[1] = abs(_y1 - _y0); + steep = dx[1] > dx[0]; + err = 0; + derr = dx[1 - steep]; + step[0] = ((_x0 < _x1) << 1) - 1; + step[1] = ((_y0 < _y1) << 1) - 1; + /*Find the first crossing from !_v to _v.*/ + for (;;) { + /*If we make it all the way to the other side, there's no crossing.*/ + if (x0[steep] == x1[steep]) + return -1; + x0[steep] += step[steep]; + err += derr; + if (err << 1 > dx[steep]) { + x0[1 - steep] += step[1 - steep]; + err -= dx[steep]; + } + if ((!_img[x0[1] * _width + x0[0]]) != _v) + break; + } + /*Find the last crossing from _v to !_v.*/ + err = 0; + for (;;) { + if (x0[steep] == x1[steep]) + break; + x1[steep] -= step[steep]; + err += derr; + if (err << 1 > dx[steep]) { + x1[1 - steep] -= step[1 - steep]; + err -= dx[steep]; + } + if ((!_img[x1[1] * _width + x1[0]]) != _v) + break; + } + /*Return the midpoint of the _v segment.*/ + _p[0] = (x0[0] + x1[0] + 1 << QR_FINDER_SUBPREC) >> 1; + _p[1] = (x0[1] + x1[1] + 1 << QR_FINDER_SUBPREC) >> 1; + return 0; +} + +static int qr_aff_line_step(const qr_aff *_aff, qr_line _l, int _v, int _du, + int *_dv) +{ + int shift; + int round; + int dv; + int n; + int d; + n = _aff->fwd[0][_v] * _l[0] + _aff->fwd[1][_v] * _l[1]; + d = _aff->fwd[0][1 - _v] * _l[0] + _aff->fwd[1][1 - _v] * _l[1]; + if (d < 0) { + n = -n; + d = -d; + } + shift = QR_MAXI(0, qr_ilog(_du) + qr_ilog(abs(n)) + 3 - QR_INT_BITS); + round = (1 << shift) >> 1; + n = n + round >> shift; + d = d + round >> shift; + /*The line should not be outside 45 degrees of horizontal/vertical. + TODO: We impose this restriction to help ensure the loop below terminates, + but it should not technically be required. + It also, however, ensures we avoid division by zero.*/ + if (abs(n) >= d) + return -1; + n = -_du * n; + dv = QR_DIVROUND(n, d); + if (abs(dv) >= _du) + return -1; + *_dv = dv; + return 0; +} + +/*Computes the Hamming distance between two bit patterns (the number of bits + that differ). + May stop counting after _maxdiff differences.*/ +static int qr_hamming_dist(unsigned _y1, unsigned _y2, int _maxdiff) +{ + unsigned y; + int ret; + y = _y1 ^ _y2; + for (ret = 0; ret < _maxdiff && y; ret++) + y &= y - 1; + return ret; +} + +/*Retrieve a bit (guaranteed to be 0 or 1) from the image, given coordinates in + subpel resolution which have not been bounds checked.*/ +static int qr_img_get_bit(const unsigned char *_img, int _width, int _height, + int _x, int _y) +{ + _x >>= QR_FINDER_SUBPREC; + _y >>= QR_FINDER_SUBPREC; + return _img[QR_CLAMPI(0, _y, _height - 1) * _width + + QR_CLAMPI(0, _x, _width - 1)] != 0; +} + +#if defined(QR_DEBUG) +#include "image.h" + +static void qr_finder_dump_aff_undistorted(qr_finder *_ul, qr_finder *_ur, + qr_finder *_dl, qr_aff *_aff, + const unsigned char *_img, + int _width, int _height) +{ + unsigned char *gimg; + FILE *fout; + int lpsz; + int pixel_size; + int dim; + int min; + int max; + int u; + int y; + int i; + int j; + lpsz = + qr_ilog(_ur->size[0] + _ur->size[1] + _dl->size[0] + _dl->size[1]) - 6; + pixel_size = 1 << lpsz; + dim = (1 << _aff->res - lpsz) + 128; + gimg = (unsigned char *)malloc(dim * dim * sizeof(*gimg)); + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) { + qr_point p; + qr_aff_project(p, _aff, (j - 64) << lpsz, (i - 64) << lpsz); + gimg[i * dim + j] = + _img[QR_CLAMPI(0, p[1] >> QR_FINDER_SUBPREC, _height - 1) * + _width + + QR_CLAMPI(0, p[0] >> QR_FINDER_SUBPREC, _width - 1)]; + } + { + min = (_ur->o[0] - 7 * _ur->size[0] >> lpsz) + 64; + if (min < 0) + min = 0; + max = (_ur->o[0] + 7 * _ur->size[0] >> lpsz) + 64; + if (max > dim) + max = dim; + for (y = -7; y <= 7; y++) { + i = (_ur->o[1] + y * _ur->size[1] >> lpsz) + 64; + if (i < 0 || i >= dim) + continue; + for (j = min; j < max; j++) + gimg[i * dim + j] = 0x7F; + } + min = (_ur->o[1] - 7 * _ur->size[1] >> lpsz) + 64; + if (min < 0) + min = 0; + max = (_ur->o[1] + 7 * _ur->size[1] >> lpsz) + 64; + if (max > dim) + max = dim; + for (u = -7; u <= 7; u++) { + j = (_ur->o[0] + u * _ur->size[0] >> lpsz) + 64; + if (j < 0 || j >= dim) + continue; + for (i = min; i < max; i++) + gimg[i * dim + j] = 0x7F; + } + } + { + min = (_dl->o[0] - 7 * _dl->size[0] >> lpsz) + 64; + if (min < 0) + min = 0; + max = (_dl->o[0] + 7 * _dl->size[0] >> lpsz) + 64; + if (max > dim) + max = dim; + for (y = -7; y <= 7; y++) { + i = (_dl->o[1] + y * _dl->size[1] >> lpsz) + 64; + if (i < 0 || i >= dim) + continue; + for (j = min; j < max; j++) + gimg[i * dim + j] = 0x7F; + } + min = (_dl->o[1] - 7 * _dl->size[1] >> lpsz) + 64; + if (min < 0) + min = 0; + max = (_dl->o[1] + 7 * _dl->size[1] >> lpsz) + 64; + if (max > dim) + max = dim; + for (u = -7; u <= 7; u++) { + j = (_dl->o[0] + u * _dl->size[0] >> lpsz) + 64; + if (j < 0 || j >= dim) + continue; + for (i = min; i < max; i++) + gimg[i * dim + j] = 0x7F; + } + } + fout = fopen("undistorted_aff.png", "wb"); + image_write_png(gimg, dim, dim, fout); + fclose(fout); + free(gimg); +} + +static void qr_finder_dump_hom_undistorted(qr_finder *_ul, qr_finder *_ur, + qr_finder *_dl, qr_hom *_hom, + const unsigned char *_img, + int _width, int _height) +{ + unsigned char *gimg; + FILE *fout; + int lpsz; + int pixel_size; + int dim; + int min; + int max; + int u; + int v; + int i; + int j; + lpsz = + qr_ilog(_ur->size[0] + _ur->size[1] + _dl->size[0] + _dl->size[1]) - 6; + pixel_size = 1 << lpsz; + dim = (1 << _hom->res - lpsz) + 256; + gimg = (unsigned char *)malloc(dim * dim * sizeof(*gimg)); + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) { + qr_point p; + qr_hom_project(p, _hom, (j - 128) << lpsz, (i - 128) << lpsz); + gimg[i * dim + j] = + _img[QR_CLAMPI(0, p[1] >> QR_FINDER_SUBPREC, _height - 1) * + _width + + QR_CLAMPI(0, p[0] >> QR_FINDER_SUBPREC, _width - 1)]; + } + { + min = (_ur->o[0] - 7 * _ur->size[0] >> lpsz) + 128; + if (min < 0) + min = 0; + max = (_ur->o[0] + 7 * _ur->size[0] >> lpsz) + 128; + if (max > dim) + max = dim; + for (v = -7; v <= 7; v++) { + i = (_ur->o[1] + v * _ur->size[1] >> lpsz) + 128; + if (i < 0 || i >= dim) + continue; + for (j = min; j < max; j++) + gimg[i * dim + j] = 0x7F; + } + min = (_ur->o[1] - 7 * _ur->size[1] >> lpsz) + 128; + if (min < 0) + min = 0; + max = (_ur->o[1] + 7 * _ur->size[1] >> lpsz) + 128; + if (max > dim) + max = dim; + for (u = -7; u <= 7; u++) { + j = (_ur->o[0] + u * _ur->size[0] >> lpsz) + 128; + if (j < 0 || j >= dim) + continue; + for (i = min; i < max; i++) + gimg[i * dim + j] = 0x7F; + } + } + { + min = (_dl->o[0] - 7 * _dl->size[0] >> lpsz) + 128; + if (min < 0) + min = 0; + max = (_dl->o[0] + 7 * _dl->size[0] >> lpsz) + 128; + if (max > dim) + max = dim; + for (v = -7; v <= 7; v++) { + i = (_dl->o[1] + v * _dl->size[1] >> lpsz) + 128; + if (i < 0 || i >= dim) + continue; + for (j = min; j < max; j++) + gimg[i * dim + j] = 0x7F; + } + min = (_dl->o[1] - 7 * _dl->size[1] >> lpsz) + 128; + if (min < 0) + min = 0; + max = (_dl->o[1] + 7 * _dl->size[1] >> lpsz) + 128; + if (max > dim) + max = dim; + for (u = -7; u <= 7; u++) { + j = (_dl->o[0] + u * _dl->size[0] >> lpsz) + 128; + if (j < 0 || j >= dim) + continue; + for (i = min; i < max; i++) + gimg[i * dim + j] = 0x7F; + } + } + fout = fopen("undistorted_hom.png", "wb"); + image_write_png(gimg, dim, dim, fout); + fclose(fout); + free(gimg); +} +#endif + +/*A homography from one region of the grid back to the image. + Unlike a qr_hom, this does not include an inverse transform and maps directly + from the grid points, not a square with power-of-two sides.*/ +struct qr_hom_cell { + int fwd[3][3]; + int x0; + int y0; + int u0; + int v0; +}; + +static void qr_hom_cell_init(qr_hom_cell *_cell, int _u0, int _v0, int _u1, + int _v1, int _u2, int _v2, int _u3, int _v3, + int _x0, int _y0, int _x1, int _y1, int _x2, + int _y2, int _x3, int _y3) +{ + int du10; + int du20; + int du30; + int du31; + int du32; + int dv10; + int dv20; + int dv30; + int dv31; + int dv32; + int dx10; + int dx20; + int dx30; + int dx31; + int dx32; + int dy10; + int dy20; + int dy30; + int dy31; + int dy32; + int a00; + int a01; + int a02; + int a10; + int a11; + int a12; + int a20; + int a21; + int a22; + int i00; + int i01; + int i10; + int i11; + int i20; + int i21; + int i22; + int b0; + int b1; + int b2; + int shift; + int round; + int x; + int y; + int w; + /*First, correct for the arrangement of the source points. + We take advantage of the fact that we know the source points have a very + limited dynamic range (so there will never be overflow) and a small amount + of projective distortion.*/ + du10 = _u1 - _u0; + du20 = _u2 - _u0; + du30 = _u3 - _u0; + du31 = _u3 - _u1; + du32 = _u3 - _u2; + dv10 = _v1 - _v0; + dv20 = _v2 - _v0; + dv30 = _v3 - _v0; + dv31 = _v3 - _v1; + dv32 = _v3 - _v2; + /*Compute the coefficients of the forward transform from the unit square to + the source point configuration.*/ + a20 = du32 * dv10 - du10 * dv32; + a21 = du20 * dv31 - du31 * dv20; + if (a20 || a21) + a22 = du32 * dv31 - du31 * dv32; + /*If the source grid points aren't in a non-affine arrangement, there's no + reason to scale everything by du32*dv31-du31*dv32. + Not doing so allows a much larger dynamic range, and is the only way we can + initialize a base cell that covers the whole grid.*/ + else + a22 = 1; + a00 = du10 * (a20 + a22); + a01 = du20 * (a21 + a22); + a10 = dv10 * (a20 + a22); + a11 = dv20 * (a21 + a22); + /*Now compute the inverse transform.*/ + i00 = a11 * a22; + i01 = -a01 * a22; + i10 = -a10 * a22; + i11 = a00 * a22; + i20 = a10 * a21 - a11 * a20; + i21 = a01 * a20 - a00 * a21; + i22 = a00 * a11 - a01 * a10; + /*Invert the coefficients. + Since i22 is the largest, we divide it by all the others. + The quotient is often exact (e.g., when the source points contain no + projective distortion), and is never zero. + Hence we can use zero to signal "infinity" when the divisor is zero.*/ + if (i00) + i00 = QR_FLIPSIGNI(QR_DIVROUND(i22, abs(i00)), i00); + if (i01) + i01 = QR_FLIPSIGNI(QR_DIVROUND(i22, abs(i01)), i01); + if (i10) + i10 = QR_FLIPSIGNI(QR_DIVROUND(i22, abs(i10)), i10); + if (i11) + i11 = QR_FLIPSIGNI(QR_DIVROUND(i22, abs(i11)), i11); + if (i20) + i20 = QR_FLIPSIGNI(QR_DIVROUND(i22, abs(i20)), i20); + if (i21) + i21 = QR_FLIPSIGNI(QR_DIVROUND(i22, abs(i21)), i21); + /*Now compute the map from the unit square into the image.*/ + dx10 = _x1 - _x0; + dx20 = _x2 - _x0; + dx30 = _x3 - _x0; + dx31 = _x3 - _x1; + dx32 = _x3 - _x2; + dy10 = _y1 - _y0; + dy20 = _y2 - _y0; + dy30 = _y3 - _y0; + dy31 = _y3 - _y1; + dy32 = _y3 - _y2; + a20 = dx32 * dy10 - dx10 * dy32; + a21 = dx20 * dy31 - dx31 * dy20; + a22 = dx32 * dy31 - dx31 * dy32; + /*Figure out if we need to downscale anything.*/ + b0 = qr_ilog(QR_MAXI(abs(dx10), abs(dy10))) + qr_ilog(abs(a20 + a22)); + b1 = qr_ilog(QR_MAXI(abs(dx20), abs(dy20))) + qr_ilog(abs(a21 + a22)); + b2 = qr_ilog(QR_MAXI(QR_MAXI(abs(a20), abs(a21)), abs(a22))); + shift = QR_MAXI(0, QR_MAXI(QR_MAXI(b0, b1), b2) - + (QR_INT_BITS - 3 - QR_ALIGN_SUBPREC)); + round = (1 << shift) >> 1; + /*Compute the final coefficients of the forward transform.*/ + a00 = QR_FIXMUL(dx10, a20 + a22, round, shift); + a01 = QR_FIXMUL(dx20, a21 + a22, round, shift); + a10 = QR_FIXMUL(dy10, a20 + a22, round, shift); + a11 = QR_FIXMUL(dy20, a21 + a22, round, shift); + /*And compose the two transforms. + Since we inverted the coefficients above, we divide by them here instead + of multiplying. + This lets us take advantage of the full dynamic range. + Note a zero divisor is really "infinity", and thus the quotient should also + be zero.*/ + _cell->fwd[0][0] = + (i00 ? QR_DIVROUND(a00, i00) : 0) + (i10 ? QR_DIVROUND(a01, i10) : 0); + _cell->fwd[0][1] = + (i01 ? QR_DIVROUND(a00, i01) : 0) + (i11 ? QR_DIVROUND(a01, i11) : 0); + _cell->fwd[1][0] = + (i00 ? QR_DIVROUND(a10, i00) : 0) + (i10 ? QR_DIVROUND(a11, i10) : 0); + _cell->fwd[1][1] = + (i01 ? QR_DIVROUND(a10, i01) : 0) + (i11 ? QR_DIVROUND(a11, i11) : 0); + _cell->fwd[2][0] = (i00 ? QR_DIVROUND(a20, i00) : 0) + + (i10 ? QR_DIVROUND(a21, i10) : 0) + + (i20 ? QR_DIVROUND(a22, i20) : 0) + round >> + shift; + _cell->fwd[2][1] = (i01 ? QR_DIVROUND(a20, i01) : 0) + + (i11 ? QR_DIVROUND(a21, i11) : 0) + + (i21 ? QR_DIVROUND(a22, i21) : 0) + round >> + shift; + _cell->fwd[2][2] = a22 + round >> shift; + /*Mathematically, a02 and a12 are exactly zero. + However, that concentrates all of the rounding error in the (_u3,_v3) + corner; we compute offsets which distribute it over the whole range.*/ + x = _cell->fwd[0][0] * du10 + _cell->fwd[0][1] * dv10; + y = _cell->fwd[1][0] * du10 + _cell->fwd[1][1] * dv10; + w = _cell->fwd[2][0] * du10 + _cell->fwd[2][1] * dv10 + _cell->fwd[2][2]; + a02 = dx10 * w - x; + a12 = dy10 * w - y; + x = _cell->fwd[0][0] * du20 + _cell->fwd[0][1] * dv20; + y = _cell->fwd[1][0] * du20 + _cell->fwd[1][1] * dv20; + w = _cell->fwd[2][0] * du20 + _cell->fwd[2][1] * dv20 + _cell->fwd[2][2]; + a02 += dx20 * w - x; + a12 += dy20 * w - y; + x = _cell->fwd[0][0] * du30 + _cell->fwd[0][1] * dv30; + y = _cell->fwd[1][0] * du30 + _cell->fwd[1][1] * dv30; + w = _cell->fwd[2][0] * du30 + _cell->fwd[2][1] * dv30 + _cell->fwd[2][2]; + a02 += dx30 * w - x; + a12 += dy30 * w - y; + _cell->fwd[0][2] = a02 + 2 >> 2; + _cell->fwd[1][2] = a12 + 2 >> 2; + _cell->x0 = _x0; + _cell->y0 = _y0; + _cell->u0 = _u0; + _cell->v0 = _v0; +} + +/*Finish a partial projection, converting from homogeneous coordinates to the + normal 2-D representation. + In loops, we can avoid many multiplies by computing the homogeneous _x, _y, + and _w incrementally, but we cannot avoid the divisions, done here.*/ +static void qr_hom_cell_fproject(qr_point _p, const qr_hom_cell *_cell, int _x, + int _y, int _w) +{ + if (_w == 0) { + _p[0] = _x < 0 ? INT_MIN : INT_MAX; + _p[1] = _y < 0 ? INT_MIN : INT_MAX; + } else { + if (_w < 0) { + _x = -_x; + _y = -_y; + _w = -_w; + } + _p[0] = QR_DIVROUND(_x, _w) + _cell->x0; + _p[1] = QR_DIVROUND(_y, _w) + _cell->y0; + } +} + +static void qr_hom_cell_project(qr_point _p, const qr_hom_cell *_cell, int _u, + int _v, int _res) +{ + _u -= _cell->u0 << _res; + _v -= _cell->v0 << _res; + qr_hom_cell_fproject(_p, _cell, + _cell->fwd[0][0] * _u + _cell->fwd[0][1] * _v + + (_cell->fwd[0][2] << _res), + _cell->fwd[1][0] * _u + _cell->fwd[1][1] * _v + + (_cell->fwd[1][2] << _res), + _cell->fwd[2][0] * _u + _cell->fwd[2][1] * _v + + (_cell->fwd[2][2] << _res)); +} + +/*Retrieves the bits corresponding to the alignment pattern template centered + at the given location in the original image (at subpel precision).*/ +static unsigned qr_alignment_pattern_fetch(qr_point _p[5][5], int _x0, int _y0, + const unsigned char *_img, + int _width, int _height) +{ + unsigned v; + int i; + int j; + int k; + int dx; + int dy; + dx = _x0 - _p[2][2][0]; + dy = _y0 - _p[2][2][1]; + v = 0; + for (k = i = 0; i < 5; i++) + for (j = 0; j < 5; j++, k++) { + v |= qr_img_get_bit(_img, _width, _height, _p[i][j][0] + dx, + _p[i][j][1] + dy) + << k; + } + return v; +} + +/*Searches for an alignment pattern near the given location.*/ +static int qr_alignment_pattern_search(qr_point _p, const qr_hom_cell *_cell, + int _u, int _v, int _r, + const unsigned char *_img, int _width, + int _height) +{ + qr_point c[4]; + int nc[4]; + qr_point p[5][5]; + qr_point pc; + unsigned best_match; + int best_dist; + int bestx; + int besty; + unsigned match; + int dist; + int u; + int v; + int x0; + int y0; + int w0; + int x; + int y; + int w; + int dxdu; + int dydu; + int dwdu; + int dxdv; + int dydv; + int dwdv; + int dx; + int dy; + int i; + int j; + /*Build up a basic template using _cell to control shape and scale. + We project the points in the template back to the image just once, since if + the alignment pattern has moved, we don't really know why. + If it's because of radial distortion, or the code wasn't flat, or something + else, there's no reason to expect that a re-projection around each + subsequent search point would be any closer to the actual shape than our + first projection. + Therefore we simply slide this template around, as is.*/ + u = (_u - 2) - _cell->u0; + v = (_v - 2) - _cell->v0; + x0 = _cell->fwd[0][0] * u + _cell->fwd[0][1] * v + _cell->fwd[0][2]; + y0 = _cell->fwd[1][0] * u + _cell->fwd[1][1] * v + _cell->fwd[1][2]; + w0 = _cell->fwd[2][0] * u + _cell->fwd[2][1] * v + _cell->fwd[2][2]; + dxdu = _cell->fwd[0][0]; + dydu = _cell->fwd[1][0]; + dwdu = _cell->fwd[2][0]; + dxdv = _cell->fwd[0][1]; + dydv = _cell->fwd[1][1]; + dwdv = _cell->fwd[2][1]; + for (i = 0; i < 5; i++) { + x = x0; + y = y0; + w = w0; + for (j = 0; j < 5; j++) { + qr_hom_cell_fproject(p[i][j], _cell, x, y, w); + x += dxdu; + y += dydu; + w += dwdu; + } + x0 += dxdv; + y0 += dydv; + w0 += dwdv; + } + bestx = p[2][2][0]; + besty = p[2][2][1]; + best_match = + qr_alignment_pattern_fetch(p, bestx, besty, _img, _width, _height); + best_dist = qr_hamming_dist(best_match, 0x1F8D63F, 25); + if (best_dist > 0) { + u = _u - _cell->u0; + v = _v - _cell->v0; + x = _cell->fwd[0][0] * u + _cell->fwd[0][1] * v + _cell->fwd[0][2] + << QR_ALIGN_SUBPREC; + y = _cell->fwd[1][0] * u + _cell->fwd[1][1] * v + _cell->fwd[1][2] + << QR_ALIGN_SUBPREC; + w = _cell->fwd[2][0] * u + _cell->fwd[2][1] * v + _cell->fwd[2][2] + << QR_ALIGN_SUBPREC; + /*Search an area at most _r modules around the target location, in + concentric squares..*/ + for (i = 1; i < _r << QR_ALIGN_SUBPREC; i++) { + int side_len; + side_len = (i << 1) - 1; + x -= dxdu + dxdv; + y -= dydu + dydv; + w -= dwdu + dwdv; + for (j = 0; j < 4 * side_len; j++) { + int dir; + qr_hom_cell_fproject(pc, _cell, x, y, w); + match = qr_alignment_pattern_fetch(p, pc[0], pc[1], _img, + _width, _height); + dist = qr_hamming_dist(match, 0x1F8D63F, best_dist + 1); + if (dist < best_dist) { + best_match = match; + best_dist = dist; + bestx = pc[0]; + besty = pc[1]; + } + if (j < 2 * side_len) { + dir = j >= side_len; + x += _cell->fwd[0][dir]; + y += _cell->fwd[1][dir]; + w += _cell->fwd[2][dir]; + } else { + dir = j >= 3 * side_len; + x -= _cell->fwd[0][dir]; + y -= _cell->fwd[1][dir]; + w -= _cell->fwd[2][dir]; + } + if (!best_dist) + break; + } + if (!best_dist) + break; + } + } + /*If the best result we got was sufficiently bad, reject the match. + If we're wrong and we include it, we can grossly distort the nearby + region, whereas using the initial starting point should at least be + consistent with the geometry we already have.*/ + if (best_dist > 6) { + _p[0] = p[2][2][0]; + _p[1] = p[2][2][1]; + return -1; + } + /*Now try to get a more accurate location of the pattern center.*/ + dx = bestx - p[2][2][0]; + dy = besty - p[2][2][1]; + memset(nc, 0, sizeof(nc)); + memset(c, 0, sizeof(c)); + /*We consider 8 lines across the finder pattern in turn. + If we actually found a symmetric pattern along that line, search for its + exact center in the image. + There are plenty more lines we could use if these don't work, but if we've + found anything remotely close to an alignment pattern, we should be able + to use most of these.*/ + for (i = 0; i < 8; i++) { + static const unsigned MASK_TESTS[8][2] = { + { 0x1040041, 0x1000001 }, { 0x0041040, 0x0001000 }, + { 0x0110110, 0x0100010 }, { 0x0011100, 0x0001000 }, + { 0x0420084, 0x0400004 }, { 0x0021080, 0x0001000 }, + { 0x0006C00, 0x0004400 }, { 0x0003800, 0x0001000 }, + }; + static const unsigned char MASK_COORDS[8][2] = { { 0, 0 }, { 1, 1 }, + { 4, 0 }, { 3, 1 }, + { 2, 0 }, { 2, 1 }, + { 0, 2 }, { 1, 2 } }; + if ((best_match & MASK_TESTS[i][0]) == MASK_TESTS[i][1]) { + int x0; + int y0; + int x1; + int y1; + x0 = p[MASK_COORDS[i][1]][MASK_COORDS[i][0]][0] + dx >> + QR_FINDER_SUBPREC; + if (x0 < 0 || x0 >= _width) + continue; + y0 = p[MASK_COORDS[i][1]][MASK_COORDS[i][0]][1] + dy >> + QR_FINDER_SUBPREC; + if (y0 < 0 || y0 >= _height) + continue; + x1 = p[4 - MASK_COORDS[i][1]][4 - MASK_COORDS[i][0]][0] + dx >> + QR_FINDER_SUBPREC; + if (x1 < 0 || x1 >= _width) + continue; + y1 = p[4 - MASK_COORDS[i][1]][4 - MASK_COORDS[i][0]][1] + dy >> + QR_FINDER_SUBPREC; + if (y1 < 0 || y1 >= _height) + continue; + if (!qr_finder_locate_crossing(_img, _width, _height, x0, y0, x1, + y1, i & 1, pc)) { + int w; + int cx; + int cy; + cx = pc[0] - bestx; + cy = pc[1] - besty; + if (i & 1) { + /*Weight crossings around the center dot more highly, as they are + generally more reliable.*/ + w = 3; + cx += cx << 1; + cy += cy << 1; + } else + w = 1; + nc[i >> 1] += w; + c[i >> 1][0] += cx; + c[i >> 1][1] += cy; + } + } + } + /*Sum offsets from lines in orthogonal directions.*/ + for (i = 0; i < 2; i++) { + int a; + int b; + a = nc[i << 1]; + b = nc[i << 1 | 1]; + if (a && b) { + int w; + w = QR_MAXI(a, b); + c[i << 1][0] = + QR_DIVROUND(w * (b * c[i << 1][0] + a * c[i << 1 | 1][0]), + a * b); + c[i << 1][1] = + QR_DIVROUND(w * (b * c[i << 1][1] + a * c[i << 1 | 1][1]), + a * b); + nc[i << 1] = w << 1; + } else { + c[i << 1][0] += c[i << 1 | 1][0]; + c[i << 1][1] += c[i << 1 | 1][1]; + nc[i << 1] += b; + } + } + /*Average offsets from pairs of orthogonal lines.*/ + c[0][0] += c[2][0]; + c[0][1] += c[2][1]; + nc[0] += nc[2]; + /*If we actually found any such lines, apply the adjustment.*/ + if (nc[0]) { + dx = QR_DIVROUND(c[0][0], nc[0]); + dy = QR_DIVROUND(c[0][1], nc[0]); + /*But only if it doesn't make things too much worse.*/ + match = qr_alignment_pattern_fetch(p, bestx + dx, besty + dy, _img, + _width, _height); + dist = qr_hamming_dist(match, 0x1F8D63F, best_dist + 1); + if (dist <= best_dist + 1) { + bestx += dx; + besty += dy; + } + } + _p[0] = bestx; + _p[1] = besty; + return 0; +} + +static int qr_hom_fit(qr_hom *_hom, qr_finder *_ul, qr_finder *_ur, + qr_finder *_dl, qr_point _p[4], const qr_aff *_aff, + isaac_ctx *_isaac, const unsigned char *_img, int _width, + int _height) +{ + qr_point *b; + int nb; + int cb; + qr_point *r; + int nr; + int cr; + qr_line l[4]; + qr_point q; + qr_point p; + int ox; + int oy; + int ru; + int rv; + int dru; + int drv; + int bu; + int bv; + int dbu; + int dbv; + int rx; + int ry; + int drxi; + int dryi; + int drxj; + int dryj; + int rdone; + int nrempty; + int rlastfit; + int bx; + int by; + int dbxi; + int dbyi; + int dbxj; + int dbyj; + int bdone; + int nbempty; + int blastfit; + int shift; + int round; + int version4; + int brx; + int bry; + int i; + /*We attempt to correct large-scale perspective distortion by fitting lines + to the edge of the code area. + We could also look for an alignment pattern now, but that wouldn't work for + version 1 codes, which have no alignment pattern. + Even if the code is supposed to have one, there's go guarantee we'd find it + intact.*/ + /*Fitting lines is easy for the edges on which we have two finder patterns. + After the fit, UL is guaranteed to be on the proper side, but if either of + the other two finder patterns aren't, something is wrong.*/ + qr_finder_ransac(_ul, _aff, _isaac, 0); + qr_finder_ransac(_dl, _aff, _isaac, 0); + qr_line_fit_finder_pair(l[0], _aff, _ul, _dl, 0); + if (qr_line_eval(l[0], _dl->c->pos[0], _dl->c->pos[1]) < 0 || + qr_line_eval(l[0], _ur->c->pos[0], _ur->c->pos[1]) < 0) { + return -1; + } + qr_finder_ransac(_ul, _aff, _isaac, 2); + qr_finder_ransac(_ur, _aff, _isaac, 2); + qr_line_fit_finder_pair(l[2], _aff, _ul, _ur, 2); + if (qr_line_eval(l[2], _dl->c->pos[0], _dl->c->pos[1]) < 0 || + qr_line_eval(l[2], _ur->c->pos[0], _ur->c->pos[1]) < 0) { + return -1; + } + /*The edges which only have one finder pattern are more difficult. + We start by fitting a line to the edge of the one finder pattern we do + have. + This can fail due to an insufficient number of sample points, and even if + it succeeds can be fairly inaccurate, because all of the points are + clustered in one corner of the QR code. + If it fails, we just use an axis-aligned line in the affine coordinate + system. + Then we walk along the edge of the entire code, looking for + light:dark:light patterns perpendicular to the edge. + Wherever we find one, we take the center of the dark portion as an + additional sample point. + At the end, we re-fit the line using all such sample points found.*/ + drv = _ur->size[1] >> 1; + qr_finder_ransac(_ur, _aff, _isaac, 1); + if (qr_line_fit_finder_edge(l[1], _ur, 1, _aff->res) >= 0) { + if (qr_line_eval(l[1], _ul->c->pos[0], _ul->c->pos[1]) < 0 || + qr_line_eval(l[1], _dl->c->pos[0], _dl->c->pos[1]) < 0) { + return -1; + } + /*Figure out the change in ru for a given change in rv when stepping along + the fitted line.*/ + if (qr_aff_line_step(_aff, l[1], 1, drv, &dru) < 0) + return -1; + } else + dru = 0; + ru = _ur->o[0] + 3 * _ur->size[0] - 2 * dru; + rv = _ur->o[1] - 2 * drv; + dbu = _dl->size[0] >> 1; + qr_finder_ransac(_dl, _aff, _isaac, 3); + if (qr_line_fit_finder_edge(l[3], _dl, 3, _aff->res) >= 0) { + if (qr_line_eval(l[3], _ul->c->pos[0], _ul->c->pos[1]) < 0 || + qr_line_eval(l[3], _ur->c->pos[0], _ur->c->pos[1]) < 0) { + return -1; + } + /*Figure out the change in bv for a given change in bu when stepping along + the fitted line.*/ + if (qr_aff_line_step(_aff, l[3], 0, dbu, &dbv) < 0) + return -1; + } else + dbv = 0; + bu = _dl->o[0] - 2 * dbu; + bv = _dl->o[1] + 3 * _dl->size[1] - 2 * dbv; + /*Set up the initial point lists.*/ + nr = rlastfit = _ur->ninliers[1]; + cr = nr + (_dl->o[1] - rv + drv - 1) / drv; + r = (qr_point *)malloc(cr * sizeof(*r)); + for (i = 0; i < _ur->ninliers[1]; i++) { + memcpy(r[i], _ur->edge_pts[1][i].pos, sizeof(r[i])); + } + nb = blastfit = _dl->ninliers[3]; + cb = nb + (_ur->o[0] - bu + dbu - 1) / dbu; + b = (qr_point *)malloc(cb * sizeof(*b)); + for (i = 0; i < _dl->ninliers[3]; i++) { + memcpy(b[i], _dl->edge_pts[3][i].pos, sizeof(b[i])); + } + /*Set up the step parameters for the affine projection.*/ + ox = (_aff->x0 << _aff->res) + (1 << _aff->res - 1); + oy = (_aff->y0 << _aff->res) + (1 << _aff->res - 1); + rx = _aff->fwd[0][0] * ru + _aff->fwd[0][1] * rv + ox; + ry = _aff->fwd[1][0] * ru + _aff->fwd[1][1] * rv + oy; + drxi = _aff->fwd[0][0] * dru + _aff->fwd[0][1] * drv; + dryi = _aff->fwd[1][0] * dru + _aff->fwd[1][1] * drv; + drxj = _aff->fwd[0][0] * _ur->size[0]; + dryj = _aff->fwd[1][0] * _ur->size[0]; + bx = _aff->fwd[0][0] * bu + _aff->fwd[0][1] * bv + ox; + by = _aff->fwd[1][0] * bu + _aff->fwd[1][1] * bv + oy; + dbxi = _aff->fwd[0][0] * dbu + _aff->fwd[0][1] * dbv; + dbyi = _aff->fwd[1][0] * dbu + _aff->fwd[1][1] * dbv; + dbxj = _aff->fwd[0][1] * _dl->size[1]; + dbyj = _aff->fwd[1][1] * _dl->size[1]; + /*Now step along the lines, looking for new sample points.*/ + nrempty = nbempty = 0; + for (;;) { + int ret; + int x0; + int y0; + int x1; + int y1; + /*If we take too many steps without encountering a non-zero pixel, assume + we have wandered off the edge and stop looking before we hit the other + side of the quiet region. + Otherwise, stop when the lines cross (if they do so inside the affine + region) or come close to crossing (outside the affine region). + TODO: We don't have any way of detecting when we've wandered into the + code interior; we could stop if the outside sample ever shows up dark, + but this could happen because of noise in the quiet region, too.*/ + rdone = rv >= QR_MINI(bv, _dl->o[1] + bv >> 1) || nrempty > 14; + bdone = bu >= QR_MINI(ru, _ur->o[0] + ru >> 1) || nbempty > 14; + if (!rdone && (bdone || rv < bu)) { + x0 = rx + drxj >> _aff->res + QR_FINDER_SUBPREC; + y0 = ry + dryj >> _aff->res + QR_FINDER_SUBPREC; + x1 = rx - drxj >> _aff->res + QR_FINDER_SUBPREC; + y1 = ry - dryj >> _aff->res + QR_FINDER_SUBPREC; + if (nr >= cr) { + cr = cr << 1 | 1; + r = (qr_point *)realloc(r, cr * sizeof(*r)); + } + ret = qr_finder_quick_crossing_check(_img, _width, _height, x0, y0, + x1, y1, 1); + if (!ret) { + ret = qr_finder_locate_crossing(_img, _width, _height, x0, y0, + x1, y1, 1, r[nr]); + } + if (ret >= 0) { + if (!ret) { + qr_aff_unproject(q, _aff, r[nr][0], r[nr][1]); + /*Move the current point halfway towards the crossing. + We don't move the whole way to give us some robustness to noise.*/ + ru = ru + q[0] >> 1; + /*But ensure that rv monotonically increases.*/ + if (q[1] + drv > rv) + rv = rv + q[1] >> 1; + rx = _aff->fwd[0][0] * ru + _aff->fwd[0][1] * rv + ox; + ry = _aff->fwd[1][0] * ru + _aff->fwd[1][1] * rv + oy; + nr++; + /*Re-fit the line to update the step direction periodically.*/ + if (nr > QR_MAXI(1, rlastfit + (rlastfit >> 2))) { + qr_line_fit_points(l[1], r, nr, _aff->res); + if (qr_aff_line_step(_aff, l[1], 1, drv, &dru) >= 0) { + drxi = + _aff->fwd[0][0] * dru + _aff->fwd[0][1] * drv; + dryi = + _aff->fwd[1][0] * dru + _aff->fwd[1][1] * drv; + } + rlastfit = nr; + } + } + nrempty = 0; + } else + nrempty++; + ru += dru; + /*Our final defense: if we overflow, stop.*/ + if (rv + drv > rv) + rv += drv; + else + nrempty = INT_MAX; + rx += drxi; + ry += dryi; + } else if (!bdone) { + x0 = bx + dbxj >> _aff->res + QR_FINDER_SUBPREC; + y0 = by + dbyj >> _aff->res + QR_FINDER_SUBPREC; + x1 = bx - dbxj >> _aff->res + QR_FINDER_SUBPREC; + y1 = by - dbyj >> _aff->res + QR_FINDER_SUBPREC; + if (nb >= cb) { + cb = cb << 1 | 1; + b = (qr_point *)realloc(b, cb * sizeof(*b)); + } + ret = qr_finder_quick_crossing_check(_img, _width, _height, x0, y0, + x1, y1, 1); + if (!ret) { + ret = qr_finder_locate_crossing(_img, _width, _height, x0, y0, + x1, y1, 1, b[nb]); + } + if (ret >= 0) { + if (!ret) { + qr_aff_unproject(q, _aff, b[nb][0], b[nb][1]); + /*Move the current point halfway towards the crossing. + We don't move the whole way to give us some robustness to noise.*/ + /*But ensure that bu monotonically increases.*/ + if (q[0] + dbu > bu) + bu = bu + q[0] >> 1; + bv = bv + q[1] >> 1; + bx = _aff->fwd[0][0] * bu + _aff->fwd[0][1] * bv + ox; + by = _aff->fwd[1][0] * bu + _aff->fwd[1][1] * bv + oy; + nb++; + /*Re-fit the line to update the step direction periodically.*/ + if (nb > QR_MAXI(1, blastfit + (blastfit >> 2))) { + qr_line_fit_points(l[3], b, nb, _aff->res); + if (qr_aff_line_step(_aff, l[3], 0, dbu, &dbv) >= 0) { + dbxi = + _aff->fwd[0][0] * dbu + _aff->fwd[0][1] * dbv; + dbyi = + _aff->fwd[1][0] * dbu + _aff->fwd[1][1] * dbv; + } + blastfit = nb; + } + } + nbempty = 0; + } else + nbempty++; + /*Our final defense: if we overflow, stop.*/ + if (bu + dbu > bu) + bu += dbu; + else + nbempty = INT_MAX; + bv += dbv; + bx += dbxi; + by += dbyi; + } else + break; + } + /*Fit the new lines. + If we _still_ don't have enough sample points, then just use an + axis-aligned line from the affine coordinate system (e.g., one parallel + to the opposite edge in the image).*/ + if (nr > 1) + qr_line_fit_points(l[1], r, nr, _aff->res); + else { + qr_aff_project(p, _aff, _ur->o[0] + 3 * _ur->size[0], _ur->o[1]); + shift = QR_MAXI(0, qr_ilog(QR_MAXI(abs(_aff->fwd[0][1]), + abs(_aff->fwd[1][1]))) - + (_aff->res + 1 >> 1)); + round = (1 << shift) >> 1; + l[1][0] = _aff->fwd[1][1] + round >> shift; + l[1][1] = -_aff->fwd[0][1] + round >> shift; + l[1][2] = -(l[1][0] * p[0] + l[1][1] * p[1]); + } + free(r); + if (nb > 1) + qr_line_fit_points(l[3], b, nb, _aff->res); + else { + qr_aff_project(p, _aff, _dl->o[0], _dl->o[1] + 3 * _dl->size[1]); + shift = QR_MAXI(0, qr_ilog(QR_MAXI(abs(_aff->fwd[0][1]), + abs(_aff->fwd[1][1]))) - + (_aff->res + 1 >> 1)); + round = (1 << shift) >> 1; + l[3][0] = _aff->fwd[1][0] + round >> shift; + l[3][1] = -_aff->fwd[0][0] + round >> shift; + l[3][2] = -(l[1][0] * p[0] + l[1][1] * p[1]); + } + free(b); + for (i = 0; i < 4; i++) { + if (qr_line_isect(_p[i], l[i & 1], l[2 + (i >> 1)]) < 0) + return -1; + /*It's plausible for points to be somewhat outside the image, but too far + and too much of the pattern will be gone for it to be decodable.*/ + if (_p[i][0] < -_width << QR_FINDER_SUBPREC || + _p[i][0] >= _width << QR_FINDER_SUBPREC + 1 || + _p[i][1] < -_height << QR_FINDER_SUBPREC || + _p[i][1] >= _height << QR_FINDER_SUBPREC + 1) { + return -1; + } + } + /*By default, use the edge intersection point for the bottom-right corner.*/ + brx = _p[3][0]; + bry = _p[3][1]; + /*However, if our average version estimate is greater than 1, NOW we try to + search for an alignment pattern. + We get a much better success rate by doing this after our initial attempt + to promote the transform to a homography than before. + You might also think it would be more reliable to use the interior finder + pattern edges, since the outer ones may be obscured or damaged, and it + would save us a reprojection below, since they would form a nice square + with the location of the alignment pattern, but this turns out to be a bad + idea. + Non-linear distortion is usually maximal on the outside edge, and thus + estimating the grid position from points on the interior means we might + get mis-aligned by the time we reach the edge.*/ + version4 = _ul->eversion[0] + _ul->eversion[1] + _ur->eversion[0] + + _dl->eversion[1]; + if (version4 > 4) { + qr_hom_cell cell; + qr_point p3; + int dim; + dim = 17 + version4; + qr_hom_cell_init(&cell, 0, 0, dim - 1, 0, 0, dim - 1, dim - 1, dim - 1, + _p[0][0], _p[0][1], _p[1][0], _p[1][1], _p[2][0], + _p[2][1], _p[3][0], _p[3][1]); + if (qr_alignment_pattern_search(p3, &cell, dim - 7, dim - 7, 4, _img, + _width, _height) >= 0) { + long long w; + long long mask; + int c21; + int dx21; + int dy21; + /*There's no real need to update the bounding box corner, and in fact we + actively perform worse if we do. + Clearly it was good enough for us to find this alignment pattern, so + it should be good enough to use for grid initialization. + The point of doing the search was to get more accurate version + estimates and a better chance of decoding the version and format info. + This is particularly important for small versions that have no encoded + version info, since any mismatch in version renders the code + undecodable.*/ + /*We do, however, need four points in a square to initialize our + homography, so project the point from the alignment center to the + corner of the code area.*/ + c21 = _p[2][0] * _p[1][1] - _p[2][1] * _p[1][0]; + dx21 = _p[2][0] - _p[1][0]; + dy21 = _p[2][1] - _p[1][1]; + w = QR_EXTMUL(dim - 7, c21, + QR_EXTMUL(dim - 13, _p[0][0] * dy21 - _p[0][1] * dx21, + QR_EXTMUL(6, p3[0] * dy21 - p3[1] * dx21, + 0))); + /*The projection failed: invalid geometry.*/ + if (w == 0) + return -1; + mask = QR_SIGNMASK(w); + w = w + mask ^ mask; + brx = (int)QR_DIVROUND( + QR_EXTMUL((dim - 7) * _p[0][0], p3[0] * dy21, + QR_EXTMUL((dim - 13) * p3[0], c21 - _p[0][1] * dx21, + QR_EXTMUL(6 * _p[0][0], c21 - p3[1] * dx21, + 0))) + + mask ^ + mask, + w); + bry = (int)QR_DIVROUND( + QR_EXTMUL((dim - 7) * _p[0][1], -p3[1] * dx21, + QR_EXTMUL((dim - 13) * p3[1], c21 + _p[0][0] * dy21, + QR_EXTMUL(6 * _p[0][1], c21 + p3[0] * dy21, + 0))) + + mask ^ + mask, + w); + } + } + /*Now we have four points that map to a square: initialize the projection.*/ + qr_hom_init(_hom, _p[0][0], _p[0][1], _p[1][0], _p[1][1], _p[2][0], + _p[2][1], brx, bry, QR_HOM_BITS); + return 0; +} + +/*The BCH(18,6,3) codes are only used for version information, which must lie + between 7 and 40 (inclusive).*/ +static const unsigned BCH18_6_CODES[34] = { + 0x07C94, 0x085BC, 0x09A99, 0x0A4D3, 0x0BBF6, 0x0C762, 0x0D847, + 0x0E60D, 0x0F928, 0x10B78, 0x1145D, 0x12A17, 0x13532, 0x149A6, + 0x15683, 0x168C9, 0x177EC, 0x18EC4, 0x191E1, 0x1AFAB, 0x1B08E, + 0x1CC1A, 0x1D33F, 0x1ED75, 0x1F250, 0x209D5, 0x216F0, 0x228BA, + 0x2379F, 0x24B0B, 0x2542E, 0x26A64, 0x27541, 0x28C69 +}; + +/*Corrects a BCH(18,6,3) code word. + _y: Contains the code word to be checked on input, and the corrected value on + output. + Return: The number of errors. + If more than 3 errors are detected, returns a negative value and + performs no correction.*/ +static int bch18_6_correct(unsigned *_y) +{ + unsigned x; + unsigned y; + int nerrs; + y = *_y; + /*Check the easy case first: see if the data bits were uncorrupted.*/ + x = y >> 12; + if (x >= 7 && x <= 40) { + nerrs = qr_hamming_dist(y, BCH18_6_CODES[x - 7], 4); + if (nerrs < 4) { + *_y = BCH18_6_CODES[x - 7]; + return nerrs; + } + } + /*Exhaustive search is faster than field operations in GF(19).*/ + for (x = 0; x < 34; x++) + if (x + 7 != y >> 12) { + nerrs = qr_hamming_dist(y, BCH18_6_CODES[x], 4); + if (nerrs < 4) { + *_y = BCH18_6_CODES[x]; + return nerrs; + } + } + return -1; +} + +#if 0 +static unsigned bch18_6_encode(unsigned _x){ + return (-(_x&1)&0x01F25)^(-(_x>>1&1)&0x0216F)^(-(_x>>2&1)&0x042DE)^ + (-(_x>>3&1)&0x085BC)^(-(_x>>4&1)&0x10B78)^(-(_x>>5&1)&0x209D5); +} +#endif + +/*Reads the version bits near a finder module and decodes the version number.*/ +static int qr_finder_version_decode(qr_finder *_f, const qr_hom *_hom, + const unsigned char *_img, int _width, + int _height, int _dir) +{ + qr_point q; + unsigned v; + int x0; + int y0; + int w0; + int dxi; + int dyi; + int dwi; + int dxj; + int dyj; + int dwj; + int ret; + int i; + int j; + int k; + v = 0; + q[_dir] = _f->o[_dir] - 7 * _f->size[_dir]; + q[1 - _dir] = _f->o[1 - _dir] - 3 * _f->size[1 - _dir]; + x0 = _hom->fwd[0][0] * q[0] + _hom->fwd[0][1] * q[1]; + y0 = _hom->fwd[1][0] * q[0] + _hom->fwd[1][1] * q[1]; + w0 = _hom->fwd[2][0] * q[0] + _hom->fwd[2][1] * q[1] + _hom->fwd22; + dxi = _hom->fwd[0][1 - _dir] * _f->size[1 - _dir]; + dyi = _hom->fwd[1][1 - _dir] * _f->size[1 - _dir]; + dwi = _hom->fwd[2][1 - _dir] * _f->size[1 - _dir]; + dxj = _hom->fwd[0][_dir] * _f->size[_dir]; + dyj = _hom->fwd[1][_dir] * _f->size[_dir]; + dwj = _hom->fwd[2][_dir] * _f->size[_dir]; + for (k = i = 0; i < 6; i++) { + int x; + int y; + int w; + x = x0; + y = y0; + w = w0; + for (j = 0; j < 3; j++, k++) { + qr_point p; + qr_hom_fproject(p, _hom, x, y, w); + v |= qr_img_get_bit(_img, _width, _height, p[0], p[1]) << k; + x += dxj; + y += dyj; + w += dwj; + } + x0 += dxi; + y0 += dyi; + w0 += dwi; + } + ret = bch18_6_correct(&v); + /*TODO: I seem to have an image with the version bits in a different order + (the transpose of the standard order). + Even if I change the order here so I can parse the version on this image, + I can't decode the rest of the code. + If this is really needed, we should just re-order the bits.*/ +#if 0 + if(ret<0){ + /*17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + 0 3 6 9 12 15 1 4 7 10 13 16 2 5 8 11 14 17 + 17 13 9 5 1 -3 10 6 2 -2 -6-10 3 -1 -5 -9-13-17*/ + v=0; + for(k=i=0;i<3;i++){ + p[_dir]=_f->o[_dir]+_f->size[_dir]*(-5-i); + for(j=0;j<6;j++,k++){ + qr_point q; + p[1-_dir]=_f->o[1-_dir]+_f->size[1-_dir]*(2-j); + qr_hom_project(q,_hom,p[0],p[1]); + v|=qr_img_get_bit(_img,_width,_height,q[0],q[1])<<k; + } + } + ret=bch18_6_correct(&v); + } +#endif + return ret >= 0 ? (int)(v >> 12) : ret; +} + +/*Reads the format info bits near the finder modules and decodes them.*/ +static int qr_finder_fmt_info_decode(qr_finder *_ul, qr_finder *_ur, + qr_finder *_dl, const qr_hom *_hom, + const unsigned char *_img, int _width, + int _height) +{ + qr_point p; + unsigned lo[2]; + unsigned hi[2]; + int u; + int v; + int x; + int y; + int w; + int dx; + int dy; + int dw; + int fmt_info[4]; + int count[4]; + int nerrs[4]; + int nfmt_info; + int besti; + int imax; + int di; + int i; + int k; + /*Read the bits around the UL corner.*/ + lo[0] = 0; + u = _ul->o[0] + 5 * _ul->size[0]; + v = _ul->o[1] - 3 * _ul->size[1]; + x = _hom->fwd[0][0] * u + _hom->fwd[0][1] * v; + y = _hom->fwd[1][0] * u + _hom->fwd[1][1] * v; + w = _hom->fwd[2][0] * u + _hom->fwd[2][1] * v + _hom->fwd22; + dx = _hom->fwd[0][1] * _ul->size[1]; + dy = _hom->fwd[1][1] * _ul->size[1]; + dw = _hom->fwd[2][1] * _ul->size[1]; + for (k = i = 0;; i++) { + /*Skip the timing pattern row.*/ + if (i != 6) { + qr_hom_fproject(p, _hom, x, y, w); + lo[0] |= qr_img_get_bit(_img, _width, _height, p[0], p[1]) << k++; + /*Don't advance q in the last iteration... we'll start the next loop from + the current position.*/ + if (i >= 8) + break; + } + x += dx; + y += dy; + w += dw; + } + hi[0] = 0; + dx = -_hom->fwd[0][0] * _ul->size[0]; + dy = -_hom->fwd[1][0] * _ul->size[0]; + dw = -_hom->fwd[2][0] * _ul->size[0]; + while (i-- > 0) { + x += dx; + y += dy; + w += dw; + /*Skip the timing pattern column.*/ + if (i != 6) { + qr_hom_fproject(p, _hom, x, y, w); + hi[0] |= qr_img_get_bit(_img, _width, _height, p[0], p[1]) << k++; + } + } + /*Read the bits next to the UR corner.*/ + lo[1] = 0; + u = _ur->o[0] + 3 * _ur->size[0]; + v = _ur->o[1] + 5 * _ur->size[1]; + x = _hom->fwd[0][0] * u + _hom->fwd[0][1] * v; + y = _hom->fwd[1][0] * u + _hom->fwd[1][1] * v; + w = _hom->fwd[2][0] * u + _hom->fwd[2][1] * v + _hom->fwd22; + dx = -_hom->fwd[0][0] * _ur->size[0]; + dy = -_hom->fwd[1][0] * _ur->size[0]; + dw = -_hom->fwd[2][0] * _ur->size[0]; + for (k = 0; k < 8; k++) { + qr_hom_fproject(p, _hom, x, y, w); + lo[1] |= qr_img_get_bit(_img, _width, _height, p[0], p[1]) << k; + x += dx; + y += dy; + w += dw; + } + /*Read the bits next to the DL corner.*/ + hi[1] = 0; + u = _dl->o[0] + 5 * _dl->size[0]; + v = _dl->o[1] - 3 * _dl->size[1]; + x = _hom->fwd[0][0] * u + _hom->fwd[0][1] * v; + y = _hom->fwd[1][0] * u + _hom->fwd[1][1] * v; + w = _hom->fwd[2][0] * u + _hom->fwd[2][1] * v + _hom->fwd22; + dx = _hom->fwd[0][1] * _dl->size[1]; + dy = _hom->fwd[1][1] * _dl->size[1]; + dw = _hom->fwd[2][1] * _dl->size[1]; + for (k = 8; k < 15; k++) { + qr_hom_fproject(p, _hom, x, y, w); + hi[1] |= qr_img_get_bit(_img, _width, _height, p[0], p[1]) << k; + x += dx; + y += dy; + w += dw; + } + /*For each group of bits we have two samples... try them in all combinations + and pick the most popular valid code, breaking ties using the number of + bit errors.*/ + imax = 2 << (hi[0] != hi[1]); + di = 1 + (lo[0] == lo[1]); + nfmt_info = 0; + for (i = 0; i < imax; i += di) { + unsigned v; + int ret; + int j; + v = (lo[i & 1] | hi[i >> 1]) ^ 0x5412; + ret = bch15_5_correct(&v); + v >>= 10; + if (ret < 0) + ret = 4; + for (j = 0;; j++) { + if (j >= nfmt_info) { + fmt_info[j] = v; + count[j] = 1; + nerrs[j] = ret; + nfmt_info++; + break; + } + if (fmt_info[j] == (int)v) { + count[j]++; + if (ret < nerrs[j]) + nerrs[j] = ret; + break; + } + } + } + besti = 0; + for (i = 1; i < nfmt_info; i++) { + if (nerrs[besti] > 3 && nerrs[i] <= 3 || count[i] > count[besti] || + count[i] == count[besti] && nerrs[i] < nerrs[besti]) { + besti = i; + } + } + return nerrs[besti] < 4 ? fmt_info[besti] : -1; +} + +/*The grid used to sample the image bits. + The grid is divided into separate cells bounded by finder patterns and/or + alignment patterns, and a separate map back to the original image is + constructed for each cell. + All of these structural elements, as well as the timing patterns, version + info, and format info, are marked in fpmask so they can easily be skipped + during decode.*/ +struct qr_sampling_grid { + qr_hom_cell *cells[6]; + unsigned *fpmask; + int cell_limits[6]; + int ncells; +}; + +/*Mark a given region as belonging to the function pattern.*/ +static void qr_sampling_grid_fp_mask_rect(qr_sampling_grid *_grid, int _dim, + int _u, int _v, int _w, int _h) +{ + int i; + int j; + int stride; + stride = _dim + QR_INT_BITS - 1 >> QR_INT_LOGBITS; + /*Note that we store bits column-wise, since that's how they're read out of + the grid.*/ + for (j = _u; j < _u + _w; j++) + for (i = _v; i < _v + _h; i++) { + _grid->fpmask[j * stride + (i >> QR_INT_LOGBITS)] |= + 1 << (i & QR_INT_BITS - 1); + } +} + +/*Determine if a given grid location is inside the function pattern.*/ +static int qr_sampling_grid_is_in_fp(const qr_sampling_grid *_grid, int _dim, + int _u, int _v) +{ + return _grid->fpmask[_u * (_dim + QR_INT_BITS - 1 >> QR_INT_LOGBITS) + + (_v >> QR_INT_LOGBITS)] >> + (_v & QR_INT_BITS - 1) & + 1; +} + +/*The spacing between alignment patterns after the second for versions >= 7. + We could compact this more, but the code to access it would eliminate the + gains.*/ +static const unsigned char QR_ALIGNMENT_SPACING[34] = { + 16, 18, 20, 22, 24, 26, 28, 20, 22, 24, 24, 26, 28, 28, 22, 24, 24, + 26, 26, 28, 28, 24, 24, 26, 26, 26, 28, 28, 24, 26, 26, 26, 28, 28 +}; + +static inline void qr_svg_points(const char *cls, qr_point *p, int n) +{ + int i; + svg_path_start(cls, 1, 0, 0); + for (i = 0; i < n; i++, p++) + svg_path_moveto(SVG_ABS, p[0][0], p[0][1]); + svg_path_end(); +} + +/*Initialize the sampling grid for each region of the code. + _version: The (decoded) version number. + _ul_pos: The location of the UL finder pattern. + _ur_pos: The location of the UR finder pattern. + _dl_pos: The location of the DL finder pattern. + _p: On input, contains estimated positions of the four corner modules. + On output, contains a bounding quadrilateral for the code. + _img: The binary input image. + _width: The width of the input image. + _height: The height of the input image. + Return: 0 on success, or a negative value on error.*/ +static void qr_sampling_grid_init(qr_sampling_grid *_grid, int _version, + const qr_point _ul_pos, + const qr_point _ur_pos, + const qr_point _dl_pos, qr_point _p[4], + const unsigned char *_img, int _width, + int _height) +{ + qr_hom_cell base_cell; + int align_pos[7]; + int dim; + int nalign; + int i; + dim = 17 + (_version << 2); + nalign = (_version / 7) + 2; + /*Create a base cell to bootstrap the alignment pattern search.*/ + qr_hom_cell_init(&base_cell, 0, 0, dim - 1, 0, 0, dim - 1, dim - 1, dim - 1, + _p[0][0], _p[0][1], _p[1][0], _p[1][1], _p[2][0], _p[2][1], + _p[3][0], _p[3][1]); + /*Allocate the array of cells.*/ + _grid->ncells = nalign - 1; + _grid->cells[0] = (qr_hom_cell *)malloc((nalign - 1) * (nalign - 1) * + sizeof(*_grid->cells[0])); + for (i = 1; i < _grid->ncells; i++) + _grid->cells[i] = _grid->cells[i - 1] + _grid->ncells; + /*Initialize the function pattern mask.*/ + _grid->fpmask = + (unsigned *)calloc(dim, (dim + QR_INT_BITS - 1 >> QR_INT_LOGBITS) * + sizeof(*_grid->fpmask)); + /*Mask out the finder patterns (and separators and format info bits).*/ + qr_sampling_grid_fp_mask_rect(_grid, dim, 0, 0, 9, 9); + qr_sampling_grid_fp_mask_rect(_grid, dim, 0, dim - 8, 9, 8); + qr_sampling_grid_fp_mask_rect(_grid, dim, dim - 8, 0, 8, 9); + /*Mask out the version number bits.*/ + if (_version > 6) { + qr_sampling_grid_fp_mask_rect(_grid, dim, 0, dim - 11, 6, 3); + qr_sampling_grid_fp_mask_rect(_grid, dim, dim - 11, 0, 3, 6); + } + /*Mask out the timing patterns.*/ + qr_sampling_grid_fp_mask_rect(_grid, dim, 9, 6, dim - 17, 1); + qr_sampling_grid_fp_mask_rect(_grid, dim, 6, 9, 1, dim - 17); + /*If we have no alignment patterns (e.g., this is a version 1 code), just use + the base cell and hope it's good enough.*/ + if (_version < 2) + memcpy(_grid->cells[0], &base_cell, sizeof(base_cell)); + else { + qr_point *q; + qr_point *p; + int j; + int k; + q = (qr_point *)malloc(nalign * nalign * sizeof(*q)); + p = (qr_point *)malloc(nalign * nalign * sizeof(*p)); + /*Initialize the alignment pattern position list.*/ + align_pos[0] = 6; + align_pos[nalign - 1] = dim - 7; + if (_version > 6) { + int d; + d = QR_ALIGNMENT_SPACING[_version - 7]; + for (i = nalign - 1; i-- > 1;) + align_pos[i] = align_pos[i + 1] - d; + } + /*Three of the corners use a finder pattern instead of a separate + alignment pattern.*/ + q[0][0] = 3; + q[0][1] = 3; + p[0][0] = _ul_pos[0]; + p[0][1] = _ul_pos[1]; + q[nalign - 1][0] = dim - 4; + q[nalign - 1][1] = 3; + p[nalign - 1][0] = _ur_pos[0]; + p[nalign - 1][1] = _ur_pos[1]; + q[(nalign - 1) * nalign][0] = 3; + q[(nalign - 1) * nalign][1] = dim - 4; + p[(nalign - 1) * nalign][0] = _dl_pos[0]; + p[(nalign - 1) * nalign][1] = _dl_pos[1]; + /*Scan for alignment patterns using a diagonal sweep.*/ + for (k = 1; k < 2 * nalign - 1; k++) { + int jmin; + int jmax; + jmax = QR_MINI(k, nalign - 1) - (k == nalign - 1); + jmin = QR_MAXI(0, k - (nalign - 1)) + (k == nalign - 1); + for (j = jmin; j <= jmax; j++) { + qr_hom_cell *cell; + int u; + int v; + int k; + i = jmax - (j - jmin); + k = i * nalign + j; + u = align_pos[j]; + v = align_pos[i]; + q[k][0] = u; + q[k][1] = v; + /*Mask out the alignment pattern.*/ + qr_sampling_grid_fp_mask_rect(_grid, dim, u - 2, v - 2, 5, 5); + /*Pick a cell to use to govern the alignment pattern search.*/ + if (i > 1 && j > 1) { + qr_point p0; + qr_point p1; + qr_point p2; + /*Each predictor is basically a straight-line extrapolation from two + neighboring alignment patterns (except possibly near the opposing + finder patterns).*/ + qr_hom_cell_project(p0, _grid->cells[i - 2] + j - 1, u, v, + 0); + qr_hom_cell_project(p1, _grid->cells[i - 2] + j - 2, u, v, + 0); + qr_hom_cell_project(p2, _grid->cells[i - 1] + j - 2, u, v, + 0); + /*Take the median of the predictions as the search center.*/ + QR_SORT2I(p0[0], p1[0]); + QR_SORT2I(p0[1], p1[1]); + QR_SORT2I(p1[0], p2[0]); + QR_SORT2I(p1[1], p2[1]); + QR_SORT2I(p0[0], p1[0]); + QR_SORT2I(p0[1], p1[1]); + /*We need a cell that has the target point at a known (u,v) location. + Since our cells don't have inverses, just construct one from our + neighboring points.*/ + cell = _grid->cells[i - 1] + j - 1; + qr_hom_cell_init(cell, q[k - nalign - 1][0], + q[k - nalign - 1][1], q[k - nalign][0], + q[k - nalign][1], q[k - 1][0], q[k - 1][1], + q[k][0], q[k][1], p[k - nalign - 1][0], + p[k - nalign - 1][1], p[k - nalign][0], + p[k - nalign][1], p[k - 1][0], p[k - 1][1], + p1[0], p1[1]); + } else if (i > 1 && j > 0) + cell = _grid->cells[i - 2] + j - 1; + else if (i > 0 && j > 1) + cell = _grid->cells[i - 1] + j - 2; + else + cell = &base_cell; + /*Use a very small search radius. + A large displacement here usually means a false positive (e.g., when + the real alignment pattern is damaged or missing), which can + severely distort the projection.*/ + qr_alignment_pattern_search(p[k], cell, u, v, 2, _img, _width, + _height); + if (i > 0 && j > 0) { + qr_hom_cell_init(_grid->cells[i - 1] + j - 1, + q[k - nalign - 1][0], q[k - nalign - 1][1], + q[k - nalign][0], q[k - nalign][1], + q[k - 1][0], q[k - 1][1], q[k][0], q[k][1], + p[k - nalign - 1][0], p[k - nalign - 1][1], + p[k - nalign][0], p[k - nalign][1], + p[k - 1][0], p[k - 1][1], p[k][0], + p[k][1]); + } + } + } + qr_svg_points("align", p, nalign * nalign); + free(q); + free(p); + } + /*Set the limits over which each cell is used.*/ + memcpy(_grid->cell_limits, align_pos + 1, + (_grid->ncells - 1) * sizeof(*_grid->cell_limits)); + _grid->cell_limits[_grid->ncells - 1] = dim; + /*Produce a bounding square for the code (to mark finder centers with). + Because of non-linear distortion, this might not actually bound the code, + but it should be good enough. + I don't think it's worth computing a convex hull or anything silly like + that.*/ + qr_hom_cell_project(_p[0], _grid->cells[0] + 0, -1, -1, 1); + qr_hom_cell_project(_p[1], _grid->cells[0] + _grid->ncells - 1, + (dim << 1) - 1, -1, 1); + qr_hom_cell_project(_p[2], _grid->cells[_grid->ncells - 1] + 0, -1, + (dim << 1) - 1, 1); + qr_hom_cell_project(_p[3], + _grid->cells[_grid->ncells - 1] + _grid->ncells - 1, + (dim << 1) - 1, (dim << 1) - 1, 1); + /*Clamp the points somewhere near the image (this is really just in case a + corner is near the plane at infinity).*/ + for (i = 0; i < 4; i++) { + _p[i][0] = QR_CLAMPI(-_width << QR_FINDER_SUBPREC, _p[i][0], + _width << QR_FINDER_SUBPREC + 1); + _p[i][1] = QR_CLAMPI(-_height << QR_FINDER_SUBPREC, _p[i][1], + _height << QR_FINDER_SUBPREC + 1); + } + /*TODO: Make fine adjustments using the timing patterns. + Possible strategy: scan the timing pattern at QR_ALIGN_SUBPREC (or finer) + resolution, use dynamic programming to match midpoints between + transitions to the ideal grid locations.*/ +} + +static void qr_sampling_grid_clear(qr_sampling_grid *_grid) +{ + free(_grid->fpmask); + free(_grid->cells[0]); +} + +#if defined(QR_DEBUG) +static void qr_sampling_grid_dump(qr_sampling_grid *_grid, int _version, + const unsigned char *_img, int _width, + int _height) +{ + unsigned char *gimg; + FILE *fout; + int dim; + int u; + int v; + int x; + int y; + int w; + int i; + int j; + int r; + int s; + dim = 17 + (_version << 2) + 8 << QR_ALIGN_SUBPREC; + gimg = (unsigned char *)malloc(dim * dim * sizeof(*gimg)); + for (i = 0; i < dim; i++) + for (j = 0; j < dim; j++) { + qr_hom_cell *cell; + if (i >= (4 << QR_ALIGN_SUBPREC) && + i <= dim - (5 << QR_ALIGN_SUBPREC) && + j >= (4 << QR_ALIGN_SUBPREC) && + j <= dim - (5 << QR_ALIGN_SUBPREC) && + ((!(i & (1 << QR_ALIGN_SUBPREC) - 1)) ^ + (!(j & (1 << QR_ALIGN_SUBPREC) - 1)))) { + gimg[i * dim + j] = 0x7F; + } else { + qr_point p; + u = (j >> QR_ALIGN_SUBPREC) - 4; + v = (i >> QR_ALIGN_SUBPREC) - 4; + for (r = 0; r < _grid->ncells - 1; r++) + if (u < _grid->cell_limits[r]) + break; + for (s = 0; s < _grid->ncells - 1; s++) + if (v < _grid->cell_limits[s]) + break; + cell = _grid->cells[s] + r; + u = j - (cell->u0 + 4 << QR_ALIGN_SUBPREC); + v = i - (cell->v0 + 4 << QR_ALIGN_SUBPREC); + x = cell->fwd[0][0] * u + cell->fwd[0][1] * v + + (cell->fwd[0][2] << QR_ALIGN_SUBPREC); + y = cell->fwd[1][0] * u + cell->fwd[1][1] * v + + (cell->fwd[1][2] << QR_ALIGN_SUBPREC); + w = cell->fwd[2][0] * u + cell->fwd[2][1] * v + + (cell->fwd[2][2] << QR_ALIGN_SUBPREC); + qr_hom_cell_fproject(p, cell, x, y, w); + gimg[i * dim + j] = + _img[QR_CLAMPI(0, p[1] >> QR_FINDER_SUBPREC, _height - 1) * + _width + + QR_CLAMPI(0, p[0] >> QR_FINDER_SUBPREC, _width - 1)]; + } + } + for (v = 0; v < 17 + (_version << 2); v++) + for (u = 0; u < 17 + (_version << 2); u++) { + if (qr_sampling_grid_is_in_fp(_grid, 17 + (_version << 2), u, v)) { + j = u + 4 << QR_ALIGN_SUBPREC; + i = v + 4 << QR_ALIGN_SUBPREC; + gimg[(i - 1) * dim + j - 1] = 0x7F; + gimg[(i - 1) * dim + j] = 0x7F; + gimg[(i - 1) * dim + j + 1] = 0x7F; + gimg[i * dim + j - 1] = 0x7F; + gimg[i * dim + j + 1] = 0x7F; + gimg[(i + 1) * dim + j - 1] = 0x7F; + gimg[(i + 1) * dim + j] = 0x7F; + gimg[(i + 1) * dim + j + 1] = 0x7F; + } + } + fout = fopen("grid.png", "wb"); + image_write_png(gimg, dim, dim, fout); + fclose(fout); + free(gimg); +} +#endif + +/*Generate the data mask corresponding to the given mask pattern.*/ +static void qr_data_mask_fill(unsigned *_mask, int _dim, int _pattern) +{ + int stride; + int i; + int j; + stride = _dim + QR_INT_BITS - 1 >> QR_INT_LOGBITS; + /*Note that we store bits column-wise, since that's how they're read out of + the grid.*/ + switch (_pattern) { + /*10101010 i+j+1&1 + 01010101 + 10101010 + 01010101*/ + case 0: { + int m; + m = 0x55; + for (j = 0; j < _dim; j++) { + memset(_mask + j * stride, m, stride * sizeof(*_mask)); + m ^= 0xFF; + } + } break; + /*11111111 i+1&1 + 00000000 + 11111111 + 00000000*/ + case 1: + memset(_mask, 0x55, _dim * stride * sizeof(*_mask)); + break; + /*10010010 (j+1)%3&1 + 10010010 + 10010010 + 10010010*/ + case 2: { + unsigned m; + m = 0xFF; + for (j = 0; j < _dim; j++) { + memset(_mask + j * stride, m & 0xFF, stride * sizeof(*_mask)); + m = m << 8 | m >> 16; + } + } break; + /*10010010 (i+j+1)%3&1 + 00100100 + 01001001 + 10010010*/ + case 3: { + unsigned mi; + unsigned mj; + mj = 0; + for (i = 0; i < (QR_INT_BITS + 2) / 3; i++) + mj |= 1 << 3 * i; + for (j = 0; j < _dim; j++) { + mi = mj; + for (i = 0; i < stride; i++) { + _mask[j * stride + i] = mi; + mi = mi >> QR_INT_BITS % 3 | mi << 3 - QR_INT_BITS % 3; + } + mj = mj >> 1 | mj << 2; + } + } break; + /*11100011 (i>>1)+(j/3)+1&1 + 11100011 + 00011100 + 00011100*/ + case 4: { + unsigned m; + m = 7; + for (j = 0; j < _dim; j++) { + memset(_mask + j * stride, (0xCC ^ -(m & 1)) & 0xFF, + stride * sizeof(*_mask)); + m = m >> 1 | m << 5; + } + } break; + /*11111111 !((i*j)%6) + 10000010 + 10010010 + 10101010*/ + case 5: { + for (j = 0; j < _dim; j++) { + unsigned m; + m = 0; + for (i = 0; i < 6; i++) + m |= !((i * j) % 6) << i; + for (i = 6; i < QR_INT_BITS; i <<= 1) + m |= m << i; + for (i = 0; i < stride; i++) { + _mask[j * stride + i] = m; + m = m >> QR_INT_BITS % 6 | m << 6 - QR_INT_BITS % 6; + } + } + } break; + /*11111111 (i*j)%3+i*j+1&1 + 11100011 + 11011011 + 10101010*/ + case 6: { + for (j = 0; j < _dim; j++) { + unsigned m; + m = 0; + for (i = 0; i < 6; i++) + m |= ((i * j) % 3 + i * j + 1 & 1) << i; + for (i = 6; i < QR_INT_BITS; i <<= 1) + m |= m << i; + for (i = 0; i < stride; i++) { + _mask[j * stride + i] = m; + m = m >> QR_INT_BITS % 6 | m << 6 - QR_INT_BITS % 6; + } + } + } break; + /*10101010 (i*j)%3+i+j+1&1 + 00011100 + 10001110 + 01010101*/ + default: { + for (j = 0; j < _dim; j++) { + unsigned m; + m = 0; + for (i = 0; i < 6; i++) + m |= ((i * j) % 3 + i + j + 1 & 1) << i; + for (i = 6; i < QR_INT_BITS; i <<= 1) + m |= m << i; + for (i = 0; i < stride; i++) { + _mask[j * stride + i] = m; + m = m >> QR_INT_BITS % 6 | m << 6 - QR_INT_BITS % 6; + } + } + } break; + } +} + +static void qr_sampling_grid_sample(const qr_sampling_grid *_grid, + unsigned *_data_bits, int _dim, + int _fmt_info, const unsigned char *_img, + int _width, int _height) +{ + int stride; + int u0; + int u1; + int j; + /*We initialize the buffer with the data mask and XOR bits into it as we read + them out of the image instead of unmasking in a separate step.*/ + qr_data_mask_fill(_data_bits, _dim, _fmt_info & 7); + stride = _dim + QR_INT_BITS - 1 >> QR_INT_LOGBITS; + u0 = 0; + svg_path_start("sampling-grid", 1, 0, 0); + /*We read data cell-by-cell to avoid having to constantly change which + projection we're using as we read each bit. + This (and the position-dependent data mask) is the reason we buffer the + bits we read instead of converting them directly to codewords here. + Note that bits are stored column-wise, since that's how we'll scan them.*/ + for (j = 0; j < _grid->ncells; j++) { + int i; + int v0; + int v1; + u1 = _grid->cell_limits[j]; + v0 = 0; + for (i = 0; i < _grid->ncells; i++) { + qr_hom_cell *cell; + int x0; + int y0; + int w0; + int u; + int du; + int dv; + v1 = _grid->cell_limits[i]; + cell = _grid->cells[i] + j; + du = u0 - cell->u0; + dv = v0 - cell->v0; + x0 = cell->fwd[0][0] * du + cell->fwd[0][1] * dv + cell->fwd[0][2]; + y0 = cell->fwd[1][0] * du + cell->fwd[1][1] * dv + cell->fwd[1][2]; + w0 = cell->fwd[2][0] * du + cell->fwd[2][1] * dv + cell->fwd[2][2]; + for (u = u0; u < u1; u++) { + int x; + int y; + int w; + int v; + x = x0; + y = y0; + w = w0; + for (v = v0; v < v1; v++) { + /*Skip doing all the divisions and bounds checks if the bit is in the + function pattern.*/ + if (!qr_sampling_grid_is_in_fp(_grid, _dim, u, v)) { + qr_point p; + qr_hom_cell_fproject(p, cell, x, y, w); + _data_bits[u * stride + (v >> QR_INT_LOGBITS)] ^= + qr_img_get_bit(_img, _width, _height, p[0], p[1]) + << (v & QR_INT_BITS - 1); + svg_path_moveto(SVG_ABS, p[0], p[1]); + } + x += cell->fwd[0][1]; + y += cell->fwd[1][1]; + w += cell->fwd[2][1]; + } + x0 += cell->fwd[0][0]; + y0 += cell->fwd[1][0]; + w0 += cell->fwd[2][0]; + } + v0 = v1; + } + u0 = u1; + } + svg_path_end(); +} + +/*Arranges the sample bits read by qr_sampling_grid_sample() into bytes and + groups those bytes into Reed-Solomon blocks. + The individual block pointers are destroyed by this routine.*/ +static void qr_samples_unpack(unsigned char **_blocks, int _nblocks, + int _nshort_data, int _nshort_blocks, + const unsigned *_data_bits, + const unsigned *_fp_mask, int _dim) +{ + unsigned bits; + int biti; + int stride; + int blocki; + int blockj; + int i; + int j; + stride = _dim + QR_INT_BITS - 1 >> QR_INT_LOGBITS; + /*If _all_ the blocks are short, don't skip anything (see below).*/ + if (_nshort_blocks >= _nblocks) + _nshort_blocks = 0; + /*Scan columns in pairs from right to left.*/ + bits = 0; + for (blocki = blockj = biti = 0, j = _dim - 1; j > 0; j -= 2) { + unsigned data1; + unsigned data2; + unsigned fp_mask1; + unsigned fp_mask2; + int nbits; + int l; + /*Scan up a pair of columns.*/ + nbits = (_dim - 1 & QR_INT_BITS - 1) + 1; + l = j * stride; + for (i = stride; i-- > 0;) { + data1 = _data_bits[l + i]; + fp_mask1 = _fp_mask[l + i]; + data2 = _data_bits[l + i - stride]; + fp_mask2 = _fp_mask[l + i - stride]; + while (nbits-- > 0) { + /*Pull a bit from the right column.*/ + if (!(fp_mask1 >> nbits & 1)) { + bits = bits << 1 | data1 >> nbits & 1; + biti++; + } + /*Pull a bit from the left column.*/ + if (!(fp_mask2 >> nbits & 1)) { + bits = bits << 1 | data2 >> nbits & 1; + biti++; + } + /*If we finished a byte, drop it in a block.*/ + if (biti >= 8) { + biti -= 8; + *_blocks[blocki++]++ = (unsigned char)(bits >> biti); + /*For whatever reason, the long blocks are at the _end_ of the list, + instead of the beginning. + Even worse, the extra bytes they get come at the end of the data + bytes, before the parity bytes. + Hence the logic here: when we've filled up the data portion of the + short blocks, skip directly to the long blocks for the next byte. + It's also the reason we increment _blocks[blocki] on each store, + instead of just indexing with blockj (after this iteration the + number of bytes in each block differs).*/ + if (blocki >= _nblocks) + blocki = ++blockj == _nshort_data ? _nshort_blocks : 0; + } + } + nbits = QR_INT_BITS; + } + j -= 2; + /*Skip the column with the vertical timing pattern.*/ + if (j == 6) + j--; + /*Scan down a pair of columns.*/ + l = j * stride; + for (i = 0; i < stride; i++) { + data1 = _data_bits[l + i]; + fp_mask1 = _fp_mask[l + i]; + data2 = _data_bits[l + i - stride]; + fp_mask2 = _fp_mask[l + i - stride]; + nbits = QR_MINI(_dim - (i << QR_INT_LOGBITS), QR_INT_BITS); + while (nbits-- > 0) { + /*Pull a bit from the right column.*/ + if (!(fp_mask1 & 1)) { + bits = bits << 1 | data1 & 1; + biti++; + } + data1 >>= 1; + fp_mask1 >>= 1; + /*Pull a bit from the left column.*/ + if (!(fp_mask2 & 1)) { + bits = bits << 1 | data2 & 1; + biti++; + } + data2 >>= 1; + fp_mask2 >>= 1; + /*If we finished a byte, drop it in a block.*/ + if (biti >= 8) { + biti -= 8; + *_blocks[blocki++]++ = (unsigned char)(bits >> biti); + /*See comments on the "up" loop for the reason behind this mess.*/ + if (blocki >= _nblocks) + blocki = ++blockj == _nshort_data ? _nshort_blocks : 0; + } + } + } + } +} + +/*Bit reading code blatantly stolen^W^Wadapted from libogg/libtheora (because + I've already debugged it and I know it works). + Portions (C) Xiph.Org Foundation 1994-2008, BSD-style license.*/ +struct qr_pack_buf { + const unsigned char *buf; + int endbyte; + int endbit; + int storage; +}; + +static void qr_pack_buf_init(qr_pack_buf *_b, const unsigned char *_data, + int _ndata) +{ + _b->buf = _data; + _b->storage = _ndata; + _b->endbyte = _b->endbit = 0; +} + +/*Assumes 0<=_bits<=16.*/ +static int qr_pack_buf_read(qr_pack_buf *_b, int _bits) +{ + const unsigned char *p; + unsigned ret; + int m; + int d; + m = 16 - _bits; + _bits += _b->endbit; + d = _b->storage - _b->endbyte; + if (d <= 2) { + /*Not the main path.*/ + if (d * 8 < _bits) { + _b->endbyte += _bits >> 3; + _b->endbit = _bits & 7; + return -1; + } + /*Special case to avoid reading p[0] below, which might be past the end of + the buffer; also skips some useless accounting.*/ + else if (!_bits) + return 0; + } + p = _b->buf + _b->endbyte; + ret = p[0] << 8 + _b->endbit; + if (_bits > 8) { + ret |= p[1] << _b->endbit; + if (_bits > 16) + ret |= p[2] >> 8 - _b->endbit; + } + _b->endbyte += _bits >> 3; + _b->endbit = _bits & 7; + return (ret & 0xFFFF) >> m; +} + +static int qr_pack_buf_avail(const qr_pack_buf *_b) +{ + return (_b->storage - _b->endbyte << 3) - _b->endbit; +} + +/*The characters available in QR_MODE_ALNUM.*/ +static const unsigned char QR_ALNUM_TABLE[45] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', + 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', '$', '%', '*', '+', '-', '.', '/', ':' +}; + +static int qr_code_data_parse(qr_code_data *_qrdata, int _version, + const unsigned char *_data, int _ndata) +{ + qr_pack_buf qpb; + unsigned self_parity; + int centries; + int len_bits_idx; + /*Entries are stored directly in the struct during parsing. + Caller cleans up any allocated data on failure.*/ + _qrdata->entries = NULL; + _qrdata->nentries = 0; + _qrdata->sa_size = 0; + self_parity = 0; + centries = 0; + /*The versions are divided into 3 ranges that each use a different number of + bits for length fields.*/ + len_bits_idx = (_version > 9) + (_version > 26); + qr_pack_buf_init(&qpb, _data, _ndata); + /*While we have enough bits to read a mode...*/ + while (qr_pack_buf_avail(&qpb) >= 4) { + qr_code_data_entry *entry; + int mode; + mode = qr_pack_buf_read(&qpb, 4); + /*Mode 0 is a terminator.*/ + if (!mode) + break; + if (_qrdata->nentries >= centries) { + centries = centries << 1 | 1; + _qrdata->entries = (qr_code_data_entry *)realloc( + _qrdata->entries, centries * sizeof(*_qrdata->entries)); + } + entry = _qrdata->entries + _qrdata->nentries++; + entry->mode = mode; + /*Set the buffer to NULL, because if parsing fails, we might try to free it + on clean-up.*/ + entry->payload.data.buf = NULL; + switch (mode) { + /*The number of bits used to encode the character count for each version + range and each data mode.*/ + static const unsigned char LEN_BITS[3][4] = { { 10, 9, 8, 8 }, + { 12, 11, 16, 10 }, + { 14, 13, 16, 12 } }; + case QR_MODE_NUM: { + unsigned char *buf; + unsigned bits; + unsigned c; + int len; + int count; + int rem; + len = qr_pack_buf_read(&qpb, LEN_BITS[len_bits_idx][0]); + if (len < 0) + return -1; + /*Check to see if there are enough bits left now, so we don't have to + in the decode loop.*/ + count = len / 3; + rem = len % 3; + if (qr_pack_buf_avail(&qpb) < + 10 * count + 7 * (rem >> 1 & 1) + 4 * (rem & 1)) + return -1; + entry->payload.data.buf = buf = + (unsigned char *)malloc(len * sizeof(*buf)); + entry->payload.data.len = len; + /*Read groups of 3 digits encoded in 10 bits.*/ + while (count-- > 0) { + bits = qr_pack_buf_read(&qpb, 10); + if (bits >= 1000) + return -1; + c = '0' + bits / 100; + self_parity ^= c; + *buf++ = (unsigned char)c; + bits %= 100; + c = '0' + bits / 10; + self_parity ^= c; + *buf++ = (unsigned char)c; + c = '0' + bits % 10; + self_parity ^= c; + *buf++ = (unsigned char)c; + } + /*Read the last two digits encoded in 7 bits.*/ + if (rem > 1) { + bits = qr_pack_buf_read(&qpb, 7); + if (bits >= 100) + return -1; + c = '0' + bits / 10; + self_parity ^= c; + *buf++ = (unsigned char)c; + c = '0' + bits % 10; + self_parity ^= c; + *buf++ = (unsigned char)c; + } + /*Or the last one digit encoded in 4 bits.*/ + else if (rem) { + bits = qr_pack_buf_read(&qpb, 4); + if (bits >= 10) + return -1; + c = '0' + bits; + self_parity ^= c; + *buf++ = (unsigned char)c; + } + } break; + case QR_MODE_ALNUM: { + unsigned char *buf; + unsigned bits; + unsigned c; + int len; + int count; + int rem; + len = qr_pack_buf_read(&qpb, LEN_BITS[len_bits_idx][1]); + if (len < 0) + return -1; + /*Check to see if there are enough bits left now, so we don't have to + in the decode loop.*/ + count = len >> 1; + rem = len & 1; + if (qr_pack_buf_avail(&qpb) < 11 * count + 6 * rem) + return -1; + entry->payload.data.buf = buf = + (unsigned char *)malloc(len * sizeof(*buf)); + entry->payload.data.len = len; + /*Read groups of two characters encoded in 11 bits.*/ + while (count-- > 0) { + bits = qr_pack_buf_read(&qpb, 11); + if (bits >= 2025) + return -1; + c = QR_ALNUM_TABLE[bits / 45]; + self_parity ^= c; + *buf++ = (unsigned char)c; + c = QR_ALNUM_TABLE[bits % 45]; + self_parity ^= c; + *buf++ = (unsigned char)c; + len -= 2; + } + /*Read the last character encoded in 6 bits.*/ + if (rem) { + bits = qr_pack_buf_read(&qpb, 6); + if (bits >= 45) + return -1; + c = QR_ALNUM_TABLE[bits]; + self_parity ^= c; + *buf++ = (unsigned char)c; + } + } break; + /*Structured-append header.*/ + case QR_MODE_STRUCT: { + int bits; + bits = qr_pack_buf_read(&qpb, 16); + if (bits < 0) + return -1; + /*We save a copy of the data in _qrdata for easy reference when + grouping structured-append codes. + If for some reason the code has multiple S-A headers, first one wins, + since it is supposed to come before everything else (TODO: should we + return an error instead?).*/ + if (_qrdata->sa_size == 0) { + _qrdata->sa_index = entry->payload.sa.sa_index = + (unsigned char)(bits >> 12 & 0xF); + _qrdata->sa_size = entry->payload.sa.sa_size = + (unsigned char)((bits >> 8 & 0xF) + 1); + _qrdata->sa_parity = entry->payload.sa.sa_parity = + (unsigned char)(bits & 0xFF); + } + } break; + case QR_MODE_BYTE: { + unsigned char *buf; + unsigned c; + int len; + len = qr_pack_buf_read(&qpb, LEN_BITS[len_bits_idx][2]); + if (len < 0) + return -1; + /*Check to see if there are enough bits left now, so we don't have to + in the decode loop.*/ + if (qr_pack_buf_avail(&qpb) < len << 3) + return -1; + entry->payload.data.buf = buf = + (unsigned char *)malloc(len * sizeof(*buf)); + entry->payload.data.len = len; + while (len-- > 0) { + c = qr_pack_buf_read(&qpb, 8); + self_parity ^= c; + *buf++ = (unsigned char)c; + } + } break; + /*FNC1 first position marker.*/ + case QR_MODE_FNC1_1ST: + break; + /*Extended Channel Interpretation data.*/ + case QR_MODE_ECI: { + unsigned val; + int bits; + /*ECI uses a variable-width encoding similar to UTF-8*/ + bits = qr_pack_buf_read(&qpb, 8); + if (bits < 0) + return -1; + /*One byte:*/ + if (!(bits & 0x80)) + val = bits; + /*Two bytes:*/ + else if (!(bits & 0x40)) { + val = bits & 0x3F << 8; + bits = qr_pack_buf_read(&qpb, 8); + if (bits < 0) + return -1; + val |= bits; + } + /*Three bytes:*/ + else if (!(bits & 0x20)) { + val = bits & 0x1F << 16; + bits = qr_pack_buf_read(&qpb, 16); + if (bits < 0) + return -1; + val |= bits; + /*Valid ECI values are 0...999999.*/ + if (val >= 1000000) + return -1; + } + /*Invalid lead byte.*/ + else + return -1; + entry->payload.eci = val; + } break; + case QR_MODE_KANJI: { + unsigned char *buf; + unsigned bits; + int len; + len = qr_pack_buf_read(&qpb, LEN_BITS[len_bits_idx][3]); + if (len < 0) + return -1; + /*Check to see if there are enough bits left now, so we don't have to + in the decode loop.*/ + if (qr_pack_buf_avail(&qpb) < 13 * len) + return -1; + entry->payload.data.buf = buf = + (unsigned char *)malloc(2 * len * sizeof(*buf)); + entry->payload.data.len = 2 * len; + /*Decode 2-byte SJIS characters encoded in 13 bits.*/ + while (len-- > 0) { + bits = qr_pack_buf_read(&qpb, 13); + bits = (bits / 0xC0 << 8 | bits % 0xC0) + 0x8140; + if (bits >= 0xA000) + bits += 0x4000; + /*TODO: Are values 0xXX7F, 0xXXFD...0xXXFF always invalid? + Should we reject them here?*/ + self_parity ^= bits; + *buf++ = (unsigned char)(bits >> 8); + *buf++ = (unsigned char)(bits & 0xFF); + } + } break; + /*FNC1 second position marker.*/ + case QR_MODE_FNC1_2ND: { + int bits; + /*FNC1 in the 2nd position encodes an Application Indicator in one + byte, which is either a letter (A...Z or a...z) or a 2-digit number. + The letters are encoded with their ASCII value plus 100, the numbers + are encoded directly with their numeric value. + Values 100...164, 191...196, and 223...255 are invalid, so we reject + them here.*/ + bits = qr_pack_buf_read(&qpb, 8); + if (!(bits >= 0 && bits < 100 || bits >= 165 && bits < 191 || + bits >= 197 && bits < 223)) { + return -1; + } + entry->payload.ai = bits; + } break; + /*Unknown mode number:*/ + default: { + /*Unfortunately, because we have to understand the format of a mode to + know how many bits it occupies, we can't skip unknown modes. + Therefore we have to fail.*/ + return -1; + } break; + } + } + /*Store the parity of the data from this code, for S-A. + The final parity is the 8-bit XOR of all the decoded bytes of literal data. + We don't combine the 2-byte kanji codes into one byte in the loops above, + because we can just do it here instead.*/ + _qrdata->self_parity = ((self_parity >> 8) ^ self_parity) & 0xFF; + /*Success.*/ + _qrdata->entries = (qr_code_data_entry *)realloc( + _qrdata->entries, _qrdata->nentries * sizeof(*_qrdata->entries)); + return 0; +} + +static void qr_code_data_clear(qr_code_data *_qrdata) +{ + int i; + for (i = 0; i < _qrdata->nentries; i++) { + if (QR_MODE_HAS_DATA(_qrdata->entries[i].mode)) { + free(_qrdata->entries[i].payload.data.buf); + } + } + free(_qrdata->entries); +} + +void qr_code_data_list_init(qr_code_data_list *_qrlist) +{ + _qrlist->qrdata = NULL; + _qrlist->nqrdata = _qrlist->cqrdata = 0; +} + +void qr_code_data_list_clear(qr_code_data_list *_qrlist) +{ + int i; + for (i = 0; i < _qrlist->nqrdata; i++) + qr_code_data_clear(_qrlist->qrdata + i); + free(_qrlist->qrdata); + qr_code_data_list_init(_qrlist); +} + +static void qr_code_data_list_add(qr_code_data_list *_qrlist, + qr_code_data *_qrdata) +{ + if (_qrlist->nqrdata >= _qrlist->cqrdata) { + _qrlist->cqrdata = _qrlist->cqrdata << 1 | 1; + _qrlist->qrdata = (qr_code_data *)realloc( + _qrlist->qrdata, _qrlist->cqrdata * sizeof(*_qrlist->qrdata)); + } + memcpy(_qrlist->qrdata + _qrlist->nqrdata++, _qrdata, sizeof(*_qrdata)); +} + +#if 0 +static const unsigned short QR_NCODEWORDS[40]={ + 26, 44, 70, 100, 134, 172, 196, 242, 292, 346, + 404, 466, 532, 581, 655, 733, 815, 901, 991,1085, + 1156,1258,1364,1474,1588,1706,1828,1921,2051,2185, + 2323,2465,2611,2761,2876,3034,3196,3362,3532,3706 +}; +#endif + +/*The total number of codewords in a QR code.*/ +static int qr_code_ncodewords(unsigned _version) +{ + unsigned nalign; + /*This is 24-27 instructions on ARM in thumb mode, or a 26-32 byte savings + over just using a table (not counting the instructions that would be + needed to do the table lookup).*/ + if (_version == 1) + return 26; + nalign = (_version / 7) + 2; + return (_version << 4) * (_version + 8) - (5 * nalign) * (5 * nalign - 2) + + 36 * (_version < 7) + 83 >> + 3; +} + +#if 0 +/*The number of parity bytes per Reed-Solomon block for each version and error + correction level.*/ +static const unsigned char QR_RS_NPAR[40][4]={ + { 7,10,13,17},{10,16,22,28},{15,26,18,22},{20,18,26,16}, + {26,24,18,22},{18,16,24,28},{20,18,18,26},{24,22,22,26}, + {30,22,20,24},{18,26,24,28},{20,30,28,24},{24,22,26,28}, + {26,22,24,22},{30,24,20,24},{22,24,30,24},{24,28,24,30}, + {28,28,28,28},{30,26,28,28},{28,26,26,26},{28,26,30,28}, + {28,26,28,30},{28,28,30,24},{30,28,30,30},{30,28,30,30}, + {26,28,30,30},{28,28,28,30},{30,28,30,30},{30,28,30,30}, + {30,28,30,30},{30,28,30,30},{30,28,30,30},{30,28,30,30}, + {30,28,30,30},{30,28,30,30},{30,28,30,30},{30,28,30,30}, + {30,28,30,30},{30,28,30,30},{30,28,30,30},{30,28,30,30} +}; +#endif + +/*Bulk data for the number of parity bytes per Reed-Solomon block.*/ +static const unsigned char QR_RS_NPAR_VALS[71] = { + /*[ 0]*/ 7, 10, 13, 17, + /*[ 4]*/ 10, 16, 22, 28, 26, 26, 26, 22, 24, 22, 22, 26, + 24, 18, 22, + /*[19]*/ 15, 26, 18, 22, 24, 30, 24, 20, 24, + /*[28]*/ 18, 16, 24, 28, 28, 28, 28, 30, 24, + /*[37]*/ 20, 18, 18, 26, 24, 28, 24, 30, 26, 28, 28, 26, + 28, 30, 30, 22, 20, 24, + /*[55]*/ 20, 18, 26, 16, + /*[59]*/ 20, 30, 28, 24, 22, 26, 28, 26, 30, 28, 30, 30 +}; + +/*An offset into QR_RS_NPAR_DATA for each version that gives the number of + parity bytes per Reed-Solomon block for each error correction level.*/ +static const unsigned char QR_RS_NPAR_OFFS[40] = { + 0, 4, 19, 55, 15, 28, 37, 12, 51, 39, 59, 62, 10, 24, + 22, 41, 31, 44, 7, 65, 47, 33, 67, 67, 48, 32, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67 +}; + +/*The number of Reed-Solomon blocks for each version and error correction + level.*/ +static const unsigned char QR_RS_NBLOCKS[40][4] = { + { 1, 1, 1, 1 }, { 1, 1, 1, 1 }, { 1, 1, 2, 2 }, + { 1, 2, 2, 4 }, { 1, 2, 4, 4 }, { 2, 4, 4, 4 }, + { 2, 4, 6, 5 }, { 2, 4, 6, 6 }, { 2, 5, 8, 8 }, + { 4, 5, 8, 8 }, { 4, 5, 8, 11 }, { 4, 8, 10, 11 }, + { 4, 9, 12, 16 }, { 4, 9, 16, 16 }, { 6, 10, 12, 18 }, + { 6, 10, 17, 16 }, { 6, 11, 16, 19 }, { 6, 13, 18, 21 }, + { 7, 14, 21, 25 }, { 8, 16, 20, 25 }, { 8, 17, 23, 25 }, + { 9, 17, 23, 34 }, { 9, 18, 25, 30 }, { 10, 20, 27, 32 }, + { 12, 21, 29, 35 }, { 12, 23, 34, 37 }, { 12, 25, 34, 40 }, + { 13, 26, 35, 42 }, { 14, 28, 38, 45 }, { 15, 29, 40, 48 }, + { 16, 31, 43, 51 }, { 17, 33, 45, 54 }, { 18, 35, 48, 57 }, + { 19, 37, 51, 60 }, { 19, 38, 53, 63 }, { 20, 40, 56, 66 }, + { 21, 43, 59, 70 }, { 22, 45, 62, 74 }, { 24, 47, 65, 77 }, + { 25, 49, 68, 81 } +}; + +/*Attempts to fully decode a QR code. + _qrdata: Returns the parsed code data. + _gf: Used for Reed-Solomon error correction. + _ul_pos: The location of the UL finder pattern. + _ur_pos: The location of the UR finder pattern. + _dl_pos: The location of the DL finder pattern. + _version: The (decoded) version number. + _fmt_info: The decoded format info. + _img: The binary input image. + _width: The width of the input image. + _height: The height of the input image. + Return: 0 on success, or a negative value on error.*/ +static int qr_code_decode(qr_code_data *_qrdata, const rs_gf256 *_gf, + const qr_point _ul_pos, const qr_point _ur_pos, + const qr_point _dl_pos, int _version, int _fmt_info, + const unsigned char *_img, int _width, int _height) +{ + qr_sampling_grid grid; + unsigned *data_bits; + unsigned char **blocks; + unsigned char *block_data; + int nblocks; + int nshort_blocks; + int ncodewords; + int block_sz; + int ecc_level; + int ndata; + int npar; + int dim; + int ret; + int i; + /*Read the bits out of the image.*/ + qr_sampling_grid_init(&grid, _version, _ul_pos, _ur_pos, _dl_pos, + _qrdata->bbox, _img, _width, _height); +#if defined(QR_DEBUG) + qr_sampling_grid_dump(&grid, _version, _img, _width, _height); +#endif + dim = 17 + (_version << 2); + data_bits = (unsigned *)malloc( + dim * (dim + QR_INT_BITS - 1 >> QR_INT_LOGBITS) * sizeof(*data_bits)); + qr_sampling_grid_sample(&grid, data_bits, dim, _fmt_info, _img, _width, + _height); + /*Group those bits into Reed-Solomon codewords.*/ + ecc_level = (_fmt_info >> 3) ^ 1; + nblocks = QR_RS_NBLOCKS[_version - 1][ecc_level]; + npar = *(QR_RS_NPAR_VALS + QR_RS_NPAR_OFFS[_version - 1] + ecc_level); + ncodewords = qr_code_ncodewords(_version); + block_sz = ncodewords / nblocks; + nshort_blocks = nblocks - (ncodewords % nblocks); + blocks = (unsigned char **)malloc(nblocks * sizeof(*blocks)); + block_data = (unsigned char *)malloc(ncodewords * sizeof(*block_data)); + blocks[0] = block_data; + for (i = 1; i < nblocks; i++) + blocks[i] = blocks[i - 1] + block_sz + (i > nshort_blocks); + qr_samples_unpack(blocks, nblocks, block_sz - npar, nshort_blocks, + data_bits, grid.fpmask, dim); + qr_sampling_grid_clear(&grid); + free(blocks); + free(data_bits); + /*Perform the error correction.*/ + ndata = 0; + ncodewords = 0; + ret = 0; + for (i = 0; i < nblocks; i++) { + int block_szi; + int ndatai; + block_szi = block_sz + (i >= nshort_blocks); + ret = rs_correct(_gf, QR_M0, block_data + ncodewords, block_szi, npar, + NULL, 0); + zprintf(1, "Number of errors corrected: %i%s\n", ret, + ret < 0 ? " (data irrecoverable)" : ""); + /*For version 1 symbols and version 2-L and 3-L symbols, we aren't allowed + to use all the parity bytes for correction. + They are instead used to improve detection. + Version 1-L reserves 3 parity bytes for detection. + Versions 1-M and 2-L reserve 2 parity bytes for detection. + Versions 1-Q, 1-H, and 3-L reserve 1 parity byte for detection. + We can ignore the version 3-L restriction because it has an odd number of + parity bytes, and we don't support erasure detection.*/ + if (ret < 0 || _version == 1 && ret > ecc_level + 1 << 1 || + _version == 2 && ecc_level == 0 && ret > 4) { + ret = -1; + break; + } + ndatai = block_szi - npar; + memmove(block_data + ndata, block_data + ncodewords, + ndatai * sizeof(*block_data)); + ncodewords += block_szi; + ndata += ndatai; + } + /*Parse the corrected bitstream.*/ + if (ret >= 0) { + ret = qr_code_data_parse(_qrdata, _version, block_data, ndata); + /*We could return any partially decoded data, but then we'd have to have + API support for that; a mode ignoring ECC errors might also be useful.*/ + if (ret < 0) + qr_code_data_clear(_qrdata); + _qrdata->version = _version; + _qrdata->ecc_level = ecc_level; + } + free(block_data); + return ret; +} + +/*Searches for an arrangement of these three finder centers that yields a valid + configuration. + _c: On input, the three finder centers to consider in any order. + Return: The detected version number, or a negative value on error.*/ +static int qr_reader_try_configuration(qr_reader *_reader, + qr_code_data *_qrdata, + const unsigned char *_img, int _width, + int _height, qr_finder_center *_c[3]) +{ + int ci[7]; + unsigned maxd; + int ccw; + int i0; + int i; + /*Sort the points in counter-clockwise order.*/ + ccw = qr_point_ccw(_c[0]->pos, _c[1]->pos, _c[2]->pos); + /*Colinear points can't be the corners of a quadrilateral.*/ + if (!ccw) + return -1; + /*Include a few extra copies of the cyclical list to avoid mods.*/ + ci[6] = ci[3] = ci[0] = 0; + ci[4] = ci[1] = 1 + (ccw < 0); + ci[5] = ci[2] = 2 - (ccw < 0); + /*Assume the points farthest from each other are the opposite corners, and + find the top-left point.*/ + maxd = qr_point_distance2(_c[1]->pos, _c[2]->pos); + i0 = 0; + for (i = 1; i < 3; i++) { + unsigned d; + d = qr_point_distance2(_c[ci[i + 1]]->pos, _c[ci[i + 2]]->pos); + if (d > maxd) { + i0 = i; + maxd = d; + } + } + /*However, try all three possible orderings, just to be sure (a severely + skewed projection could move opposite corners closer than adjacent).*/ + for (i = i0; i < i0 + 3; i++) { + qr_aff aff; + qr_hom hom; + qr_finder ul; + qr_finder ur; + qr_finder dl; + qr_point bbox[4]; + int res; + int ur_version; + int dl_version; + int fmt_info; + ul.c = _c[ci[i]]; + ur.c = _c[ci[i + 1]]; + dl.c = _c[ci[i + 2]]; + /*Estimate the module size and version number from the two opposite corners. + The module size is not constant in the image, so we compute an affine + projection from the three points we have to a square domain, and + estimate it there. + Although it should be the same along both axes, we keep separate + estimates to account for any remaining projective distortion.*/ + res = QR_INT_BITS - 2 - QR_FINDER_SUBPREC - + qr_ilog(QR_MAXI(_width, _height) - 1); + qr_aff_init(&aff, ul.c->pos, ur.c->pos, dl.c->pos, res); + qr_aff_unproject(ur.o, &aff, ur.c->pos[0], ur.c->pos[1]); + qr_finder_edge_pts_aff_classify(&ur, &aff); + if (qr_finder_estimate_module_size_and_version(&ur, 1 << res, + 1 << res) < 0) + continue; + qr_aff_unproject(dl.o, &aff, dl.c->pos[0], dl.c->pos[1]); + qr_finder_edge_pts_aff_classify(&dl, &aff); + if (qr_finder_estimate_module_size_and_version(&dl, 1 << res, + 1 << res) < 0) + continue; + /*If the estimated versions are significantly different, reject the + configuration.*/ + if (abs(ur.eversion[1] - dl.eversion[0]) > QR_LARGE_VERSION_SLACK) + continue; + qr_aff_unproject(ul.o, &aff, ul.c->pos[0], ul.c->pos[1]); + qr_finder_edge_pts_aff_classify(&ul, &aff); + if (qr_finder_estimate_module_size_and_version(&ul, 1 << res, + 1 << res) < 0 || + abs(ul.eversion[1] - ur.eversion[1]) > QR_LARGE_VERSION_SLACK || + abs(ul.eversion[0] - dl.eversion[0]) > QR_LARGE_VERSION_SLACK) { + continue; + } +#if defined(QR_DEBUG) + qr_finder_dump_aff_undistorted(&ul, &ur, &dl, &aff, _img, _width, + _height); +#endif + /*If we made it this far, upgrade the affine homography to a full + homography.*/ + if (qr_hom_fit(&hom, &ul, &ur, &dl, bbox, &aff, &_reader->isaac, _img, + _width, _height) < 0) { + continue; + } + memcpy(_qrdata->bbox, bbox, sizeof(bbox)); + qr_hom_unproject(ul.o, &hom, ul.c->pos[0], ul.c->pos[1]); + qr_hom_unproject(ur.o, &hom, ur.c->pos[0], ur.c->pos[1]); + qr_hom_unproject(dl.o, &hom, dl.c->pos[0], dl.c->pos[1]); + qr_finder_edge_pts_hom_classify(&ur, &hom); + if (qr_finder_estimate_module_size_and_version(&ur, ur.o[0] - ul.o[0], + ur.o[0] - ul.o[0]) < 0) { + continue; + } + qr_finder_edge_pts_hom_classify(&dl, &hom); + if (qr_finder_estimate_module_size_and_version(&dl, dl.o[1] - ul.o[1], + dl.o[1] - ul.o[1]) < 0) { + continue; + } +#if defined(QR_DEBUG) + qr_finder_dump_hom_undistorted(&ul, &ur, &dl, &hom, _img, _width, + _height); +#endif + /*If we have a small version (less than 7), there's no encoded version + information. + If the estimated version on the two corners matches and is sufficiently + small, we assume this is the case.*/ + if (ur.eversion[1] == dl.eversion[0] && ur.eversion[1] < 7) { + /*We used to do a whole bunch of extra geometric checks for small + versions, because with just an affine correction, it was fairly easy + to estimate two consistent module sizes given a random configuration. + However, now that we're estimating a full homography, these appear to + be unnecessary.*/ +#if 0 + static const signed char LINE_TESTS[12][6]={ + /*DL left, UL > 0, UR > 0*/ + {2,0,0, 1,1, 1}, + /*DL right, UL > 0, UR < 0*/ + {2,1,0, 1,1,-1}, + /*UR top, UL > 0, DL > 0*/ + {1,2,0, 1,2, 1}, + /*UR bottom, UL > 0, DL < 0*/ + {1,3,0, 1,2,-1}, + /*UR left, DL < 0, UL < 0*/ + {1,0,2,-1,0,-1}, + /*UR right, DL > 0, UL > 0*/ + {1,1,2, 1,0, 1}, + /*DL top, UR < 0, UL < 0*/ + {2,2,1,-1,0,-1}, + /*DL bottom, UR > 0, UL > 0*/ + {2,3,1, 1,0, 1}, + /*UL left, DL > 0, UR > 0*/ + {0,0,2, 1,1, 1}, + /*UL right, DL > 0, UR < 0*/ + {0,1,2, 1,1,-1}, + /*UL top, UR > 0, DL > 0*/ + {0,2,1, 1,2, 1}, + /*UL bottom, UR > 0, DL < 0*/ + {0,3,1, 1,2,-1} + }; + qr_finder *f[3]; + int j; + /*Start by decoding the format information. + This is cheap, but unlikely to reject invalid configurations. + 56.25% of all bitstrings are valid, and we mix and match several pieces + until we find a valid combination, so our real chances of finding a + valid codeword in random bits are even higher.*/ + fmt_info=qr_finder_fmt_info_decode(&ul,&ur,&dl,&aff,_img,_width,_height); + if(fmt_info<0)continue; + /*Now we fit lines to the edges of each finder pattern and check to make + sure the centers of the other finder patterns lie on the proper side.*/ + f[0]=&ul; + f[1]=&ur; + f[2]=&dl; + for(j=0;j<12;j++){ + const signed char *t; + qr_line l0; + int *p; + t=LINE_TESTS[j]; + qr_finder_ransac(f[t[0]],&aff,&_reader->isaac,t[1]); + /*We may not have enough points to fit a line accurately here. + If not, we just skip the test.*/ + if(qr_line_fit_finder_edge(l0,f[t[0]],t[1],res)<0)continue; + p=f[t[2]]->c->pos; + if(qr_line_eval(l0,p[0],p[1])*t[3]<0)break; + p=f[t[4]]->c->pos; + if(qr_line_eval(l0,p[0],p[1])*t[5]<0)break; + } + if(j<12)continue; + /*All tests passed.*/ +#endif + ur_version = ur.eversion[1]; + } else { + /*If the estimated versions are significantly different, reject the + configuration.*/ + if (abs(ur.eversion[1] - dl.eversion[0]) > QR_LARGE_VERSION_SLACK) + continue; + /*Otherwise we try to read the actual version data from the image. + If the real version is not sufficiently close to our estimated version, + then we assume there was an unrecoverable decoding error (so many bit + errors we were within 3 errors of another valid code), and throw that + value away. + If no decoded version could be sufficiently close, we don't even try.*/ + if (ur.eversion[1] >= 7 - QR_LARGE_VERSION_SLACK) { + ur_version = qr_finder_version_decode(&ur, &hom, _img, _width, + _height, 0); + if (abs(ur_version - ur.eversion[1]) > QR_LARGE_VERSION_SLACK) + ur_version = -1; + } else + ur_version = -1; + if (dl.eversion[0] >= 7 - QR_LARGE_VERSION_SLACK) { + dl_version = qr_finder_version_decode(&dl, &hom, _img, _width, + _height, 1); + if (abs(dl_version - dl.eversion[0]) > QR_LARGE_VERSION_SLACK) + dl_version = -1; + } else + dl_version = -1; + /*If we got at least one valid version, or we got two and they match, + then we found a valid configuration.*/ + if (ur_version >= 0) { + if (dl_version >= 0 && dl_version != ur_version) + continue; + } else if (dl_version < 0) + continue; + else + ur_version = dl_version; + } + qr_finder_edge_pts_hom_classify(&ul, &hom); + if (qr_finder_estimate_module_size_and_version(&ul, ur.o[0] - dl.o[0], + dl.o[1] - ul.o[1]) < 0 || + abs(ul.eversion[1] - ur.eversion[1]) > QR_SMALL_VERSION_SLACK || + abs(ul.eversion[0] - dl.eversion[0]) > QR_SMALL_VERSION_SLACK) { + continue; + } + fmt_info = qr_finder_fmt_info_decode(&ul, &ur, &dl, &hom, _img, _width, + _height); + if (fmt_info < 0 || + qr_code_decode(_qrdata, &_reader->gf, ul.c->pos, ur.c->pos, + dl.c->pos, ur_version, fmt_info, _img, _width, + _height) < 0) { + /*The code may be flipped. + Try again, swapping the UR and DL centers. + We should get a valid version either way, so it's relatively cheap to + check this, as we've already filtered out a lot of invalid + configurations.*/ + QR_SWAP2I(hom.inv[0][0], hom.inv[1][0]); + QR_SWAP2I(hom.inv[0][1], hom.inv[1][1]); + QR_SWAP2I(hom.fwd[0][0], hom.fwd[0][1]); + QR_SWAP2I(hom.fwd[1][0], hom.fwd[1][1]); + QR_SWAP2I(hom.fwd[2][0], hom.fwd[2][1]); + QR_SWAP2I(ul.o[0], ul.o[1]); + QR_SWAP2I(ul.size[0], ul.size[1]); + QR_SWAP2I(ur.o[0], ur.o[1]); + QR_SWAP2I(ur.size[0], ur.size[1]); + QR_SWAP2I(dl.o[0], dl.o[1]); + QR_SWAP2I(dl.size[0], dl.size[1]); +#if defined(QR_DEBUG) + qr_finder_dump_hom_undistorted(&ul, &dl, &ur, &hom, _img, _width, + _height); +#endif + fmt_info = qr_finder_fmt_info_decode(&ul, &dl, &ur, &hom, _img, + _width, _height); + if (fmt_info < 0) + continue; + QR_SWAP2I(bbox[1][0], bbox[2][0]); + QR_SWAP2I(bbox[1][1], bbox[2][1]); + memcpy(_qrdata->bbox, bbox, sizeof(bbox)); + if (qr_code_decode(_qrdata, &_reader->gf, ul.c->pos, dl.c->pos, + ur.c->pos, ur_version, fmt_info, _img, _width, + _height) < 0) { + continue; + } + } + return ur_version; + } + return -1; +} + +void qr_reader_match_centers(qr_reader *_reader, qr_code_data_list *_qrlist, + qr_finder_center *_centers, int _ncenters, + const unsigned char *_img, int _width, int _height) +{ + /*The number of centers should be small, so an O(n^3) exhaustive search of + which ones go together should be reasonable.*/ + unsigned char *mark; + int nfailures_max; + int nfailures; + int i; + int j; + int k; + mark = (unsigned char *)calloc(_ncenters, sizeof(*mark)); + nfailures_max = QR_MAXI(8192, _width * _height >> 9); + nfailures = 0; + for (i = 0; i < _ncenters; i++) { + /*TODO: We might be able to accelerate this step significantly by + considering the remaining finder centers in a more intelligent order, + based on the first finder center we just chose.*/ + for (j = i + 1; i <_ncenters && !mark[i] && j < _ncenters; j++) { + for (k = j + 1; j<_ncenters && !mark[j] && k < _ncenters; k++) + if (!mark[k]) { + qr_finder_center *c[3]; + qr_code_data qrdata; + int version; + c[0] = _centers + i; + c[1] = _centers + j; + c[2] = _centers + k; + version = qr_reader_try_configuration(_reader, &qrdata, + _img, _width, _height, + c); + if (version >= 0) { + int ninside; + int l; + /*Add the data to the list.*/ + qr_code_data_list_add(_qrlist, &qrdata); + /*Convert the bounding box we're returning to the user to normal + image coordinates.*/ + for (l = 0; l < 4; l++) { + _qrlist->qrdata[_qrlist->nqrdata - 1].bbox[l][0] >>= + QR_FINDER_SUBPREC; + _qrlist->qrdata[_qrlist->nqrdata - 1].bbox[l][1] >>= + QR_FINDER_SUBPREC; + } + /*Mark these centers as used.*/ + mark[i] = mark[j] = mark[k] = 1; + /*Find any other finder centers located inside this code.*/ + for (l = ninside = 0; l < _ncenters; l++) + if (!mark[l]) { + if (qr_point_ccw(qrdata.bbox[0], qrdata.bbox[1], + _centers[l].pos) >= 0 && + qr_point_ccw(qrdata.bbox[1], qrdata.bbox[3], + _centers[l].pos) >= 0 && + qr_point_ccw(qrdata.bbox[3], qrdata.bbox[2], + _centers[l].pos) >= 0 && + qr_point_ccw(qrdata.bbox[2], qrdata.bbox[0], + _centers[l].pos) >= 0) { + mark[l] = 2; + ninside++; + } + } + if (ninside >= 3) { + /*We might have a "Double QR": a code inside a code. + Copy the relevant centers to a new array and do a search confined + to that subset.*/ + qr_finder_center *inside; + inside = (qr_finder_center *)malloc( + ninside * sizeof(*inside)); + for (l = ninside = 0; l < _ncenters; l++) { + if (mark[l] == 2) + *&inside[ninside++] = *&_centers[l]; + } + qr_reader_match_centers(_reader, _qrlist, inside, + ninside, _img, _width, + _height); + free(inside); + } + /*Mark _all_ such centers used: codes cannot partially overlap.*/ + for (l = 0; l < _ncenters; l++) + if (mark[l] == 2) + mark[l] = 1; + nfailures = 0; + } else if (++nfailures > nfailures_max) { + /*Give up. + We're unlikely to find a valid code in all this clutter, and we + could spent quite a lot of time trying.*/ + i = j = k = _ncenters; + } + } + } + } + free(mark); +} + +int _zbar_qr_found_line(qr_reader *reader, int dir, const qr_finder_line *line) +{ + /* minimally intrusive brute force version */ + qr_finder_lines *lines = &reader->finder_lines[dir]; + + if (lines->nlines >= lines->clines) { + lines->clines *= 2; + lines->lines = + realloc(lines->lines, ++lines->clines * sizeof(*lines->lines)); + } + + memcpy(lines->lines + lines->nlines++, line, sizeof(*line)); + + return (0); +} + +static inline void qr_svg_centers(const qr_finder_center *centers, int ncenters) +{ + int i, j; + svg_path_start("centers", 1, 0, 0); + for (i = 0; i < ncenters; i++) + svg_path_moveto(SVG_ABS, centers[i].pos[0], centers[i].pos[1]); + svg_path_end(); + + svg_path_start("edge-pts", 1, 0, 0); + for (i = 0; i < ncenters; i++) { + const qr_finder_center *cen = centers + i; + for (j = 0; j < cen->nedge_pts; j++) + svg_path_moveto(SVG_ABS, cen->edge_pts[j].pos[0], + cen->edge_pts[j].pos[1]); + } + svg_path_end(); +} + +int _zbar_qr_decode(qr_reader *reader, zbar_image_scanner_t *iscn, + zbar_image_t *img) +{ + int nqrdata = 0, ncenters; + qr_finder_edge_pt *edge_pts = NULL; + qr_finder_center *centers = NULL; + + if (reader->finder_lines[0].nlines < 9 || + reader->finder_lines[1].nlines < 9) + return (0); + + svg_group_start("finder", 0, 1. / (1 << QR_FINDER_SUBPREC), 0, 0, 0); + + ncenters = qr_finder_centers_locate(¢ers, &edge_pts, reader, 0, 0); + + zprintf(14, "%dx%d finders, %d centers:\n", reader->finder_lines[0].nlines, + reader->finder_lines[1].nlines, ncenters); + qr_svg_centers(centers, ncenters); + + if (ncenters >= 3) { + void *bin = qr_binarize(img->data, img->width, img->height); + + qr_code_data_list qrlist; + qr_code_data_list_init(&qrlist); + + qr_reader_match_centers(reader, &qrlist, centers, ncenters, bin, + img->width, img->height); + + if (qrlist.nqrdata > 0) + nqrdata = qr_code_data_list_extract_text(&qrlist, iscn, img); + + qr_code_data_list_clear(&qrlist); + free(bin); + } + svg_group_end(); + + if (centers) + free(centers); + if (edge_pts) + free(edge_pts); + return (nqrdata); +} diff --git a/zbar/qrcode/qrdec.h b/zbar/qrcode/qrdec.h new file mode 100644 index 0000000..40cb204 --- /dev/null +++ b/zbar/qrcode/qrdec.h @@ -0,0 +1,171 @@ +/*Copyright (C) 2008-2009 Timothy B. Terriberry (tterribe@xiph.org) + You can redistribute this library and/or modify it under the terms of the + GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version.*/ +#if !defined(_qrdec_H) +#define _qrdec_H (1) + +#include <zbar.h> + +typedef struct qr_code_data_entry qr_code_data_entry; +typedef struct qr_code_data qr_code_data; +typedef struct qr_code_data_list qr_code_data_list; + +typedef enum qr_mode +{ + /*Numeric digits ('0'...'9').*/ + QR_MODE_NUM = 1, + /*Alphanumeric characters ('0'...'9', 'A'...'Z', plus the punctuation + ' ', '$', '%', '*', '+', '-', '.', '/', ':').*/ + QR_MODE_ALNUM, + /*Structured-append header.*/ + QR_MODE_STRUCT, + /*Raw 8-bit bytes.*/ + QR_MODE_BYTE, + /*FNC1 marker (for more info, see http://www.mecsw.com/specs/uccean128.html). + In the "first position" data is formatted in accordance with GS1 General + Specifications.*/ + QR_MODE_FNC1_1ST, + /*Mode 6 reserved?*/ + /*Extended Channel Interpretation code.*/ + QR_MODE_ECI = 7, + /*SJIS kanji characters.*/ + QR_MODE_KANJI, + /*FNC1 marker (for more info, see http://www.mecsw.com/specs/uccean128.html). + In the "second position" data is formatted in accordance with an industry + application as specified by AIM Inc.*/ + QR_MODE_FNC1_2ND +} qr_mode; + +/*Check if a mode has a data buffer associated with it. + Currently this is only modes with exactly one bit set.*/ +#define QR_MODE_HAS_DATA(_mode) (!((_mode) & (_mode)-1)) + +/*ECI may be used to signal a character encoding for the data.*/ +typedef enum qr_eci_encoding +{ + /*GLI0 is like CP437, but the encoding is reset at the beginning of each + structured append symbol.*/ + QR_ECI_GLI0, + /*GLI1 is like ISO8859_1, but the encoding is reset at the beginning of each + structured append symbol.*/ + QR_ECI_GLI1, + /*The remaining encodings do not reset at the start of the next structured + append symbol.*/ + QR_ECI_CP437, + /*Western European.*/ + QR_ECI_ISO8859_1, + /*Central European.*/ + QR_ECI_ISO8859_2, + /*South European.*/ + QR_ECI_ISO8859_3, + /*North European.*/ + QR_ECI_ISO8859_4, + /*Cyrillic.*/ + QR_ECI_ISO8859_5, + /*Arabic.*/ + QR_ECI_ISO8859_6, + /*Greek.*/ + QR_ECI_ISO8859_7, + /*Hebrew.*/ + QR_ECI_ISO8859_8, + /*Turkish.*/ + QR_ECI_ISO8859_9, + /*Nordic.*/ + QR_ECI_ISO8859_10, + /*Thai.*/ + QR_ECI_ISO8859_11, + /*There is no ISO/IEC 8859-12.*/ + /*Baltic rim.*/ + QR_ECI_ISO8859_13 = QR_ECI_ISO8859_11 + 2, + /*Celtic.*/ + QR_ECI_ISO8859_14, + /*Western European with euro.*/ + QR_ECI_ISO8859_15, + /*South-Eastern European (with euro).*/ + QR_ECI_ISO8859_16, + /*ECI 000019 is reserved?*/ + /*Shift-JIS.*/ + QR_ECI_SJIS = 20, + /*UTF-8.*/ + QR_ECI_UTF8 = 26 +} qr_eci_encoding; + +/*A single unit of parsed QR code data.*/ +struct qr_code_data_entry { + /*The mode of this data block.*/ + qr_mode mode; + union { + /*Data buffer for modes that have one.*/ + struct { + unsigned char *buf; + int len; + } data; + /*Decoded "Extended Channel Interpretation" data.*/ + unsigned eci; + /*Decoded "Application Indicator" for FNC1 in 2nd position.*/ + int ai; + /*Structured-append header data.*/ + struct { + unsigned char sa_index; + unsigned char sa_size; + unsigned char sa_parity; + } sa; + } payload; +}; + +/*Low-level QR code data.*/ +struct qr_code_data { + /*The decoded data entries.*/ + qr_code_data_entry *entries; + int nentries; + /*The code version (1...40).*/ + unsigned char version; + /*The ECC level (0...3, corresponding to 'L', 'M', 'Q', and 'H').*/ + unsigned char ecc_level; + /*Structured-append information.*/ + /*The index of this code in the structured-append group. + If sa_size is zero, this is undefined.*/ + unsigned char sa_index; + /*The size of the structured-append group, or 0 if there was no S-A header.*/ + unsigned char sa_size; + /*The parity of the entire structured-append group. + If sa_size is zero, this is undefined.*/ + unsigned char sa_parity; + /*The parity of this code. + If sa_size is zero, this is undefined.*/ + unsigned char self_parity; + /*An approximate bounding box for the code. + Points appear in the order up-left, up-right, down-left, down-right, + relative to the orientation of the QR code.*/ + qr_point bbox[4]; +}; + +struct qr_code_data_list { + qr_code_data *qrdata; + int nqrdata; + int cqrdata; +}; + +/*Extract symbol data from a list of QR codes and attach to the image. + All text is converted to UTF-8. + For binary/byte mode QR codes: if configured with ZBAR_CFG_BINARY, + the bytes will be returned as is. Otherwise, the encoding will be + automatically determined and the data will be converted to that character set. + Any structured-append group that does not have all of its members is decoded + as ZBAR_PARTIAL with ZBAR_PARTIAL components for the discontinuities. + Note that isolated members of a structured-append group may be decoded with + the wrong character set, since the correct setting cannot be propagated + between codes. + Return: The number of symbols which were successfully extracted from the + codes; this will be at most the number of codes.*/ +int qr_code_data_list_extract_text(const qr_code_data_list *_qrlist, + zbar_image_scanner_t *iscn, + zbar_image_t *img); + +/*TODO: Parse DoCoMo standard barcode data formats. + See http://www.nttdocomo.co.jp/english/service/imode/make/content/barcode/function/application/ + for details.*/ + +#endif diff --git a/zbar/qrcode/qrdectxt.c b/zbar/qrcode/qrdectxt.c new file mode 100644 index 0000000..5676e33 --- /dev/null +++ b/zbar/qrcode/qrdectxt.c @@ -0,0 +1,573 @@ +/*Copyright (C) 2008-2010 Timothy B. Terriberry (tterribe@xiph.org) + You can redistribute this library and/or modify it under the terms of the + GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version.*/ +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <iconv.h> + +#include "decoder.h" +#include "error.h" +#include "image.h" +#include "img_scanner.h" +#include "qrcode.h" +#include "qrdec.h" +#include "util.h" + +#define ENC_LIST_SIZE 4 + +static int text_is_ascii(const unsigned char *_text, int _len) +{ + int i; + for (i = 0; i < _len; i++) + if (_text[i] >= 0x80) + return 0; + return 1; +} + +static int text_is_latin1(const unsigned char *_text, int _len) +{ + int i; + for (i = 0; i < _len; i++) { + /*The following line fails to compile correctly with gcc 3.4.4 on ARM with + any optimizations enabled.*/ + if (_text[i] >= 0x80 && _text[i] < 0xA0) + return 0; + } + return 1; +} + +static int text_is_big5(const unsigned char *_text, int _len) +{ + int i; + for (i = 0; i < _len; i++) { + if (_text[i] == 0xFF) + return 0; + else if (_text[i] >= 0x80) { // first byte is big5 + i++; + if (i >= _len) // second byte not exists + return 0; + if (_text[i] < 0x40 || (_text[i] > 0x7E && _text[i] < 0xA1) || + _text[i] > 0xFE) { // second byte not in range + return 0; + } + } else { // normal ascii encoding, it's okay + } + } + return 1; +} + +static void enc_list_mtf(iconv_t _enc_list[ENC_LIST_SIZE], iconv_t _enc) +{ + int i; + for (i = 0; i < ENC_LIST_SIZE; i++) + if (_enc_list[i] == _enc) { + int j; + for (j = i; j-- > 0;) + _enc_list[j + 1] = _enc_list[j]; + _enc_list[0] = _enc; + break; + } +} + +int qr_code_data_list_extract_text(const qr_code_data_list *_qrlist, + zbar_image_scanner_t *iscn, + zbar_image_t *img) +{ + iconv_t sjis_cd; + iconv_t utf8_cd; + iconv_t latin1_cd; + iconv_t big5_cd; + const qr_code_data *qrdata; + int nqrdata; + unsigned char *mark; + int ntext; + int i; + int raw_binary = 0; + zbar_image_scanner_get_config(iscn, ZBAR_QRCODE, ZBAR_CFG_BINARY, + &raw_binary); + qrdata = _qrlist->qrdata; + nqrdata = _qrlist->nqrdata; + mark = (unsigned char *)calloc(nqrdata, sizeof(*mark)); + ntext = 0; + /*This is the encoding the standard says is the default.*/ + latin1_cd = iconv_open("UTF-8", "ISO8859-1"); + /*But this one is often used, as well.*/ + sjis_cd = iconv_open("UTF-8", "SJIS"); + /*This is a trivial conversion just to check validity without extra code.*/ + utf8_cd = iconv_open("UTF-8", "UTF-8"); + /* add support for big5 encoding. */ + big5_cd = iconv_open("UTF-8", "BIG-5"); + for (i = 0; i < nqrdata; i++) + if (!mark[i]) { + const qr_code_data *qrdataj; + const qr_code_data_entry *entry; + iconv_t enc_list[ENC_LIST_SIZE]; + iconv_t eci_cd; + int sa[16]; + int sa_size; + char *sa_text; + size_t sa_ntext; + size_t sa_ctext; + int fnc1; + int fnc1_2ai; + int has_kanji; + int eci; + int err; + int j; + int k; + zbar_symbol_t *syms = NULL, **sym = &syms; + qr_point dir; + int horiz; + char *bytebuf_text; + size_t bytebuf_ntext; + + /*Step 0: Collect the other QR codes belonging to this S-A group.*/ + if (qrdata[i].sa_size) { + unsigned sa_parity; + sa_size = qrdata[i].sa_size; + sa_parity = qrdata[i].sa_parity; + for (j = 0; j < sa_size; j++) + sa[j] = -1; + for (j = i; j < nqrdata; j++) + if (!mark[j]) { + /*TODO: We could also match version, ECC level, etc. if size and + parity alone are too ambiguous.*/ + if (qrdata[j].sa_size == sa_size && + qrdata[j].sa_parity == sa_parity && + sa[qrdata[j].sa_index] < 0) { + sa[qrdata[j].sa_index] = j; + mark[j] = 1; + } + } + /*TODO: If the S-A group is complete, check the parity.*/ + } else { + sa[0] = i; + sa_size = 1; + } + + sa_ctext = 0; + fnc1 = 0; + fnc1_2ai = 0; + has_kanji = 0; + /*Step 1: Detect FNC1 markers and estimate the required buffer size.*/ + for (j = 0; j < sa_size; j++) + if (sa[j] >= 0) { + qrdataj = qrdata + sa[j]; + for (k = 0; k < qrdataj->nentries; k++) { + int shift; + entry = qrdataj->entries + k; + shift = 0; + switch (entry->mode) { + /*FNC1 applies to the entire code and ignores subsequent markers.*/ + case QR_MODE_FNC1_1ST: { + if (!fnc1) + fnc1 = MOD(ZBAR_MOD_GS1); + } break; + case QR_MODE_FNC1_2ND: { + if (!fnc1) { + fnc1 = MOD(ZBAR_MOD_AIM); + fnc1_2ai = entry->payload.ai; + sa_ctext += 2; + } + } break; + /*We assume at most 4 UTF-8 bytes per input byte. + I believe this is true for all the encodings we actually use.*/ + case QR_MODE_KANJI: + has_kanji = 1; + case QR_MODE_BYTE: + shift = 2; + default: { + /*The remaining two modes are already valid UTF-8.*/ + if (QR_MODE_HAS_DATA(entry->mode)) { + sa_ctext += entry->payload.data.len << shift; + } + } break; + } + } + } + + /*Step 2: Convert the entries.*/ + sa_text = (char *)malloc((sa_ctext + 1) * sizeof(*sa_text)); + sa_ntext = 0; + /*Add the encoded Application Indicator for FNC1 in the second position.*/ + if (fnc1 == MOD(ZBAR_MOD_AIM)) { + if (fnc1_2ai < 100) { + /*The Application Indicator is a 2-digit number.*/ + sa_text[sa_ntext++] = '0' + fnc1_2ai / 10; + sa_text[sa_ntext++] = '0' + fnc1_2ai % 10; + } + /*The Application Indicator is a single letter. + We already checked that it lies in one of the ranges A...Z, a...z + when we decoded it.*/ + else + sa_text[sa_ntext++] = (char)(fnc1_2ai - 100); + } + eci = -1; + enc_list[0] = sjis_cd; + enc_list[1] = latin1_cd; + enc_list[2] = big5_cd; + enc_list[3] = utf8_cd; + eci_cd = (iconv_t)-1; + err = 0; + + bytebuf_text = (char *)malloc((sa_ctext + 1) * sizeof(*sa_text)); + bytebuf_ntext = 0; + + for (j = 0; j < sa_size && !err; j++, sym = &(*sym)->next) { + *sym = _zbar_image_scanner_alloc_sym(iscn, ZBAR_QRCODE, 0); + (*sym)->datalen = sa_ntext; + if (sa[j] < 0) { + /* generic placeholder for unfinished results */ + (*sym)->type = ZBAR_PARTIAL; + + /*Skip all contiguous missing segments.*/ + for (j++; j < sa_size && sa[j] < 0; j++) + ; + /*If there aren't any more, stop.*/ + if (j >= sa_size) + break; + + /* mark break in data */ + sa_text[sa_ntext++] = '\0'; + (*sym)->datalen = sa_ntext; + + /* advance to next symbol */ + sym = &(*sym)->next; + *sym = _zbar_image_scanner_alloc_sym(iscn, ZBAR_QRCODE, 0); + } + + qrdataj = qrdata + sa[j]; + /* expose bounding box */ + sym_add_point(*sym, qrdataj->bbox[0][0], qrdataj->bbox[0][1]); + sym_add_point(*sym, qrdataj->bbox[2][0], qrdataj->bbox[2][1]); + sym_add_point(*sym, qrdataj->bbox[3][0], qrdataj->bbox[3][1]); + sym_add_point(*sym, qrdataj->bbox[1][0], qrdataj->bbox[1][1]); + + /* approx symbol "up" direction */ + dir[0] = (qrdataj->bbox[0][0] - qrdataj->bbox[2][0] + + qrdataj->bbox[1][0] - qrdataj->bbox[3][0]); + dir[1] = (qrdataj->bbox[2][1] - qrdataj->bbox[0][1] + + qrdataj->bbox[3][1] - qrdataj->bbox[1][1]); + horiz = abs(dir[0]) > abs(dir[1]); + (*sym)->orient = horiz + 2 * (dir[1 - horiz] < 0); + + for (k = 0; k <= qrdataj->nentries && !err; k++) { + size_t inleft; + size_t outleft; + char *in; + char *out; + + // Check if bytebuf_text is empty INSIDE for loop. + if (bytebuf_ntext > 0) { + entry = (k == qrdataj->nentries) ? NULL : + qrdataj->entries + k; + // next entry is not byte mode, convert bytes to text. + if (entry == NULL || (entry->mode != QR_MODE_BYTE && + entry->mode != QR_MODE_KANJI)) { + in = bytebuf_text; + inleft = bytebuf_ntext; + out = sa_text + sa_ntext; + outleft = sa_ctext - sa_ntext; + /*If we have no specified encoding, attempt to auto-detect it + unless configured with ZBAR_CFG_BINARY.*/ + if (eci < 0) { + if (raw_binary) { + /* copy all remaining bytes to output buffer. */ + memcpy(out, in, inleft); + sa_ntext += inleft; + bytebuf_ntext = 0; + } else { + int ei; + /*If there was data encoded in kanji mode, assume it's SJIS.*/ + if (has_kanji) + enc_list_mtf(enc_list, sjis_cd); + /*Otherwise check for the UTF-8 BOM. + UTF-8 is rarely specified with ECI, and few decoders + currently support doing so, so this is the best way for + encoders to reliably indicate it.*/ + else if (inleft >= 3 && + in[0] == (char)0xEF && + in[1] == (char)0xBB && + in[2] == (char)0xBF) { + in += 3; + inleft -= 3; + /*Actually try converting (to check validity).*/ + err = utf8_cd == (iconv_t)-1 || + iconv(utf8_cd, &in, &inleft, &out, + &outleft) == (size_t)-1; + if (!err) { + sa_ntext = out - sa_text; + enc_list_mtf(enc_list, utf8_cd); + bytebuf_ntext = 0; + } + in = bytebuf_text; + inleft = bytebuf_ntext; + out = sa_text + sa_ntext; + outleft = sa_ctext - sa_ntext; + } + /*If the text is 8-bit clean, prefer UTF-8 over SJIS, since + SJIS will corrupt the backslashes used for DoCoMo formats.*/ + else if (text_is_ascii((unsigned char *)in, + inleft)) { + enc_list_mtf(enc_list, utf8_cd); + } + /* Check if it's big5 encoding. */ + else if (text_is_big5((unsigned char *)in, + inleft)) { + enc_list_mtf(enc_list, big5_cd); + } + + /*Try our list of encodings.*/ + for (ei = 0; ei < ENC_LIST_SIZE; ei++) + if (enc_list[ei] != (iconv_t)-1) { + /*According to the 2005 version of the standard, + ISO/IEC 8859-1 (one hyphen) is supposed to be used, but + reality is not always so (and in the 2000 version of the + standard, it was JIS8/SJIS that was the default). + It's got an invalid range that is used often with SJIS + and UTF-8, though, which makes detection easier. + However, iconv() does not properly reject characters in + those ranges, since ISO-8859-1 (two hyphens) defines a + number of seldom-used control code characters there. + So if we see any of those characters, move this + conversion to the end of the list.*/ + if (ei < 3 && + enc_list[ei] == latin1_cd && + !text_is_latin1( + (unsigned char *)in, + inleft)) { + int ej; + for (ej = ei + 1; + ej < ENC_LIST_SIZE; ej++) + enc_list[ej - 1] = + enc_list[ej]; + enc_list[3] = latin1_cd; + } + err = iconv(enc_list[ei], &in, + &inleft, &out, + &outleft) == (size_t)-1; + if (!err) { + sa_ntext = out - sa_text; + enc_list_mtf(enc_list, + enc_list[ei]); + break; + } + in = bytebuf_text; + inleft = bytebuf_ntext; + out = sa_text + sa_ntext; + outleft = sa_ctext - sa_ntext; + } + } + } + /*We were actually given a character set; use it. + The spec says that in this case, data should be treated as if it + came from the given character set even when encoded in kanji + mode.*/ + else { + err = eci_cd == (iconv_t)-1 || + iconv(eci_cd, &in, &inleft, &out, + &outleft) == (size_t)-1; + if (!err) + sa_ntext = out - sa_text; + } + bytebuf_ntext = 0; + } + } + if (k == qrdataj->nentries) + break; + + entry = qrdataj->entries + k; + switch (entry->mode) { + case QR_MODE_NUM: { + if (sa_ctext - sa_ntext >= + (size_t)entry->payload.data.len) { + memcpy(sa_text + sa_ntext, entry->payload.data.buf, + entry->payload.data.len * sizeof(*sa_text)); + sa_ntext += entry->payload.data.len; + } else + err = 1; + } break; + case QR_MODE_ALNUM: { + char *p; + in = (char *)entry->payload.data.buf; + inleft = entry->payload.data.len; + /*FNC1 uses '%' as an escape character.*/ + if (fnc1) + for (;;) { + size_t plen; + char c; + p = memchr(in, '%', inleft * sizeof(*in)); + if (p == NULL) + break; + plen = p - in; + if (sa_ctext - sa_ntext < plen + 1) + break; + memcpy(sa_text + sa_ntext, in, + plen * sizeof(*in)); + sa_ntext += plen; + /*Two '%'s is a literal '%'*/ + if (plen + 1 < inleft && p[1] == '%') { + c = '%'; + plen++; + p++; + } + /*One '%' is the ASCII group separator.*/ + else + c = 0x1D; + sa_text[sa_ntext++] = c; + inleft -= plen + 1; + in = p + 1; + } + else + p = NULL; + if (p != NULL || sa_ctext - sa_ntext < inleft) + err = 1; + else { + memcpy(sa_text + sa_ntext, in, + inleft * sizeof(*sa_text)); + sa_ntext += inleft; + } + } break; + /* DONE: This handles a multi-byte sequence split between + multiple data blocks. */ + case QR_MODE_BYTE: + case QR_MODE_KANJI: { + // copy byte to bytebuf + in = (char *)entry->payload.data.buf; + inleft = entry->payload.data.len; + memcpy(bytebuf_text + bytebuf_ntext, in, + inleft * sizeof(*bytebuf_text)); + bytebuf_ntext += inleft; + } break; + /*Check to see if a character set was specified.*/ + case QR_MODE_ECI: { + const char *enc; + char buf[16]; + unsigned cur_eci; + cur_eci = entry->payload.eci; + if (cur_eci <= QR_ECI_ISO8859_16 && cur_eci != 14) { + if (cur_eci != QR_ECI_GLI0 && + cur_eci != QR_ECI_CP437) { + sprintf(buf, "ISO8859-%i", + QR_MAXI(cur_eci, 3) - 2); + enc = buf; + } + /*Note that CP437 requires an iconv compiled with + --enable-extra-encodings, and thus may not be available.*/ + else + enc = "CP437"; + } else if (cur_eci == QR_ECI_SJIS) + enc = "SJIS"; + else if (cur_eci == QR_ECI_UTF8) + enc = "UTF-8"; + /*Don't know what this ECI code specifies, but not an encoding that + we recognize.*/ + else + continue; + eci = cur_eci; + eci_cd = iconv_open("UTF-8", enc); + } break; + /*Silence stupid compiler warnings.*/ + default: + break; + } + } + /*If eci should be reset between codes, do so.*/ + if (eci <= QR_ECI_GLI1) { + eci = -1; + if (eci_cd != (iconv_t)-1) { + iconv_close(eci_cd); + eci_cd = (iconv_t)-1; + } + } + } + + free(bytebuf_text); + + if (eci_cd != (iconv_t)-1) + iconv_close(eci_cd); + if (!err) { + zbar_symbol_t *sa_sym; + sa_text[sa_ntext++] = '\0'; + if (sa_ctext + 1 > sa_ntext) { + sa_text = + (char *)realloc(sa_text, sa_ntext * sizeof(*sa_text)); + } + + if (sa_size == 1) + sa_sym = syms; + else { + /* cheap out w/axis aligned bbox for now */ + int xmin = img->width, xmax = -2; + int ymin = img->height, ymax = -2; + + /* create "virtual" container symbol for composite result */ + sa_sym = + _zbar_image_scanner_alloc_sym(iscn, ZBAR_QRCODE, 0); + sa_sym->syms = _zbar_symbol_set_create(); + sa_sym->syms->head = syms; + + /* fixup data references */ + for (; syms; syms = syms->next) { + int next; + _zbar_symbol_refcnt(syms, 1); + if (syms->type == ZBAR_PARTIAL) + sa_sym->type = ZBAR_PARTIAL; + else + for (j = 0; j < syms->npts; j++) { + int u = syms->pts[j].x; + if (xmin >= u) + xmin = u - 1; + if (xmax <= u) + xmax = u + 1; + u = syms->pts[j].y; + if (ymin >= u) + ymin = u - 1; + if (ymax <= u) + ymax = u + 1; + } + syms->data = sa_text + syms->datalen; + next = (syms->next) ? syms->next->datalen : sa_ntext; + if (next > syms->datalen) + syms->datalen = next - syms->datalen - 1; + else { + zprintf( + 1, "Assertion `next > syms->datalen' failed\n"); + syms->datalen = 0; + } + } + if (xmax >= -1) { + sym_add_point(sa_sym, xmin, ymin); + sym_add_point(sa_sym, xmin, ymax); + sym_add_point(sa_sym, xmax, ymax); + sym_add_point(sa_sym, xmax, ymin); + } + } + sa_sym->data = sa_text; + sa_sym->data_alloc = sa_ntext; + sa_sym->datalen = sa_ntext - 1; + sa_sym->modifiers = fnc1; + + _zbar_image_scanner_add_sym(iscn, sa_sym); + } else { + _zbar_image_scanner_recycle_syms(iscn, syms); + free(sa_text); + } + } + if (utf8_cd != (iconv_t)-1) + iconv_close(utf8_cd); + if (sjis_cd != (iconv_t)-1) + iconv_close(sjis_cd); + if (latin1_cd != (iconv_t)-1) + iconv_close(latin1_cd); + if (big5_cd != (iconv_t)-1) + iconv_close(big5_cd); + free(mark); + return ntext; +} diff --git a/zbar/qrcode/rs.c b/zbar/qrcode/rs.c new file mode 100644 index 0000000..bb195e9 --- /dev/null +++ b/zbar/qrcode/rs.c @@ -0,0 +1,907 @@ +/*Copyright (C) 1991-1995 Henry Minsky (hqm@ua.com, hqm@ai.mit.edu) + Copyright (C) 2008-2009 Timothy B. Terriberry (tterribe@xiph.org) + You can redistribute this library and/or modify it under the terms of the + GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version.*/ +#include "rs.h" +#include <stdlib.h> +#include <string.h> + +/*Reed-Solomon encoder and decoder. + Original implementation (C) Henry Minsky (hqm@ua.com, hqm@ai.mit.edu), + Universal Access 1991-1995. + Updates by Timothy B. Terriberry (C) 2008-2009: + - Properly reject codes when error-locator polynomial has repeated roots or + non-trivial irreducible factors. + - Removed the hard-coded parity size and performed general API cleanup. + - Allow multiple representations of GF(2**8), since different standards use + different irreducible polynomials. + - Allow different starting indices for the generator polynomial, since + different standards use different values. + - Greatly reduced the computation by eliminating unnecessary operations. + - Explicitly solve for the roots of low-degree polynomials instead of using + an exhaustive search. + This is another major speed boost when there are few errors.*/ + +/*Galois Field arithmetic in GF(2**8).*/ + +void rs_gf256_init(rs_gf256 *_gf, unsigned _ppoly) +{ + unsigned p; + int i; + /*Initialize the table of powers of a primtive root, alpha=0x02.*/ + p = 1; + for (i = 0; i < 256; i++) { + _gf->exp[i] = _gf->exp[i + 255] = p; + p = ((p << 1) ^ (-(p >> 7) & _ppoly)) & 0xFF; + } + /*Invert the table to recover the logs.*/ + for (i = 0; i < 255; i++) + _gf->log[_gf->exp[i]] = i; + /*Note that we rely on the fact that _gf->log[0]=0 below.*/ + _gf->log[0] = 0; +} + +/*Multiplication in GF(2**8) using logarithms.*/ +static unsigned rs_gmul(const rs_gf256 *_gf, unsigned _a, unsigned _b) +{ + return _a == 0 || _b == 0 ? 0 : _gf->exp[_gf->log[_a] + _gf->log[_b]]; +} + +/*Division in GF(2**8) using logarithms. + The result of division by zero is undefined.*/ +static unsigned rs_gdiv(const rs_gf256 *_gf, unsigned _a, unsigned _b) +{ + return _a == 0 ? 0 : _gf->exp[_gf->log[_a] + 255 - _gf->log[_b]]; +} + +/*Multiplication in GF(2**8) when one of the numbers is known to be non-zero + (proven by representing it by its logarithm).*/ +static unsigned rs_hgmul(const rs_gf256 *_gf, unsigned _a, unsigned _logb) +{ + return _a == 0 ? 0 : _gf->exp[_gf->log[_a] + _logb]; +} + +/*Square root in GF(2**8) using logarithms.*/ +static unsigned rs_gsqrt(const rs_gf256 *_gf, unsigned _a) +{ + unsigned loga; + if (!_a) + return 0; + loga = _gf->log[_a]; + return _gf->exp[loga + (255 & -(loga & 1)) >> 1]; +} + +/*Polynomial root finding in GF(2**8). + Each routine returns a list of the distinct roots (i.e., with duplicates + removed).*/ + +/*Solve a quadratic equation x**2 + _b*x + _c in GF(2**8) using the method + of~\cite{Wal99}. + Returns the number of distinct roots. + ARTICLE{Wal99, + author="C. Wayne Walker", + title="New Formulas for Solving Quadratic Equations over Certain Finite + Fields", + journal="{IEEE} Transactions on Information Theory", + volume=45, + number=1, + pages="283--284", + month=Jan, + year=1999 + }*/ +static int rs_quadratic_solve(const rs_gf256 *_gf, unsigned _b, unsigned _c, + unsigned char _x[2]) +{ + unsigned b; + unsigned logb; + unsigned logb2; + unsigned logb4; + unsigned logb8; + unsigned logb12; + unsigned logb14; + unsigned logc; + unsigned logc2; + unsigned logc4; + unsigned c8; + unsigned g3; + unsigned z3; + unsigned l3; + unsigned c0; + unsigned g2; + unsigned l2; + unsigned z2; + int inc; + /*If _b is zero, all we need is a square root.*/ + if (!_b) { + _x[0] = rs_gsqrt(_gf, _c); + return 1; + } + /*If _c is zero, 0 and _b are the roots.*/ + if (!_c) { + _x[0] = 0; + _x[1] = _b; + return 2; + } + logb = _gf->log[_b]; + logc = _gf->log[_c]; + /*If _b lies in GF(2**4), scale x to move it out.*/ + inc = logb % (255 / 15) == 0; + if (inc) { + b = _gf->exp[logb + 254]; + logb = _gf->log[b]; + _c = _gf->exp[logc + 253]; + logc = _gf->log[_c]; + } else + b = _b; + logb2 = _gf->log[_gf->exp[logb << 1]]; + logb4 = _gf->log[_gf->exp[logb2 << 1]]; + logb8 = _gf->log[_gf->exp[logb4 << 1]]; + logb12 = _gf->log[_gf->exp[logb4 + logb8]]; + logb14 = _gf->log[_gf->exp[logb2 + logb12]]; + logc2 = _gf->log[_gf->exp[logc << 1]]; + logc4 = _gf->log[_gf->exp[logc2 << 1]]; + c8 = _gf->exp[logc4 << 1]; + g3 = rs_hgmul(_gf, + _gf->exp[logb14 + logc] ^ _gf->exp[logb12 + logc2] ^ + _gf->exp[logb8 + logc4] ^ c8, + logb); + /*If g3 doesn't lie in GF(2**4), then our roots lie in an extension field. + Note that we rely on the fact that _gf->log[0]==0 here.*/ + if (_gf->log[g3] % (255 / 15) != 0) + return 0; + /*Construct the corresponding quadratic in GF(2**4): + x**2 + x/alpha**(255/15) + l3/alpha**(2*(255/15))*/ + z3 = rs_gdiv(_gf, g3, _gf->exp[logb8 << 1] ^ b); + l3 = rs_hgmul(_gf, rs_gmul(_gf, z3, z3) ^ rs_hgmul(_gf, z3, logb) ^ _c, + 255 - logb2); + c0 = rs_hgmul(_gf, l3, 255 - 2 * (255 / 15)); + /*Construct the corresponding quadratic in GF(2**2): + x**2 + x/alpha**(255/3) + l2/alpha**(2*(255/3))*/ + g2 = + rs_hgmul(_gf, + rs_hgmul(_gf, c0, 255 - 2 * (255 / 15)) ^ rs_gmul(_gf, c0, c0), + 255 - 255 / 15); + z2 = rs_gdiv(_gf, g2, + _gf->exp[255 - (255 / 15) * 4] ^ _gf->exp[255 - (255 / 15)]); + l2 = rs_hgmul(_gf, + rs_gmul(_gf, z2, z2) ^ rs_hgmul(_gf, z2, 255 - (255 / 15)) ^ + c0, + 2 * (255 / 15)); + /*Back substitute to the solution in the original field.*/ + _x[0] = _gf->exp[_gf->log[z3 ^ rs_hgmul(_gf, + rs_hgmul(_gf, l2, 255 / 3) ^ + rs_hgmul(_gf, z2, 255 / 15), + logb)] + + inc]; + _x[1] = _x[0] ^ _b; + return 2; +} + +/*Solve a cubic equation x**3 + _a*x**2 + _b*x + _c in GF(2**8). + Returns the number of distinct roots.*/ +static int rs_cubic_solve(const rs_gf256 *_gf, unsigned _a, unsigned _b, + unsigned _c, unsigned char _x[3]) +{ + unsigned k; + unsigned logd; + unsigned d2; + unsigned logd2; + unsigned logw; + int nroots; + /*If _c is zero, factor out the 0 root.*/ + if (!_c) { + nroots = rs_quadratic_solve(_gf, _a, _b, _x); + if (_b) + _x[nroots++] = 0; + return nroots; + } + /*Substitute x=_a+y*sqrt(_a**2+_b) to get y**3 + y + k == 0, + k = (_a*_b+c)/(_a**2+b)**(3/2).*/ + k = rs_gmul(_gf, _a, _b) ^ _c; + d2 = rs_gmul(_gf, _a, _a) ^ _b; + if (!d2) { + int logx; + if (!k) { + /*We have a triple root.*/ + _x[0] = _a; + return 1; + } + logx = _gf->log[k]; + if (logx % 3 != 0) + return 0; + logx /= 3; + _x[0] = _a ^ _gf->exp[logx]; + _x[1] = _a ^ _gf->exp[logx + 255 / 3]; + _x[2] = _a ^ _x[0] ^ _x[1]; + return 3; + } + logd2 = _gf->log[d2]; + logd = logd2 + (255 & -(logd2 & 1)) >> 1; + k = rs_gdiv(_gf, k, _gf->exp[logd + logd2]); + /*Substitute y=w+1/w and z=w**3 to get z**2 + k*z + 1 == 0.*/ + nroots = rs_quadratic_solve(_gf, k, 1, _x); + if (nroots < 1) { + /*The Reed-Solomon code is only valid if we can find 3 distinct roots in + GF(2**8), so if we know there's only one, we don't actually need to find + it. + Note that we're also called by the quartic solver, but if we contain a + non-trivial irreducible factor, than so does the original + quartic~\cite{LW72}, and failing to return a root here actually saves us + some work there, also.*/ + return 0; + } + /*Recover w from z.*/ + logw = _gf->log[_x[0]]; + if (logw) { + if (logw % 3 != 0) + return 0; + logw /= 3; + /*Recover x from w.*/ + _x[0] = + _gf->exp[_gf->log[_gf->exp[logw] ^ _gf->exp[255 - logw]] + logd] ^ + _a; + logw += 255 / 3; + _x[1] = + _gf->exp[_gf->log[_gf->exp[logw] ^ _gf->exp[255 - logw]] + logd] ^ + _a; + _x[2] = _x[0] ^ _x[1] ^ _a; + return 3; + } else { + _x[0] = _a; + /*In this case _x[1] is a double root, so we know the Reed-Solomon code is + invalid. + Note that we still have to return at least one root, because if we're + being called by the quartic solver, the quartic might still have 4 + distinct roots. + But we don't need more than one root, so we can avoid computing the + expensive one.*/ + /*_x[1]=_gf->exp[_gf->log[_gf->exp[255/3]^_gf->exp[2*(255/3)]]+logd]^_a;*/ + return 1; + } +} + +/*Solve a quartic equation x**4 + _a*x**3 + _b*x**2 + _c*x + _d in GF(2**8) by + decomposing it into the cases given by~\cite{LW72}. + Returns the number of distinct roots. + @ARTICLE{LW72, + author="Philip A. Leonard and Kenneth S. Williams", + title="Quartics over $GF(2^n)$", + journal="Proceedings of the American Mathematical Society", + volume=36, + number=2, + pages="347--450", + month=Dec, + year=1972 + }*/ +static int rs_quartic_solve(const rs_gf256 *_gf, unsigned _a, unsigned _b, + unsigned _c, unsigned _d, unsigned char _x[3]) +{ + unsigned r; + unsigned s; + unsigned t; + unsigned b; + int nroots; + int i; + /*If _d is zero, factor out the 0 root.*/ + if (!_d) { + nroots = rs_cubic_solve(_gf, _a, _b, _c, _x); + if (_c) + _x[nroots++] = 0; + return nroots; + } + if (_a) { + unsigned loga; + /*Substitute x=(1/y) + sqrt(_c/_a) to eliminate the cubic term.*/ + loga = _gf->log[_a]; + r = rs_hgmul(_gf, _c, 255 - loga); + s = rs_gsqrt(_gf, r); + t = _d ^ rs_gmul(_gf, _b, r) ^ rs_gmul(_gf, r, r); + if (t) { + unsigned logti; + logti = 255 - _gf->log[t]; + /*The result is still quartic, but with no cubic term.*/ + nroots = rs_quartic_solve( + _gf, 0, rs_hgmul(_gf, _b ^ rs_hgmul(_gf, s, loga), logti), + _gf->exp[loga + logti], _gf->exp[logti], _x); + for (i = 0; i < nroots; i++) + _x[i] = _gf->exp[255 - _gf->log[_x[i]]] ^ s; + } else { + /*s must be a root~\cite{LW72}, and is in fact a double-root~\cite{CCO69}. + Thus we're left with only a quadratic to solve. + @ARTICLE{CCO69, + author="Robert T. Chien and B. D. Cunningham and I. B. Oldham", + title="Hybrid Methods for Finding Roots of a Polynomial---With + Applications to {BCH} Decoding", + journal="{IEEE} Transactions on Information Theory", + volume=15, + number=2, + pages="329--335", + month=Mar, + year=1969 + }*/ + nroots = rs_quadratic_solve(_gf, _a, _b ^ r, _x); + /*s may be a triple root if s=_b/_a, but not quadruple, since _a!=0.*/ + if (nroots != 2 || _x[0] != s && _x[1] != s) + _x[nroots++] = s; + } + return nroots; + } + /*If there are no odd powers, it's really just a quadratic in disguise.*/ + if (!_c) + return rs_quadratic_solve(_gf, rs_gsqrt(_gf, _b), rs_gsqrt(_gf, _d), + _x); + /*Factor into (x**2 + r*x + s)*(x**2 + r*x + t) by solving for r, which can + be shown to satisfy r**3 + _b*r + _c == 0.*/ + nroots = rs_cubic_solve(_gf, 0, _b, _c, _x); + if (nroots < 1) { + /*The Reed-Solomon code is only valid if we can find 4 distinct roots in + GF(2**8). + If the cubic does not factor into 3 (possibly duplicate) roots, then we + know that the quartic must have a non-trivial irreducible factor.*/ + return 0; + } + r = _x[0]; + /*Now solve for s and t.*/ + b = rs_gdiv(_gf, _c, r); + nroots = rs_quadratic_solve(_gf, b, _d, _x); + if (nroots < 2) + return 0; + s = _x[0]; + t = _x[1]; + /*_c=r*(s^t) was non-zero, so s and t must be distinct. + But if z is a root of z**2 ^ r*z ^ s, then so is (z^r), and s = z*(z^r). + Hence if z is also a root of z**2 ^ r*z ^ t, then t = s, a contradiction. + Thus all four roots are distinct, if they exist.*/ + nroots = rs_quadratic_solve(_gf, r, s, _x); + return nroots + rs_quadratic_solve(_gf, r, t, _x + nroots); +} + +/*Polynomial arithmetic with coefficients in GF(2**8).*/ + +static void rs_poly_zero(unsigned char *_p, int _dp1) +{ + memset(_p, 0, _dp1 * sizeof(*_p)); +} + +static void rs_poly_copy(unsigned char *_p, const unsigned char *_q, int _dp1) +{ + memcpy(_p, _q, _dp1 * sizeof(*_p)); +} + +/*Multiply the polynomial by the free variable, x (shift the coefficients). + The number of coefficients, _dp1, must be non-zero.*/ +static void rs_poly_mul_x(unsigned char *_p, const unsigned char *_q, int _dp1) +{ + memmove(_p + 1, _q, (_dp1 - 1) * sizeof(*_p)); + _p[0] = 0; +} + +/*Divide the polynomial by the free variable, x (shift the coefficients). + The number of coefficients, _dp1, must be non-zero.*/ +static void rs_poly_div_x(unsigned char *_p, const unsigned char *_q, int _dp1) +{ + memmove(_p, _q + 1, (_dp1 - 1) * sizeof(*_p)); + _p[_dp1 - 1] = 0; +} + +/*Compute the first (d+1) coefficients of the product of a degree e and a + degree f polynomial.*/ +static void rs_poly_mult(const rs_gf256 *_gf, unsigned char *_p, int _dp1, + const unsigned char *_q, int _ep1, + const unsigned char *_r, int _fp1) +{ + int m; + int i; + rs_poly_zero(_p, _dp1); + m = _ep1 < _dp1 ? _ep1 : _dp1; + for (i = 0; i < m; i++) + if (_q[i] != 0) { + unsigned logqi; + int n; + int j; + n = _dp1 - i < _fp1 ? _dp1 - i : _fp1; + logqi = _gf->log[_q[i]]; + for (j = 0; j < n; j++) + _p[i + j] ^= rs_hgmul(_gf, _r[j], logqi); + } +} + +/*Decoding.*/ + +/*Computes the syndrome of a codeword.*/ +static void rs_calc_syndrome(const rs_gf256 *_gf, int _m0, unsigned char *_s, + int _npar, const unsigned char *_data, int _ndata) +{ + int i; + int j; + for (j = 0; j < _npar; j++) { + unsigned alphaj; + unsigned sj; + sj = 0; + alphaj = _gf->log[_gf->exp[j + _m0]]; + for (i = 0; i < _ndata; i++) + sj = _data[i] ^ rs_hgmul(_gf, sj, alphaj); + _s[j] = sj; + } +} + +/*Berlekamp-Peterson and Berlekamp-Massey Algorithms for error-location, + modified to handle known erasures, from \cite{CC81}, p. 205. + This finds the coefficients of the error locator polynomial. + The roots are then found by looking for the values of alpha**n where + evaluating the polynomial yields zero. + Error correction is done using the error-evaluator equation on p. 207. + @BOOK{CC81, + author="George C. Clark, Jr and J. Bibb Cain", + title="Error-Correction Coding for Digital Communications", + series="Applications of Communications Theory", + publisher="Springer", + address="New York, NY", + month=Jun, + year=1981 + }*/ + +/*Initialize lambda to the product of (1-x*alpha**e[i]) for erasure locations + e[i]. + Note that the user passes in array indices counting from the beginning of the + data, while our polynomial indexes starting from the end, so + e[i]=(_ndata-1)-_erasures[i].*/ +static void rs_init_lambda(const rs_gf256 *_gf, unsigned char *_lambda, + int _npar, const unsigned char *_erasures, + int _nerasures, int _ndata) +{ + int i; + int j; + rs_poly_zero(_lambda, (_npar < 4 ? 4 : _npar) + 1); + _lambda[0] = 1; + for (i = 0; i < _nerasures; i++) + for (j = i + 1; j > 0; j--) { + _lambda[j] ^= + rs_hgmul(_gf, _lambda[j - 1], _ndata - 1 - _erasures[i]); + } +} + +/*From \cite{CC81}, p. 216. + Returns the number of errors detected (degree of _lambda).*/ +static int rs_modified_berlekamp_massey(const rs_gf256 *_gf, + unsigned char *_lambda, + const unsigned char *_s, + unsigned char *_omega, int _npar, + const unsigned char *_erasures, + int _nerasures, int _ndata) +{ + unsigned char tt[256]; + int n; + int l; + int k; + int i; + /*Initialize _lambda, the error locator-polynomial, with the location of + known erasures.*/ + rs_init_lambda(_gf, _lambda, _npar, _erasures, _nerasures, _ndata); + rs_poly_copy(tt, _lambda, _npar + 1); + l = _nerasures; + k = 0; + for (n = _nerasures + 1; n <= _npar; n++) { + unsigned d; + rs_poly_mul_x(tt, tt, n - k + 1); + d = 0; + for (i = 0; i <= l; i++) + d ^= rs_gmul(_gf, _lambda[i], _s[n - 1 - i]); + if (d != 0) { + unsigned logd; + logd = _gf->log[d]; + if (l < n - k) { + int t; + for (i = 0; i <= n - k; i++) { + unsigned tti; + tti = tt[i]; + tt[i] = rs_hgmul(_gf, _lambda[i], 255 - logd); + _lambda[i] = _lambda[i] ^ rs_hgmul(_gf, tti, logd); + } + t = n - k; + k = n - l; + l = t; + } else + for (i = 0; i <= l; i++) + _lambda[i] = _lambda[i] ^ rs_hgmul(_gf, tt[i], logd); + } + } + rs_poly_mult(_gf, _omega, _npar, _lambda, l + 1, _s, _npar); + return l; +} + +/*Finds all the roots of an error-locator polynomial _lambda by evaluating it + at successive values of alpha, and returns the positions of the associated + errors in _epos. + Returns the number of valid roots identified.*/ +static int rs_find_roots(const rs_gf256 *_gf, unsigned char *_epos, + const unsigned char *_lambda, int _nerrors, int _ndata) +{ + unsigned alpha; + int nroots; + int i; + nroots = 0; + if (_nerrors <= 4) { + /*Explicit solutions for higher degrees are possible. + Chien uses large lookup tables to solve quintics, and Truong et al. give + special algorithms for degree up through 11, though they use exhaustive + search (with reduced complexity) for some portions. + Quartics are good enough for reading CDs, and represent a reasonable code + complexity trade-off without requiring any extra tables. + Note that _lambda[0] is always 1.*/ + _nerrors = rs_quartic_solve(_gf, _lambda[1], _lambda[2], _lambda[3], + _lambda[4], _epos); + for (i = 0; i < _nerrors; i++) + if (_epos[i]) { + alpha = _gf->log[_epos[i]]; + if ((int)alpha < _ndata) + _epos[nroots++] = alpha; + } + return nroots; + } else + for (alpha = 0; (int)alpha < _ndata; alpha++) { + unsigned alphai; + unsigned sum; + sum = 0; + alphai = 0; + for (i = 0; i <= _nerrors; i++) { + sum ^= rs_hgmul(_gf, _lambda[_nerrors - i], alphai); + alphai = _gf->log[_gf->exp[alphai + alpha]]; + } + if (!sum) + _epos[nroots++] = alpha; + } + return nroots; +} + +/*Corrects a codeword with _ndata<256 bytes, of which the last _npar are parity + bytes. + Known locations of errors can be passed in the _erasures array. + Twice as many (up to _npar) errors with a known location can be corrected + compared to errors with an unknown location. + Returns the number of errors corrected if successful, or a negative number if + the message could not be corrected because too many errors were detected.*/ +int rs_correct(const rs_gf256 *_gf, int _m0, unsigned char *_data, int _ndata, + int _npar, const unsigned char *_erasures, int _nerasures) +{ + /*lambda must have storage for at least five entries to avoid special cases + in the low-degree polynomial solver.*/ + unsigned char lambda[256]; + unsigned char omega[256]; + unsigned char epos[256]; + unsigned char s[256]; + int i; + /*If we already have too many erasures, we can't possibly succeed.*/ + if (_nerasures > _npar) + return -1; + /*Compute the syndrome values.*/ + rs_calc_syndrome(_gf, _m0, s, _npar, _data, _ndata); + /*Check for a non-zero value.*/ + for (i = 0; i < _npar; i++) + if (s[i]) { + int nerrors; + int j; + /*Construct the error locator polynomial.*/ + nerrors = rs_modified_berlekamp_massey(_gf, lambda, s, omega, _npar, + _erasures, _nerasures, + _ndata); + /*If we can't locate any errors, we can't force the syndrome values to + zero, and must have a decoding error. + Conversely, if we have too many errors, there's no reason to even attempt + the root search.*/ + if (nerrors <= 0 || nerrors - _nerasures > _npar - _nerasures >> 1) + return -1; + /*Compute the locations of the errors. + If they are not all distinct, or some of them were outside the valid + range for our block size, we have a decoding error.*/ + if (rs_find_roots(_gf, epos, lambda, nerrors, _ndata) < nerrors) + return -1; + /*Now compute the error magnitudes.*/ + for (i = 0; i < nerrors; i++) { + unsigned a; + unsigned b; + unsigned alpha; + unsigned alphan1; + unsigned alphan2; + unsigned alphanj; + alpha = epos[i]; + /*Evaluate omega at alpha**-1.*/ + a = 0; + alphan1 = 255 - alpha; + alphanj = 0; + for (j = 0; j < _npar; j++) { + a ^= rs_hgmul(_gf, omega[j], alphanj); + alphanj = _gf->log[_gf->exp[alphanj + alphan1]]; + } + /*Evaluate the derivative of lambda at alpha**-1 + All the odd powers vanish.*/ + b = 0; + alphan2 = _gf->log[_gf->exp[alphan1 << 1]]; + alphanj = alphan1 + _m0 * alpha % 255; + for (j = 1; j <= _npar; j += 2) { + b ^= rs_hgmul(_gf, lambda[j], alphanj); + alphanj = _gf->log[_gf->exp[alphanj + alphan2]]; + } + /*Apply the correction.*/ + _data[_ndata - 1 - alpha] ^= rs_gdiv(_gf, a, b); + } + return nerrors; + } + return 0; +} + +/*Encoding.*/ + +/*Create an _npar-coefficient generator polynomial for a Reed-Solomon code + with _npar<256 parity bytes.*/ +void rs_compute_genpoly(const rs_gf256 *_gf, int _m0, unsigned char *_genpoly, + int _npar) +{ + int i; + if (_npar <= 0) + return; + rs_poly_zero(_genpoly, _npar); + _genpoly[0] = 1; + /*Multiply by (x+alpha^i) for i = 1 ... _ndata.*/ + for (i = 0; i < _npar; i++) { + unsigned alphai; + int n; + int j; + n = i + 1 < _npar - 1 ? i + 1 : _npar - 1; + alphai = _gf->log[_gf->exp[_m0 + i]]; + for (j = n; j > 0; j--) + _genpoly[j] = _genpoly[j - 1] ^ rs_hgmul(_gf, _genpoly[j], alphai); + _genpoly[0] = rs_hgmul(_gf, _genpoly[0], alphai); + } +} + +/*Adds _npar<=_ndata parity bytes to an _ndata-_npar byte message. + _data must contain room for _ndata<256 bytes.*/ +void rs_encode(const rs_gf256 *_gf, unsigned char *_data, int _ndata, + const unsigned char *_genpoly, int _npar) +{ + unsigned char *lfsr; + unsigned d; + int i; + int j; + if (_npar <= 0) + return; + lfsr = _data + _ndata - _npar; + rs_poly_zero(lfsr, _npar); + for (i = 0; i < _ndata - _npar; i++) { + d = _data[i] ^ lfsr[0]; + if (d) { + unsigned logd; + logd = _gf->log[d]; + for (j = 0; j < _npar - 1; j++) { + lfsr[j] = lfsr[j + 1] ^ + rs_hgmul(_gf, _genpoly[_npar - 1 - j], logd); + } + lfsr[_npar - 1] = rs_hgmul(_gf, _genpoly[0], logd); + } else + rs_poly_div_x(lfsr, lfsr, _npar); + } +} + +#if defined(RS_TEST_ENC) +#include <stdio.h> +#include <stdlib.h> + +int main(void) +{ + rs_gf256 gf; + int k; + rs_gf256_init(&gf, QR_PPOLY); + srand(0); + for (k = 0; k < 64 * 1024; k++) { + unsigned char genpoly[256]; + unsigned char data[256]; + unsigned char epos[256]; + int ndata; + int npar; + int nerrors; + int i; + ndata = rand() & 0xFF; + npar = ndata > 0 ? rand() % ndata : 0; + for (i = 0; i < ndata - npar; i++) + data[i] = rand() & 0xFF; + rs_compute_genpoly(&gf, QR_M0, genpoly, npar); + rs_encode(&gf, QR_M0, data, ndata, genpoly, npar); + /*Write a clean version of the codeword.*/ + printf("%i %i", ndata, npar); + for (i = 0; i < ndata; i++) + printf(" %i", data[i]); + printf(" 0\n"); + /*Write the correct output to compare the decoder against.*/ + fprintf(stderr, "Success!\n", nerrors); + for (i = 0; i < ndata; i++) + fprintf(stderr, "%i%s", data[i], i + 1 < ndata ? " " : "\n"); + if (npar > 0) { + /*Corrupt it.*/ + nerrors = rand() % (npar + 1); + if (nerrors > 0) { + /*This test is not quite correct: there could be so many errors it + comes within (npar>>1) errors of another valid codeword. + I don't know a simple way to test for that without trying to decode + the corrupt codeword, though, which is the very code we're trying to + test.*/ + if (nerrors <= npar >> 1) { + fprintf(stderr, "Success!\n", nerrors); + for (i = 0; i < ndata; i++) { + fprintf(stderr, "%i%s", data[i], + i + 1 < ndata ? " " : "\n"); + } + } else + fprintf(stderr, "Failure.\n"); + fprintf(stderr, "Success!\n", nerrors); + for (i = 0; i < ndata; i++) + fprintf(stderr, "%i%s", data[i], + i + 1 < ndata ? " " : "\n"); + for (i = 0; i < ndata; i++) + epos[i] = i; + for (i = 0; i < nerrors; i++) { + unsigned char e; + int ei; + ei = rand() % (ndata - i) + i; + e = epos[ei]; + epos[ei] = epos[i]; + epos[i] = e; + data[e] ^= rand() % 255 + 1; + } + /*First with no erasure locations.*/ + printf("%i %i", ndata, npar); + for (i = 0; i < ndata; i++) + printf(" %i", data[i]); + printf(" 0\n"); + /*Now with erasure locations.*/ + printf("%i %i", ndata, npar); + for (i = 0; i < ndata; i++) + printf(" %i", data[i]); + printf(" %i", nerrors); + for (i = 0; i < nerrors; i++) + printf(" %i", epos[i]); + printf("\n"); + } + } + } + return 0; +} +#endif + +#if defined(RS_TEST_DEC) +#include <stdio.h> + +int main(void) +{ + rs_gf256 gf; + rs_gf256_init(&gf, QR_PPOLY); + for (;;) { + unsigned char data[255]; + unsigned char erasures[255]; + int idata[255]; + int ierasures[255]; + int ndata; + int npar; + int nerasures; + int nerrors; + int i; + if (scanf("%i", &ndata) < 1 || ndata < 0 || ndata > 255 || + scanf("%i", &npar) < 1 || npar < 0 || npar > ndata) + break; + for (i = 0; i < ndata; i++) { + if (scanf("%i", idata + i) < 1 || idata[i] < 0 || idata[i] > 255) + break; + data[i] = idata[i]; + } + if (i < ndata) + break; + if (scanf("%i", &nerasures) < 1 || nerasures < 0 || nerasures > ndata) + break; + for (i = 0; i < nerasures; i++) { + if (scanf("%i", ierasures + i) < 1 || ierasures[i] < 0 || + ierasures[i] >= ndata) + break; + erasures[i] = ierasures[i]; + } + nerrors = + rs_correct(&gf, QR_M0, data, ndata, npar, erasures, nerasures); + if (nerrors >= 0) { + unsigned char data2[255]; + unsigned char genpoly[255]; + for (i = 0; i < ndata - npar; i++) + data2[i] = data[i]; + rs_compute_genpoly(&gf, QR_M0, genpoly, npar); + rs_encode(&gf, QR_M0, data2, ndata, genpoly, npar); + for (i = ndata - npar; i < ndata; i++) + if (data[i] != data2[i]) { + printf("Abject failure! %i!=%i\n", data[i], data2[i]); + } + printf("Success!\n", nerrors); + for (i = 0; i < ndata; i++) + printf("%i%s", data[i], i + 1 < ndata ? " " : "\n"); + } else + printf("Failure.\n"); + } + return 0; +} +#endif + +#if defined(RS_TEST_ROOTS) +#include <stdio.h> + +/*Exhaustively test the root finder.*/ +int main(void) +{ + rs_gf256 gf; + int a; + int b; + int c; + int d; + rs_gf256_init(&gf, QR_PPOLY); + for (a = 0; a < 256; a++) + for (b = 0; b < 256; b++) + for (c = 0; c < 256; c++) + for (d = 0; d < 256; d++) { + unsigned char x[4]; + unsigned char r[4]; + unsigned x2; + unsigned e[5]; + int nroots; + int mroots; + int i; + int j; + nroots = rs_quartic_solve(&gf, a, b, c, d, x); + for (i = 0; i < nroots; i++) { + x2 = rs_gmul(&gf, x[i], x[i]); + e[0] = rs_gmul(&gf, x2, x2) ^ + rs_gmul(&gf, a, rs_gmul(&gf, x[i], x2)) ^ + rs_gmul(&gf, b, x2) ^ rs_gmul(&gf, c, x[i]) ^ d; + if (e[0]) { + printf( + "Invalid root: (0x%02X)**4 ^ 0x%02X*(0x%02X)**3 ^ " + "0x%02X*(0x%02X)**2 ^ 0x%02X(0x%02X) ^ 0x%02X = 0x%02X\n", + x[i], a, x[i], b, x[i], c, x[i], d, e[0]); + } + for (j = 0; j < i; j++) + if (x[i] == x[j]) { + printf( + "Repeated root %i=%i: (0x%02X)**4 ^ 0x%02X*(0x%02X)**3 ^ " + "0x%02X*(0x%02X)**2 ^ 0x%02X(0x%02X) ^ 0x%02X = 0x%02X\n", + i, j, x[i], a, x[i], b, x[i], c, x[i], d, + e[0]); + } + } + mroots = 0; + for (j = 1; j < 256; j++) { + int logx; + int logx2; + logx = gf.log[j]; + logx2 = gf.log[gf.exp[logx << 1]]; + e[mroots] = + d ^ rs_hgmul(&gf, c, logx) ^ + rs_hgmul(&gf, b, logx2) ^ + rs_hgmul(&gf, a, gf.log[gf.exp[logx + logx2]]) ^ + gf.exp[logx2 << 1]; + if (!e[mroots]) + r[mroots++] = j; + } + /*We only care about missing roots if the quartic had 4 distinct, non-zero + roots.*/ + if (mroots == 4) + for (j = 0; j < mroots; j++) { + for (i = 0; i < nroots; i++) + if (x[i] == r[j]) + break; + if (i >= nroots) { + printf( + "Missing root: (0x%02X)**4 ^ 0x%02X*(0x%02X)**3 ^ " + "0x%02X*(0x%02X)**2 ^ 0x%02X(0x%02X) ^ 0x%02X = 0x%02X\n", + r[j], a, r[j], b, r[j], c, r[j], d, e[j]); + } + } + } + return 0; +} +#endif diff --git a/zbar/qrcode/rs.h b/zbar/qrcode/rs.h new file mode 100644 index 0000000..21b47dc --- /dev/null +++ b/zbar/qrcode/rs.h @@ -0,0 +1,66 @@ +/*Copyright (C) 1991-1995 Henry Minsky (hqm@ua.com, hqm@ai.mit.edu) + Copyright (C) 2008-2009 Timothy B. Terriberry (tterribe@xiph.org) + You can redistribute this library and/or modify it under the terms of the + GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version.*/ +#if !defined(_qrcode_rs_H) +#define _qrcode_rs_H (1) + +/*This is one of 16 irreducible primitive polynomials of degree 8: + x**8+x**4+x**3+x**2+1. + Under such a polynomial, x (i.e., 0x02) is a generator of GF(2**8). + The high order 1 bit is implicit. + From~\cite{MD88}, Ch. 5, p. 275 by Patel. + @BOOK{MD88, + author="C. Dennis Mee and Eric D. Daniel", + title="Video, Audio, and Instrumentation Recording", + series="Magnetic Recording", + volume=3, + publisher="McGraw-Hill Education", + address="Columbus, OH", + month=Jun, + year=1988 + }*/ +#define QR_PPOLY (0x1D) + +/*The index to start the generator polynomial from (0...254).*/ +#define QR_M0 (0) + +typedef struct rs_gf256 rs_gf256; + +struct rs_gf256 { + /*A logarithm table in GF(2**8).*/ + unsigned char log[256]; + /*An exponential table in GF(2**8): exp[i] contains x^i reduced modulo the + irreducible primitive polynomial used to define the field. + The extra 256 entries are used to do arithmetic mod 255, since some extra + table lookups are generally faster than doing the modulus.*/ + unsigned char exp[511]; +}; + +/*Initialize discrete logarithm tables for GF(2**8) using a given primitive + irreducible polynomial.*/ +void rs_gf256_init(rs_gf256 *_gf, unsigned _ppoly); + +/*Corrects a codeword with _ndata<256 bytes, of which the last _npar are parity + bytes. + Known locations of errors can be passed in the _erasures array. + Twice as many (up to _npar) errors with a known location can be corrected + compared to errors with an unknown location. + Returns the number of errors corrected if successful, or a negative number if + the message could not be corrected because too many errors were detected.*/ +int rs_correct(const rs_gf256 *_gf, int _m0, unsigned char *_data, int _ndata, + int _npar, const unsigned char *_erasures, int _nerasures); + +/*Create an _npar-coefficient generator polynomial for a Reed-Solomon code with + _npar<256 parity bytes.*/ +void rs_compute_genpoly(const rs_gf256 *_gf, int _m0, unsigned char *_genpoly, + int _npar); + +/*Adds _npar<=_ndata parity bytes to an _ndata-_npar byte message. + _data must contain room for _ndata<256 bytes.*/ +void rs_encode(const rs_gf256 *_gf, unsigned char *_data, int _ndata, + const unsigned char *_genpoly, int _npar); + +#endif diff --git a/zbar/qrcode/util.c b/zbar/qrcode/util.c new file mode 100644 index 0000000..a0a5eab --- /dev/null +++ b/zbar/qrcode/util.c @@ -0,0 +1,144 @@ +/*Copyright (C) 2008-2009 Timothy B. Terriberry (tterribe@xiph.org) + You can redistribute this library and/or modify it under the terms of the + GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version.*/ +#include "util.h" +#include <stdlib.h> + +/*Computes floor(sqrt(_val)) exactly.*/ +unsigned qr_isqrt(unsigned _val) +{ + unsigned g; + unsigned b; + int bshift; + /*Uses the second method from + http://www.azillionmonkeys.com/qed/sqroot.html + The main idea is to search for the largest binary digit b such that + (g+b)*(g+b) <= _val, and add it to the solution g.*/ + g = 0; + b = 0x8000; + for (bshift = 16; bshift-- > 0;) { + unsigned t; + t = (g << 1) + b << bshift; + if (t <= _val) { + g += b; + _val -= t; + } + b >>= 1; + } + return g; +} + +/*Computes sqrt(_x*_x+_y*_y) using CORDIC. + This implementation is valid for all 32-bit inputs and returns a result + accurate to about 27 bits of precision. + It has been tested for all positive 16-bit inputs, where it returns correctly + rounded results in 99.998% of cases and the maximum error is + 0.500137134862598032 (for _x=48140, _y=63018). + Very nearly all results less than (1<<16) are correctly rounded. + All Pythagorean triples with a hypotenuse of less than ((1<<27)-1) evaluate + correctly, and the total bias over all Pythagorean triples is -0.04579, with + a relative RMS error of 7.2864E-10 and a relative peak error of 7.4387E-9.*/ +unsigned qr_ihypot(int _x, int _y) +{ + unsigned x; + unsigned y; + int mask; + int shift; + int u; + int v; + int i; + x = _x = abs(_x); + y = _y = abs(_y); + mask = -(x > y) & (_x ^ _y); + x ^= mask; + y ^= mask; + _y ^= mask; + shift = 31 - qr_ilog(y); + shift = QR_MAXI(shift, 0); + x = (unsigned)((x << shift) * 0x9B74EDAAULL >> 32); + _y = (int)((_y << shift) * 0x9B74EDA9LL >> 32); + u = x; + mask = -(_y < 0); + x += _y + mask ^ mask; + _y -= u + mask ^ mask; + u = x + 1 >> 1; + v = _y + 1 >> 1; + mask = -(_y < 0); + x += v + mask ^ mask; + _y -= u + mask ^ mask; + for (i = 1; i < 16; i++) { + int r; + u = x + 1 >> 2; + r = (1 << 2 * i) >> 1; + v = _y + r >> 2 * i; + mask = -(_y < 0); + x += v + mask ^ mask; + _y = _y - (u + mask ^ mask) << 1; + } + return x + ((1U << shift) >> 1) >> shift; +} + +#if defined(__GNUC__) && defined(HAVE_FEATURES_H) +#include <features.h> +#if __GNUC_PREREQ(3, 4) +#include <limits.h> +#if INT_MAX >= 2147483647 +#define QR_CLZ0 sizeof(unsigned) * CHAR_BIT +#define QR_CLZ(_x) (__builtin_clz(_x)) +#elif LONG_MAX >= 2147483647L +#define QR_CLZ0 sizeof(unsigned long) * CHAR_BIT +#define QR_CLZ(_x) (__builtin_clzl(_x)) +#endif +#endif +#endif + +int qr_ilog(unsigned _v) +{ +#if defined(QR_CLZ) + /*Note that __builtin_clz is not defined when _x==0, according to the gcc + documentation (and that of the x86 BSR instruction that implements it), so + we have to special-case it.*/ + return QR_CLZ0 - QR_CLZ(_v) & -!!_v; +#else + int ret; + int m; + m = !!(_v & 0xFFFF0000) << 4; + _v >>= m; + ret = m; + m = !!(_v & 0xFF00) << 3; + _v >>= m; + ret |= m; + m = !!(_v & 0xF0) << 2; + _v >>= m; + ret |= m; + m = !!(_v & 0xC) << 1; + _v >>= m; + ret |= m; + ret |= !!(_v & 0x2); + return ret + !!_v; +#endif +} + +#if defined(QR_TEST_SQRT) +#include <math.h> +#include <stdio.h> + +/*Exhaustively test the integer square root function.*/ +int main(void) +{ + unsigned u; + u = 0; + do { + unsigned r; + unsigned s; + r = qr_isqrt(u); + s = (int)sqrt(u); + if (r != s) + printf("%u: %u!=%u\n", u, r, s); + u++; + } while (u); + return 0; +} +#endif diff --git a/zbar/qrcode/util.h b/zbar/qrcode/util.h new file mode 100644 index 0000000..6b605c2 --- /dev/null +++ b/zbar/qrcode/util.h @@ -0,0 +1,56 @@ +/*Copyright (C) 2008-2009 Timothy B. Terriberry (tterribe@xiph.org) + You can redistribute this library and/or modify it under the terms of the + GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version.*/ +#if !defined(_qrcode_util_H) +#define _qrcode_util_H (1) + +#define QR_MAXI(_a, _b) ((_a) - ((_a) - (_b) & -((_b) > (_a)))) +#define QR_MINI(_a, _b) ((_a) + ((_b) - (_a) & -((_b) < (_a)))) +#define QR_SIGNI(_x) (((_x) > 0) - ((_x) < 0)) +#define QR_SIGNMASK(_x) (-((_x) < 0)) +/*Unlike copysign(), simply inverts the sign of _a if _b is negative.*/ +#define QR_FLIPSIGNI(_a, _b) ((_a) + QR_SIGNMASK(_b) ^ QR_SIGNMASK(_b)) +#define QR_COPYSIGNI(_a, _b) QR_FLIPSIGNI(abs(_a), _b) +/*Divides a signed integer by a positive value with exact rounding.*/ +#define QR_DIVROUND(_x, _y) (((_x) + QR_FLIPSIGNI(_y >> 1, _x)) / (_y)) +#define QR_CLAMPI(_a, _b, _c) (QR_MAXI(_a, QR_MINI(_b, _c))) +#define QR_CLAMP255(_x) \ + ((unsigned char)((((_x) < 0) - 1) & ((_x) | -((_x) > 255)))) +#define QR_SWAP2I(_a, _b) \ + do { \ + int t__; \ + t__ = (_a); \ + (_a) = (_b); \ + (_b) = t__; \ + } while (0) +/*Swaps two integers _a and _b if _a>_b.*/ +#define QR_SORT2I(_a, _b) \ + do { \ + int t__; \ + t__ = QR_MINI(_a, _b) ^ (_a); \ + (_a) ^= t__; \ + (_b) ^= t__; \ + } while (0) +#define QR_ILOG0(_v) (!!((_v)&0x2)) +#define QR_ILOG1(_v) (((_v)&0xC) ? 2 + QR_ILOG0((_v) >> 2) : QR_ILOG0(_v)) +#define QR_ILOG2(_v) (((_v)&0xF0) ? 4 + QR_ILOG1((_v) >> 4) : QR_ILOG1(_v)) +#define QR_ILOG3(_v) (((_v)&0xFF00) ? 8 + QR_ILOG2((_v) >> 8) : QR_ILOG2(_v)) +#define QR_ILOG4(_v) \ + (((_v)&0xFFFF0000) ? 16 + QR_ILOG3((_v) >> 16) : QR_ILOG3(_v)) +/*Computes the integer logarithm of a (positive, 32-bit) constant.*/ +#define QR_ILOG(_v) ((int)QR_ILOG4((unsigned)(_v))) + +/*Multiplies 32-bit numbers _a and _b, adds (possibly 64-bit) number _r, and + takes bits [_s,_s+31] of the result.*/ +#define QR_FIXMUL(_a, _b, _r, _s) ((int)((_a) * (long long)(_b) + (_r) >> (_s))) +/*Multiplies 32-bit numbers _a and _b, adds (possibly 64-bit) number _r, and + gives all 64 bits of the result.*/ +#define QR_EXTMUL(_a, _b, _r) ((_a) * (long long)(_b) + (_r)) + +unsigned qr_isqrt(unsigned _val); +unsigned qr_ihypot(int _x, int _y); +int qr_ilog(unsigned _val); + +#endif diff --git a/zbar/refcnt.c b/zbar/refcnt.c new file mode 100644 index 0000000..e3a5cd9 --- /dev/null +++ b/zbar/refcnt.c @@ -0,0 +1,47 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (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 "refcnt.h" + +#if !defined(_WIN32) && !defined(TARGET_OS_MAC) && defined(HAVE_LIBPTHREAD) + +pthread_once_t initialized = PTHREAD_ONCE_INIT; +pthread_mutex_t _zbar_reflock; + +static void initialize(void) +{ + pthread_mutex_init(&_zbar_reflock, NULL); +} + +void _zbar_refcnt_init() +{ + pthread_once(&initialized, initialize); +} + +#else + +void _zbar_refcnt_init() +{ +} + +#endif diff --git a/zbar/refcnt.h b/zbar/refcnt.h new file mode 100644 index 0000000..259fab3 --- /dev/null +++ b/zbar/refcnt.h @@ -0,0 +1,90 @@ +/*------------------------------------------------------------------------ + * 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 + *------------------------------------------------------------------------*/ +#ifndef _REFCNT_H_ +#define _REFCNT_H_ + +#include "config.h" +#include <assert.h> + +#if defined(_WIN32) +#include <windows.h> + +typedef LONG refcnt_t; + +static inline int _zbar_refcnt(refcnt_t *cnt, int delta) +{ + int rc = -1; + if (delta > 0) + while (delta--) + rc = InterlockedIncrement(cnt); + else if (delta < 0) + while (delta++) + rc = InterlockedDecrement(cnt); + assert(rc >= 0); + return (rc); +} + +#elif defined(TARGET_OS_MAC) +#include <libkern/OSAtomic.h> + +typedef int32_t refcnt_t; + +static inline int _zbar_refcnt(refcnt_t *cnt, int delta) +{ + int rc = OSAtomicAdd32Barrier(delta, cnt); + assert(rc >= 0); + return (rc); +} + +#elif defined(HAVE_LIBPTHREAD) +#include <pthread.h> + +typedef int refcnt_t; + +extern pthread_mutex_t _zbar_reflock; + +static inline int _zbar_refcnt(refcnt_t *cnt, int delta) +{ + pthread_mutex_lock(&_zbar_reflock); + int rc = (*cnt += delta); + pthread_mutex_unlock(&_zbar_reflock); + assert(rc >= 0); + return (rc); +} + +#else + +typedef int refcnt_t; + +static inline int _zbar_refcnt(refcnt_t *cnt, int delta) +{ + int rc = (*cnt += delta); + assert(rc >= 0); + return (rc); +} + +#endif + +void _zbar_refcnt_init(void); + +#endif diff --git a/zbar/scanner.c b/zbar/scanner.c new file mode 100644 index 0000000..1db14f5 --- /dev/null +++ b/zbar/scanner.c @@ -0,0 +1,311 @@ +/*------------------------------------------------------------------------ + * 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" +#include <stddef.h> +#include <stdlib.h> /* malloc, free, abs */ +#include <string.h> /* memset */ + +#include <zbar.h> +#include "svg.h" + +#ifdef DEBUG_SCANNER +#define DEBUG_LEVEL (DEBUG_SCANNER) +#endif +#include "debug.h" + +#ifndef ZBAR_FIXED +#define ZBAR_FIXED 5 +#endif +#define ROUND (1 << (ZBAR_FIXED - 1)) + +/* FIXME add runtime config API for these */ +#ifndef ZBAR_SCANNER_THRESH_MIN +#define ZBAR_SCANNER_THRESH_MIN 4 +#endif + +#ifndef ZBAR_SCANNER_THRESH_INIT_WEIGHT +#define ZBAR_SCANNER_THRESH_INIT_WEIGHT .44 +#endif +#define THRESH_INIT \ + ((unsigned)((ZBAR_SCANNER_THRESH_INIT_WEIGHT * (1 << (ZBAR_FIXED + 1)) + \ + 1) / \ + 2)) + +#ifndef ZBAR_SCANNER_THRESH_FADE +#define ZBAR_SCANNER_THRESH_FADE 8 +#endif + +#ifndef ZBAR_SCANNER_EWMA_WEIGHT +#define ZBAR_SCANNER_EWMA_WEIGHT .78 +#endif +#define EWMA_WEIGHT \ + ((unsigned)((ZBAR_SCANNER_EWMA_WEIGHT * (1 << (ZBAR_FIXED + 1)) + 1) / 2)) + +/* scanner state */ +struct zbar_scanner_s { + zbar_decoder_t *decoder; /* associated bar width decoder */ + unsigned y1_min_thresh; /* minimum threshold */ + + unsigned x; /* relative scan position of next sample */ + int y0[4]; /* short circular buffer of average intensities */ + + int y1_sign; /* slope at last crossing */ + unsigned y1_thresh; /* current slope threshold */ + + unsigned cur_edge; /* interpolated position of tracking edge */ + unsigned last_edge; /* interpolated position of last located edge */ + unsigned width; /* last element width */ +}; + +zbar_scanner_t *zbar_scanner_create(zbar_decoder_t *dcode) +{ + zbar_scanner_t *scn = malloc(sizeof(zbar_scanner_t)); + scn->decoder = dcode; + scn->y1_min_thresh = ZBAR_SCANNER_THRESH_MIN; + zbar_scanner_reset(scn); + return (scn); +} + +void zbar_scanner_destroy(zbar_scanner_t *scn) +{ + free(scn); +} + +zbar_symbol_type_t zbar_scanner_reset(zbar_scanner_t *scn) +{ + memset(&scn->x, 0, sizeof(zbar_scanner_t) - offsetof(zbar_scanner_t, x)); + scn->y1_thresh = scn->y1_min_thresh; + if (scn->decoder) + zbar_decoder_reset(scn->decoder); + return (ZBAR_NONE); +} + +unsigned zbar_scanner_get_width(const zbar_scanner_t *scn) +{ + return (scn->width); +} + +unsigned zbar_scanner_get_edge(const zbar_scanner_t *scn, unsigned offset, + int prec) +{ + unsigned edge = scn->last_edge - offset - (1 << ZBAR_FIXED) - ROUND; + prec = ZBAR_FIXED - prec; + if (prec > 0) + return (edge >> prec); + else if (!prec) + return (edge); + else + return (edge << -prec); +} + +zbar_color_t zbar_scanner_get_color(const zbar_scanner_t *scn) +{ + return ((scn->y1_sign <= 0) ? ZBAR_SPACE : ZBAR_BAR); +} + +static inline unsigned calc_thresh(zbar_scanner_t *scn) +{ + /* threshold 1st to improve noise rejection */ + unsigned dx, thresh = scn->y1_thresh; + unsigned long t; + if ((thresh <= scn->y1_min_thresh) || !scn->width) { + dbprintf(1, " tmin=%d", scn->y1_min_thresh); + return (scn->y1_min_thresh); + } + /* slowly return threshold to min */ + dx = (scn->x << ZBAR_FIXED) - scn->last_edge; + t = thresh * dx; + t /= scn->width; + t /= ZBAR_SCANNER_THRESH_FADE; + dbprintf(1, " thr=%d t=%ld x=%d last=%d.%d (%d)", thresh, t, scn->x, + scn->last_edge >> ZBAR_FIXED, + scn->last_edge & ((1 << ZBAR_FIXED) - 1), dx); + if (thresh > t) { + thresh -= t; + if (thresh > scn->y1_min_thresh) + return (thresh); + } + scn->y1_thresh = scn->y1_min_thresh; + return (scn->y1_min_thresh); +} + +static inline zbar_symbol_type_t process_edge(zbar_scanner_t *scn, int y1) +{ + if (!scn->y1_sign) + scn->last_edge = scn->cur_edge = (1 << ZBAR_FIXED) + ROUND; + else if (!scn->last_edge) + scn->last_edge = scn->cur_edge; + + scn->width = scn->cur_edge - scn->last_edge; + dbprintf(1, " sgn=%d cur=%d.%d w=%d (%s)\n", scn->y1_sign, + scn->cur_edge >> ZBAR_FIXED, + scn->cur_edge & ((1 << ZBAR_FIXED) - 1), scn->width, + ((y1 > 0) ? "SPACE" : "BAR")); + scn->last_edge = scn->cur_edge; + +#if DEBUG_SVG > 1 + svg_path_moveto(SVG_ABS, scn->last_edge - (1 << ZBAR_FIXED) - ROUND, 0); +#endif + + /* pass to decoder */ + if (scn->decoder) + return (zbar_decode_width(scn->decoder, scn->width)); + return (ZBAR_PARTIAL); +} + +inline zbar_symbol_type_t zbar_scanner_flush(zbar_scanner_t *scn) +{ + unsigned x; + if (!scn->y1_sign) + return (ZBAR_NONE); + + x = (scn->x << ZBAR_FIXED) + ROUND; + + if (scn->cur_edge != x || scn->y1_sign > 0) { + zbar_symbol_type_t edge = process_edge(scn, -scn->y1_sign); + dbprintf(1, "flush0:"); + scn->cur_edge = x; + scn->y1_sign = -scn->y1_sign; + return (edge); + } + + scn->y1_sign = scn->width = 0; + if (scn->decoder) + return (zbar_decode_width(scn->decoder, 0)); + return (ZBAR_PARTIAL); +} + +zbar_symbol_type_t zbar_scanner_new_scan(zbar_scanner_t *scn) +{ + zbar_symbol_type_t edge = ZBAR_NONE; + while (scn->y1_sign) { + zbar_symbol_type_t tmp = zbar_scanner_flush(scn); + if (tmp < 0 || tmp > edge) + edge = tmp; + } + + /* reset scanner and associated decoder */ + memset(&scn->x, 0, sizeof(zbar_scanner_t) - offsetof(zbar_scanner_t, x)); + scn->y1_thresh = scn->y1_min_thresh; + if (scn->decoder) + zbar_decoder_new_scan(scn->decoder); + return (edge); +} + +zbar_symbol_type_t zbar_scan_y(zbar_scanner_t *scn, int y) +{ + /* FIXME calc and clip to max y range... */ + /* retrieve short value history */ + register int x = scn->x; + register int y0_1 = scn->y0[(x - 1) & 3]; + register int y0_0 = y0_1; + register int y0_2, y0_3, y1_1, y2_1, y2_2; + zbar_symbol_type_t edge; + if (x) { + /* update weighted moving average */ + y0_0 += ((int)((y - y0_1) * EWMA_WEIGHT)) >> ZBAR_FIXED; + scn->y0[x & 3] = y0_0; + } else + y0_0 = y0_1 = scn->y0[0] = scn->y0[1] = scn->y0[2] = scn->y0[3] = y; + y0_2 = scn->y0[(x - 2) & 3]; + y0_3 = scn->y0[(x - 3) & 3]; + /* 1st differential @ x-1 */ + y1_1 = y0_1 - y0_2; + { + register int y1_2 = y0_2 - y0_3; + if ((abs(y1_1) < abs(y1_2)) && ((y1_1 >= 0) == (y1_2 >= 0))) + y1_1 = y1_2; + } + + /* 2nd differentials @ x-1 & x-2 */ + y2_1 = y0_0 - (y0_1 * 2) + y0_2; + y2_2 = y0_1 - (y0_2 * 2) + y0_3; + + dbprintf(1, "scan: x=%d y=%d y0=%d y1=%d y2=%d", x, y, y0_1, y1_1, y2_1); + + edge = ZBAR_NONE; + /* 2nd zero-crossing is 1st local min/max - could be edge */ + if ((!y2_1 || ((y2_1 > 0) ? y2_2 < 0 : y2_2 > 0)) && + (calc_thresh(scn) <= abs(y1_1))) { + /* check for 1st sign change */ + char y1_rev = (scn->y1_sign > 0) ? y1_1 < 0 : y1_1 > 0; + if (y1_rev) + /* intensity change reversal - finalize previous edge */ + edge = process_edge(scn, y1_1); + + if (y1_rev || (abs(scn->y1_sign) < abs(y1_1))) { + int d; + scn->y1_sign = y1_1; + + /* adaptive thresholding */ + /* start at multiple of new min/max */ + scn->y1_thresh = (abs(y1_1) * THRESH_INIT + ROUND) >> ZBAR_FIXED; + dbprintf(1, "\tthr=%d", scn->y1_thresh); + if (scn->y1_thresh < scn->y1_min_thresh) + scn->y1_thresh = scn->y1_min_thresh; + + /* update current edge */ + d = y2_1 - y2_2; + scn->cur_edge = 1 << ZBAR_FIXED; + if (!d) + scn->cur_edge >>= 1; + else if (y2_1) + /* interpolate zero crossing */ + scn->cur_edge -= ((y2_1 << ZBAR_FIXED) + 1) / d; + scn->cur_edge += x << ZBAR_FIXED; + dbprintf(1, "\n"); + } + } else + dbprintf(1, "\n"); + /* FIXME add fall-thru pass to decoder after heuristic "idle" period + (eg, 6-8 * last width) */ + scn->x = x + 1; + return (edge); +} + +/* undocumented API for drawing cutesy debug graphics */ +void zbar_scanner_get_state(const zbar_scanner_t *scn, unsigned *x, + unsigned *cur_edge, unsigned *last_edge, int *y0, + int *y1, int *y2, int *y1_thresh) +{ + register int y0_0 = scn->y0[(scn->x - 1) & 3]; + register int y0_1 = scn->y0[(scn->x - 2) & 3]; + register int y0_2 = scn->y0[(scn->x - 3) & 3]; + zbar_scanner_t *mut_scn; + if (x) + *x = scn->x - 1; + if (last_edge) + *last_edge = scn->last_edge; + if (y0) + *y0 = y0_1; + if (y1) + *y1 = y0_1 - y0_2; + if (y2) + *y2 = y0_0 - (y0_1 * 2) + y0_2; + /* NB not quite accurate (uses updated x) */ + mut_scn = (zbar_scanner_t *)scn; + if (y1_thresh) + *y1_thresh = calc_thresh(mut_scn); + dbprintf(1, "\n"); +} diff --git a/zbar/sqcode.c b/zbar/sqcode.c new file mode 100644 index 0000000..422c803 --- /dev/null +++ b/zbar/sqcode.c @@ -0,0 +1,578 @@ +/*Copyright (C) 2018 Javier Serrano Polo <javier@jasp.net> + You can redistribute this library and/or modify it under the terms of the + GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version.*/ +#include "config.h" + +#include "sqcode.h" + +#include <stdbool.h> +#include <stdlib.h> + +#include "image.h" +#include "img_scanner.h" + +typedef enum +{ + SHAPE_DOT, + SHAPE_CORNER, + SHAPE_OTHER, + SHAPE_VOID +} shape_t; + +typedef struct { + float x; + float y; +} sq_point; + +typedef struct { + shape_t type; + unsigned x0; + unsigned y0; + unsigned width; + unsigned height; + sq_point center; +} sq_dot; + +struct sq_reader { + bool enabled; +}; + +/*Initializes a client reader handle.*/ +static void sq_reader_init(sq_reader *reader) +{ + reader->enabled = true; +} + +/*Allocates a client reader handle.*/ +sq_reader *_zbar_sq_create(void) +{ + sq_reader *reader = malloc(sizeof(sq_reader)); + if (reader) + sq_reader_init(reader); + return reader; +} + +/*Frees a client reader handle.*/ +void _zbar_sq_destroy(sq_reader *reader) +{ + free(reader); +} + +/* reset finder state between scans */ +void _zbar_sq_reset(sq_reader *reader) +{ + reader->enabled = true; +} + +int _zbar_sq_new_config(sq_reader *reader, unsigned config) +{ + reader->enabled = config; + return 0; +} + +static const char base64_table[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' +}; + +static char *base64_encode_buffer(const char *s, size_t size) +{ + char *e; + size_t encoded_size = (size + 2) / 3 * 4 + 1; + char *encoded = malloc(encoded_size); + if (!encoded) + return NULL; + e = encoded; + for (;;) { + unsigned char c = (*s >> 2) & 0x3f; + *e++ = base64_table[c]; + c = (*s++ << 4) & 0x30; + if (!--size) { + *e++ = base64_table[c]; + *e++ = '='; + *e++ = '='; + break; + } + c |= (*s >> 4) & 0x0f; + *e++ = base64_table[c]; + c = (*s++ << 2) & 0x3c; + if (!--size) { + *e++ = base64_table[c]; + *e++ = '='; + break; + } + c |= (*s >> 6) & 0x03; + *e++ = base64_table[c]; + c = *s++ & 0x3f; + *e++ = base64_table[c]; + if (!--size) + break; + } + *e = '\0'; + return encoded; +} + +static bool sq_extract_text(zbar_image_scanner_t *iscn, const char *buf, + size_t len) +{ + size_t b64_len; + zbar_symbol_t *sym = _zbar_image_scanner_alloc_sym(iscn, ZBAR_SQCODE, 0); + sym->data = base64_encode_buffer(buf, len); + if (!sym->data) { + _zbar_image_scanner_recycle_syms(iscn, sym); + return true; + } + b64_len = (len + 2) / 3 * 4; + sym->data_alloc = b64_len + 1; + sym->datalen = b64_len; + _zbar_image_scanner_add_sym(iscn, sym); + return false; +} + +static bool is_black_color(const unsigned char c) +{ + return c <= 0x7f; +} + +static bool is_black(zbar_image_t *img, int x, int y) +{ + const unsigned char *data; + if (x < 0 || (unsigned)x >= img->width || y < 0 || + (unsigned)y >= img->height) + return false; + data = img->data; + return is_black_color(data[y * img->width + x]); +} + +static void set_dot_center(sq_dot *dot, float x, float y) +{ + dot->center.x = x; + dot->center.y = y; +} + +static void sq_scan_shape(zbar_image_t *img, sq_dot *dot, int start_x, + int start_y) +{ + int x, y; + unsigned x0, y0, width, height, x_sum, y_sum, total_weight; + const unsigned char *data; + if (!is_black(img, start_x, start_y)) { + dot->type = SHAPE_VOID; + dot->x0 = start_x; + dot->y0 = start_y; + dot->width = 0; + dot->height = 0; + set_dot_center(dot, start_x, start_y); + return; + } + + x0 = start_x; + y0 = start_y; + width = 1; + height = 1; + +new_point: + for (x = x0 - 1; x < (int)(x0 + width + 1); x++) { + if (is_black(img, x, y0 - 1)) { + y0 = y0 - 1; + height++; + goto new_point; + } + if (is_black(img, x, y0 + height)) { + height++; + goto new_point; + } + } + for (y = y0; y < (int)(y0 + height); y++) { + if (is_black(img, x0 - 1, y)) { + x0 = x0 - 1; + width++; + goto new_point; + } + if (is_black(img, x0 + width, y)) { + width++; + goto new_point; + } + } + + dot->x0 = x0; + dot->y0 = y0; + dot->width = width; + dot->height = height; + + /* Is it a corner? */ + if (is_black(img, x0 + 0.25 * width, y0 + 0.25 * height) && + !is_black(img, x0 + 0.75 * width, y0 + 0.25 * height) && + !is_black(img, x0 + 0.25 * width, y0 + 0.75 * height) && + is_black(img, x0 + 0.75 * width, y0 + 0.75 * height)) { + dot->type = SHAPE_CORNER; + set_dot_center(dot, x0 + 0.5 * width, y0 + 0.5 * height); + return; + } + + /* Set dot center */ + data = img->data; + x_sum = 0; + y_sum = 0; + total_weight = 0; + for (y = y0; y < (int)(y0 + height); y++) { + int x; + for (x = x0; x < (int)(x0 + width); x++) { + unsigned char weight; + if (!is_black(img, x, y)) + continue; + weight = 0xff - data[y * img->width + x]; + x_sum += weight * x; + y_sum += weight * y; + total_weight += weight; + } + } + dot->type = SHAPE_DOT; + set_dot_center(dot, x_sum / (float)total_weight + 0.5, + y_sum / (float)total_weight + 0.5); + + /* TODO: Is it other shape? White hole? Really a dot? */ +} + +static void set_middle_point(sq_point *middle, const sq_point *start, + const sq_point *end) +{ + middle->x = (start->x + end->x) / 2; + middle->y = (start->y + end->y) / 2; +} + +bool find_left_dot(zbar_image_t *img, sq_dot *dot, unsigned *found_x, + unsigned *found_y) +{ + int y, x; + for (y = dot->y0; y < (int)(dot->y0 + dot->height); y++) { + for (x = dot->x0 - 1; x >= (int)(dot->x0 - 2 * dot->width); x--) { + if (is_black(img, x, y)) { + *found_x = x; + *found_y = y; + return true; + } + } + } + return false; +} + +bool find_right_dot(zbar_image_t *img, sq_dot *dot, unsigned *found_x, + unsigned *found_y) +{ + int y, x; + for (y = dot->y0; y < (int)(dot->y0 + dot->height); y++) { + for (x = dot->x0 + dot->width; x < (int)(dot->x0 + 3 * dot->width); + x++) { + if (is_black(img, x, y)) { + *found_x = x; + *found_y = y; + return true; + } + } + } + return false; +} + +bool find_bottom_dot(zbar_image_t *img, sq_dot *dot, unsigned *found_x, + unsigned *found_y) +{ + int x, y; + + for (x = dot->x0 + dot->width - 1; x >= (int)dot->x0; x--) { + for (y = dot->y0 + dot->height; y < (int)(dot->y0 + 3 * dot->height); + y++) { + if (is_black(img, x, y)) { + *found_x = x; + *found_y = y; + return true; + } + } + } + return false; +} + +int _zbar_sq_decode(sq_reader *reader, zbar_image_scanner_t *iscn, + zbar_image_t *img) +{ + unsigned scan_y, scan_x, y; + sq_dot start_dot, top_left_dot, top_right_dot, bottom_left_dot, + bottom_right_dot, bottom_left2_dot; + bool start_corner, error; + sq_point *top_border, *left_border, *right_border, *bottom_border; + size_t border_len, cur_len, offset, bit_side_len, bit_len, byte_len, idx; + float inc_x, inc_y; + void *ptr; + char *buf; + + if (!reader->enabled) + return 0; + + if (img->format != fourcc('Y', '8', '0', '0')) { + fputs("Unexpected image format\n", stderr); + return 1; + } + + /* Starting pixel */ + for (scan_y = 0; scan_y < img->height; scan_y++) { + for (scan_x = 0; scan_x < img->width; scan_x++) { + if (is_black(img, scan_x, scan_y)) + goto found_start; + } + } + return 1; + +found_start:; + /* Starting dot */ + sq_scan_shape(img, &start_dot, scan_x, scan_y); + + start_corner = start_dot.type == SHAPE_CORNER; + + error = true; + + top_border = NULL; + left_border = NULL; + right_border = NULL; + bottom_border = NULL; + + if (start_corner) { + border_len = 0; + } else { + border_len = 1; + top_border = malloc(sizeof(sq_point)); + if (!top_border) + return 1; + top_border[0] = start_dot.center; + } + + top_left_dot = start_dot; + while (find_left_dot(img, &top_left_dot, &scan_x, &scan_y)) { + sq_scan_shape(img, &top_left_dot, scan_x, scan_y); + if (top_left_dot.type != SHAPE_DOT) + goto free_borders; + if (border_len) { + void *ptr; + size_t i; + border_len += 2; + ptr = realloc(top_border, border_len * sizeof(sq_point)); + if (!ptr) + goto free_borders; + top_border = ptr; + for (i = border_len - 1; i >= 2; i--) + top_border[i] = top_border[i - 2]; + top_border[0] = top_left_dot.center; + set_middle_point(&top_border[1], &top_border[0], &top_border[2]); + } else { + border_len = 1; + top_border = malloc(sizeof(sq_point)); + if (!top_border) + return 1; + top_border[0] = top_left_dot.center; + } + } + if (top_left_dot.type != SHAPE_DOT) + goto free_borders; + + top_right_dot = start_dot; + if (!start_corner) { + while (find_right_dot(img, &top_right_dot, &scan_x, &scan_y)) { + void *ptr; + sq_scan_shape(img, &top_right_dot, scan_x, scan_y); + if (top_right_dot.type == SHAPE_CORNER) + break; + if (top_right_dot.type != SHAPE_DOT) + goto free_borders; + border_len += 2; + ptr = realloc(top_border, border_len * sizeof(sq_point)); + if (!ptr) + goto free_borders; + top_border = ptr; + top_border[border_len - 1] = top_right_dot.center; + set_middle_point(&top_border[border_len - 2], + &top_border[border_len - 3], + &top_border[border_len - 1]); + } + } + if (border_len < 3) + goto free_borders; + inc_x = top_border[border_len - 1].x - top_border[border_len - 3].x; + inc_y = top_border[border_len - 1].y - top_border[border_len - 3].y; + border_len += 3; + ptr = realloc(top_border, border_len * sizeof(sq_point)); + if (!ptr) + goto free_borders; + top_border = ptr; + top_border[border_len - 3].x = top_border[border_len - 4].x + 0.5 * inc_x; + top_border[border_len - 3].y = top_border[border_len - 4].y + 0.5 * inc_y; + top_border[border_len - 2].x = top_border[border_len - 4].x + inc_x; + top_border[border_len - 2].y = top_border[border_len - 4].y + inc_y; + top_border[border_len - 1].x = top_border[border_len - 4].x + 1.5 * inc_x; + top_border[border_len - 1].y = top_border[border_len - 4].y + 1.5 * inc_y; + + left_border = malloc(border_len * sizeof(sq_point)); + if (!left_border) + goto free_borders; + left_border[0] = top_border[0]; + + bottom_left_dot = top_left_dot; + cur_len = 1; + while (find_bottom_dot(img, &bottom_left_dot, &scan_x, &scan_y)) { + sq_scan_shape(img, &bottom_left_dot, scan_x, scan_y); + if (bottom_left_dot.type == SHAPE_CORNER) + break; + if (bottom_left_dot.type != SHAPE_DOT) + goto free_borders; + cur_len += 2; + if (cur_len > border_len) + goto free_borders; + left_border[cur_len - 1] = bottom_left_dot.center; + set_middle_point(&left_border[cur_len - 2], &left_border[cur_len - 3], + &left_border[cur_len - 1]); + } + if (cur_len != border_len - 3 || bottom_left_dot.type != SHAPE_CORNER) + goto free_borders; + inc_x = left_border[cur_len - 1].x - left_border[cur_len - 3].x; + inc_y = left_border[cur_len - 1].y - left_border[cur_len - 3].y; + left_border[border_len - 3].x = left_border[border_len - 4].x + 0.5 * inc_x; + left_border[border_len - 3].y = left_border[border_len - 4].y + 0.5 * inc_y; + left_border[border_len - 2].x = left_border[border_len - 4].x + inc_x; + left_border[border_len - 2].y = left_border[border_len - 4].y + inc_y; + left_border[border_len - 1].x = left_border[border_len - 4].x + 1.5 * inc_x; + left_border[border_len - 1].y = left_border[border_len - 4].y + 1.5 * inc_y; + + right_border = malloc(border_len * sizeof(sq_point)); + if (!right_border) + goto free_borders; + + bottom_right_dot = top_right_dot; + cur_len = 3; + while (find_bottom_dot(img, &bottom_right_dot, &scan_x, &scan_y)) { + sq_scan_shape(img, &bottom_right_dot, scan_x, scan_y); + if (bottom_right_dot.type != SHAPE_DOT) + goto free_borders; + if (cur_len == 3) { + cur_len++; + if (cur_len > border_len) + goto free_borders; + right_border[cur_len - 1] = bottom_right_dot.center; + } else { + cur_len += 2; + if (cur_len > border_len) + goto free_borders; + right_border[cur_len - 1] = bottom_right_dot.center; + set_middle_point(&right_border[cur_len - 2], + &right_border[cur_len - 3], + &right_border[cur_len - 1]); + } + } + if (cur_len != border_len || border_len < 6) + return 1; + inc_x = right_border[5].x - right_border[3].x; + inc_y = right_border[5].y - right_border[3].y; + right_border[2].x = right_border[3].x - 0.5 * inc_x; + right_border[2].y = right_border[3].y - 0.5 * inc_y; + right_border[1].x = right_border[3].x - inc_x; + right_border[1].y = right_border[3].y - inc_y; + right_border[0].x = right_border[3].x - 1.5 * inc_x; + right_border[0].y = right_border[3].y - 1.5 * inc_y; + + bottom_border = malloc(border_len * sizeof(sq_point)); + if (!bottom_border) + goto free_borders; + bottom_border[border_len - 1] = right_border[border_len - 1]; + + bottom_left2_dot = bottom_right_dot; + offset = border_len - 1; + while (find_left_dot(img, &bottom_left2_dot, &scan_x, &scan_y)) { + sq_scan_shape(img, &bottom_left2_dot, scan_x, scan_y); + if (bottom_left2_dot.type == SHAPE_CORNER) + break; + if (bottom_left2_dot.type != SHAPE_DOT) + goto free_borders; + if (offset < 2) + goto free_borders; + offset -= 2; + bottom_border[offset] = bottom_left2_dot.center; + set_middle_point(&bottom_border[offset + 1], &bottom_border[offset], + &bottom_border[offset + 2]); + } + if (offset != 3 || bottom_left2_dot.type != SHAPE_CORNER) + goto free_borders; + inc_x = bottom_border[5].x - bottom_border[3].x; + inc_y = bottom_border[5].y - bottom_border[3].y; + bottom_border[2].x = bottom_border[3].x - 0.5 * inc_x; + bottom_border[2].y = bottom_border[3].y - 0.5 * inc_y; + bottom_border[1].x = bottom_border[3].x - inc_x; + bottom_border[1].y = bottom_border[3].y - inc_y; + bottom_border[0].x = bottom_border[3].x - 1.5 * inc_x; + bottom_border[0].y = bottom_border[3].y - 1.5 * inc_y; + + /* Size check */ + if (border_len < 8 + 2 * (1 + 2) || border_len > 65535) + goto free_borders; + bit_side_len = border_len - 2 * (1 + 2); + + bit_len = bit_side_len * bit_side_len; + if (bit_len % 8) + goto free_borders; + byte_len = bit_len / 8; + buf = calloc(byte_len, sizeof(char)); + if (!buf) + goto free_borders; + + idx = 0; + for (y = 3; y <= border_len - 4; y++) { + unsigned x; + for (x = 3; x <= border_len - 4; x++) { + unsigned char bottom_right_color, mixed_color; + float bottom_weight = y / (float)(border_len - 1); + float top_weight = 1 - bottom_weight; + float right_weight = x / (float)(border_len - 1); + float left_weight = 1 - right_weight; + + sq_point top_left_source = { + top_border[x].x + left_border[y].x - left_border[0].x, + top_border[x].y + left_border[y].y - left_border[0].y + }; + + sq_point bottom_right_source = { + bottom_border[x].x + right_border[y].x - + right_border[border_len - 1].x, + bottom_border[x].y + right_border[y].y - + right_border[border_len - 1].y + }; + + const unsigned char *data = img->data; + unsigned sample_x = top_left_source.x; + unsigned sample_y = top_left_source.y; + unsigned char top_left_color = + data[sample_y * img->width + sample_x]; + sample_x = bottom_right_source.x; + sample_y = bottom_right_source.y; + bottom_right_color = data[sample_y * img->width + sample_x]; + + mixed_color = + ((top_weight + left_weight) * top_left_color + + (bottom_weight + right_weight) * bottom_right_color) / + 2; + + if (is_black_color(mixed_color)) + buf[idx / 8] |= 1 << 7 - idx % 8; + idx++; + } + } + error = sq_extract_text(iscn, buf, byte_len); + free(buf); + +free_borders: + free(top_border); + free(left_border); + free(right_border); + free(bottom_border); + return error ? 1 : 0; +} diff --git a/zbar/sqcode.h b/zbar/sqcode.h new file mode 100644 index 0000000..b3f5430 --- /dev/null +++ b/zbar/sqcode.h @@ -0,0 +1,21 @@ +/*Copyright (C) 2018 Javier Serrano Polo <javier@jasp.net> + You can redistribute this library and/or modify it under the terms of the + GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) any later + version.*/ +#ifndef _SQCODE_H_ +#define _SQCODE_H_ + +#include <zbar.h> + +typedef struct sq_reader sq_reader; + +sq_reader *_zbar_sq_create(void); +void _zbar_sq_destroy(sq_reader *reader); +void _zbar_sq_reset(sq_reader *reader); + +int _zbar_sq_new_config(sq_reader *reader, unsigned config); +int _zbar_sq_decode(sq_reader *reader, zbar_image_scanner_t *iscn, + zbar_image_t *img); + +#endif diff --git a/zbar/svg.c b/zbar/svg.c new file mode 100644 index 0000000..f4788dd --- /dev/null +++ b/zbar/svg.c @@ -0,0 +1,187 @@ +/*------------------------------------------------------------------------ + * Copyright 2009 (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 <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +#include "svg.h" + +static const char svg_head[] = + "<?xml version='1.0'?>\n" + "<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN'" + " 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>\n" + "<svg version='1.1' id='top' width='8in' height='8in'" + " preserveAspectRatio='xMidYMid' overflow='visible'" + " viewBox='%g,%g %g,%g' xmlns:xlink='http://www.w3.org/1999/xlink'" + " xmlns='http://www.w3.org/2000/svg'>\n" + "<defs><style type='text/css'><![CDATA[\n" + "* { image-rendering: optimizeSpeed }\n" + "image { opacity: .9 }\n" + "path, line, circle { fill: none; stroke-width: .5;" + " stroke-linejoin: round; stroke-linecap: butt;" + " stroke-opacity: .666; fill-opacity: .666 }\n" + /*"#hedge line, #vedge line { stroke: #34f }\n"*/ + "#hedge, #vedge { stroke: yellow }\n" + "#target * { fill: none; stroke: #f24 }\n" + "#mdot * { fill: #e2f; stroke: none }\n" + "#ydot * { fill: yellow; stroke: none }\n" + "#cross * { stroke: #44f }\n" + ".hedge { marker: url(#hedge) }\n" + ".vedge { marker: url(#vedge) }\n" + ".scanner .hedge, .scanner .vedge { stroke-width: 8 }\n" + ".finder .hedge, .finder .vedge { /*stroke: #a0c;*/ stroke-width: .9 }\n" + ".cluster { stroke: #a0c; stroke-width: 1; stroke-opacity: .45 }\n" + ".cluster.valid { stroke: #c0a; stroke-width: 1; stroke-opacity: .666 }\n" + ".h.cluster { marker: url(#vedge) }\n" + ".v.cluster { marker: url(#hedge) }\n" + ".centers { marker: url(#target); stroke-width: 3 }\n" + ".edge-pts { marker: url(#ydot); stroke-width: .5 }\n" + ".align { marker: url(#mdot); stroke-width: 1.5 }\n" + ".sampling-grid { stroke-width: .75; marker: url(#cross) }\n" + "]]></style>\n" + "<marker id='hedge' overflow='visible'><line x1='-2' x2='2'/></marker>\n" + "<marker id='vedge' overflow='visible'><line y1='-2' y2='2'/></marker>\n" + "<marker id='ydot' overflow='visible'><circle r='2'/></marker>\n" + "<marker id='mdot' overflow='visible'><circle r='2'/></marker>\n" + "<marker id='cross' overflow='visible'><path d='M-2,0h4 M0,-2v4'/></marker>\n" + "<marker id='target' overflow='visible'><path d='M-4,0h8 M0,-4v8'/><circle r='2'/></marker>\n" + "</defs>\n"; + +static FILE *svg = NULL; + +void svg_open(const char *name, double x, double y, double w, double h) +{ + svg = fopen(name, "w"); + if (!svg) + return; + + /* FIXME calc scaled size */ + fprintf(svg, svg_head, x, y, w, h); +} + +void svg_close() +{ + if (!svg) + return; + fprintf(svg, "</svg>\n"); + fclose(svg); + svg = NULL; +} + +void svg_commentf(const char *format, ...) +{ + if (!svg) + return; + fprintf(svg, "<!-- "); + va_list args; + va_start(args, format); + vfprintf(svg, format, args); + va_end(args); + fprintf(svg, " -->\n"); +} + +void svg_image(const char *name, double width, double height) +{ + if (!svg) + return; + fprintf(svg, "<image width='%g' height='%g' xlink:href='%s'/>\n", width, + height, name); +} + +void svg_group_start(const char *cls, double deg, double sx, double sy, + double x, double y) +{ + if (!svg) + return; + fprintf(svg, "<g class='%s'", cls); + if (sx != 1. || sy != 1 || deg || x || y) { + fprintf(svg, " transform='"); + if (deg) + fprintf(svg, "rotate(%g)", deg); + if (x || y) + fprintf(svg, "translate(%g,%g)", x, y); + if (sx != 1. || sy != 1.) { + if (!sy) + fprintf(svg, "scale(%g)", sx); + else + fprintf(svg, "scale(%g,%g)", sx, sy); + } + fprintf(svg, "'"); + } + fprintf(svg, ">\n"); +} + +void svg_group_end() +{ + fprintf(svg, "</g>\n"); +} + +void svg_path_start(const char *cls, double scale, double x, double y) +{ + if (!svg) + return; + fprintf(svg, "<path class='%s'", cls); + if (scale != 1. || x || y) { + fprintf(svg, " transform='"); + if (x || y) + fprintf(svg, "translate(%g,%g)", x, y); + if (scale != 1.) + fprintf(svg, "scale(%g)", scale); + fprintf(svg, "'"); + } + fprintf(svg, " d='"); +} + +void svg_path_end() +{ + if (!svg) + return; + fprintf(svg, "'/>\n"); +} + +void svg_path_close() +{ + if (!svg) + return; + fprintf(svg, "z"); +} + +void svg_path_moveto(svg_absrel_t abs, double x, double y) +{ + if (!svg) + return; + fprintf(svg, " %c%g,%g", (abs) ? 'M' : 'm', x, y); +} + +void svg_path_lineto(svg_absrel_t abs, double x, double y) +{ + if (!svg) + return; + if (x && y) + fprintf(svg, "%c%g,%g", (abs) ? 'L' : 'l', x, y); + else if (x) + fprintf(svg, "%c%g", (abs) ? 'H' : 'h', x); + else if (y) + fprintf(svg, "%c%g", (abs) ? 'V' : 'v', y); +} diff --git a/zbar/svg.h b/zbar/svg.h new file mode 100644 index 0000000..0908deb --- /dev/null +++ b/zbar/svg.h @@ -0,0 +1,68 @@ +/*------------------------------------------------------------------------ + * Copyright 2009 (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 + *------------------------------------------------------------------------*/ +#ifndef _SVG_H_ +#define _SVG_H_ + +#ifdef DEBUG_SVG + +typedef enum +{ + SVG_REL, + SVG_ABS +} svg_absrel_t; + +void svg_open(const char *name, double x, double y, double w, double h); +void svg_close(void); + +void svg_commentf(const char *format, ...); +void svg_image(const char *name, double width, double height); + +void svg_group_start(const char *cls, double rotate, double scalex, + double scaley, double x, double y); +void svg_group_end(void); + +void svg_path_start(const char *cls, double scale, double x, double y); +void svg_path_end(void); +void svg_path_close(void); +void svg_path_moveto(svg_absrel_t abs, double x, double y); +void svg_path_lineto(svg_absrel_t abs, double x, double y); + +#else + +#define svg_open(...) +#define svg_close(...) + +#define svg_image(...) + +#define svg_group_start(...) +#define svg_group_end(...) + +#define svg_path_start(...) +#define svg_path_end(...) +#define svg_path_moveto(...) +#define svg_path_lineto(...) +#define svg_path_close(...) + +#endif + +#endif diff --git a/zbar/symbol.c b/zbar/symbol.c new file mode 100644 index 0000000..a518435 --- /dev/null +++ b/zbar/symbol.c @@ -0,0 +1,520 @@ +/*------------------------------------------------------------------------ + * 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" +#include <assert.h> +#include <stdio.h> +#include <string.h> + +#include <zbar.h> +#include "symbol.h" + +const char *zbar_get_symbol_name(zbar_symbol_type_t sym) +{ + switch (sym & ZBAR_SYMBOL) { + case ZBAR_EAN2: + return ("EAN-2"); + case ZBAR_EAN5: + return ("EAN-5"); + case ZBAR_EAN8: + return ("EAN-8"); + case ZBAR_UPCE: + return ("UPC-E"); + case ZBAR_ISBN10: + return ("ISBN-10"); + case ZBAR_UPCA: + return ("UPC-A"); + case ZBAR_EAN13: + return ("EAN-13"); + case ZBAR_ISBN13: + return ("ISBN-13"); + case ZBAR_COMPOSITE: + return ("COMPOSITE"); + case ZBAR_I25: + return ("I2/5"); + case ZBAR_DATABAR: + return ("DataBar"); + case ZBAR_DATABAR_EXP: + return ("DataBar-Exp"); + case ZBAR_CODABAR: + return ("Codabar"); + case ZBAR_CODE39: + return ("CODE-39"); + case ZBAR_CODE93: + return ("CODE-93"); + case ZBAR_CODE128: + return ("CODE-128"); + case ZBAR_PDF417: + return ("PDF417"); + case ZBAR_QRCODE: + return ("QR-Code"); + case ZBAR_SQCODE: + return ("SQ-Code"); + default: + return ("UNKNOWN"); + } +} + +const char *zbar_get_addon_name(zbar_symbol_type_t sym) +{ + return (""); +} + +const char *zbar_get_config_name(zbar_config_t cfg) +{ + switch (cfg) { + case ZBAR_CFG_ENABLE: + return ("ENABLE"); + case ZBAR_CFG_ADD_CHECK: + return ("ADD_CHECK"); + case ZBAR_CFG_EMIT_CHECK: + return ("EMIT_CHECK"); + case ZBAR_CFG_ASCII: + return ("ASCII"); + case ZBAR_CFG_BINARY: + return ("BINARY"); + case ZBAR_CFG_MIN_LEN: + return ("MIN_LEN"); + case ZBAR_CFG_MAX_LEN: + return ("MAX_LEN"); + case ZBAR_CFG_UNCERTAINTY: + return ("UNCERTAINTY"); + case ZBAR_CFG_POSITION: + return ("POSITION"); + case ZBAR_CFG_X_DENSITY: + return ("X_DENSITY"); + case ZBAR_CFG_Y_DENSITY: + return ("Y_DENSITY"); + default: + return (""); + } +} + +const char *zbar_get_modifier_name(zbar_modifier_t mod) +{ + switch (mod) { + case ZBAR_MOD_GS1: + return ("GS1"); + case ZBAR_MOD_AIM: + return ("AIM"); + default: + return (""); + } +} + +const char *zbar_get_orientation_name(zbar_orientation_t orient) +{ + switch (orient) { + case ZBAR_ORIENT_UP: + return ("UP"); + case ZBAR_ORIENT_RIGHT: + return ("RIGHT"); + case ZBAR_ORIENT_DOWN: + return ("DOWN"); + case ZBAR_ORIENT_LEFT: + return ("LEFT"); + default: + return ("UNKNOWN"); + } +} + +#ifndef _MSC_VER +static const signed char _zbar_symbol_hash[ZBAR_CODE128 + 1] = { + [0 ... ZBAR_CODE128] = -1, + + /* [ZBAR_FOO] = 0, is empty */ + [ZBAR_SQCODE] = 1, + [ZBAR_CODE128] = 2, + [ZBAR_EAN13] = 3, + [ZBAR_UPCA] = 4, + [ZBAR_EAN8] = 5, + [ZBAR_UPCE] = 6, + [ZBAR_ISBN13] = 7, + [ZBAR_ISBN10] = 8, + [ZBAR_CODE39] = 9, + [ZBAR_I25] = 10, + [ZBAR_PDF417] = 11, + [ZBAR_QRCODE] = 12, + [ZBAR_DATABAR] = 13, + [ZBAR_DATABAR_EXP] = 14, + [ZBAR_CODE93] = 15, + [ZBAR_EAN2] = 16, + [ZBAR_EAN5] = 17, + [ZBAR_COMPOSITE] = 18, + [ZBAR_CODABAR] = 19, + + /* Please update NUM_SYMS accordingly */ +}; + +static const signed char *_init_hash() +{ + return _zbar_symbol_hash; +}; +#else +/* + * Needed By Microsoft C. Even on Visual Studio 2019, C99 designated + * identifiers aren't supported! So, we need this hack. + */ +static const signed char *_init_hash() +{ + static signed char hash[ZBAR_CODE128 + 1] = { -1 }; + static int was_initialized = 0; + + if (was_initialized) + return (const signed char *)hash; + + memset(hash, -1, sizeof(hash)); + + /* Keep in sync with the C99 implementation */ + hash[ZBAR_SQCODE] = 1, hash[ZBAR_CODE128] = 2, hash[ZBAR_EAN13] = 3, + hash[ZBAR_UPCA] = 4, hash[ZBAR_EAN8] = 5, hash[ZBAR_UPCE] = 6, + hash[ZBAR_ISBN13] = 7, hash[ZBAR_ISBN10] = 8, hash[ZBAR_CODE39] = 9, + hash[ZBAR_I25] = 10, hash[ZBAR_PDF417] = 11, hash[ZBAR_QRCODE] = 12, + hash[ZBAR_DATABAR] = 13, hash[ZBAR_DATABAR_EXP] = 14, + hash[ZBAR_CODE93] = 15, hash[ZBAR_EAN2] = 16, hash[ZBAR_EAN5] = 17, + hash[ZBAR_COMPOSITE] = 18, hash[ZBAR_CODABAR] = 19; + + was_initialized = 1; + + return (const signed char *)hash; +}; +#endif + +int _zbar_get_symbol_hash(zbar_symbol_type_t sym) +{ + int h; + const signed char *hash = _init_hash(); + + assert(sym >= ZBAR_PARTIAL && sym <= ZBAR_CODE128); + + h = hash[sym]; + assert(h >= 0 && h < NUM_SYMS); + + return h; +} + +void _zbar_symbol_free(zbar_symbol_t *sym) +{ + if (sym->syms) { + zbar_symbol_set_ref(sym->syms, -1); + sym->syms = NULL; + } + if (sym->pts) + free(sym->pts); + if (sym->data_alloc && sym->data) + free(sym->data); + free(sym); +} + +void zbar_symbol_ref(const zbar_symbol_t *sym, int refs) +{ + zbar_symbol_t *ncsym = (zbar_symbol_t *)sym; + _zbar_symbol_refcnt(ncsym, refs); +} + +zbar_symbol_type_t zbar_symbol_get_type(const zbar_symbol_t *sym) +{ + return (sym->type); +} + +unsigned int zbar_symbol_get_configs(const zbar_symbol_t *sym) +{ + return (sym->configs); +} + +unsigned int zbar_symbol_get_modifiers(const zbar_symbol_t *sym) +{ + return (sym->modifiers); +} + +const char *zbar_symbol_get_data(const zbar_symbol_t *sym) +{ + return (sym->data); +} + +unsigned int zbar_symbol_get_data_length(const zbar_symbol_t *sym) +{ + return (sym->datalen); +} + +int zbar_symbol_get_count(const zbar_symbol_t *sym) +{ + return (sym->cache_count); +} + +int zbar_symbol_get_quality(const zbar_symbol_t *sym) +{ + return (sym->quality); +} + +unsigned zbar_symbol_get_loc_size(const zbar_symbol_t *sym) +{ + return (sym->npts); +} + +int zbar_symbol_get_loc_x(const zbar_symbol_t *sym, unsigned idx) +{ + if (idx < sym->npts) + return (sym->pts[idx].x); + else + return (-1); +} + +int zbar_symbol_get_loc_y(const zbar_symbol_t *sym, unsigned idx) +{ + if (idx < sym->npts) + return (sym->pts[idx].y); + else + return (-1); +} + +zbar_orientation_t zbar_symbol_get_orientation(const zbar_symbol_t *sym) +{ + return (sym->orient); +} + +const zbar_symbol_t *zbar_symbol_next(const zbar_symbol_t *sym) +{ + return ((sym) ? sym->next : NULL); +} + +const zbar_symbol_set_t *zbar_symbol_get_components(const zbar_symbol_t *sym) +{ + return (sym->syms); +} + +const zbar_symbol_t *zbar_symbol_first_component(const zbar_symbol_t *sym) +{ + return ((sym && sym->syms) ? sym->syms->head : NULL); +} + +unsigned base64_encode(char *dst, const char *src, unsigned srclen) +{ + static const char alphabet[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + char *start = dst; + int nline = 19; + for (; srclen; srclen -= 3) { + unsigned int buf = *(src++) << 16; + if (srclen > 1) + buf |= *(src++) << 8; + if (srclen > 2) + buf |= *(src++); + *(dst++) = alphabet[(buf >> 18) & 0x3f]; + *(dst++) = alphabet[(buf >> 12) & 0x3f]; + *(dst++) = (srclen > 1) ? alphabet[(buf >> 6) & 0x3f] : '='; + *(dst++) = (srclen > 2) ? alphabet[buf & 0x3f] : '='; + if (srclen < 3) + break; + if (!--nline) { + *(dst++) = '\n'; + nline = 19; + } + } + *(dst++) = '\n'; + *(dst++) = '\0'; + return (dst - start - 1); +} + +enum +{ + TMPL_START, + TMPL_MOD_START, + TMPL_MOD_ITEM, + TMPL_MOD_END, + TMPL_COUNT, + TMPL_DATA_START, + TMPL_FORMAT, + TMPL_CDATA, + TMPL_NL, + TMPL_END, +}; + +/* FIXME should be big enough to store XML extra data */ +#define MAX_STATIC (1 << 16) + +#define MAX_MOD (5 * ZBAR_MOD_NUM) +#define MAX_CFG (10 * ZBAR_CFG_NUM) +#define MAX_INT_DIGITS 10 + +#define TMPL_COPY(t) \ + do { \ + static const char *_st = (t); \ + i = strlen(_st); \ + memcpy(*buf + n, _st, i + 1); \ + n += i; \ + assert(n <= maxlen); \ + } while (0) + +#define TMPL_FMT(t, ...) \ + do { \ + static const char *_st = (t); \ + i = snprintf(*buf + n, maxlen - n, _st, __VA_ARGS__); \ + assert(i > 0); \ + n += i; \ + assert(n <= maxlen); \ + } while (0) + +char *zbar_symbol_xml(const zbar_symbol_t *sym, char **buf, unsigned *len) +{ + unsigned int mods, cfgs; + unsigned int datalen, maxlen; + int i, n = 0, p; + + const char *type = zbar_get_symbol_name(sym->type); + const char *orient = zbar_get_orientation_name(sym->orient); + + /* check for binary data */ + unsigned char *data = (unsigned char *)sym->data; + char binary = ((data[0] == 0xff && data[1] == 0xfe) || + (data[0] == 0xfe && data[1] == 0xff) || + !strncmp(sym->data, "<?xml", 5)); + for (i = 0; !binary && i < sym->datalen; i++) { + unsigned char c = sym->data[i]; + binary = ((c < 0x20 && ((~0x00002600 >> c) & 1)) || + (c >= 0x7f && c < 0xa0) || + (c == ']' && i + 2 < sym->datalen && + sym->data[i + 1] == ']' && sym->data[i + 2] == '>')); + } + + datalen = strlen(sym->data); + if (binary) + datalen = (sym->datalen + 2) / 3 * 4 + sym->datalen / 57 + 3; + + maxlen = (MAX_STATIC + strlen(type) + strlen(orient) + datalen + + MAX_INT_DIGITS + 1); + mods = sym->modifiers; + if (mods) + maxlen += MAX_MOD; + cfgs = sym->configs & ~(1 << ZBAR_CFG_ENABLE); + if (cfgs) + maxlen += MAX_CFG; + if (binary) + maxlen += MAX_INT_DIGITS; + + if (!*buf || (*len < maxlen)) { + if (*buf) + free(*buf); + *buf = malloc(maxlen); + /* FIXME check OOM */ + *len = maxlen; + } + + TMPL_FMT("<symbol type='%s' quality='%d' orientation='%s'", type, + sym->quality, orient); + + if (mods) { + int j; + TMPL_COPY(" modifiers='"); + for (j = 0; mods && j < ZBAR_MOD_NUM; j++, mods >>= 1) + if (mods & 1) + TMPL_FMT("%s ", zbar_get_modifier_name(j)); + /* cleanup trailing space */ + n--; + TMPL_COPY("'"); + } + + if (cfgs) { + int j; + TMPL_COPY(" configs='"); + for (j = 0; cfgs && j < ZBAR_CFG_NUM; j++, cfgs >>= 1) + if (cfgs & 1) + TMPL_FMT("%s ", zbar_get_config_name(j)); + /* cleanup trailing space */ + n--; + TMPL_COPY("'"); + } + + if (sym->cache_count) + TMPL_FMT(" count='%d'", sym->cache_count); + + TMPL_COPY("><polygon points='"); + if (sym->npts > 0 ) + TMPL_FMT("%+d,%+d", sym->pts[0].x, sym->pts[0].y); + for(p = 1; p < sym->npts; p++) + TMPL_FMT(" %+d,%+d", sym->pts[p].x, sym->pts[p].y); + + TMPL_COPY("'/><data"); + if (binary) + TMPL_FMT(" format='base64' length='%d'", sym->datalen); + TMPL_COPY("><![CDATA["); + + if (!binary) { + memcpy(*buf + n, sym->data, sym->datalen + 1); + n += sym->datalen; + } else { + TMPL_COPY("\n"); + n += base64_encode(*buf + n, sym->data, sym->datalen); + } + assert(n <= maxlen); + + TMPL_COPY("]]></data></symbol>"); + + *len = n; + return (*buf); +} + +zbar_symbol_set_t *_zbar_symbol_set_create() +{ + zbar_symbol_set_t *syms = calloc(1, sizeof(*syms)); + _zbar_refcnt(&syms->refcnt, 1); + return (syms); +} + +inline void _zbar_symbol_set_free(zbar_symbol_set_t *syms) +{ + zbar_symbol_t *sym, *next; + for (sym = syms->head; sym; sym = next) { + next = sym->next; + sym->next = NULL; + _zbar_symbol_refcnt(sym, -1); + } + syms->head = NULL; + free(syms); +} + +void zbar_symbol_set_ref(const zbar_symbol_set_t *syms, int delta) +{ + zbar_symbol_set_t *ncsyms = (zbar_symbol_set_t *)syms; + if (!_zbar_refcnt(&ncsyms->refcnt, delta) && delta <= 0) + _zbar_symbol_set_free(ncsyms); +} + +int zbar_symbol_set_get_size(const zbar_symbol_set_t *syms) +{ + return (syms->nsyms); +} + +const zbar_symbol_t *zbar_symbol_set_first_symbol(const zbar_symbol_set_t *syms) +{ + zbar_symbol_t *sym = syms->tail; + if (sym) + return (sym->next); + return (syms->head); +} + +const zbar_symbol_t * +zbar_symbol_set_first_unfiltered(const zbar_symbol_set_t *syms) +{ + return (syms->head); +} diff --git a/zbar/symbol.h b/zbar/symbol.h new file mode 100644 index 0000000..5ea97a3 --- /dev/null +++ b/zbar/symbol.h @@ -0,0 +1,96 @@ +/*------------------------------------------------------------------------ + * 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 + *------------------------------------------------------------------------*/ +#ifndef _SYMBOL_H_ +#define _SYMBOL_H_ + +#include <stdlib.h> +#include <zbar.h> +#include "refcnt.h" + +#define NUM_SYMS 20 + +typedef struct point_s { + int x, y; +} point_t; + +struct zbar_symbol_set_s { + refcnt_t refcnt; + int nsyms; /* number of filtered symbols */ + zbar_symbol_t *head; /* first of decoded symbol results */ + zbar_symbol_t *tail; /* last of unfiltered symbol results */ +}; + +struct zbar_symbol_s { + zbar_symbol_type_t type; /* symbol type */ + unsigned int configs; /* symbology boolean config bitmask */ + unsigned int modifiers; /* symbology modifier bitmask */ + unsigned int data_alloc; /* allocation size of data */ + unsigned int datalen; /* length of binary symbol data */ + char *data; /* symbol data */ + + unsigned pts_alloc; /* allocation size of pts */ + unsigned npts; /* number of points in location polygon */ + point_t *pts; /* list of points in location polygon */ + zbar_orientation_t orient; /* coarse orientation */ + + refcnt_t refcnt; /* reference count */ + zbar_symbol_t *next; /* linked list of results (or siblings) */ + zbar_symbol_set_t *syms; /* components of composite result */ + unsigned long time; /* relative symbol capture time */ + int cache_count; /* cache state */ + int quality; /* relative symbol reliability metric */ +}; + +extern int _zbar_get_symbol_hash(zbar_symbol_type_t); + +extern void _zbar_symbol_free(zbar_symbol_t *); + +extern zbar_symbol_set_t *_zbar_symbol_set_create(void); +extern void _zbar_symbol_set_free(zbar_symbol_set_t *); + +static inline void sym_add_point(zbar_symbol_t *sym, int x, int y) +{ + int i = sym->npts; + if (++sym->npts >= sym->pts_alloc) + sym->pts = realloc(sym->pts, ++sym->pts_alloc * sizeof(point_t)); + sym->pts[i].x = x; + sym->pts[i].y = y; +} + +static inline void _zbar_symbol_refcnt(zbar_symbol_t *sym, int delta) +{ + if (!_zbar_refcnt(&sym->refcnt, delta) && delta <= 0) + _zbar_symbol_free(sym); +} + +static inline void _zbar_symbol_set_add(zbar_symbol_set_t *syms, + zbar_symbol_t *sym) +{ + sym->next = syms->head; + syms->head = sym; + syms->nsyms++; + + _zbar_symbol_refcnt(sym, 1); +} + +#endif diff --git a/zbar/thread.h b/zbar/thread.h new file mode 100644 index 0000000..56a9828 --- /dev/null +++ b/zbar/thread.h @@ -0,0 +1,123 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (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 + *------------------------------------------------------------------------*/ +#ifndef _ZBAR_THREAD_H_ +#define _ZBAR_THREAD_H_ + +/* simple platform thread abstraction + */ + +#include "config.h" +#include "event.h" + +#if defined(_WIN32) + +#include <windows.h> +#define HAVE_THREADS +#define ZTHREAD DWORD WINAPI + +typedef DWORD(WINAPI zbar_thread_proc_t)(void *); + +typedef DWORD zbar_thread_id_t; + +#elif defined(HAVE_LIBPTHREAD) + +#include <pthread.h> +#include <signal.h> +#define HAVE_THREADS +#define ZTHREAD void * + +typedef ZTHREAD(zbar_thread_proc_t)(void *); + +typedef pthread_t zbar_thread_id_t; + +#else + +#undef HAVE_THREADS +#undef ZTHREAD + +typedef void zbar_thread_proc_t; +typedef int zbar_thread_id_t; + +#endif + +typedef struct zbar_thread_s { + zbar_thread_id_t tid; + int started, running; + zbar_event_t notify, activity; +} zbar_thread_t; + +#if defined(_WIN32) + +static inline void _zbar_thread_init(zbar_thread_t *thr) +{ + thr->running = 1; + _zbar_event_trigger(&thr->activity); +} + +static inline zbar_thread_id_t _zbar_thread_self() +{ + return (GetCurrentThreadId()); +} + +static inline int _zbar_thread_is_self(zbar_thread_id_t tid) +{ + return (tid == GetCurrentThreadId()); +} + +#elif defined(HAVE_LIBPTHREAD) + +static inline void _zbar_thread_init(zbar_thread_t *thr) +{ + sigset_t sigs; + sigfillset(&sigs); + pthread_sigmask(SIG_BLOCK, &sigs, NULL); + thr->running = 1; + _zbar_event_trigger(&thr->activity); +} + +static inline zbar_thread_id_t _zbar_thread_self(void) +{ + return (pthread_self()); +} + +static inline int _zbar_thread_is_self(zbar_thread_id_t tid) +{ + return (pthread_equal(tid, pthread_self())); +} + +#else + +#define _zbar_thread_start(...) -1 +#define _zbar_thread_stop(...) 0 +#define _zbar_thread_self(...) 0 +#define _zbar_thread_is_self(...) 1 + +#endif + +#ifdef HAVE_THREADS +extern int _zbar_thread_start(zbar_thread_t *, zbar_thread_proc_t *, void *, + zbar_mutex_t *); +extern int _zbar_thread_stop(zbar_thread_t *, zbar_mutex_t *); +#endif + +#endif diff --git a/zbar/timer.h b/zbar/timer.h new file mode 100644 index 0000000..95f3195 --- /dev/null +++ b/zbar/timer.h @@ -0,0 +1,146 @@ +/*------------------------------------------------------------------------ + * 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 + *------------------------------------------------------------------------*/ +#ifndef _ZBAR_TIMER_H_ +#define _ZBAR_TIMER_H_ + +#include <time.h> +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> /* gettimeofday */ +#endif + +/* platform timer abstraction + * + * zbar_timer_t stores the absolute expiration of a delay from + * when the timer was initialized. + * + * _zbar_timer_init() initialized timer with specified ms delay. + * returns timer or NULL if timeout < 0 (no/infinite timeout) + * _zbar_timer_check() returns ms remaining until expiration. + * will be <= 0 if timer has expired + */ + +#if _POSIX_TIMERS > 0 + +typedef struct timespec zbar_timer_t; + +static inline int _zbar_timer_now() +{ + struct timespec now; + clock_gettime(CLOCK_REALTIME, &now); + return (now.tv_sec * 1000 + now.tv_nsec / 1000000); +} + +static inline zbar_timer_t *_zbar_timer_init(zbar_timer_t *timer, int delay) +{ + if (delay < 0) + return (NULL); + + clock_gettime(CLOCK_REALTIME, timer); + timer->tv_nsec += (delay % 1000) * 1000000; + timer->tv_sec += (delay / 1000) + (timer->tv_nsec / 1000000000); + timer->tv_nsec %= 1000000000; + return (timer); +} + +static inline int _zbar_timer_check(zbar_timer_t *timer) +{ + struct timespec now; + int delay; + if (!timer) + return (-1); + + clock_gettime(CLOCK_REALTIME, &now); + delay = ((timer->tv_sec - now.tv_sec) * 1000 + + (timer->tv_nsec - now.tv_nsec) / 1000000); + return ((delay >= 0) ? delay : 0); +} + +#elif defined(_WIN32) + +#include <windows.h> + +typedef DWORD zbar_timer_t; + +static inline int _zbar_timer_now() +{ + return (timeGetTime()); +} + +static inline zbar_timer_t *_zbar_timer_init(zbar_timer_t *timer, int delay) +{ + if (delay < 0) + return (NULL); + + *timer = timeGetTime() + delay; + return (timer); +} + +static inline int _zbar_timer_check(zbar_timer_t *timer) +{ + int delay; + if (!timer) + return (INFINITE); + + delay = *timer - timeGetTime(); + return ((delay >= 0) ? delay : 0); +} + +#elif defined(HAVE_SYS_TIME_H) + +typedef struct timeval zbar_timer_t; + +static inline int _zbar_timer_now() +{ + struct timeval now; + gettimeofday(&now, NULL); + return (now.tv_sec * 1000 + now.tv_usec / 1000); +} + +static inline zbar_timer_t *_zbar_timer_init(zbar_timer_t *timer, int delay) +{ + if (delay < 0) + return (NULL); + + gettimeofday(timer, NULL); + timer->tv_usec += (delay % 1000) * 1000; + timer->tv_sec += (delay / 1000) + (timer->tv_usec / 1000000); + timer->tv_usec %= 1000000; + return (timer); +} + +static inline int _zbar_timer_check(zbar_timer_t *timer) +{ + struct timeval now; + if (!timer) + return (-1); + + gettimeofday(&now, NULL); + return ((timer->tv_sec - now.tv_sec) * 1000 + + (timer->tv_usec - now.tv_usec) / 1000); +} + +#else +#error "unable to find a timer interface" +#endif + +#endif diff --git a/zbar/video.c b/zbar/video.c new file mode 100644 index 0000000..664b037 --- /dev/null +++ b/zbar/video.c @@ -0,0 +1,452 @@ +/*------------------------------------------------------------------------ + * 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 "video.h" +#include "image.h" + +#ifdef HAVE_LIBJPEG +extern struct jpeg_decompress_struct *_zbar_jpeg_decomp_create(void); +extern void _zbar_jpeg_decomp_destroy(struct jpeg_decompress_struct *cinfo); +#endif + +static void _zbar_video_recycle_image(zbar_image_t *img) +{ + zbar_video_t *vdo = img->src; + assert(vdo); + assert(img->srcidx >= 0); + video_lock(vdo); + if (vdo->images[img->srcidx] != img) + vdo->images[img->srcidx] = img; + if (vdo->active) + vdo->nq(vdo, img); + else + video_unlock(vdo); +} + +static void _zbar_video_recycle_shadow(zbar_image_t *img) +{ + zbar_video_t *vdo = img->src; + assert(vdo); + assert(img->srcidx == -1); + video_lock(vdo); + img->next = vdo->shadow_image; + vdo->shadow_image = img; + video_unlock(vdo); +} + +zbar_video_t *zbar_video_create() +{ + zbar_video_t *vdo = calloc(1, sizeof(zbar_video_t)); + int i; + if (!vdo) + return (NULL); + err_init(&vdo->err, ZBAR_MOD_VIDEO); + vdo->fd = -1; + + (void)_zbar_mutex_init(&vdo->qlock); + + /* pre-allocate images */ + vdo->num_images = ZBAR_VIDEO_IMAGES_MAX; + vdo->images = calloc(ZBAR_VIDEO_IMAGES_MAX, sizeof(zbar_image_t *)); + if (!vdo->images) { + zbar_video_destroy(vdo); + return (NULL); + } + + for (i = 0; i < ZBAR_VIDEO_IMAGES_MAX; i++) { + zbar_image_t *img = vdo->images[i] = zbar_image_create(); + if (!img) { + zbar_video_destroy(vdo); + return (NULL); + } + img->refcnt = 0; + img->cleanup = _zbar_video_recycle_image; + img->srcidx = i; + img->src = vdo; + } + + return (vdo); +} + +void zbar_video_destroy(zbar_video_t *vdo) +{ + if (vdo->intf != VIDEO_INVALID) + zbar_video_open(vdo, NULL); + if (vdo->images) { + int i; + for (i = 0; i < ZBAR_VIDEO_IMAGES_MAX; i++) + if (vdo->images[i]) + _zbar_image_free(vdo->images[i]); + free(vdo->images); + } + while (vdo->shadow_image) { + zbar_image_t *img = vdo->shadow_image; + vdo->shadow_image = img->next; + free((void *)img->data); + img->data = NULL; + free(img); + } + if (vdo->buf) + free(vdo->buf); + if (vdo->formats) + free(vdo->formats); + if (vdo->emu_formats) + free(vdo->emu_formats); + + if (vdo->free) + vdo->free(vdo); + + err_cleanup(&vdo->err); + _zbar_mutex_destroy(&vdo->qlock); + +#ifdef HAVE_LIBJPEG + if (vdo->jpeg_img) { + zbar_image_destroy(vdo->jpeg_img); + vdo->jpeg_img = NULL; + } + if (vdo->jpeg) { + _zbar_jpeg_decomp_destroy(vdo->jpeg); + vdo->jpeg = NULL; + } +#endif + free(vdo); +} + +int zbar_video_open(zbar_video_t *vdo, const char *dev) +{ + char *ldev = NULL; + int rc; + zbar_video_enable(vdo, 0); + video_lock(vdo); + if (vdo->intf != VIDEO_INVALID) { + if (vdo->cleanup) { + vdo->cleanup(vdo); + vdo->cleanup = NULL; + } + zprintf(1, "closed camera (fd=%d)\n", vdo->fd); + vdo->intf = VIDEO_INVALID; + } + video_unlock(vdo); + + if (!dev) + return (0); + + if ((unsigned char)dev[0] < 0x10) { + /* default linux device, overloaded for other platforms */ + int id = dev[0]; + dev = ldev = strdup("/dev/video0"); + ldev[10] = '0' + id; + } + + rc = _zbar_video_open(vdo, dev); + + if (ldev) + free(ldev); + return (rc); +} + +int zbar_video_get_fd(const zbar_video_t *vdo) +{ + if (vdo->intf == VIDEO_INVALID) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "video device not opened")); + if (vdo->intf != VIDEO_V4L2) + return (err_capture(vdo, SEV_WARNING, ZBAR_ERR_UNSUPPORTED, __func__, + "video driver does not support polling")); + return (vdo->fd); +} + +int zbar_video_request_size(zbar_video_t *vdo, unsigned width, unsigned height) +{ + if (vdo->initialized) + /* FIXME re-init different format? */ + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "already initialized, unable to resize")); + + vdo->width = width; + vdo->height = height; + zprintf(1, "request size: %d x %d\n", width, height); + return (0); +} + +int zbar_video_request_interface(zbar_video_t *vdo, int ver) +{ + if (vdo->intf != VIDEO_INVALID) + return ( + err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "device already opened, unable to change interface")); + vdo->intf = (video_interface_t)ver; + zprintf(1, "request interface version %d\n", vdo->intf); + return (0); +} + +int zbar_video_request_iomode(zbar_video_t *vdo, int iomode) +{ + if (vdo->intf != VIDEO_INVALID) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "device already opened, unable to change iomode")); + if (iomode < 0 || iomode > VIDEO_USERPTR) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "invalid iomode requested")); + vdo->iomode = iomode; + return (0); +} + +int zbar_video_get_width(const zbar_video_t *vdo) +{ + return (vdo->width); +} + +int zbar_video_get_height(const zbar_video_t *vdo) +{ + return (vdo->height); +} + +uint32_t zbar_video_get_format(const zbar_video_t *vdo) +{ + return (vdo->format); +} + +static inline int video_init_images(zbar_video_t *vdo) +{ + int i; + assert(vdo->datalen); + if (vdo->iomode != VIDEO_MMAP) { + assert(!vdo->buf); + vdo->buflen = vdo->num_images * vdo->datalen; + vdo->buf = calloc(1, vdo->buflen); + if (!vdo->buf) + return (err_capture(vdo, SEV_FATAL, ZBAR_ERR_NOMEM, __func__, + "unable to allocate image buffers")); + zprintf(1, "pre-allocated %d %s buffers size=0x%lx\n", vdo->num_images, + (vdo->iomode == VIDEO_READWRITE) ? "READ" : "USERPTR", + vdo->buflen); + } + for (i = 0; i < vdo->num_images; i++) { + zbar_image_t *img = vdo->images[i]; + img->format = vdo->format; + zbar_image_set_size(img, vdo->width, vdo->height); + if (vdo->iomode != VIDEO_MMAP) { + unsigned long offset = i * vdo->datalen; + img->datalen = vdo->datalen; + img->data = (uint8_t *)vdo->buf + offset; + zprintf(2, " [%02d] @%08lx\n", i, offset); + } + } + return (0); +} + +int zbar_video_init(zbar_video_t *vdo, unsigned long fmt) +{ +#ifdef HAVE_LIBJPEG + const zbar_format_def_t *vidfmt; +#endif + if (vdo->initialized) + /* FIXME re-init different format? */ + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "already initialized, re-init unimplemented")); + + if (vdo->init(vdo, fmt)) + return (-1); + vdo->format = fmt; + if (video_init_images(vdo)) + return (-1); +#ifdef HAVE_LIBJPEG + vidfmt = _zbar_format_lookup(fmt); + if (vidfmt && vidfmt->group == ZBAR_FMT_JPEG) { + zbar_image_t *img; + /* prepare for decoding */ + if (!vdo->jpeg) + vdo->jpeg = _zbar_jpeg_decomp_create(); + if (vdo->jpeg_img) + zbar_image_destroy(vdo->jpeg_img); + + /* create intermediate image for decoder to use*/ + img = vdo->jpeg_img = zbar_image_create(); + img->format = fourcc('Y', '8', '0', '0'); + zbar_image_set_size(img, vdo->width, vdo->height); + img->datalen = vdo->width * vdo->height; + } +#endif + vdo->initialized = 1; + return (0); +} + +int zbar_video_enable(zbar_video_t *vdo, int enable) +{ + if (vdo->active == enable) + return (0); + + if (enable) { + if (vdo->intf == VIDEO_INVALID) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "video device not opened")); + + if (!vdo->initialized && zbar_negotiate_format(vdo, NULL)) + return (-1); + } + + if (video_lock(vdo)) + return (-1); + vdo->active = enable; + if (enable) { + /* enqueue all buffers */ + int i; + for (i = 0; i < vdo->num_images; i++) + if (vdo->nq(vdo, vdo->images[i]) || + ((i + 1 < vdo->num_images) && video_lock(vdo))) + return (-1); + + return (vdo->start(vdo)); + } else { + int i; + for (i = 0; i < vdo->num_images; i++) + vdo->images[i]->next = NULL; + vdo->nq_image = vdo->dq_image = NULL; + if (video_unlock(vdo)) + return (-1); + + return (vdo->stop(vdo)); + } +} + +zbar_image_t *zbar_video_next_image(zbar_video_t *vdo) +{ + unsigned frame; + zbar_image_t *img; + + if (video_lock(vdo)) + return (NULL); + if (!vdo->active) { + video_unlock(vdo); + return (NULL); + } + + frame = vdo->frame++; + img = vdo->dq(vdo); + if (img) { + img->seq = frame; + if (vdo->num_images < 2) { + /* return a *copy* of the video image and immediately recycle + * the driver's buffer to avoid deadlocking the resources + */ + zbar_image_t *tmp = img; + video_lock(vdo); + img = vdo->shadow_image; + vdo->shadow_image = (img) ? img->next : NULL; + video_unlock(vdo); + + if (!img) { + img = zbar_image_create(); + assert(img); + img->refcnt = 0; + img->src = vdo; + /* recycle the shadow images */ + + img->format = vdo->format; + zbar_image_set_size(img, vdo->width, vdo->height); + img->datalen = vdo->datalen; + img->data = malloc(vdo->datalen); + } + img->cleanup = _zbar_video_recycle_shadow; + img->seq = frame; + memcpy((void *)img->data, tmp->data, img->datalen); + _zbar_video_recycle_image(tmp); + } else + img->cleanup = _zbar_video_recycle_image; + _zbar_image_refcnt(img, 1); + } + return (img); +} + +/** @brief return if fun unsupported, otherwise continue */ +#define return_if_not_supported(fun, name) \ + { \ + if (!(fun)) { \ + zprintf(1, "video driver does not implement %s\n", name); \ + return ZBAR_ERR_UNSUPPORTED; \ + } \ + } +#define return_if_non_zero(a) \ + { \ + int rv = a; \ + if (rv != 0) \ + return (rv); \ + } + +int zbar_video_set_control(zbar_video_t *vdo, const char *control_name, + int value) +{ + int loc_value, rv; + return_if_not_supported(vdo->set_control, "set_control"); + loc_value = value; + + rv = vdo->set_control(vdo, control_name, &loc_value); + + if (rv == 0) + zprintf(1, "value of %s set to: %d\n", control_name, loc_value); + return (rv); +} + +int zbar_video_get_control(zbar_video_t *vdo, const char *control_name, + int *value) +{ + return_if_not_supported(vdo->get_control, "get_control"); + return (vdo->get_control(vdo, control_name, value)); +} + +struct video_controls_s *zbar_video_get_controls(const zbar_video_t *vdo, + int index) +{ + int i = 0; + struct video_controls_s *p = vdo->controls; + + while (p && i != index) { + i++; + p = p->next; + } + + if (!p) + return NULL; + + return p; +} + +struct video_resolution_s *zbar_video_get_resolutions(const zbar_video_t *vdo, + int index) +{ + int i = 0; + struct video_resolution_s *p = vdo->res; + + while (i != index) { + if (!p->width || !p->height) + return NULL; + i++; + p++; + } + + if (!p->width || !p->height) + return NULL; + + return p; +} diff --git a/zbar/video.h b/zbar/video.h new file mode 100644 index 0000000..fdb7394 --- /dev/null +++ b/zbar/video.h @@ -0,0 +1,183 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (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 + *------------------------------------------------------------------------*/ +#ifndef _VIDEO_H_ +#define _VIDEO_H_ + +#include "config.h" + +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include <zbar.h> + +#include "error.h" +#include "image.h" +#include "mutex.h" + +/* number of images to preallocate */ +#define ZBAR_VIDEO_IMAGES_MAX 4 + +typedef enum video_interface_e +{ + VIDEO_INVALID = 0, /* uninitialized */ + VIDEO_V4L1, /* v4l protocol version 1 */ + VIDEO_V4L2, /* v4l protocol version 2 */ + VIDEO_VFW, /* video for windows */ + VIDEO_DSHOW /* direct show */ +} video_interface_t; + +typedef enum video_iomode_e +{ + VIDEO_READWRITE = 1, /* standard system calls */ + VIDEO_MMAP, /* mmap interface */ + VIDEO_USERPTR, /* userspace buffers */ +} video_iomode_t; + +typedef struct video_state_s video_state_t; + +struct zbar_video_s { + errinfo_t err; /* error reporting */ + int fd; /* open camera device */ + unsigned width, height; /* video frame size */ + + video_interface_t intf; /* input interface type */ + video_iomode_t iomode; /* video data transfer mode */ + unsigned initialized : 1; /* format selected and images mapped */ + unsigned active : 1; /* current streaming state */ + + uint32_t format; /* selected fourcc */ + unsigned palette; /* v4l1 format index corresponding to format */ + uint32_t *formats; /* 0 terminated list of supported formats */ + uint32_t *emu_formats; /* 0 terminated list of emulated formats */ + + struct video_resolution_s *res; /* 0 terminated list of resolutions */ + + struct video_controls_s *controls; /* linked list of controls */ + + unsigned long datalen; /* size of image data for selected format */ + unsigned long buflen; /* total size of image data buffer */ + void *buf; /* image data buffer */ + + unsigned frame; /* frame count */ + + zbar_mutex_t qlock; /* lock image queue */ + int num_images; /* number of allocated images */ + zbar_image_t **images; /* indexed list of images */ + zbar_image_t *nq_image; /* last image enqueued */ + zbar_image_t *dq_image; /* first image to dequeue (when ordered) */ + zbar_image_t *shadow_image; /* special case internal double buffering */ + + video_state_t *state; /* platform/interface specific state */ + +#ifdef HAVE_LIBJPEG + struct jpeg_decompress_struct *jpeg; /* JPEG decompressor */ + zbar_image_t *jpeg_img; /* temporary image */ +#endif + + /* interface dependent methods */ + int (*init)(zbar_video_t *, uint32_t); + int (*cleanup)(zbar_video_t *); + int (*start)(zbar_video_t *); + int (*stop)(zbar_video_t *); + int (*nq)(zbar_video_t *, zbar_image_t *); + /** set value of video control + * implemented by v4l2_set_control() + * @param name name of a control, acceptable names are listed + * in processor.h + * @param value pointer to value of a type specified by flags + */ + int (*set_control)(zbar_video_t *, const char *name, void *value); + /** get value of video control + * implemented by v4l2_get_control() + * @param name name of a control, acceptable names are listed + * in processor.h + * @param value pointer to a receiver of a value of a type + * specified by flags + */ + int (*get_control)(zbar_video_t *, const char *name, void *value); + + void (*free)(zbar_video_t *); + + zbar_image_t *(*dq)(zbar_video_t *); +}; + +/* video.next_image and video.recycle_image have to be thread safe + * wrt/other apis + */ +static inline int video_lock(zbar_video_t *vdo) +{ + int rc = 0; + if ((rc = _zbar_mutex_lock(&vdo->qlock))) { + err_capture(vdo, SEV_FATAL, ZBAR_ERR_LOCKING, __func__, + "unable to acquire lock"); + vdo->err.errnum = rc; + return (-1); + } + return (0); +} + +static inline int video_unlock(zbar_video_t *vdo) +{ + int rc = 0; + if ((rc = _zbar_mutex_unlock(&vdo->qlock))) { + err_capture(vdo, SEV_FATAL, ZBAR_ERR_LOCKING, __func__, + "unable to release lock"); + vdo->err.errnum = rc; + return (-1); + } + return (0); +} + +static inline int video_nq_image(zbar_video_t *vdo, zbar_image_t *img) +{ + /* maintains queued buffers in order */ + img->next = NULL; + if (vdo->nq_image) + vdo->nq_image->next = img; + vdo->nq_image = img; + if (!vdo->dq_image) + vdo->dq_image = img; + return (video_unlock(vdo)); +} + +static inline zbar_image_t *video_dq_image(zbar_video_t *vdo) +{ + zbar_image_t *img = vdo->dq_image; + if (img) { + vdo->dq_image = img->next; + img->next = NULL; + } + if (video_unlock(vdo)) + /* FIXME reclaim image */ + return (NULL); + return (img); +} + +/* PAL interface */ +extern int _zbar_video_open(zbar_video_t *, const char *); + +#endif diff --git a/zbar/video/dshow.c b/zbar/video/dshow.c new file mode 100644 index 0000000..d304dfe --- /dev/null +++ b/zbar/video/dshow.c @@ -0,0 +1,1334 @@ +/*------------------------------------------------------------------------ + * Copyright 2012 (c) Klaus Triendl <klaus@triendl.eu> + * Copyright 2012 (c) Jarek Czekalski <jarekczek@poczta.onet.pl> + * + * 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/zbarw + *------------------------------------------------------------------------*/ + +#include "config.h" + +#include <assert.h> +#include <objbase.h> +#include <strmif.h> +#include <control.h> + +#include <qedit.h> + +#include <amvideo.h> // include after ddraw.h has been included from any dshow header + +#include <initguid.h> + +#include "misc.h" +#include "thread.h" +#include "video.h" + +#define ZBAR_DEFINE_STATIC_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, \ + b8) \ + static const GUID name; \ + static const GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }; + +// define a special guid that can be used for fourcc formats +// 00000000-0000-0010-8000-00AA00389B71 == MEDIASUBTYPE_FOURCC_PLACEHOLDER +ZBAR_DEFINE_STATIC_GUID(MEDIASUBTYPE_FOURCC_PLACEHOLDER, 0x00000000, 0x0000, + 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); + +#define OUR_GUID_ENTRY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + ZBAR_DEFINE_STATIC_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8); + +#include <uuids.h> + +DEFINE_GUID(IID_IUnknown, 0x00000000, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x46); +DEFINE_GUID(IID_ISampleGrabber, 0x6b652fff, 0x11fe, 0x4fce, 0x92, 0xad, 0x02, + 0x66, 0xb5, 0xd7, 0xc7, 0x8f); +DEFINE_GUID(IID_ISampleGrabberCB, 0x0579154a, 0x2b53, 0x4994, 0xb0, 0xd0, 0xe7, + 0x73, 0x14, 0x8e, 0xff, 0x85); +DEFINE_GUID(IID_IBaseFilter, 0x56a86895, 0x0ad4, 0x11ce, 0xb0, 0x3a, 0x00, 0x20, + 0xaf, 0x0b, 0xa7, 0x70); +DEFINE_GUID(IID_ICreateDevEnum, 0x29840822, 0x5b84, 0x11d0, 0xbd, 0x3b, 0x00, + 0xa0, 0xc9, 0x11, 0xce, 0x86); +DEFINE_GUID(IID_IGraphBuilder, 0x56a868a9, 0x0ad4, 0x11ce, 0xb0, 0x3a, 0x00, + 0x20, 0xaf, 0x0b, 0xa7, 0x70); +DEFINE_GUID(IID_IMediaControl, 0x56a868b1, 0x0ad4, 0x11ce, 0xb0, 0x3a, 0x00, + 0x20, 0xaf, 0x0b, 0xa7, 0x70); +DEFINE_GUID(IID_IPropertyBag, 0x55272a00, 0x42cb, 0x11ce, 0x81, 0x35, 0x00, + 0xaa, 0x00, 0x4b, 0xb8, 0x51); +DEFINE_GUID(IID_IAMStreamConfig, 0xc6e13340, 0x30ac, 0x11d0, 0xa1, 0x8c, 0x00, + 0xa0, 0xc9, 0x11, 0x89, 0x56); +DEFINE_GUID(IID_ICaptureGraphBuilder2, 0x93e5a4e0, 0x2d50, 0x11d2, 0xab, 0xfa, + 0x00, 0xa0, 0xc9, 0xc6, 0xe3, 0x8d); +DEFINE_GUID(CLSID_NullRenderer, 0xc1f400a4, 0x3f08, 0x11d3, 0x9f, 0x0b, 0x00, + 0x60, 0x08, 0x03, 0x9e, 0x37); +DEFINE_GUID(CLSID_SampleGrabber, 0xc1f400a0, 0x3f08, 0x11d3, 0x9f, 0x0b, 0x00, + 0x60, 0x08, 0x03, 0x9e, 0x37); + +#define BIH_FMT \ + "%ldx%ld @%dbpp (%lx) cmp=%.4s(%08lx) res=%ldx%ld clr=%ld/%ld (%lx)" +#define BIH_FIELDS(bih) \ + (bih)->biWidth, (bih)->biHeight, (bih)->biBitCount, (bih)->biSizeImage, \ + (char *)&(bih)->biCompression, (bih)->biCompression, \ + (bih)->biXPelsPerMeter, (bih)->biYPelsPerMeter, (bih)->biClrImportant, \ + (bih)->biClrUsed, (bih)->biSize + +// taken from Capturing an Image winapi sample +#define BMP_SIZE(cx, cy, bitsPerPix) \ + ((((cx) * (bitsPerPix) + 31) / 32) * 4 * (cy)) + +#define COM_SAFE_RELEASE(ppIface) \ + if (*ppIface) \ + (*ppIface)->lpVtbl->Release(*ppIface) + +#define CHECK_COM_ERROR(hr, msg, stmt) \ + if (FAILED(hr)) { \ + LPSTR sysmsg = NULL; \ + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | \ + FORMAT_MESSAGE_FROM_SYSTEM | \ + FORMAT_MESSAGE_IGNORE_INSERTS, \ + NULL, hr, \ + 0 /* MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US) */, \ + (LPSTR)&sysmsg, 0, NULL); \ + zprintf(6, "%s, hresult: 0x%lx %s", msg, hr, sysmsg); \ + LocalFree(sysmsg); \ + stmt; \ + } + +static const struct uuid_desc_s { + const GUID *guid; + const char *name; +} known_uuids[] = { +#define OUR_GUID_ENTRY(m_name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + { &m_name, #m_name }, +#include <ksuuids.h> + { NULL, NULL } +}; + +static const REFERENCE_TIME _100ns_unit = 1 * 1000 * 1000 * 1000 / 100; + +// format to which we convert MJPG streams +static const REFGUID mjpg_conversion_mediatype = &MEDIASUBTYPE_RGB32; +static const int mjpg_conversion_fmt = fourcc('B', 'G', 'R', '4'); +static const int mjpg_conversion_fmt_bpp = 32; + +static long grabbed_count = 0; + +// Destroy (Release) the format block for a media type. +static void DestroyMediaType(AM_MEDIA_TYPE *mt) +{ + if (mt->cbFormat != 0) + CoTaskMemFree(mt->pbFormat); + + if (mt->pUnk) + IUnknown_Release(mt->pUnk); +} + +// Destroy the format block for a media type and free the media type +static void DeleteMediaType(AM_MEDIA_TYPE *mt) +{ + if (!mt) + return; + DestroyMediaType(mt); + CoTaskMemFree(mt); +} + +static void make_fourcc_subtype(GUID *subtype, uint32_t fmt) +{ + *subtype = MEDIASUBTYPE_FOURCC_PLACEHOLDER; + subtype->Data1 = fmt; +} + +static int dshow_is_fourcc_guid(REFGUID subtype) +{ + // make up a fourcc guid in spe + GUID clsid; + make_fourcc_subtype(&clsid, subtype->Data1); + + return IsEqualGUID(subtype, &clsid); +} + +/// Checks whether the given AM_MEDIA_TYPE contains a +/// VIDEOINFOHEADER block. +static inline int dshow_has_vih(AM_MEDIA_TYPE *mt) +{ + // documentation for AM_MEDIA_TYPE emphasizes to do a thorough check + int isvih = (IsEqualGUID(&mt->formattype, &FORMAT_VideoInfo) && + mt->cbFormat >= sizeof(VIDEOINFOHEADER) && mt->pbFormat); + return isvih; +} + +/// Access the BITMAPINFOHEADER in the given AM_MEDIA_TYPE, +/// access is non-const +static inline BITMAPINFOHEADER *dshow_access_bih(AM_MEDIA_TYPE *mt) +{ + VIDEOINFOHEADER *vih = (VIDEOINFOHEADER *)mt->pbFormat; + return &vih->bmiHeader; +} + +/// Access the BITMAPINFOHEADER in the given AM_MEDIA_TYPE, +/// access is const +static inline const BITMAPINFOHEADER *dshow_caccess_bih(const AM_MEDIA_TYPE *mt) +{ + VIDEOINFOHEADER *vih = (VIDEOINFOHEADER *)mt->pbFormat; + return &vih->bmiHeader; +} + +/// Flips the image vertically copying it from srcBuf to img. +/** @param bpp Bits Per Pixel */ +static void flip_vert(zbar_image_t *const img, void *const srcBuf, int bpp) +{ + BYTE *dst, *src; + int i, n; + + // The formula below works only if bpp%8==0 + long bytesPerLine = 1L * img->width * bpp / 8; + assert(img->datalen >= img->height * bytesPerLine); + dst = (BYTE *)img->data; + src = ((BYTE *)srcBuf) + (img->height - 1) * bytesPerLine; + for (i = 0, n = img->height; i < n; i++) { + memcpy(dst, src, bytesPerLine); + dst += bytesPerLine; + src -= bytesPerLine; + } + assert(src + bytesPerLine == srcBuf); +} + +/// Internal format information +struct int_format_s { + uint32_t fourcc; + /// index for IAMStreamConfig::GetStreamCaps + /// @note compression in bih structure may differ from one used by zbar + int idx_caps; + resolution_list_t resolutions; +}; +typedef struct int_format_s int_format_t; + +struct video_state_s { + zbar_thread_t thread; /* capture message pump */ + HANDLE captured; + HANDLE notify; /* capture thread status change */ + int bi_size; /* size of bih */ + BITMAPINFOHEADER *bih; /* video format details of grabbed samples; + format might be among fourcc or BI_RGB */ + int do_flip_bitmap; /* whether uncompressed bitmap images are + bottom-up and need to be vertically + flipped */ + zbar_image_t *image; /* current capturing frame */ + + IGraphBuilder *graph; /* dshow graph manager */ + IMediaControl *mediacontrol; /* dshow graph control */ + IBaseFilter *camera; /* dshow source filter */ + ISampleGrabber *samplegrabber; /* dshow intermediate filter */ + IBaseFilter *grabberbase; /* samplegrabber's IBaseFilter interface */ + IBaseFilter *nullrenderer; + ICaptureGraphBuilder2 *builder; + IAMStreamConfig *camstreamconfig; /* dshow stream configuration interface */ + int caps_size; /* length of stream config caps */ + /// 0 terminated list of supported internal formats. + /** The size of this + * array matches the size of {@link zbar_video_s#formats} array and + * the consecutive entries correspond to each other, making a mapping + * between internal (camera) formats and zbar formats + * (presented to zbar processor). */ + int_format_t *int_formats; + resolution_t def_resolution; /* initial resolution read + from the camera */ +}; + +static void dshow_destroy_video_state_t(video_state_t *state) +{ + COM_SAFE_RELEASE(&state->camstreamconfig); + COM_SAFE_RELEASE(&state->builder); + COM_SAFE_RELEASE(&state->nullrenderer); + COM_SAFE_RELEASE(&state->grabberbase); + COM_SAFE_RELEASE(&state->samplegrabber); + COM_SAFE_RELEASE(&state->camera); + COM_SAFE_RELEASE(&state->mediacontrol); + COM_SAFE_RELEASE(&state->graph); + + if (state->captured) + CloseHandle(state->captured); + + free(state->bih); + + if (state->int_formats) { + int_format_t *fmt; + for (fmt = state->int_formats; !is_struct_null(fmt); fmt++) { + resolution_list_cleanup(&fmt->resolutions); + } + } + free(state->int_formats); +} + +/// Returns the index of the given format in formats array. +/** If not found, returns -1. The array is constructed like + * <code>vdo->formats</code>, with the terminating null. */ +static int get_format_index(uint32_t *fmts, uint32_t fmt0) +{ + uint32_t *fmt; + int i = 0; + for (fmt = fmts; *fmt; fmt++) { + if (*fmt == fmt0) + break; + i++; + } + if (*fmt) + return i; + else + return -1; +} + +/// Returns the index of the given format in internal formats array. +/** If not found, returns -1. The array is constructed like + * <code>vdo->state->int_formats</code>, with the terminating zeroed + * element. */ +/** @param fmt0 A fourcc format code */ +static int get_int_format_index(int_format_t *fmts, uint32_t fmt0) +{ + int_format_t *fmt; + int i = 0; + for (fmt = fmts; !is_struct_null(fmt); fmt++) { + if (fmt->fourcc == fmt0) + break; + i++; + } + if (!is_struct_null(fmt)) + return i; + else + return -1; +} + +/// Gets the fourcc code for the given media type. +/** This is necessary because of non-standard coding of video + * formats by Windows, for example BGR3/4 = BI_RGB . */ +static uint32_t get_fourcc_for_mt(const AM_MEDIA_TYPE *mt) +{ + if IsEqualGUID (&mt->subtype, &MEDIASUBTYPE_RGB24) + return fourcc('B', 'G', 'R', '3'); + else if IsEqualGUID (&mt->subtype, &MEDIASUBTYPE_RGB32) + return fourcc('B', 'G', 'R', '4'); + else if (dshow_is_fourcc_guid(&mt->subtype)) + return mt->subtype.Data1; + else + return 0; +} + +/// Dumps the mapping of internal and external formats, if the debug level +/// is sufficient. +static void dump_formats(zbar_video_t *vdo) +{ + video_state_t *state = vdo->state; + uint32_t *fmt; + int_format_t *int_fmt; + zprintf(8, "Detected formats: (internal) / (translated for zbar)\n"); + fmt = vdo->formats; + int_fmt = state->int_formats; + while (*fmt) { + zprintf(8, " %.4s / %.4s, resolutions: %lu\n", + (char *)&int_fmt->fourcc, (char *)fmt, + int_fmt->resolutions.cnt); + fmt++; + int_fmt++; + } +} + +/// Allocates a string containing given guid. +/** If it's among known CLSID's, its name is returned. + * Returned string should be freed with `free`. */ +static char *get_clsid_string(REFGUID guid) +{ + char *msg = NULL; + + // first try to determine guid name from the list + int i; + for (i = 0; known_uuids[i].name; i++) { + if (IsEqualGUID(known_uuids[i].guid, guid)) { + msg = malloc(strlen(known_uuids[i].name) + 1); + strcpy(msg, known_uuids[i].name); + break; + } + } + + // if that failed, give the ugly string + if (msg == NULL) { + LPOLESTR str = L"ERROR"; + HRESULT hr = StringFromCLSID(guid, &str); + int c = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL); + msg = malloc(c); + WideCharToMultiByte(CP_UTF8, 0, str, -1, msg, c, NULL, NULL); + if (!FAILED(hr)) + CoTaskMemFree(str); + } + + return msg; +} + +/// Maps internal format MJPG (if found) to a format known to +/// zbar. +/** The conversion will be done by dshow mjpeg decompressor + * before passing the image to zbar. */ +static void prepare_mjpg_format_mapping(zbar_video_t *vdo) +{ + int iMjpgConv; + video_state_t *state = vdo->state; + /// The format we will convert MJPG to + uint32_t fmtConv = mjpg_conversion_fmt; + int iMjpg = + get_int_format_index(state->int_formats, fourcc('M', 'J', 'P', 'G')); + if (iMjpg < 0) + return; + assert(vdo->formats[iMjpg] == fourcc('M', 'J', 'P', 'G')); + + // If we already have fmtConv, it will lead to duplicating it in + // external formats. + // We can't leave it this way, because when zbar wants to use fmtConv + // we must have only one internal format for that. + // It's better to drop MJPG then, as it's a compressed format and + // we prefer better quality images. + + // The index of fmtConv before mapping mjpg to fmtConv + iMjpgConv = get_int_format_index(state->int_formats, fmtConv); + + if (iMjpgConv >= 0) { + // remove the iMjpgConv entry by moving the following entries by 1 + int i; + for (i = iMjpgConv; vdo->formats[i]; i++) { + vdo->formats[i] = vdo->formats[i + 1]; + state->int_formats[i] = state->int_formats[i + 1]; + } + // The number of formats is reduced by 1 now, but to realloc just + // to save 2 times 4 bytes? Too much fuss. + } else { + vdo->formats[iMjpg] = fmtConv; + } +} + +/// sample grabber callback implementation (derived from ISampleGrabberCB) +typedef struct zbar_samplegrabber_cb { + // baseclass + const struct ISampleGrabberCB _; + // COM refcount + ULONG refcount; + + zbar_video_t *vdo; + +} zbar_samplegrabber_cb; + +// ISampleGrabber methods (implementation below) + +HRESULT __stdcall zbar_samplegrabber_cb_QueryInterface(ISampleGrabberCB *_This, + REFIID riid, + void **ppvObject); +ULONG __stdcall zbar_samplegrabber_cb_AddRef(ISampleGrabberCB *_This); +ULONG __stdcall zbar_samplegrabber_cb_Release(ISampleGrabberCB *_This); +HRESULT __stdcall zbar_samplegrabber_cb_SampleCB(ISampleGrabberCB *_This, + double sampletime, + IMediaSample *sample); +// note: original MS version expects a long for the buffer length, wine version a LONG +//HRESULT __stdcall zbar_samplegrabber_cb_BufferCB(ISampleGrabberCB* _This, double sampletime, BYTE* buffer, long bufferlen); +HRESULT __stdcall zbar_samplegrabber_cb_BufferCB(ISampleGrabberCB *_This, + double sampletime, + BYTE *buffer, LONG bufferlen); + +static struct ISampleGrabberCBVtbl SampleGrabberCBVtbl = { + &zbar_samplegrabber_cb_QueryInterface, &zbar_samplegrabber_cb_AddRef, + &zbar_samplegrabber_cb_Release, &zbar_samplegrabber_cb_SampleCB, + &zbar_samplegrabber_cb_BufferCB +}; + +static zbar_samplegrabber_cb *new_zbar_samplegrabber_cb(zbar_video_t *vdo) +{ + // allocate memory + zbar_samplegrabber_cb *o = calloc(1, sizeof(zbar_samplegrabber_cb)); + + // construct parent + ISampleGrabberCB *base = (ISampleGrabberCB *)o; + base->lpVtbl = &SampleGrabberCBVtbl; + + // construct object + o->refcount = 1; + o->vdo = vdo; + + return o; +} + +static void delete_zbar_samplegrabber_cb(zbar_samplegrabber_cb *o) +{ + zprintf(16, "thr=%04lx\n", _zbar_thread_self()); + + // no destruction necessary + + free(o); +} + +HRESULT __stdcall zbar_samplegrabber_cb_QueryInterface(ISampleGrabberCB *_This, + REFIID riid, + void **ppvObject) +{ + if (IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_ISampleGrabberCB)) { + *ppvObject = _This; + return NOERROR; + } else { + *ppvObject = NULL; + return E_NOINTERFACE; + } +} + +ULONG __stdcall zbar_samplegrabber_cb_AddRef(ISampleGrabberCB *_This) +{ + zbar_samplegrabber_cb *This = (zbar_samplegrabber_cb *)_This; + + return ++This->refcount; +} + +ULONG __stdcall zbar_samplegrabber_cb_Release(ISampleGrabberCB *_This) +{ + zbar_samplegrabber_cb *This = (zbar_samplegrabber_cb *)_This; + + ULONG refcnt = --This->refcount; + if (!refcnt) + delete_zbar_samplegrabber_cb(This); + + return refcnt; +} + +HRESULT __stdcall zbar_samplegrabber_cb_SampleCB(ISampleGrabberCB *_This, + double sampletime, + IMediaSample *sample) +{ + return E_NOTIMPL; +} + +//HRESULT __stdcall zbar_samplegrabber_cb_BufferCB(ISampleGrabberCB* _This, double sampletime, BYTE* buffer, long bufferlen) +HRESULT __stdcall zbar_samplegrabber_cb_BufferCB(ISampleGrabberCB *_This, + double sampletime, + BYTE *buffer, LONG bufferlen) +{ + zbar_samplegrabber_cb *This; + zbar_video_t *vdo; + zbar_image_t *img; + if (!buffer || !bufferlen) + return S_OK; + + grabbed_count++; + This = (zbar_samplegrabber_cb *)_This; + vdo = This->vdo; + + _zbar_mutex_lock(&vdo->qlock); + + zprintf(16, "got sample no %ld: %p (%ld), thr=%04lx\n", grabbed_count, + buffer, bufferlen, _zbar_thread_self()); + + img = vdo->state->image; + if (!img) { + _zbar_mutex_lock(&vdo->qlock); + img = video_dq_image(vdo); + // note: video_dq_image() has unlocked the mutex + } + if (img) { + zprintf(16, "copying into img: %p (srcidx: %d, data: %p, len: %ld)\n", + img, img->srcidx, img->data, img->datalen); + + assert(img->datalen == bufferlen); + + // The image needs to be copied now. Usually memcpy is ok, + // but in case of MJPG the picture is upside-down. + if (vdo->state->do_flip_bitmap) + flip_vert(img, buffer, vdo->state->bih->biBitCount); + else + memcpy((void *)img->data, buffer, img->datalen); + + vdo->state->image = img; + SetEvent(vdo->state->captured); + } + + _zbar_mutex_unlock(&vdo->qlock); + + return S_OK; +} + +static ZTHREAD dshow_capture_thread(void *arg) +{ + MSG msg; + int rc = 0; + + zbar_video_t *vdo = arg; + video_state_t *state = vdo->state; + zbar_thread_t *thr = &state->thread; + + _zbar_mutex_lock(&vdo->qlock); + + _zbar_thread_init(thr); + zprintf(4, "spawned dshow capture thread (thr=%04lx)\n", + _zbar_thread_self()); + + while (thr->started && rc >= 0 && rc <= 1) { + _zbar_mutex_unlock(&vdo->qlock); + + rc = MsgWaitForMultipleObjects(1, &thr->notify, 0, INFINITE, + QS_ALLINPUT); + if (rc == 1) + while (PeekMessage(&msg, NULL, 0, 0, PM_NOYIELD | PM_REMOVE)) + if (rc > 0) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + _zbar_mutex_lock(&vdo->qlock); + } + + //done: + thr->running = 0; + _zbar_event_trigger(&thr->activity); + _zbar_mutex_unlock(&vdo->qlock); + return 0; +} + +static int dshow_nq(zbar_video_t *vdo, zbar_image_t *img) +{ + zprintf(16, "img: %p (srcidx: %d, data: %p, len: %ld), thr=%04lx\n", img, + img->srcidx, img->data, img->datalen, _zbar_thread_self()); + return video_nq_image(vdo, img); +} + +/// Platform dependent part of #zbar_video_next_image, which blocks +/// until an image is available. +/** Must be called with video lock held and returns + * with the lock released. + * <p>Waits for the image from `vdo->state->image`. If available, + * this field is nulled. Releases the lock temporarily when waiting for + * the `vdo->state->captured` signal. */ +static zbar_image_t *dshow_dq(zbar_video_t *vdo) +{ + zbar_image_t *img = vdo->state->image; + if (!img) { + DWORD rc; + _zbar_mutex_unlock(&vdo->qlock); + rc = WaitForSingleObject(vdo->state->captured, INFINITE); + // note: until we get the lock again the grabber thread might + // already provide the next sample (which is fine) + _zbar_mutex_lock(&vdo->qlock); + + switch (rc) { + case WAIT_OBJECT_0: + img = vdo->state->image; + break; + case WAIT_ABANDONED: + err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "event handle abandoned"); + break; + case WAIT_FAILED: + err_capture(vdo, SEV_ERROR, ZBAR_ERR_WINAPI, __func__, + "Waiting for image failed"); + break; + } + } + + zprintf(16, "img: %p (srcidx: %d, data: %p, len: %ld), thr=%04lx\n", img, + img ? img->srcidx : 0, img ? img->data : NULL, + img ? img->datalen : 0, _zbar_thread_self()); + + vdo->state->image = NULL; + ResetEvent(vdo->state->captured); + video_unlock(vdo); + + return img; +} + +static int dshow_start(zbar_video_t *vdo) +{ + HRESULT hr; + video_state_t *state = vdo->state; + ResetEvent(state->captured); + + zprintf(16, "thr=%04lx\n", _zbar_thread_self()); + + hr = IMediaControl_Run(state->mediacontrol); + CHECK_COM_ERROR(hr, "couldn't start video stream", (void)0); + if (FAILED(hr)) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "starting video stream")); + return 0; +} + +static int dshow_stop(zbar_video_t *vdo) +{ + HRESULT hr; + video_state_t *state = vdo->state; + + zprintf(16, "thr=%04lx\n", _zbar_thread_self()); + + hr = IMediaControl_Stop(state->mediacontrol); + CHECK_COM_ERROR(hr, "couldn't stop video stream", (void)0); + if (FAILED(hr)) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "stopping video stream")); + + _zbar_mutex_lock(&vdo->qlock); + if (state->image) + state->image = NULL; + SetEvent(state->captured); + _zbar_mutex_unlock(&vdo->qlock); + return 0; +} + +static int dshow_set_format(zbar_video_t *vdo, uint32_t fmt) +{ + int rc = 0; // return code + video_state_t *state; + int_format_t *int_fmt; + BYTE *caps; + AM_MEDIA_TYPE *mt = NULL, *currentmt = NULL; + HRESULT hr; + BITMAPINFOHEADER *bih; + + const zbar_format_def_t *fmtdef = _zbar_format_lookup(fmt); + int fmt_ind = get_format_index(vdo->formats, fmt); + if (!fmtdef->format || fmt_ind < 0) + return (err_capture_int(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "unsupported vfw format: %x", fmt)); + + state = vdo->state; + int_fmt = &state->int_formats[fmt_ind]; + + // prepare media type structure as read from GetStreamCaps + caps = malloc(state->caps_size); + if (!caps) + err_capture(vdo, SEV_FATAL, ZBAR_ERR_NOMEM, __func__, ""); + + hr = IAMStreamConfig_GetStreamCaps(state->camstreamconfig, + int_fmt->idx_caps, &mt, caps); + free(caps); + CHECK_COM_ERROR(hr, "querying chosen stream caps failed", goto cleanup) + bih = dshow_access_bih(mt); + + // then, adjust the format + if (!vdo->width || !vdo->height) { + // video size not requested, take camera default + resolution_t resolution = vdo->state->def_resolution; + // the initial resolution may be not suitable for the current format + get_closest_resolution(&resolution, &int_fmt->resolutions); + vdo->width = resolution.cx; + vdo->height = resolution.cy; + } + bih->biWidth = vdo->width; + bih->biHeight = vdo->height; + + zprintf(4, "setting camera format: %.4s(%08x) " BIH_FMT "\n", + (char *)&int_fmt->fourcc, int_fmt->fourcc, BIH_FIELDS(bih)); + + hr = IAMStreamConfig_SetFormat(state->camstreamconfig, mt); + CHECK_COM_ERROR(hr, "setting camera format failed", goto cleanup) + + // re-read format, image data size might have changed + hr = IAMStreamConfig_GetFormat(state->camstreamconfig, ¤tmt); + CHECK_COM_ERROR(hr, "queried currentmt", goto cleanup); + + bih = dshow_access_bih(currentmt); + + if (get_fourcc_for_mt(currentmt) != int_fmt->fourcc) { + rc = err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "video format set ignored"); + goto cleanup; + } + + vdo->format = fmt; + vdo->width = bih->biWidth; + vdo->height = bih->biHeight; + vdo->datalen = bih->biSizeImage; + + // datalen was set based on the internal format, but sometimes it's + // different from the format reported to zbar processor + if (vdo->formats[fmt_ind] != state->int_formats[fmt_ind].fourcc) { + // See prepare_mjpg_format_mapping for possible differences + // between internal and zbar format. + vdo->datalen = + BMP_SIZE(vdo->width, vdo->height, mjpg_conversion_fmt_bpp); + } + + zprintf(4, "set camera format: %.4s(%08x) " BIH_FMT "\n", + (char *)&int_fmt->fourcc, int_fmt->fourcc, BIH_FIELDS(bih)); + +cleanup: + DeleteMediaType(mt); + DeleteMediaType(currentmt); + + if (FAILED(hr)) + return err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "setting dshow format failed"); + else + // note: if an error happened it was already captured and reported + return rc; +} + +static int dshow_init(zbar_video_t *vdo, uint32_t fmt) +{ + HRESULT hr; + video_state_t *state; + int fmt_ind; + ISampleGrabberCB *grabbercb; + REFERENCE_TIME avgtime_perframe; + + if (dshow_set_format(vdo, fmt)) + return -1; + + state = vdo->state; + fmt_ind = get_format_index(vdo->formats, fmt); + + // install sample grabber callback + grabbercb = (ISampleGrabberCB *)new_zbar_samplegrabber_cb(vdo); + hr = ISampleGrabber_SetCallback(vdo->state->samplegrabber, grabbercb, 1); + ISampleGrabberCB_Release(grabbercb); + if (FAILED(hr)) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_BUSY, __func__, + "setting capture callbacks")); + + // set up directshow graph + + // special handling for MJPG streams: + // we use the stock mjpeg decompressor filter + if (state->int_formats[fmt_ind].fourcc == fourcc('M', 'J', 'P', 'G')) { + IBaseFilter *mjpgdecompressor = NULL; + AM_MEDIA_TYPE conv_mt = { 0 }; + + hr = CoCreateInstance(&CLSID_MjpegDec, NULL, CLSCTX_INPROC_SERVER, + &IID_IBaseFilter, (void **)&mjpgdecompressor); + CHECK_COM_ERROR(hr, "failed to create mjpeg decompressor filter", + (void)0) + if (FAILED(hr)) { + return err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "failed to create mjpeg decompressor filter"); + } + + // add mjpeg decompressor to graph + hr = IGraphBuilder_AddFilter(state->graph, mjpgdecompressor, + L"MJPEG decompressor"); + CHECK_COM_ERROR(hr, "adding MJPEG decompressor", goto mjpg_cleanup) + + // explicitly convert MJPG to RGB32 + // (sample grabber will only accept this format) + // + // note: the mjpeg decompressor's output seems to be RGB32 (BGR4) + // by default. + // This fact has been a decisive factor to choosing the mapping + // (BGR4 [zbar input] -> MJPG [camera output]). + // Because zbar requests BGR4 we have to ensure that we really do + // provide it. + conv_mt.majortype = MEDIATYPE_Video; + conv_mt.subtype = *mjpg_conversion_mediatype; + conv_mt.formattype = FORMAT_VideoInfo; + hr = ISampleGrabber_SetMediaType(state->samplegrabber, &conv_mt); + CHECK_COM_ERROR(hr, "setting mjpg conversion media type", + goto mjpg_cleanup) + // no need to destroy conv_mt + + hr = ICaptureGraphBuilder2_RenderStream( + state->builder, &PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, + (IUnknown *)state->camera, NULL, mjpgdecompressor); + CHECK_COM_ERROR(hr, "rendering filter graph 1", goto mjpg_cleanup) + + hr = ICaptureGraphBuilder2_RenderStream(state->builder, NULL, + &MEDIATYPE_Video, + (IUnknown *)mjpgdecompressor, + state->grabberbase, + state->nullrenderer); + CHECK_COM_ERROR(hr, "rendering filter graph 2", goto mjpg_cleanup) + + mjpg_cleanup: + IBaseFilter_Release(mjpgdecompressor); + + if (FAILED(hr)) { + return err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "rendering filter graph failed"); + } + } else { + // ensure (again) that the sample grabber gets only + // video media types with VIDEOINFOHEADER + AM_MEDIA_TYPE grab_mt = { 0 }; + grab_mt.majortype = MEDIATYPE_Video; + grab_mt.formattype = FORMAT_VideoInfo; + hr = ISampleGrabber_SetMediaType(state->samplegrabber, &grab_mt); + CHECK_COM_ERROR(hr, "setting sample grabber media type", + goto render_cleanup) + // no need to destroy grab_mt + + hr = ICaptureGraphBuilder2_RenderStream( + state->builder, &PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, + (IUnknown *)state->camera, state->grabberbase, state->nullrenderer); + CHECK_COM_ERROR(hr, "rendering filter graph", goto render_cleanup) + + render_cleanup: + if (FAILED(hr)) { + return err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "rendering filter graph failed"); + } + } + + // scope: after the graph is built (and the pins connected) we query the + // final media type from sample grabber's input pin; + { + AM_MEDIA_TYPE input_mt = { 0 }; + const BITMAPINFOHEADER *bih; + + hr = ISampleGrabber_GetConnectedMediaType(state->samplegrabber, + &input_mt); + CHECK_COM_ERROR(hr, + "couldn't query input media type from sample grabber", + goto cleanup1) + + assert(dshow_has_vih(&input_mt)); + bih = dshow_caccess_bih(&input_mt); + + // adjust state->bih + state->bi_size = bih->biSize; + state->bih = realloc(state->bih, state->bi_size); + memcpy(state->bih, bih, state->bi_size); + + if (bih->biCompression == BI_RGB) { + // check bitmap orientation for native BI_RGB: + // positive biHeight: bottom-up bitmap -> flip + // negative biHeight: top-down bitmap + state->do_flip_bitmap = (bih->biHeight > 0); + } + + cleanup1: + DestroyMediaType(&input_mt); + + if (FAILED(hr)) + return -1; + } + + // query camera stream parameters; + avgtime_perframe = 0; + + // scope: query avgtime_perframe from camera + { + AM_MEDIA_TYPE *currentmt = NULL; + VIDEOINFOHEADER *vih; + hr = IAMStreamConfig_GetFormat(state->camstreamconfig, ¤tmt); + CHECK_COM_ERROR(hr, "querying camera format failed", goto cleanup2) + + assert(dshow_has_vih(currentmt)); + vih = (VIDEOINFOHEADER *)currentmt->pbFormat; + avgtime_perframe = vih->AvgTimePerFrame; + + cleanup2: + DeleteMediaType(currentmt); + } + + // directshow keeps ownership of the image passed to the callback, + // hence, keeping a pointer to the original data (iomode VIDEO_MMAP) is a bad idea + // in a multi-threaded environment. + // real case szenario on a windows 7 tablet with intel atom: + // with vdo->iomode=VIDEO_MMAP and vdo->num_images=1: + // 1) directshow graph provides a sample, sets the data pointer of vdo->state->img + // 2) dshow_dq (called from proc_video_thread()/zbar_video_next_image()) fetches the image and makes a copy of it + // 3) directshow graph provides the next sample, sets the data pointer of vdo->state->img + // 4) dshow_nq (called from ) resets the data pointer of vdo->state->img (nullptr) + // 5) dshow_dq returns vdo->state->img without data (nullptr) + // + // now, we could deal with this special case, but zbar_video_next_image() makes a copy of the sample anyway when + // vdo->num_images==1 (thus VIDEO_MMAP won't save us anything); therefore rather use image buffers provided + // by zbar (see video_init_images()) + vdo->iomode = VIDEO_USERPTR; + // keep zbar's default + //vdo->num_images = ZBAR_VIDEO_IMAGES_MAX; + + // note: vdo->format and vdo->datalen have been set accordingly in dshow_set_format() + + zprintf(3, "initialized video capture: %.4s(%08x), %" PRId64 " frames/s\n", + (char *)&fmt, fmt, _100ns_unit / avgtime_perframe); + + return 0; +} + +static int dshow_cleanup(zbar_video_t *vdo) +{ + video_state_t *state; + zprintf(16, "thr=%04lx\n", _zbar_thread_self()); + + /* close open device */ + state = vdo->state; + + _zbar_thread_stop(&state->thread, &vdo->qlock); + + dshow_destroy_video_state_t(state); + free(state); + vdo->state = NULL; + + CoUninitialize(); + + return 0; +} + +static int dshow_determine_formats(zbar_video_t *vdo) +{ + video_state_t *state = vdo->state; + BYTE *caps; + int n = 0, i; + + // collect formats + int resolutions; + HRESULT hr = IAMStreamConfig_GetNumberOfCapabilities(state->camstreamconfig, + &resolutions, + &state->caps_size); + CHECK_COM_ERROR(hr, "couldn't query camera capabilities", return -1) + + zprintf( + 6, + "number of formats/resolutions supported by the camera as reported by directshow: %d\n", + resolutions); + zprintf(6, "list of those with a VIDEOINFOHEADER:\n"); + + vdo->formats = calloc(resolutions + 1, sizeof(uint32_t)); + state->int_formats = calloc(resolutions + 1, sizeof(int_format_t)); + + // this is actually a VIDEO_STREAM_CONFIG_CAPS structure, which is mostly deprecated anyway, + // so we just reserve enough buffer but treat it as opaque otherwise + caps = malloc(state->caps_size); + for (i = 0; i < resolutions; ++i) { + AM_MEDIA_TYPE *mt; + int is_supported; + + HRESULT hr = + IAMStreamConfig_GetStreamCaps(state->camstreamconfig, i, &mt, caps); + CHECK_COM_ERROR(hr, "querying stream capability failed", continue) + is_supported = 0; + + if (dshow_has_vih(mt)) { + uint32_t fmt; + const BITMAPINFOHEADER *bih = dshow_caccess_bih(mt); + + zprintf(6, BIH_FMT "\n", BIH_FIELDS(bih)); + fmt = get_fourcc_for_mt(mt); + // This is actually a check if the format is recognized. + // TODO: Check if the format is really supported by zbar. + is_supported = (fmt != 0); + + if (is_supported) { + resolution_t resolution; + int j; + + // first search for existing fourcc format + for (j = 0; j < n; ++j) { + if (state->int_formats[i].fourcc == fmt) + break; + } + // push back if not found + if (j == n) { + state->int_formats[n].fourcc = fmt; + state->int_formats[n].idx_caps = i; + resolution_list_init(&state->int_formats[n].resolutions); + vdo->formats[n] = fmt; + ++n; + } + + resolution.cx = bih->biWidth; + resolution.cy = bih->biHeight; + resolution_list_add(&state->int_formats[j].resolutions, + &resolution); + } + } + // note: other format types could be possible, e.g. VIDEOINFOHEADER2 ... + + if (!is_supported) { + char *formattype = get_clsid_string(&mt->formattype); + char *subtype = get_clsid_string(&mt->subtype); + zprintf(6, "unsupported format: %s / %s\n", formattype, subtype); + free(formattype); + free(subtype); + } + + DeleteMediaType(mt); + } + free(caps); + + zprintf(6, "number of supported fourcc formats: %d\n", n); + + vdo->formats = realloc(vdo->formats, (n + 1) * sizeof(uint32_t)); + state->int_formats = + realloc(state->int_formats, (n + 1) * sizeof(int_format_t)); + prepare_mjpg_format_mapping(vdo); + dump_formats(vdo); + + if (n == 0) + return -1; + + return 0; +} + +static int dshow_probe(zbar_video_t *vdo) +{ + video_state_t *state; + AM_MEDIA_TYPE *currentmt; + HRESULT hr; + if (dshow_determine_formats(vdo)) + return -1; + + state = vdo->state; + + // query current format + + hr = IAMStreamConfig_GetFormat(state->camstreamconfig, ¤tmt); + CHECK_COM_ERROR(hr, "couldn't query current camera format", return -1) + + if (!dshow_has_vih(currentmt)) { + char *formattype = get_clsid_string(¤tmt->formattype); + char *subtype = get_clsid_string(¤tmt->subtype); + zprintf(1, + "encountered unsupported initial format, " + "no VIDEOINFOHEADER video format type: %s / %s\n", + formattype, subtype); + free(formattype); + free(subtype); + + // if we cannot read the current resolution, we need sane defaults + vdo->state->def_resolution.cx = 640; + vdo->state->def_resolution.cy = 480; + } else { + const BITMAPINFOHEADER *current_bih = dshow_caccess_bih(currentmt); + assert(current_bih); + zprintf(3, "initial format: %.4s(%08lx) " BIH_FMT "\n", + (char *)¤t_bih->biCompression, current_bih->biCompression, + BIH_FIELDS(current_bih)); + + // store initial size + vdo->state->def_resolution.cx = current_bih->biWidth; + vdo->state->def_resolution.cy = current_bih->biHeight; + } + + DeleteMediaType(currentmt); + + vdo->intf = VIDEO_DSHOW; + vdo->init = dshow_init; + vdo->start = dshow_start; + vdo->stop = dshow_stop; + vdo->cleanup = dshow_cleanup; + vdo->nq = dshow_nq; + vdo->dq = dshow_dq; + + return 0; +} + +// search camera by index or by device path +static IBaseFilter *dshow_search_camera(const char *dev) +{ + IBaseFilter *camera = NULL; + ICreateDevEnum *devenumerator = NULL; + IEnumMoniker *enummoniker = NULL; + HRESULT hr; + BSTR wdev; + int docontinue; + int devid; + + int reqid = -1; + if ((!strncmp(dev, "/dev/video", 10) || + !strncmp(dev, "\\dev\\video", 10)) && + dev[10] >= '0' && dev[10] <= '9' && !dev[11]) + reqid = dev[10] - '0'; + else if (strlen(dev) == 1 && dev[0] >= '0' && dev[0] <= '9') + reqid = dev[0] - '0'; + + zprintf(6, "searching for camera (#%d): %s\n", reqid, dev); + + hr = CoCreateInstance(&CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, + &IID_ICreateDevEnum, (void **)&devenumerator); + CHECK_COM_ERROR(hr, "failed to create system device enumerator", goto done) + + hr = ICreateDevEnum_CreateClassEnumerator(devenumerator, + &CLSID_VideoInputDeviceCategory, + &enummoniker, 0); + CHECK_COM_ERROR(hr, "failed to create enumerator moniker", goto done) + + if (hr != S_OK) { + zprintf(6, "no video devices available"); + goto done; + } + + // turn device name (the GUID) from char to wide char + wdev = SysAllocStringLen(NULL, strlen(dev)); + if (!wdev) + goto done; + MultiByteToWideChar(CP_UTF8, 0, dev, -1, wdev, strlen(dev) + 1); + + // Go through and find capture device + for (devid = 0, docontinue = 1; docontinue; ++devid) { + IMoniker *moniker = NULL; + IPropertyBag *propbag = NULL; + VARIANT devpath_variant; + VARIANT friendlyname_variant; + + hr = IEnumMoniker_Next(enummoniker, 1, &moniker, NULL); + // end of monikers + if (hr != S_OK) + break; + + VariantInit(&devpath_variant); + VariantInit(&friendlyname_variant); + + hr = IMoniker_BindToStorage(moniker, NULL, NULL, &IID_IPropertyBag, + (void **)&propbag); + CHECK_COM_ERROR(hr, "failed to get property bag from moniker", + goto breakout) + + hr = IPropertyBag_Read(propbag, L"DevicePath", &devpath_variant, NULL); + CHECK_COM_ERROR(hr, "failed to read DevicePath from camera device", + goto breakout) + hr = IPropertyBag_Read(propbag, L"FriendlyName", &friendlyname_variant, + NULL); + CHECK_COM_ERROR(hr, "failed to read FriendlyName from camera device", + goto breakout) + + if ((reqid >= 0) ? devid == reqid : + !wcscmp(wdev, V_BSTR(&devpath_variant))) { + // create camera from moniker + hr = IMoniker_BindToObject(moniker, NULL, NULL, &IID_IBaseFilter, + (void **)&camera); + CHECK_COM_ERROR(hr, "failed to get camera device", goto breakout) + } + + if (camera) { + zwprintf(1, L"using camera #%d: %s (%s)\n", devid, + V_BSTR(&friendlyname_variant), V_BSTR(&devpath_variant)); + goto breakout; + } else + goto cleanup; + + breakout: + docontinue = 0; + cleanup: + VariantClear(&friendlyname_variant); + VariantClear(&devpath_variant); + COM_SAFE_RELEASE(&propbag); + COM_SAFE_RELEASE(&moniker); + } + SysFreeString(wdev); + +done: + COM_SAFE_RELEASE(&enummoniker); + COM_SAFE_RELEASE(&devenumerator); + + if (!camera) + zprintf(6, "no camera found\n"); + + return camera; +} + +int _zbar_video_open(zbar_video_t *vdo, const char *dev) +{ + HRESULT hr; + // assume failure + int ret = -1; + + video_state_t *state; + state = vdo->state = calloc(1, sizeof(video_state_t)); + + state->camera = dshow_search_camera(dev); + if (!state->camera) + goto done; + + if (!state->captured) + // create manual reset event + state->captured = CreateEvent(NULL, TRUE, FALSE, NULL); + else + ResetEvent(state->captured); + + if (_zbar_thread_start(&state->thread, dshow_capture_thread, vdo, NULL)) + return -1; + + // create filter graph instance + hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, + &IID_IGraphBuilder, (void **)&state->graph); + CHECK_COM_ERROR(hr, "graph builder creation", goto done) + + // query media control from filter graph + hr = IGraphBuilder_QueryInterface(state->graph, &IID_IMediaControl, + (void **)&state->mediacontrol); + CHECK_COM_ERROR(hr, "querying media control", goto done) + + // create sample grabber instance + hr = CoCreateInstance(&CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, + &IID_ISampleGrabber, (void **)&state->samplegrabber); + CHECK_COM_ERROR(hr, "samplegrabber creation", goto done) + + // query base filter interface from sample grabber + hr = ISampleGrabber_QueryInterface(state->samplegrabber, &IID_IBaseFilter, + (void **)&state->grabberbase); + CHECK_COM_ERROR(hr, "grabberbase query", goto done) + + // capture graph without preview window + hr = CoCreateInstance(&CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, + &IID_IBaseFilter, (void **)&state->nullrenderer); + CHECK_COM_ERROR(hr, "null renderer creation", goto done) + + // add camera to graph + hr = IGraphBuilder_AddFilter(state->graph, state->camera, L"Camera"); + CHECK_COM_ERROR(hr, "adding camera", goto done) + + // add sample grabber to graph + hr = IGraphBuilder_AddFilter(state->graph, state->grabberbase, + L"Sample Grabber"); + CHECK_COM_ERROR(hr, "adding samplegrabber", goto done) + + // add nullrenderer to graph + hr = IGraphBuilder_AddFilter(state->graph, state->nullrenderer, + L"Null Renderer"); + CHECK_COM_ERROR(hr, "adding null renderer", goto done) + + // Create the Capture Graph Builder. + hr = CoCreateInstance(&CLSID_CaptureGraphBuilder2, NULL, + CLSCTX_INPROC_SERVER, &IID_ICaptureGraphBuilder2, + (void **)&state->builder); + CHECK_COM_ERROR(hr, "capturegraph builder creation", goto done) + + // tell graph builder about the filter graph + hr = ICaptureGraphBuilder2_SetFiltergraph(state->builder, state->graph); + CHECK_COM_ERROR(hr, "setting filtergraph", goto done) + + // TODO: + // finding the streamconfig interface on the camera's preview output pin (specifying PIN_CATEGORY_PREVIEW, MEDIATYPE_Video) + // should work according to the documentation but it doesn't. + // Because devices may have separate pins for capture and preview or have a video port pin (PIN_CATEGORY_VIDEOPORT) + // instead of a preview pin, I do hope that we get the streamconfig interface for the correct pin + hr = ICaptureGraphBuilder2_FindInterface( + state->builder, &LOOK_DOWNSTREAM_ONLY, NULL, state->camera, + &IID_IAMStreamConfig, (void **)&state->camstreamconfig); + CHECK_COM_ERROR(hr, "querying camera's streamconfig interface", goto done) + + if (dshow_probe(vdo)) + goto done; + + // success + ret = 0; + +done: + if (ret) { + if (state->camera) + _zbar_thread_stop(&state->thread, NULL); + dshow_destroy_video_state_t(state); + free(state); + vdo->state = NULL; + CoUninitialize(); + + return err_capture_str(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "failed to connect to camera '%s'", dev); + } + + return ret; +} diff --git a/zbar/video/null.c b/zbar/video/null.c new file mode 100644 index 0000000..93b828d --- /dev/null +++ b/zbar/video/null.c @@ -0,0 +1,35 @@ +/*------------------------------------------------------------------------ + * Copyright 2008-2009 (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 "video.h" + +static inline int null_error(void *m, const char *func) +{ + return (err_capture(m, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, func, + "not compiled with video input support")); +} + +int _zbar_video_open(zbar_video_t *vdo, const char *device) +{ + return (null_error(vdo, __func__)); +} diff --git a/zbar/video/v4l.c b/zbar/video/v4l.c new file mode 100644 index 0000000..2039c72 --- /dev/null +++ b/zbar/video/v4l.c @@ -0,0 +1,73 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2011 (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_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_LIBV4L2_H +#include <fcntl.h> +#include <libv4l2.h> +#else +#define v4l2_open open +#define v4l2_close close +#endif + +#include "video.h" + +extern int _zbar_v4l1_probe(zbar_video_t *); +extern int _zbar_v4l2_probe(zbar_video_t *); + +int _zbar_video_open(zbar_video_t *vdo, const char *dev) +{ + vdo->fd = v4l2_open(dev, O_RDWR); + if (vdo->fd < 0) + return (err_capture_str(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "opening video device '%s'", dev)); + zprintf(1, "opened camera device %s (fd=%d)\n", dev, vdo->fd); + + int rc = -1; +#ifdef HAVE_LINUX_VIDEODEV2_H + if (vdo->intf != VIDEO_V4L1) + rc = _zbar_v4l2_probe(vdo); +#endif +#ifdef HAVE_LINUX_VIDEODEV_H + if (rc && vdo->intf != VIDEO_V4L2) + rc = _zbar_v4l1_probe(vdo); +#endif + + if (rc && vdo->fd >= 0) { + v4l2_close(vdo->fd); + vdo->fd = -1; + } + return (rc); +} diff --git a/zbar/video/v4l1.c b/zbar/video/v4l1.c new file mode 100644 index 0000000..cb0b9c1 --- /dev/null +++ b/zbar/video/v4l1.c @@ -0,0 +1,404 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2011 (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_INTTYPES_H +#include <inttypes.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <string.h> +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif +#ifdef HAVE_SYS_MMAN_H +#include <sys/mman.h> +#endif +#include <linux/videodev.h> + +#include "image.h" +#include "video.h" + +typedef struct v4l1_format_s { + uint32_t format; + uint8_t bpp; +} v4l1_format_t; + +/* static v4l1 "palette" mappings + * documentation for v4l1 formats is terrible... + */ +static const v4l1_format_t v4l1_formats[17] = { + /* format bpp */ + { 0, 0 }, + { fourcc('G', 'R', 'E', 'Y'), 8 }, /* GREY */ + { fourcc('H', 'I', '2', '4'), 8 }, /* HI240 (BT848) */ + + /* component ordering for RGB palettes is unspecified, + * convention appears to place red in the most significant bits + * FIXME is this true for other drivers? big endian machines? + */ + { fourcc('R', 'G', 'B', 'P'), 16 }, /* RGB565 */ + { fourcc('B', 'G', 'R', '3'), 24 }, /* RGB24 */ + { fourcc('B', 'G', 'R', '4'), 32 }, /* RGB32 */ + { fourcc('R', 'G', 'B', 'O'), 16 }, /* RGB555 */ + { fourcc('Y', 'U', 'Y', '2'), 16 }, /* YUV422 (8 bpp?!) */ + { fourcc('Y', 'U', 'Y', 'V'), 16 }, /* YUYV */ + { fourcc('U', 'Y', 'V', 'Y'), 16 }, /* UYVY */ + { 0, 12 }, /* YUV420 (24 bpp?) FIXME?! */ + { fourcc('Y', '4', '1', 'P'), 12 }, /* YUV411 */ + { 0, 0 }, /* Bt848 raw */ + { fourcc('4', '2', '2', 'P'), 16 }, /* YUV422P (24 bpp?) */ + { fourcc('4', '1', '1', 'P'), 12 }, /* YUV411P */ + { fourcc('Y', 'U', '1', '2'), 12 }, /* YUV420P */ + { fourcc('Y', 'U', 'V', '9'), 9 }, /* YUV410P */ +}; + +static int v4l1_nq(zbar_video_t *vdo, zbar_image_t *img) +{ + if (video_nq_image(vdo, img)) + return (-1); + + if (vdo->iomode != VIDEO_MMAP) + return (0); + + struct video_mmap vmap; + vmap.frame = img->srcidx; + vmap.width = vdo->width; + vmap.height = vdo->height; + vmap.format = vdo->palette; + if (ioctl(vdo->fd, VIDIOCMCAPTURE, &vmap) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "initiating video capture (VIDIOCMCAPTURE)")); + + return (0); +} + +static zbar_image_t *v4l1_dq(zbar_video_t *vdo) +{ + video_iomode_t iomode = vdo->iomode; + int fd = vdo->fd; + zbar_image_t *img = video_dq_image(vdo); + if (!img) + return (NULL); + + if (iomode == VIDEO_MMAP) { + int frame = img->srcidx; + if (ioctl(fd, VIDIOCSYNC, &frame) < 0) + return (NULL); + } else if (read(fd, (void *)img->data, img->datalen) != img->datalen) + return (NULL); + + return (img); +} + +static int v4l1_mmap_buffers(zbar_video_t *vdo) +{ +#ifdef HAVE_SYS_MMAN_H + /* map camera image to memory */ + struct video_mbuf vbuf; + memset(&vbuf, 0, sizeof(vbuf)); + if (ioctl(vdo->fd, VIDIOCGMBUF, &vbuf) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "querying video frame buffers (VIDIOCGMBUF)")); + assert(vbuf.frames && vbuf.size); + + zprintf(1, "mapping %d buffers size=0x%x\n", vbuf.frames, vbuf.size); + vdo->buflen = vbuf.size; + vdo->buf = + mmap(0, vbuf.size, PROT_READ | PROT_WRITE, MAP_SHARED, vdo->fd, 0); + if (vdo->buf == MAP_FAILED) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "mapping video frame buffers")); + + int i; + for (i = 0; i < vbuf.frames; i++) { + zbar_image_t *img = vdo->images[i]; + zprintf(2, " [%02d] @%08x\n", img->srcidx, vbuf.offsets[i]); + img->data = vdo->buf + vbuf.offsets[i]; + img->datalen = vdo->datalen; + int next_offset = + ((i + 1 < vdo->num_images) ? vbuf.offsets[i + 1] : vbuf.size); + if (next_offset < vbuf.offsets[i] + vdo->datalen) + fprintf(stderr, + "WARNING: insufficient v4l1 video buffer size:\n" + "\tvbuf[%d]=%x vbuf[%d]=%x datalen=%lx\n" + "\timage=%d x %d %.4s(%08x) palette=%d\n", + i, vbuf.offsets[i], i + 1, next_offset, vdo->datalen, + vdo->width, vdo->height, (char *)&vdo->format, vdo->format, + vdo->palette); + } + return (0); +#else + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, __func__, + "memory mapping not supported")); +#endif +} + +static int v4l1_start(zbar_video_t *vdo) +{ + return (0); +} + +static int v4l1_stop(zbar_video_t *vdo) +{ + return (0); +} + +static inline int v4l1_set_format(zbar_video_t *vdo, uint32_t fmt) +{ + struct video_picture vpic; + memset(&vpic, 0, sizeof(vpic)); + if (ioctl(vdo->fd, VIDIOCGPICT, &vpic) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "querying video format (VIDIOCGPICT)")); + + vdo->palette = 0; + int ifmt; + for (ifmt = 1; ifmt <= VIDEO_PALETTE_YUV410P; ifmt++) + if (v4l1_formats[ifmt].format == fmt) + break; + if (!fmt || ifmt >= VIDEO_PALETTE_YUV410P) + return (err_capture_int(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "invalid v4l1 format: %x", fmt)); + + vpic.palette = ifmt; + vpic.depth = v4l1_formats[ifmt].bpp; + if (ioctl(vdo->fd, VIDIOCSPICT, &vpic) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "setting format (VIDIOCSPICT)")); + + memset(&vpic, 0, sizeof(vpic)); + if (ioctl(vdo->fd, VIDIOCGPICT, &vpic) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "querying video format (VIDIOCGPICT)")); + + if (vpic.palette != ifmt || vpic.depth != v4l1_formats[ifmt].bpp) { + fprintf(stderr, + "WARNING: set v4l1 palette %d which should have depth %d bpp\n" + " but probed palette %d with depth %d bpp?" + " ...continuing anyway\n", + ifmt, v4l1_formats[ifmt].bpp, vpic.palette, vpic.depth); + err_capture_int(vdo, SEV_WARNING, ZBAR_ERR_INVALID, __func__, + "driver format (%x) inconsistency", fmt); + } + vdo->format = fmt; + vdo->palette = ifmt; + vdo->datalen = (vdo->width * vdo->height * v4l1_formats[ifmt].bpp + 7) >> 3; + + zprintf(1, "set new format: %.4s(%08x) depth=%d palette=%d size=0x%lx\n", + (char *)&vdo->format, vdo->format, vpic.depth, vdo->palette, + vdo->datalen); + return (0); +} + +static int v4l1_init(zbar_video_t *vdo, uint32_t fmt) +{ + if (v4l1_set_format(vdo, fmt)) + return (-1); + if (vdo->iomode == VIDEO_MMAP && v4l1_mmap_buffers(vdo)) + return (-1); + return (0); +} + +static int v4l1_cleanup(zbar_video_t *vdo) +{ +#ifdef HAVE_SYS_MMAN_H + /* FIXME should avoid holding onto mmap'd buffers so long? */ + if (vdo->iomode == VIDEO_MMAP && vdo->buf) { + if (munmap(vdo->buf, vdo->buflen)) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "unmapping video frame buffers")); + vdo->buf = NULL; + /* FIXME reset image */ + } +#endif + + /* close open device */ + if (vdo->fd >= 0) { + close(vdo->fd); + vdo->fd = -1; + } + return (0); +} + +static int v4l1_probe_iomode(zbar_video_t *vdo) +{ + vdo->iomode = VIDEO_READWRITE; +#ifdef HAVE_SYS_MMAN_H + struct video_mbuf vbuf; + memset(&vbuf, 0, sizeof(vbuf)); + if (ioctl(vdo->fd, VIDIOCGMBUF, &vbuf) < 0) { + if (errno != EINVAL) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "querying video frame buffers (VIDIOCGMBUF)")); + /* not supported */ + return (0); + } + if (!vbuf.frames || !vbuf.size) + return (0); + vdo->iomode = VIDEO_MMAP; + if (vdo->num_images > vbuf.frames) + vdo->num_images = vbuf.frames; +#endif + zprintf(1, "using %d images in %s mode\n", vdo->num_images, + (vdo->iomode == VIDEO_READWRITE) ? "READ" : "MMAP"); + return (0); +} + +static inline int v4l1_probe_formats(zbar_video_t *vdo) +{ + struct video_picture vpic; + memset(&vpic, 0, sizeof(vpic)); + if (ioctl(vdo->fd, VIDIOCGPICT, &vpic) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "querying format (VIDIOCGPICT)")); + + vdo->format = 0; + if (vpic.palette <= VIDEO_PALETTE_YUV410P) + vdo->format = v4l1_formats[vpic.palette].format; + + zprintf(1, "current format: %.4s(%08x) depth=%d palette=%d\n", + (char *)&vdo->format, vdo->format, vpic.depth, vpic.palette); + + vdo->formats = calloc(16, sizeof(uint32_t)); + if (!vdo->formats) + return (err_capture(vdo, SEV_FATAL, ZBAR_ERR_NOMEM, __func__, + "allocating format list")); + + int num_formats = 0; + zprintf(2, "probing supported formats:\n"); + int i; + for (i = 1; i <= VIDEO_PALETTE_YUV410P; i++) { + if (!v4l1_formats[i].format) + continue; + vpic.depth = v4l1_formats[i].bpp; + vpic.palette = i; + if (ioctl(vdo->fd, VIDIOCSPICT, &vpic) < 0) { + zprintf(2, " [%02d] %.4s...no (set fails)\n", i, + (char *)&v4l1_formats[i].format); + continue; + } + if (ioctl(vdo->fd, VIDIOCGPICT, &vpic) < 0 || vpic.palette != i) { + zprintf(2, " [%02d] %.4s...no (set ignored)\n", i, + (char *)&v4l1_formats[i].format); + continue; + } + zprintf(2, " [%02d] %.4s...yes\n", i, + (char *)&v4l1_formats[i].format); + vdo->formats[num_formats++] = v4l1_formats[i].format; + } + vdo->formats = realloc(vdo->formats, (num_formats + 1) * sizeof(uint32_t)); + assert(vdo->formats); + + return (v4l1_set_format(vdo, vdo->format)); +} + +static inline int v4l1_init_window(zbar_video_t *vdo) +{ + struct video_window vwin; + memset(&vwin, 0, sizeof(vwin)); + if (ioctl(vdo->fd, VIDIOCGWIN, &vwin) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "querying video window settings (VIDIOCGWIN)")); + + zprintf(1, "current window: %d x %d @(%d, %d)%s\n", vwin.width, vwin.height, + vwin.x, vwin.y, (vwin.flags & 1) ? " INTERLACE" : ""); + + if (vwin.width == vdo->width && vwin.height == vdo->height) + /* max window already set */ + return (0); + + struct video_window maxwin; + memcpy(&maxwin, &vwin, sizeof(maxwin)); + maxwin.width = vdo->width; + maxwin.height = vdo->height; + + zprintf(1, "setting max win: %d x %d @(%d, %d)%s\n", maxwin.width, + maxwin.height, maxwin.x, maxwin.y, + (maxwin.flags & 1) ? " INTERLACE" : ""); + if (ioctl(vdo->fd, VIDIOCSWIN, &maxwin) < 0) { + zprintf(1, "set FAILED...trying to recover original window\n"); + /* ignore errors (driver broken anyway) */ + ioctl(vdo->fd, VIDIOCSWIN, &vwin); + } + + /* re-query resulting parameters */ + memset(&vwin, 0, sizeof(vwin)); + if (ioctl(vdo->fd, VIDIOCGWIN, &vwin) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "querying video window settings (VIDIOCGWIN)")); + + zprintf(1, " final window: %d x %d @(%d, %d)%s\n", vwin.width, + vwin.height, vwin.x, vwin.y, (vwin.flags & 1) ? " INTERLACE" : ""); + vdo->width = vwin.width; + vdo->height = vwin.height; + return (0); +} + +int _zbar_v4l1_probe(zbar_video_t *vdo) +{ + /* check capabilities */ + struct video_capability vcap; + memset(&vcap, 0, sizeof(vcap)); + if (ioctl(vdo->fd, VIDIOCGCAP, &vcap) < 0) + return ( + err_capture(vdo, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, __func__, + "video4linux version 1 not supported (VIDIOCGCAP)")); + + zprintf(1, "%s (%sCAPTURE) (%d x %d) - (%d x %d)\n", vcap.name, + (vcap.type & VID_TYPE_CAPTURE) ? "" : "*NO* ", vcap.minwidth, + vcap.minheight, vcap.maxwidth, vcap.maxheight); + + if (!(vcap.type & VID_TYPE_CAPTURE)) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, __func__, + "v4l1 device does not support CAPTURE")); + + if (!vdo->width || !vdo->height) { + vdo->width = vcap.maxwidth; + vdo->height = vcap.maxheight; + } + + if (v4l1_init_window(vdo) || v4l1_probe_formats(vdo) || + v4l1_probe_iomode(vdo)) + return (-1); + + vdo->intf = VIDEO_V4L1; + vdo->init = v4l1_init; + vdo->cleanup = v4l1_cleanup; + vdo->start = v4l1_start; + vdo->stop = v4l1_stop; + vdo->nq = v4l1_nq; + vdo->dq = v4l1_dq; + return (0); +} diff --git a/zbar/video/v4l2.c b/zbar/video/v4l2.c new file mode 100644 index 0000000..338a4d1 --- /dev/null +++ b/zbar/video/v4l2.c @@ -0,0 +1,1237 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (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_INTTYPES_H +#include <inttypes.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <assert.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif +#ifdef HAVE_SYS_MMAN_H +#include <sys/mman.h> +#endif +#ifdef HAVE_LIBV4L2_H +#include <fcntl.h> +#include <libv4l2.h> +#else +#define v4l2_close close +#define v4l2_ioctl ioctl +#define v4l2_mmap mmap +#define v4l2_munmap munmap +#endif +#include <linux/videodev2.h> + +#include "image.h" +#include "video.h" + +#define V4L2_FORMATS_MAX 64 +#define V4L2_FORMATS_SIZE_MAX 256 + +typedef struct video_controls_priv_s { + struct video_controls_s s; + + // Private fields + __u32 id; +} video_controls_priv_t; + +static int v4l2_nq(zbar_video_t *vdo, zbar_image_t *img) +{ + if (vdo->iomode == VIDEO_READWRITE) + return (video_nq_image(vdo, img)); + + if (video_unlock(vdo)) + return (-1); + + struct v4l2_buffer vbuf; + memset(&vbuf, 0, sizeof(vbuf)); + vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (vdo->iomode == VIDEO_MMAP) { + vbuf.memory = V4L2_MEMORY_MMAP; + vbuf.index = img->srcidx; + } else { + vbuf.memory = V4L2_MEMORY_USERPTR; + vbuf.m.userptr = (unsigned long)img->data; + vbuf.length = img->datalen; + vbuf.index = img->srcidx; /* FIXME workaround broken drivers */ + } + if (v4l2_ioctl(vdo->fd, VIDIOC_QBUF, &vbuf) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "queuing video buffer (VIDIOC_QBUF)")); + return (0); +} + +static zbar_image_t *v4l2_dq(zbar_video_t *vdo) +{ + zbar_image_t *img; + int fd = vdo->fd; + + if (vdo->iomode != VIDEO_READWRITE) { + video_iomode_t iomode = vdo->iomode; + if (video_unlock(vdo)) + return (NULL); + + struct v4l2_buffer vbuf; + memset(&vbuf, 0, sizeof(vbuf)); + vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (iomode == VIDEO_MMAP) + vbuf.memory = V4L2_MEMORY_MMAP; + else + vbuf.memory = V4L2_MEMORY_USERPTR; + + if (v4l2_ioctl(fd, VIDIOC_DQBUF, &vbuf) < 0) + return (NULL); + + if (iomode == VIDEO_MMAP) { + assert(vbuf.index >= 0); + assert(vbuf.index < vdo->num_images); + img = vdo->images[vbuf.index]; + } else { + /* reverse map pointer back to image (FIXME) */ + assert(vbuf.m.userptr >= (unsigned long)vdo->buf); + assert(vbuf.m.userptr < (unsigned long)(vdo->buf + vdo->buflen)); + int i = (vbuf.m.userptr - (unsigned long)vdo->buf) / vdo->datalen; + assert(i >= 0); + assert(i < vdo->num_images); + img = vdo->images[i]; + assert(vbuf.m.userptr == (unsigned long)img->data); + } + } else { + img = video_dq_image(vdo); + if (!img) + return (NULL); + + /* FIXME should read entire image */ + ssize_t datalen = read(fd, (void *)img->data, img->datalen); + if (datalen < 0) { + perror("v4l2_dq read"); + return (NULL); + } else if (datalen != img->datalen) + zprintf(0, "WARNING: read() size mismatch: 0x%lx != 0x%lx\n", + datalen, img->datalen); + } + return (img); +} + +static int v4l2_start(zbar_video_t *vdo) +{ + if (vdo->iomode == VIDEO_READWRITE) + return (0); + + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (v4l2_ioctl(vdo->fd, VIDIOC_STREAMON, &type) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "starting video stream (VIDIOC_STREAMON)")); + return (0); +} + +static int v4l2_stop(zbar_video_t *vdo) +{ + if (vdo->iomode == VIDEO_READWRITE) + return (0); + + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (v4l2_ioctl(vdo->fd, VIDIOC_STREAMOFF, &type) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "stopping video stream (VIDIOC_STREAMOFF)")); + return (0); +} + +static int v4l2_cleanup(zbar_video_t *vdo) +{ + if (vdo->iomode == VIDEO_READWRITE) + return (0); + + struct v4l2_requestbuffers rb; + memset(&rb, 0, sizeof(rb)); + rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (vdo->iomode == VIDEO_MMAP) { + rb.memory = V4L2_MEMORY_MMAP; + int i; + for (i = 0; i < vdo->num_images; i++) { + zbar_image_t *img = vdo->images[i]; + if (img->data && v4l2_munmap((void *)img->data, img->datalen)) + err_capture(vdo, SEV_WARNING, ZBAR_ERR_SYSTEM, __func__, + "unmapping video frame buffers"); + img->data = NULL; + img->datalen = 0; + } + } else + rb.memory = V4L2_MEMORY_USERPTR; + + /* requesting 0 buffers + * should implicitly disable streaming + */ + if (v4l2_ioctl(vdo->fd, VIDIOC_REQBUFS, &rb) < 0) + err_capture(vdo, SEV_WARNING, ZBAR_ERR_SYSTEM, __func__, + "releasing video frame buffers (VIDIOC_REQBUFS)"); + + /* v4l2_close v4l2_open device */ + if (vdo->fd >= 0) { + v4l2_close(vdo->fd); + vdo->fd = -1; + } + return (0); +} + +static int v4l2_mmap_buffers(zbar_video_t *vdo) +{ + struct v4l2_buffer vbuf; + memset(&vbuf, 0, sizeof(vbuf)); + vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vbuf.memory = V4L2_MEMORY_MMAP; + + int i; + for (i = 0; i < vdo->num_images; i++) { + vbuf.index = i; + if (v4l2_ioctl(vdo->fd, VIDIOC_QUERYBUF, &vbuf) < 0) + /* FIXME cleanup */ + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "querying video buffer (VIDIOC_QUERYBUF)")); + + if (vbuf.length < vdo->datalen) + fprintf( + stderr, + "WARNING: insufficient v4l2 video buffer size:\n" + "\tvbuf[%d].length=%x datalen=%lx image=%d x %d %.4s(%08x)\n", + i, vbuf.length, vdo->datalen, vdo->width, vdo->height, + (char *)&vdo->format, vdo->format); + + zbar_image_t *img = vdo->images[i]; + img->datalen = vbuf.length; + img->data = v4l2_mmap(NULL, vbuf.length, PROT_READ | PROT_WRITE, + MAP_SHARED, vdo->fd, vbuf.m.offset); + if (img->data == MAP_FAILED) + /* FIXME cleanup */ + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "mapping video frame buffers")); + zprintf(2, " buf[%d] 0x%lx bytes @%p\n", i, img->datalen, img->data); + } + return (0); +} + +static int v4l2_request_buffers(zbar_video_t *vdo, uint32_t num_images) +{ + struct v4l2_requestbuffers rb; + memset(&rb, 0, sizeof(rb)); + rb.count = num_images; + rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if (vdo->iomode == VIDEO_MMAP) + rb.memory = V4L2_MEMORY_MMAP; + else + rb.memory = V4L2_MEMORY_USERPTR; + + if (v4l2_ioctl(vdo->fd, VIDIOC_REQBUFS, &rb) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "requesting video frame buffers (VIDIOC_REQBUFS)")); + if (num_images && rb.count) + vdo->num_images = rb.count; + return (0); +} + +static int v4l2_set_format(zbar_video_t *vdo, uint32_t fmt) +{ + struct v4l2_format vfmt; + struct v4l2_pix_format *vpix = &vfmt.fmt.pix; + memset(&vfmt, 0, sizeof(vfmt)); + vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vpix->width = vdo->width; + vpix->height = vdo->height; + vpix->pixelformat = fmt; + vpix->field = V4L2_FIELD_NONE; + int rc = 0; + if ((rc = v4l2_ioctl(vdo->fd, VIDIOC_S_FMT, &vfmt)) < 0) { + /* several broken drivers return an error if we request + * no interlacing (NB v4l2 spec violation) + * ...try again with an interlaced request + */ + zprintf(1, "VIDIOC_S_FMT returned %d(%d), trying interlaced...\n", rc, + errno); + + /* FIXME this might be _ANY once we can de-interlace */ + vpix->field = V4L2_FIELD_INTERLACED; + + if (v4l2_ioctl(vdo->fd, VIDIOC_S_FMT, &vfmt) < 0) + return (err_capture_int(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "setting format %x (VIDIOC_S_FMT)", fmt)); + + zprintf(0, "WARNING: broken driver returned error when non-interlaced" + " format requested\n"); + } + + struct v4l2_format newfmt; + struct v4l2_pix_format *newpix = &newfmt.fmt.pix; + memset(&newfmt, 0, sizeof(newfmt)); + newfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (v4l2_ioctl(vdo->fd, VIDIOC_G_FMT, &newfmt) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "querying format (VIDIOC_G_FMT)")); + + if (newpix->field != V4L2_FIELD_NONE) + err_capture(vdo, SEV_WARNING, ZBAR_ERR_INVALID, __func__, + "video driver only supports interlaced format," + " vertical scanning may not work"); + + if (newpix->pixelformat != fmt + /* FIXME bpl/bpp checks? */) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "video driver can't provide compatible format")); + + vdo->format = fmt; + vdo->width = newpix->width; + vdo->height = newpix->height; + vdo->datalen = newpix->sizeimage; + + zprintf(1, "set new format: %.4s(%08x) %u x %u (0x%lx)\n", + (char *)&vdo->format, vdo->format, vdo->width, vdo->height, + vdo->datalen); + return (0); +} + +static int v4l2_init(zbar_video_t *vdo, uint32_t fmt) +{ + struct v4l2_requestbuffers rb; + if (v4l2_set_format(vdo, fmt)) + return (-1); + + memset(&rb, 0, sizeof(rb)); + rb.count = vdo->num_images; + rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (vdo->iomode == VIDEO_MMAP) + rb.memory = V4L2_MEMORY_MMAP; + else + rb.memory = V4L2_MEMORY_USERPTR; + + if (v4l2_ioctl(vdo->fd, VIDIOC_REQBUFS, &rb) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "requesting video frame buffers (VIDIOC_REQBUFS)")); + + if (!rb.count) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "driver returned 0 buffers")); + + if (vdo->num_images > rb.count) + vdo->num_images = rb.count; + + zprintf(1, "using %u buffers (of %d requested)\n", rb.count, + vdo->num_images); + + if (vdo->iomode == VIDEO_MMAP) + return (v4l2_mmap_buffers(vdo)); + if (vdo->iomode == VIDEO_USERPTR) + return (v4l2_request_buffers(vdo, vdo->num_images)); + return (0); +} + +static int v4l2_probe_iomode(zbar_video_t *vdo) +{ + struct v4l2_requestbuffers rb; + memset(&rb, 0, sizeof(rb)); + rb.count = vdo->num_images; + rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (vdo->iomode == VIDEO_MMAP) + rb.memory = V4L2_MEMORY_MMAP; + else + rb.memory = V4L2_MEMORY_USERPTR; + + if (v4l2_ioctl(vdo->fd, VIDIOC_REQBUFS, &rb) < 0) { + if (vdo->iomode) + return (err_capture_int(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "unsupported iomode requested (%d)", + vdo->iomode)); + else if (errno != EINVAL) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "querying streaming mode (VIDIOC_REQBUFS)")); +#ifdef HAVE_SYS_MMAN_H + err_capture(vdo, SEV_WARNING, ZBAR_ERR_SYSTEM, __func__, + "USERPTR failed. Falling back to mmap"); + vdo->iomode = VIDEO_MMAP; +#else + return err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "Userptr not supported, and zbar was compiled without mmap support")); +#endif + } else { + if (!vdo->iomode) + rb.memory = V4L2_MEMORY_USERPTR; + /* Update the num_images with the max supported by the driver */ + if (rb.count) + vdo->num_images = rb.count; + else + err_capture( + vdo, SEV_WARNING, ZBAR_ERR_SYSTEM, __func__, + "Something is wrong: number of buffers returned by REQBUF is zero!"); + + /* requesting 0 buffers + * This cleans up the buffers allocated previously on probe + */ + rb.count = 0; + if (v4l2_ioctl(vdo->fd, VIDIOC_REQBUFS, &rb) < 0) + err_capture(vdo, SEV_WARNING, ZBAR_ERR_SYSTEM, __func__, + "releasing video frame buffers (VIDIOC_REQBUFS)"); + } + return (0); +} + +static inline void v4l2_max_size(zbar_video_t *vdo, uint32_t pixfmt, + uint32_t *max_width, uint32_t *max_height) +{ + int mwidth = 0, mheight = 0, i; + struct v4l2_frmsizeenum frm; + + for (i = 0; i < V4L2_FORMATS_SIZE_MAX; i++) { + memset(&frm, 0, sizeof(frm)); + frm.index = i; + frm.pixel_format = pixfmt; + + if (v4l2_ioctl(vdo->fd, VIDIOC_ENUM_FRAMESIZES, &frm)) + break; + + switch (frm.type) { + case V4L2_FRMSIZE_TYPE_DISCRETE: + mwidth = frm.discrete.width; + mheight = frm.discrete.height; + break; + case V4L2_FRMSIZE_TYPE_CONTINUOUS: + case V4L2_FRMSIZE_TYPE_STEPWISE: + mwidth = frm.stepwise.max_width; + mheight = frm.stepwise.max_height; + break; + default: + continue; + } + if (mwidth > *max_width) + *max_width = mwidth; + if (mheight > *max_height) + *max_height = mheight; + } +} + +static inline int v4l2_probe_formats(zbar_video_t *vdo) +{ + int n_formats = 0, n_emu_formats = 0; + uint32_t max_width = 0, max_height = 0; + + if (vdo->width && vdo->height) + zprintf(1, "Caller requested an specific size: %d x %d\n", vdo->width, + vdo->height); + + zprintf(2, "enumerating supported formats:\n"); + struct v4l2_fmtdesc desc; + memset(&desc, 0, sizeof(desc)); + desc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + for (desc.index = 0; desc.index < V4L2_FORMATS_MAX; desc.index++) { + if (v4l2_ioctl(vdo->fd, VIDIOC_ENUM_FMT, &desc) < 0) + break; + zprintf(2, " [%d] %.4s : %s%s%s\n", desc.index, + (char *)&desc.pixelformat, desc.description, + (desc.flags & V4L2_FMT_FLAG_COMPRESSED) ? " COMPRESSED" : "", + (desc.flags & V4L2_FMT_FLAG_EMULATED) ? " EMULATED" : ""); + if (desc.flags & V4L2_FMT_FLAG_EMULATED) { + vdo->emu_formats = realloc(vdo->emu_formats, + (n_emu_formats + 2) * sizeof(uint32_t)); + vdo->emu_formats[n_emu_formats++] = desc.pixelformat; + } else { + vdo->formats = + realloc(vdo->formats, (n_formats + 2) * sizeof(uint32_t)); + vdo->formats[n_formats++] = desc.pixelformat; + } + + if (!vdo->width || !vdo->height) + v4l2_max_size(vdo, desc.pixelformat, &max_width, &max_height); + } + + if (!vdo->width || !vdo->height) { + zprintf(1, "Max supported size: %d x %d\n", max_width, max_height); + if (max_width && max_height) { + vdo->width = max_width; + vdo->height = max_height; + } else { + /* fallback to large size, driver reduces to max available */ + vdo->width = 640 * 64; + vdo->height = 480 * 64; + } + } + + if (!desc.index) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "enumerating video formats (VIDIOC_ENUM_FMT)")); + if (vdo->formats) + vdo->formats[n_formats] = 0; + if (vdo->emu_formats) + vdo->emu_formats[n_emu_formats] = 0; + if (!vdo->formats && vdo->emu_formats) { + /* + * If only emu formats are available, just move them to vdo->formats. + * This happens when libv4l detects that the only available fourcc + * formats are webcam proprietary formats or bayer formats. + */ + vdo->formats = vdo->emu_formats; + vdo->emu_formats = NULL; + } + + zprintf(2, "Found %d formats and %d emulated formats.\n", n_formats, + n_emu_formats); + + struct v4l2_format fmt; + struct v4l2_pix_format *pix = &fmt.fmt.pix; + memset(&fmt, 0, sizeof(fmt)); + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (v4l2_ioctl(vdo->fd, VIDIOC_G_FMT, &fmt) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "querying current video format (VIDIO_G_FMT)")); + + zprintf(1, "current format: %.4s(%08x) %u x %u%s (line=0x%x size=0x%x)\n", + (char *)&pix->pixelformat, pix->pixelformat, pix->width, + pix->height, (pix->field != V4L2_FIELD_NONE) ? " INTERLACED" : "", + pix->bytesperline, pix->sizeimage); + + vdo->format = pix->pixelformat; + vdo->datalen = pix->sizeimage; + if (pix->width == vdo->width && pix->height == vdo->height) + return (0); + + struct v4l2_format maxfmt; + struct v4l2_pix_format *maxpix = &maxfmt.fmt.pix; + memcpy(&maxfmt, &fmt, sizeof(maxfmt)); + maxpix->width = vdo->width; + maxpix->height = vdo->height; + + zprintf(1, "setting requested size: %d x %d\n", vdo->width, vdo->height); + if (v4l2_ioctl(vdo->fd, VIDIOC_S_FMT, &maxfmt) < 0) { + zprintf(1, "set FAILED...trying to recover original format\n"); + /* ignore errors (driver broken anyway) */ + v4l2_ioctl(vdo->fd, VIDIOC_S_FMT, &fmt); + } + + memset(&fmt, 0, sizeof(fmt)); + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (v4l2_ioctl(vdo->fd, VIDIOC_G_FMT, &fmt) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "querying current video format (VIDIOC_G_FMT)")); + + zprintf(1, "final format: %.4s(%08x) %u x %u%s (line=0x%x size=0x%x)\n", + (char *)&pix->pixelformat, pix->pixelformat, pix->width, + pix->height, (pix->field != V4L2_FIELD_NONE) ? " INTERLACED" : "", + pix->bytesperline, pix->sizeimage); + + vdo->width = pix->width; + vdo->height = pix->height; + vdo->datalen = pix->sizeimage; + return (0); +} + +static inline int v4l2_reset_crop(zbar_video_t *vdo) +{ + /* check cropping */ + struct v4l2_cropcap ccap; + memset(&ccap, 0, sizeof(ccap)); + ccap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (v4l2_ioctl(vdo->fd, VIDIOC_CROPCAP, &ccap) < 0) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "querying crop support (VIDIOC_CROPCAP)")); + + zprintf(1, "crop bounds: %d x %d @ (%d, %d)\n", ccap.bounds.width, + ccap.bounds.height, ccap.bounds.left, ccap.bounds.top); + zprintf(1, "current crop win: %d x %d @ (%d, %d) aspect %d / %d\n", + ccap.defrect.width, ccap.defrect.height, ccap.defrect.left, + ccap.defrect.top, ccap.pixelaspect.numerator, + ccap.pixelaspect.denominator); + +#if 0 + // This logic causes the device to fallback to the current resolution + if(!vdo->width || !vdo->height) { + vdo->width = ccap.defrect.width; + vdo->height = ccap.defrect.height; + } +#endif + + /* reset crop parameters */ + struct v4l2_crop crop; + memset(&crop, 0, sizeof(crop)); + crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + crop.c = ccap.defrect; + if (v4l2_ioctl(vdo->fd, VIDIOC_S_CROP, &crop) < 0 && errno != EINVAL && + errno != ENOTTY) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_SYSTEM, __func__, + "setting default crop window (VIDIOC_S_CROP)")); + return (0); +} + +/** locate a control entry + */ +static struct video_controls_priv_s *v4l2_g_control_def(zbar_video_t *vdo, + const char *name) +{ + struct video_controls_priv_s *p = (void *)vdo->controls; + + while (p) { + if (!strcasecmp(p->s.name, name)) + break; + p = p->s.next; + } + + if (!p->s.name) { + zprintf(1, "Control not found: %s", name); + return NULL; + } + + return p; +} + +void v4l2_free_controls(zbar_video_t *vdo) +{ + int i; + + if (vdo->controls) { + struct video_controls_s *p = vdo->controls; + while (p) { + free(p->name); + free(p->group); + if (p->menu) { + for (i = 0; i < p->menu_size; i++) + free(p->menu[i].name); + free(p->menu); + } + p = p->next; + } + free(vdo->controls); + } + vdo->controls = NULL; +} + +#ifdef VIDIOC_QUERY_EXT_CTRL +static const char *v4l2_ctrl_type(uint32_t type) +{ + switch (type) { + // All controls below are available since, at least, Kernel 2.6.31 + case V4L2_CTRL_TYPE_INTEGER: + return "int"; + case V4L2_CTRL_TYPE_BOOLEAN: + return "bool"; + case V4L2_CTRL_TYPE_MENU: + return "menu"; + case V4L2_CTRL_TYPE_BUTTON: + return "button"; + case V4L2_CTRL_TYPE_INTEGER64: + return "int64"; + case V4L2_CTRL_TYPE_CTRL_CLASS: + return "ctrl class"; + case V4L2_CTRL_TYPE_STRING: + return "string"; +#ifdef V4L2_CTRL_TYPE_INTEGER_MENU + case V4L2_CTRL_TYPE_INTEGER_MENU: + return "int menu"; +#endif +#ifdef V4L2_CTRL_TYPE_U32 + // Newer controls. All of them should be there since Kernel 3.16 + case V4L2_CTRL_TYPE_BITMASK: + return "bitmask"; + case V4L2_CTRL_TYPE_U8: + return "compound u8"; + case V4L2_CTRL_TYPE_U16: + return "compound u16"; + case V4L2_CTRL_TYPE_U32: + return "compound 32"; +#endif + default: + return "unknown"; + } +} + +static const char *v4l2_ctrl_class(uint32_t class) +{ + switch (class) { + // All classes below are available since, at least, Kernel 2.6.31 + case V4L2_CTRL_CLASS_USER: + return "User"; + case V4L2_CTRL_CLASS_MPEG: + return "MPEG-compression"; + case V4L2_CTRL_CLASS_CAMERA: + return "Camera"; + case V4L2_CTRL_CLASS_FM_TX: + return "FM Modulator"; +#ifdef V4L2_CTRL_CLASS_DETECT + // Newer classes added up to Kernel 3.16 + case V4L2_CTRL_CLASS_FLASH: + return "Camera flash"; + case V4L2_CTRL_CLASS_JPEG: + return "JPEG-compression"; + case V4L2_CTRL_CLASS_IMAGE_SOURCE: + return "Image source"; + case V4L2_CTRL_CLASS_IMAGE_PROC: + return "Image processing"; + case V4L2_CTRL_CLASS_DV: + return "Digital Video"; + case V4L2_CTRL_CLASS_FM_RX: + return "FM Receiver"; + case V4L2_CTRL_CLASS_RF_TUNER: + return "RF tuner"; + case V4L2_CTRL_CLASS_DETECT: + return "Detection"; +#endif + default: + return "Unknown"; + } +} + +// return values: 1: ignore, 0: added, -1: silently ignore +static int v4l2_add_control(zbar_video_t *vdo, + struct v4l2_query_ext_ctrl *query, + struct video_controls_priv_s **ptr) +{ + // Control is disabled, ignore it. Please notice that disabled controls + // can be re-enabled. The right thing here would be to get those too, + // and add a logic to + if (query->flags & V4L2_CTRL_FLAG_DISABLED) + return 1; + + /* Silently ignore control classes */ + if (query->type == V4L2_CTRL_TYPE_CTRL_CLASS) + return -1; + + // There's not much sense on displaying permanent read-only controls + if (query->flags & V4L2_CTRL_FLAG_READ_ONLY) + return 1; + + // Allocate a new element on the linked list + if (!vdo->controls) { + *ptr = calloc(1, sizeof(**ptr)); + vdo->controls = (void *)*ptr; + } else { + (*ptr)->s.next = calloc(1, sizeof(**ptr)); + *ptr = (*ptr)->s.next; + } + + // Fill control data + (*ptr)->id = query->id; + (*ptr)->s.name = strdup((const char *)query->name); + (*ptr)->s.group = strdup(v4l2_ctrl_class(V4L2_CTRL_ID2CLASS(query->id))); + switch (query->type) { + case V4L2_CTRL_TYPE_INTEGER: + (*ptr)->s.type = VIDEO_CNTL_INTEGER; + (*ptr)->s.min = query->minimum; + (*ptr)->s.max = query->maximum; + (*ptr)->s.def = query->default_value; + (*ptr)->s.step = query->step; + return (0); + case V4L2_CTRL_TYPE_INTEGER64: + (*ptr)->s.type = VIDEO_CNTL_INTEGER64; + (*ptr)->s.min = query->minimum; + (*ptr)->s.max = query->maximum; + (*ptr)->s.def = query->default_value; + (*ptr)->s.step = query->step; + return (0); + case V4L2_CTRL_TYPE_BOOLEAN: + (*ptr)->s.type = VIDEO_CNTL_BOOLEAN; + return (0); + case V4L2_CTRL_TYPE_BUTTON: + (*ptr)->s.type = VIDEO_CNTL_BUTTON; + return (0); + case V4L2_CTRL_TYPE_STRING: + (*ptr)->s.type = VIDEO_CNTL_STRING; + return (0); +#ifdef V4L2_CTRL_TYPE_INTEGER_MENU + case V4L2_CTRL_TYPE_INTEGER_MENU: +#endif + case V4L2_CTRL_TYPE_MENU: { + struct v4l2_querymenu menu; + struct video_control_menu_s *first = NULL, *p; + int n_menu = 0; + + memset(&menu, 0, sizeof(menu)); + menu.id = query->id; + + for (menu.index = query->minimum; menu.index <= query->maximum; + menu.index++) { + if (!ioctl(vdo->fd, VIDIOC_QUERYMENU, &menu)) { + first = realloc(first, (n_menu + 1) * sizeof(*(*ptr)->s.menu)); + + p = &first[n_menu]; + p->value = menu.index; + +#ifdef V4L2_CTRL_TYPE_INTEGER_MENU + if (query->type == V4L2_CTRL_TYPE_INTEGER_MENU) + asprintf(p->name, "%i", menu.value); + else +#endif /* V4L2_CTRL_TYPE_INTEGER_MENU */ + p->name = strdup((const char *)menu.name); + + n_menu++; + } + } + (*ptr)->s.menu = first; + (*ptr)->s.menu_size = n_menu; + (*ptr)->s.min = query->minimum; + (*ptr)->s.max = query->maximum; + (*ptr)->s.def = query->default_value; + (*ptr)->s.type = VIDEO_CNTL_MENU; + return (0); + } + default: + return (1); + } +} + +static int v4l2_query_controls(zbar_video_t *vdo) +{ + struct video_controls_priv_s *ptr = NULL; + struct v4l2_query_ext_ctrl query; + int ignore; + const char *old_class = NULL; + + // Free controls list if not NULL + v4l2_free_controls(vdo); + + memset(&query, 0, sizeof(query)); + query.id = V4L2_CTRL_FLAG_NEXT_CTRL; + while (!v4l2_ioctl(vdo->fd, VIDIOC_QUERY_EXT_CTRL, &query)) { + ignore = v4l2_add_control(vdo, &query, &ptr); + + if (ignore >= 0 && _zbar_verbosity) { + int i; + const char *class = v4l2_ctrl_class(V4L2_CTRL_ID2CLASS(query.id)); + if (class != old_class) + zprintf(1, "Control class %s:\n", class); + + zprintf(1, "%-10s %-32s - 0x%x%s\n", v4l2_ctrl_type(query.type), + query.name, query.id, ignore ? " - Ignored" : ""); + + for (i = 0; i < ptr->s.menu_size; i++) + zprintf(1, " %" PRId64 ": %s\n", ptr->s.menu[i].value, + ptr->s.menu[i].name); + + old_class = class; + } + + query.id |= V4L2_CTRL_FLAG_NEXT_CTRL; + } + + return (0); +} + +static int v4l2_s_control(zbar_video_t *vdo, const char *name, void *value) +{ + struct v4l2_ext_controls ctrls; + struct v4l2_ext_control c; + struct video_controls_priv_s *p; + + p = v4l2_g_control_def(vdo, name); + if (!p) + return ZBAR_ERR_UNSUPPORTED; // we have no such a control on the list + + memset(&ctrls, 0, sizeof(ctrls)); + ctrls.count = 1; +#ifdef V4L2_CTRL_ID2WHICH + ctrls.which = V4L2_CTRL_ID2WHICH(p->id); +#else + ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id); +#endif + ctrls.controls = &c; + + memset(&c, 0, sizeof(c)); + c.id = p->id; + + switch (p->s.type) { + case VIDEO_CNTL_INTEGER: + case VIDEO_CNTL_BOOLEAN: + case VIDEO_CNTL_BUTTON: + case VIDEO_CNTL_MENU: + c.value = *(int *)value; + break; +#if 0 + //FIXME: Need to check callers with respect bufffer size + case VIDEO_CNTL_INTEGER64: + c.value64 = *(int64_t *)value; + break; +#endif + default: + return ZBAR_ERR_UNSUPPORTED; + } + + int rv = v4l2_ioctl(vdo->fd, VIDIOC_S_EXT_CTRLS, &ctrls); + if (rv) { + zprintf(1, "v4l2 set user control \"%s\" returned %d\n", p->s.name, rv); + rv = ZBAR_ERR_INVALID; + } + zprintf(1, "%-32s id: 0x%x set to value %d\n", name, p->id, *(int *)value); + + return 0; +} + +static int v4l2_g_control(zbar_video_t *vdo, const char *name, void *value) +{ + struct v4l2_ext_controls ctrls; + struct v4l2_ext_control c; + struct video_controls_priv_s *p; + + p = v4l2_g_control_def(vdo, name); + if (!p) + return ZBAR_ERR_UNSUPPORTED; // we have no such a control on the list + + memset(&ctrls, 0, sizeof(ctrls)); + ctrls.count = 1; +#ifdef V4L2_CTRL_ID2WHICH + ctrls.which = V4L2_CTRL_ID2WHICH(p->id); +#else + ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id); +#endif + ctrls.controls = &c; + + memset(&c, 0, sizeof(c)); + c.id = p->id; + + int rv = v4l2_ioctl(vdo->fd, VIDIOC_G_EXT_CTRLS, &ctrls); + if (rv) { + zprintf(1, "v4l2 get user control \"%s\" returned %d\n", p->s.name, rv); + return ZBAR_ERR_UNSUPPORTED; + } + + switch (p->s.type) { + case VIDEO_CNTL_INTEGER: + case VIDEO_CNTL_BOOLEAN: + case VIDEO_CNTL_BUTTON: + case VIDEO_CNTL_MENU: + *(int *)value = c.value; + zprintf(1, "v4l2 get user control \"%s\" = %d\n", p->s.name, c.value); + return (0); +#if 0 + //FIXME: Need to check callers with respect bufffer size + case VIDEO_CNTL_INTEGER64: + *(int64_t *)value = c.value64; + return(0); +#endif + default: + return ZBAR_ERR_UNSUPPORTED; + } +} + +#else /* For very old Kernels < 3.16 (2014) */ +static void v4l2_add_control(zbar_video_t *vdo, char *group_name, + struct v4l2_queryctrl *query, + struct video_controls_priv_s **ptr) +{ + // Control is disabled, ignore it. Please notice that disabled controls + // can be re-enabled. The right thing here would be to get those too, + // and add a logic to + if (query->flags & V4L2_CTRL_FLAG_DISABLED) + return; + + // FIXME: add support also for other control types + if (!((query->type == V4L2_CTRL_TYPE_INTEGER) || + (query->type == V4L2_CTRL_TYPE_BOOLEAN))) + return; + + zprintf(1, "%s %s ctrl %-32s id: 0x%x\n", group_name, + (query->type == V4L2_CTRL_TYPE_INTEGER) ? "int " : "bool", + query->name, query->id); + + // Allocate a new element on the linked list + if (!vdo->controls) { + *ptr = calloc(1, sizeof(**ptr)); + vdo->controls = (void *)*ptr; + } else { + (*ptr)->s.next = calloc(1, sizeof(**ptr)); + *ptr = (*ptr)->s.next; + } + + // Fill control data + (*ptr)->id = query->id; + (*ptr)->s.name = strdup((const char *)query->name); + if (query->type == V4L2_CTRL_TYPE_INTEGER) { + (*ptr)->s.type = VIDEO_CNTL_INTEGER; + (*ptr)->s.min = query->minimum; + (*ptr)->s.max = query->maximum; + (*ptr)->s.def = query->default_value; + (*ptr)->s.step = query->step; + } else { + (*ptr)->s.type = VIDEO_CNTL_BOOLEAN; + } +} + +static int v4l2_query_controls(zbar_video_t *vdo) +{ + int id = 0; + struct video_controls_priv_s *ptr; + struct v4l2_queryctrl query; + + // Free controls list if not NULL + ptr = (void *)vdo->controls; + while (ptr) { + free(ptr->s.name); + ptr = ptr->s.next; + } + free(vdo->controls); + vdo->controls = NULL; + ptr = NULL; + + id = 0; + do { + query.id = id | V4L2_CTRL_FLAG_NEXT_CTRL; + if (v4l2_ioctl(vdo->fd, VIDIOC_QUERYCTRL, &query)) + break; + + v4l2_add_control(vdo, "extended", &query, &ptr); + id = query.id; + } while (1); + + id = V4L2_CID_PRIVATE_BASE; + do { + query.id = id; + if (v4l2_ioctl(vdo->fd, VIDIOC_QUERYCTRL, &query)) + break; + v4l2_add_control(vdo, "private", &query, &ptr); + id = query.id; + } while (1); + + return (0); +} + +static int v4l2_s_control(zbar_video_t *vdo, const char *name, void *value) +{ + struct v4l2_control cs; + struct video_controls_priv_s *p; + + p = v4l2_g_control_def(vdo, name); + if (!p) + return ZBAR_ERR_UNSUPPORTED; // we have no such a control on the list + + zprintf(1, "%-32s id: 0x%x set to value %d\n", name, p->id, *(int *)value); + + // FIXME: add support for VIDIOC_S_EXT_CTRL + memset(&cs, 0, sizeof(cs)); + cs.id = p->id; + cs.value = *(int *)value; + int rv = v4l2_ioctl(vdo->fd, VIDIOC_S_CTRL, &cs); + if (rv != 0) { + zprintf(1, "v4l2 set user control \"%s\" returned %d\n", p->s.name, rv); + rv = ZBAR_ERR_INVALID; + } + return rv; +} + +static int v4l2_g_control(zbar_video_t *vdo, const char *name, void *value) +{ + struct v4l2_control cs; + struct video_controls_priv_s *p; + + p = v4l2_g_control_def(vdo, name); + if (!p) + return ZBAR_ERR_UNSUPPORTED; // we have no such a control on the list + + memset(&cs, 0, sizeof(cs)); + + cs.id = p->id; + cs.value = *(int *)value; + int rv = v4l2_ioctl(vdo->fd, VIDIOC_G_CTRL, &cs); + *(int *)value = cs.value; + if (rv != 0) { + zprintf(1, "v4l2 get user control \"%s\" returned %d\n", p->s.name, rv); + rv = ZBAR_ERR_UNSUPPORTED; + } + return rv; +} +#endif /* VIDIOC_QUERY_EXT_CTRL */ + +static int v4l2_sort_resolutions(const void *__a, const void *__b) +{ + const struct video_resolution_s *a = __a; + const struct video_resolution_s *b = __b; + int r; + + r = (int)b->width - a->width; + if (!r) + r = (int)b->height - a->height; + + return r; +} + +static float v4l2_get_max_fps_discrete(zbar_video_t *vdo, + struct v4l2_frmsizeenum *frmsize) +{ + struct v4l2_frmivalenum frmival = { 0 }; + float fps, max_fps = -1; + + frmival.width = frmsize->discrete.width; + frmival.height = frmsize->discrete.height; + frmival.pixel_format = frmsize->pixel_format; + frmival.index = 0; + + for (frmival.index = 0; + !v4l2_ioctl(vdo->fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival); + frmival.index++) { + fps = + ((float)frmival.discrete.denominator) / frmival.discrete.numerator; + if (fps > max_fps) + max_fps = fps; + } + return max_fps; +} + +static void v4l2_insert_resolution(zbar_video_t *vdo, unsigned int *n_res, + unsigned int width, unsigned int height, + float max_fps) +{ + unsigned int i; + + for (i = 0; i < *n_res; i++) { + if (vdo->res[i].width == width && vdo->res[i].height == height) + return; + } + + vdo->res = + realloc(vdo->res, (*n_res + 1) * sizeof(struct video_resolution_s)); + + vdo->res[*n_res].width = width; + vdo->res[*n_res].height = height; + vdo->res[*n_res].max_fps = max_fps; + + (*n_res)++; +} + +static int v4l2_get_supported_resolutions(zbar_video_t *vdo) +{ + struct v4l2_fmtdesc fmt = { 0 }; + struct v4l2_frmsizeenum frmsize = { 0 }; + int i; + unsigned int width, height, n_res = 0; + + vdo->res = NULL; + + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + for (fmt.index = 0; !v4l2_ioctl(vdo->fd, VIDIOC_ENUM_FMT, &fmt); + fmt.index++) { + if (vdo->format != fmt.pixelformat) + continue; + + frmsize.pixel_format = fmt.pixelformat; + frmsize.index = 0; + + while (!v4l2_ioctl(vdo->fd, VIDIOC_ENUM_FRAMESIZES, &frmsize)) { + if (frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) { + v4l2_insert_resolution(vdo, &n_res, frmsize.discrete.width, + frmsize.discrete.height, + v4l2_get_max_fps_discrete(vdo, + &frmsize)); + } else if (frmsize.type == V4L2_FRMSIZE_TYPE_STEPWISE) { + for (i = 0; i <= 4; i++) { + width = frmsize.stepwise.min_width + + i * + (frmsize.stepwise.max_width - + frmsize.stepwise.min_width) / + 4; + height = frmsize.stepwise.min_height + + i * + (frmsize.stepwise.max_height - + frmsize.stepwise.min_height) / + 4; + v4l2_insert_resolution(vdo, &n_res, width, height, -1); + } + } + frmsize.index++; + } + } + qsort(vdo->res, n_res, sizeof(struct video_resolution_s), + v4l2_sort_resolutions); + + for (i = 0; i < n_res; i++) { + zprintf(1, "%dx%d (%0.2f fps)\n", vdo->res[i].width, vdo->res[i].height, + vdo->res[i].max_fps); + } + + /* Make the list zero-terminated */ + v4l2_insert_resolution(vdo, &n_res, 0, 0, 0); + + return 0; +} + +int _zbar_v4l2_probe(zbar_video_t *vdo) +{ + /* check capabilities */ + struct v4l2_capability vcap; + memset(&vcap, 0, sizeof(vcap)); + if (v4l2_ioctl(vdo->fd, VIDIOC_QUERYCAP, &vcap) < 0) + return (err_capture( + vdo, SEV_WARNING, ZBAR_ERR_UNSUPPORTED, __func__, + "video4linux version 2 not supported (VIDIOC_QUERYCAP)")); + + zprintf(1, "%.32s on %.32s driver %.16s (version %u.%u.%u)\n", vcap.card, + (vcap.bus_info[0]) ? (char *)vcap.bus_info : "<unknown>", + vcap.driver, (vcap.version >> 16) & 0xff, + (vcap.version >> 8) & 0xff, vcap.version & 0xff); + zprintf(1, " capabilities:%s%s%s%s\n", + (vcap.device_caps & V4L2_CAP_VIDEO_CAPTURE) ? " CAPTURE" : "", + (vcap.device_caps & V4L2_CAP_VIDEO_OVERLAY) ? " OVERLAY" : "", + (vcap.device_caps & V4L2_CAP_READWRITE) ? " READWRITE" : "", + (vcap.device_caps & V4L2_CAP_STREAMING) ? " STREAMING" : ""); + + if (!(vcap.device_caps & V4L2_CAP_VIDEO_CAPTURE) || + !(vcap.device_caps & (V4L2_CAP_READWRITE | V4L2_CAP_STREAMING))) + return (err_capture(vdo, SEV_WARNING, ZBAR_ERR_UNSUPPORTED, __func__, + "v4l2 device does not support usable CAPTURE")); + + if (v4l2_reset_crop(vdo)) + /* ignoring errors (driver cropping support questionable) */; + + if (v4l2_probe_formats(vdo)) + return (-1); + + if (v4l2_query_controls(vdo)) + return (-1); + + if (v4l2_get_supported_resolutions(vdo)) + return (-1); + + /* FIXME report error and fallback to readwrite? (if supported...) */ + if (vdo->iomode != VIDEO_READWRITE && + (vcap.device_caps & V4L2_CAP_STREAMING) && v4l2_probe_iomode(vdo)) + return (-1); + if (!vdo->iomode) + vdo->iomode = VIDEO_READWRITE; + + zprintf(1, "using I/O mode: %s\n", + (vdo->iomode == VIDEO_READWRITE) ? "READWRITE" : + (vdo->iomode == VIDEO_MMAP) ? "MMAP" : + (vdo->iomode == VIDEO_USERPTR) ? "USERPTR" : + "<UNKNOWN>"); + + vdo->intf = VIDEO_V4L2; + vdo->init = v4l2_init; + vdo->cleanup = v4l2_cleanup; + vdo->start = v4l2_start; + vdo->stop = v4l2_stop; + vdo->nq = v4l2_nq; + vdo->dq = v4l2_dq; + vdo->set_control = v4l2_s_control; + vdo->get_control = v4l2_g_control; + vdo->free = v4l2_free_controls; + return (0); +} diff --git a/zbar/video/vfw.c b/zbar/video/vfw.c new file mode 100644 index 0000000..a9e050d --- /dev/null +++ b/zbar/video/vfw.c @@ -0,0 +1,511 @@ +/*------------------------------------------------------------------------ + * Copyright 2009 (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 "thread.h" +#include "video.h" + +#include <vfw.h> + +#include <assert.h> + +#define MAX_DRIVERS 10 +#define MAX_NAME 128 + +#define BIH_FMT \ + "%ldx%ld @%dbpp (%lx) cmp=%.4s(%08lx) res=%ldx%ld clr=%ld/%ld (%lx)" +#define BIH_FIELDS(bih) \ + (bih)->biWidth, (bih)->biHeight, (bih)->biBitCount, (bih)->biSizeImage, \ + (char *)&(bih)->biCompression, (bih)->biCompression, \ + (bih)->biXPelsPerMeter, (bih)->biYPelsPerMeter, (bih)->biClrImportant, \ + (bih)->biClrUsed, (bih)->biSize + +struct video_state_s { + zbar_thread_t thread; /* capture message pump */ + HANDLE captured; + HWND hwnd; /* vfw interface */ + HANDLE notify; /* capture thread status change */ + int bi_size; /* size of bih */ + BITMAPINFOHEADER *bih; /* video format details */ + zbar_image_t *image; /* currently capturing frame */ +}; + +static const uint32_t vfw_formats[] = { + /* planar YUV formats */ + fourcc('I', '4', '2', '0'), + /* FIXME YU12 is IYUV in windows */ + fourcc('Y', 'V', '1', '2'), + /* FIXME IMC[1-4]? */ + + /* planar Y + packed UV plane */ + fourcc('N', 'V', '1', '2'), + + /* packed YUV formats */ + fourcc('U', 'Y', 'V', 'Y'), fourcc('Y', 'U', 'Y', '2'), /* FIXME add YVYU */ + /* FIXME AYUV? Y411? Y41P? */ + + /* packed rgb formats */ + fourcc('B', 'G', 'R', '3'), fourcc('B', 'G', 'R', '4'), + + fourcc('Y', 'V', 'U', '9'), + + /* basic grayscale format */ + fourcc('G', 'R', 'E', 'Y'), fourcc('Y', '8', '0', '0'), + + /* compressed formats */ + fourcc('J', 'P', 'E', 'G'), + + /* terminator */ + 0 +}; + +#define VFW_NUM_FORMATS (sizeof(vfw_formats) / sizeof(uint32_t)) + +static ZTHREAD vfw_capture_thread(void *arg) +{ + MSG msg; + int rc = 0; + + zbar_video_t *vdo = arg; + video_state_t *state = vdo->state; + zbar_thread_t *thr = &state->thread; + + state->hwnd = capCreateCaptureWindow(NULL, WS_POPUP, 0, 0, 1, 1, NULL, 0); + if (!state->hwnd) + goto done; + + _zbar_mutex_lock(&vdo->qlock); + _zbar_thread_init(thr); + zprintf(4, "spawned vfw capture thread (thr=%04lx)\n", _zbar_thread_self()); + + while (thr->started && rc >= 0 && rc <= 1) { + _zbar_mutex_unlock(&vdo->qlock); + + rc = MsgWaitForMultipleObjects(1, &thr->notify, 0, INFINITE, + QS_ALLINPUT); + if (rc == 1) + while (PeekMessage(&msg, NULL, 0, 0, PM_NOYIELD | PM_REMOVE)) + if (rc > 0) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + _zbar_mutex_lock(&vdo->qlock); + } + +done: + thr->running = 0; + _zbar_event_trigger(&thr->activity); + _zbar_mutex_unlock(&vdo->qlock); + return (0); +} + +static LRESULT CALLBACK vfw_stream_cb(HWND hwnd, VIDEOHDR *hdr) +{ + zbar_video_t *vdo; + zbar_image_t *img; + + if (!hwnd || !hdr) + return (0); + vdo = (void *)capGetUserData(hwnd); + + _zbar_mutex_lock(&vdo->qlock); + img = vdo->state->image; + if (!img) { + _zbar_mutex_lock(&vdo->qlock); + img = video_dq_image(vdo); + } + if (img) { + img->data = hdr->lpData; + img->datalen = hdr->dwBufferLength; + vdo->state->image = img; + SetEvent(vdo->state->captured); + } + _zbar_mutex_unlock(&vdo->qlock); + + return (1); +} + +static LRESULT CALLBACK vfw_error_cb(HWND hwnd, int errid, const char *errmsg) +{ + zbar_video_t *vdo; + if (!hwnd) + return (0); + vdo = (void *)capGetUserData(hwnd); + zprintf(2, "id=%d msg=%s\n", errid, errmsg); + _zbar_mutex_lock(&vdo->qlock); + vdo->state->image = NULL; + SetEvent(vdo->state->captured); + _zbar_mutex_unlock(&vdo->qlock); + return (1); +} + +static int vfw_nq(zbar_video_t *vdo, zbar_image_t *img) +{ + img->data = NULL; + img->datalen = 0; + return (video_nq_image(vdo, img)); +} + +/// Platform dependent part of #zbar_video_next_image, which blocks +/// until an image is available. +/** Must be called with video lock held and returns + * with the lock released. + * <p>Waits for the image from `vdo->state->image`. If available, + * this field is nulled. Releases the lock temporarily when waiting for + * the `vdo->state->captured` signal. */ +static zbar_image_t *vfw_dq(zbar_video_t *vdo) +{ + zbar_image_t *img = vdo->state->image; + if (!img) { + int rc; + _zbar_mutex_unlock(&vdo->qlock); + rc = WaitForSingleObject(vdo->state->captured, INFINITE); + // note: until we get the lock again the grabber thread might + // already provide the next sample (which is fine) + _zbar_mutex_lock(&vdo->qlock); + + switch (rc) { + case WAIT_OBJECT_0: + img = vdo->state->image; + break; + case WAIT_ABANDONED: + err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "event handle abandoned"); + break; + case WAIT_FAILED: + err_capture(vdo, SEV_ERROR, ZBAR_ERR_WINAPI, __func__, + "Waiting for image failed"); + break; + } + } + + vdo->state->image = NULL; + ResetEvent(vdo->state->captured); + + video_unlock(vdo); + return (img); +} + +static int vfw_start(zbar_video_t *vdo) +{ + ResetEvent(vdo->state->captured); + + if (!capCaptureSequenceNoFile(vdo->state->hwnd)) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "starting video stream")); + return (0); +} + +static int vfw_stop(zbar_video_t *vdo) +{ + video_state_t *state = vdo->state; + if (!capCaptureAbort(state->hwnd)) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "stopping video stream")); + + _zbar_mutex_lock(&vdo->qlock); + if (state->image) + state->image = NULL; + SetEvent(state->captured); + _zbar_mutex_unlock(&vdo->qlock); + return (0); +} + +static int vfw_set_format(zbar_video_t *vdo, uint32_t fmt) +{ + BITMAPINFOHEADER *bih; + const zbar_format_def_t *fmtdef = _zbar_format_lookup(fmt); + if (!fmtdef->format) + return (err_capture_int(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "unsupported vfw format: %x", fmt)); + + bih = vdo->state->bih; + assert(bih); + bih->biWidth = vdo->width; + bih->biHeight = vdo->height; + switch (fmtdef->group) { + case ZBAR_FMT_GRAY: + bih->biBitCount = 8; + break; + case ZBAR_FMT_YUV_PLANAR: + case ZBAR_FMT_YUV_PACKED: + case ZBAR_FMT_YUV_NV: + bih->biBitCount = + 8 + (16 >> (fmtdef->p.yuv.xsub2 + fmtdef->p.yuv.ysub2)); + break; + case ZBAR_FMT_RGB_PACKED: + bih->biBitCount = fmtdef->p.rgb.bpp * 8; + break; + default: + bih->biBitCount = 0; + } + bih->biClrUsed = bih->biClrImportant = 0; + bih->biCompression = fmt; + + zprintf(8, "setting format: %.4s(%08x) " BIH_FMT "\n", (char *)&fmt, fmt, + BIH_FIELDS(bih)); + + if (!capSetVideoFormat(vdo->state->hwnd, bih, vdo->state->bi_size)) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "setting video format")); + + if (!capGetVideoFormat(vdo->state->hwnd, bih, vdo->state->bi_size)) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "getting video format")); + + if (bih->biCompression != fmt) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "video format set ignored")); + + vdo->format = fmt; + vdo->width = bih->biWidth; + vdo->height = bih->biHeight; + vdo->datalen = bih->biSizeImage; + + zprintf(4, "set new format: %.4s(%08x) " BIH_FMT "\n", (char *)&fmt, fmt, + BIH_FIELDS(bih)); + return (0); +} + +static int vfw_init(zbar_video_t *vdo, uint32_t fmt) +{ + HWND hwnd; + CAPTUREPARMS cp; + + if (vfw_set_format(vdo, fmt)) + return (-1); + + hwnd = vdo->state->hwnd; + if (!capCaptureGetSetup(hwnd, &cp, sizeof(cp))) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_WINAPI, __func__, + "retrieving capture parameters")); + + cp.dwRequestMicroSecPerFrame = 33333; + cp.fMakeUserHitOKToCapture = 0; + cp.wPercentDropForError = 90; + cp.fYield = 1; + cp.wNumVideoRequested = vdo->num_images; + cp.fCaptureAudio = 0; + cp.vKeyAbort = 0; + cp.fAbortLeftMouse = 0; + cp.fAbortRightMouse = 0; + cp.fLimitEnabled = 0; + + if (!capCaptureSetSetup(hwnd, &cp, sizeof(cp))) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_WINAPI, __func__, + "setting capture parameters")); + + if (!capCaptureGetSetup(hwnd, &cp, sizeof(cp))) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_WINAPI, __func__, + "checking capture parameters")); + + /* ignore errors since we skipped checking fHasOverlay */ + capOverlay(hwnd, 0); + + if (!capPreview(hwnd, 0) || !capPreviewScale(hwnd, 0)) + err_capture(vdo, SEV_WARNING, ZBAR_ERR_WINAPI, __func__, + "disabling preview"); + + if (!capSetCallbackOnVideoStream(hwnd, vfw_stream_cb) || + !capSetCallbackOnError(hwnd, vfw_error_cb)) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_BUSY, __func__, + "setting capture callbacks")); + + vdo->num_images = cp.wNumVideoRequested; + vdo->iomode = VIDEO_MMAP; /* driver provides "locked" buffers */ + + zprintf(3, "initialized video capture: %d buffers %ldms/frame\n", + vdo->num_images, cp.dwRequestMicroSecPerFrame); + + return (0); +} + +static int vfw_cleanup(zbar_video_t *vdo) +{ + /* close open device */ + video_state_t *state = vdo->state; + /* NB this has to go here so the thread can pump messages during cleanup */ + capDriverDisconnect(state->hwnd); + DestroyWindow(state->hwnd); + state->hwnd = NULL; + _zbar_thread_stop(&state->thread, &vdo->qlock); + + if (state->captured) { + CloseHandle(state->captured); + state->captured = NULL; + } + return (0); +} + +static int vfw_probe_format(zbar_video_t *vdo, uint32_t fmt) +{ + BITMAPINFOHEADER *bih; + const zbar_format_def_t *fmtdef = _zbar_format_lookup(fmt); + if (!fmtdef) + return (0); + + zprintf(4, " trying %.4s(%08x)...\n", (char *)&fmt, fmt); + bih = vdo->state->bih; + bih->biWidth = vdo->width; + bih->biHeight = vdo->height; + switch (fmtdef->group) { + case ZBAR_FMT_GRAY: + bih->biBitCount = 8; + break; + case ZBAR_FMT_YUV_PLANAR: + case ZBAR_FMT_YUV_PACKED: + case ZBAR_FMT_YUV_NV: + bih->biBitCount = + 8 + (16 >> (fmtdef->p.yuv.xsub2 + fmtdef->p.yuv.ysub2)); + break; + case ZBAR_FMT_RGB_PACKED: + bih->biBitCount = fmtdef->p.rgb.bpp * 8; + break; + default: + bih->biBitCount = 0; + } + bih->biCompression = fmt; + + if (!capSetVideoFormat(vdo->state->hwnd, bih, vdo->state->bi_size)) { + zprintf(4, "\tno (set fails)\n"); + return (0); + } + + if (!capGetVideoFormat(vdo->state->hwnd, bih, vdo->state->bi_size)) + return (0 /*FIXME error...*/); + + zprintf(6, "\tactual: " BIH_FMT "\n", BIH_FIELDS(bih)); + + if (bih->biCompression != fmt) { + zprintf(4, "\tno (set ignored)\n"); + return (0); + } + + zprintf(4, "\tyes\n"); + return (1); +} + +static int vfw_probe(zbar_video_t *vdo) +{ + BITMAPINFOHEADER *bih; + int n = 0; + const uint32_t *fmt; + + video_state_t *state = vdo->state; + state->bi_size = capGetVideoFormatSize(state->hwnd); + bih = state->bih = realloc(state->bih, state->bi_size); + /* FIXME check OOM */ + + if (!capSetUserData(state->hwnd, (LONG)vdo) || !state->bi_size || !bih || + !capGetVideoFormat(state->hwnd, bih, state->bi_size)) + return (err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "setting up video capture")); + + zprintf(3, "initial format: " BIH_FMT " (bisz=%x)\n", BIH_FIELDS(bih), + state->bi_size); + + if (!vdo->width || !vdo->height) { + vdo->width = bih->biWidth; + vdo->height = bih->biHeight; + } + vdo->datalen = bih->biSizeImage; + + zprintf(2, "probing supported formats:\n"); + vdo->formats = calloc(VFW_NUM_FORMATS, sizeof(uint32_t)); + + for (fmt = vfw_formats; *fmt; fmt++) + if (vfw_probe_format(vdo, *fmt)) + vdo->formats[n++] = *fmt; + + vdo->formats = realloc(vdo->formats, (n + 1) * sizeof(uint32_t)); + + vdo->width = bih->biWidth; + vdo->height = bih->biHeight; + vdo->intf = VIDEO_VFW; + vdo->init = vfw_init; + vdo->start = vfw_start; + vdo->stop = vfw_stop; + vdo->cleanup = vfw_cleanup; + vdo->nq = vfw_nq; + vdo->dq = vfw_dq; + return (0); +} + +int _zbar_video_open(zbar_video_t *vdo, const char *dev) +{ + int reqid = -1; + char name[MAX_NAME], desc[MAX_NAME]; + int devid; + + video_state_t *state = vdo->state; + if (!state) + state = vdo->state = calloc(1, sizeof(video_state_t)); + + if ((!strncmp(dev, "/dev/video", 10) || + !strncmp(dev, "\\dev\\video", 10)) && + dev[10] >= '0' && dev[10] <= '9' && !dev[11]) + reqid = dev[10] - '0'; + else if (strlen(dev) == 1 && dev[0] >= '0' && dev[0] <= '9') + reqid = dev[0] - '0'; + + zprintf(6, "searching for camera: %s (%d)\n", dev, reqid); + for (devid = 0; devid < MAX_DRIVERS; devid++) { + if (!capGetDriverDescription(devid, name, MAX_NAME, desc, MAX_NAME)) { + /* FIXME TBD error */ + zprintf(6, " [%d] not found...\n", devid); + continue; + } + zprintf(6, " [%d] %.100s - %.100s\n", devid, name, desc); + if ((reqid >= 0) ? devid == reqid : !strncmp(dev, name, MAX_NAME)) + break; + } + if (devid >= MAX_DRIVERS) + return (err_capture_str(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "video device not found '%s'", dev)); + + if (!state->captured) + state->captured = CreateEvent(NULL, TRUE, FALSE, NULL); + else + ResetEvent(state->captured); + + if (_zbar_thread_start(&state->thread, vfw_capture_thread, vdo, NULL)) + return (-1); + + /* FIXME error */ + assert(state->hwnd); + + if (!capDriverConnect(state->hwnd, devid)) { + _zbar_thread_stop(&state->thread, NULL); + return (err_capture_str(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "failed to connect to camera '%s'", dev)); + } + + zprintf(1, "opened camera: %.60s (%d) (thr=%04lx)\n", name, devid, + _zbar_thread_self()); + + if (vfw_probe(vdo)) { + _zbar_thread_stop(&state->thread, NULL); + return (-1); + } + return (0); +} diff --git a/zbar/window.c b/zbar/window.c new file mode 100644 index 0000000..80a952e --- /dev/null +++ b/zbar/window.c @@ -0,0 +1,305 @@ +/*------------------------------------------------------------------------ + * 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 "window.h" +#include <time.h> /* clock_gettime */ +#include "image.h" +#include "timer.h" +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> /* gettimeofday */ +#endif + +zbar_window_t *zbar_window_create() +{ + zbar_window_t *w = calloc(1, sizeof(zbar_window_t)); + if (!w) + return (NULL); + err_init(&w->err, ZBAR_MOD_WINDOW); + w->overlay = 1; + (void)_zbar_mutex_init(&w->imglock); + return (w); +} + +void zbar_window_destroy(zbar_window_t *w) +{ + /* detach */ + zbar_window_attach(w, NULL, 0); + err_cleanup(&w->err); + _zbar_mutex_destroy(&w->imglock); + free(w); +} + +int zbar_window_attach(zbar_window_t *w, void *display, unsigned long drawable) +{ + /* release image */ + zbar_window_draw(w, NULL); + if (w->cleanup) { + w->cleanup(w); + w->cleanup = NULL; + w->draw_image = NULL; + } + if (w->formats) { + free(w->formats); + w->formats = NULL; + } + w->src_format = 0; + w->src_width = w->src_height = 0; + w->scaled_size.x = w->scaled_size.y = 0; + w->dst_width = w->dst_height = 0; + w->max_width = w->max_height = 1 << 15; + w->scale_num = w->scale_den = 1; + return (_zbar_window_attach(w, display, drawable)); +} + +static void window_outline_symbol(zbar_window_t *w, uint32_t color, + const zbar_symbol_t *sym) +{ + if (sym->syms) { + const zbar_symbol_t *s; + for (s = sym->syms->head; s; s = s->next) + window_outline_symbol(w, 1, s); + } + _zbar_window_draw_polygon(w, color, sym->pts, sym->npts); +} + +static inline int window_draw_overlay(zbar_window_t *w) +{ + if (!w->overlay) + return (0); + if (w->overlay >= 1 && w->image && w->image->syms) { + /* FIXME outline each symbol */ + const zbar_symbol_t *sym = w->image->syms->head; + for (; sym; sym = sym->next) { + uint32_t color = ((sym->cache_count < 0) ? 4 : 2); + if (sym->type == ZBAR_QRCODE || sym->type == ZBAR_SQCODE) + window_outline_symbol(w, color, sym); + else { + /* FIXME linear bbox broken */ + point_t org = w->scaled_offset; + int i; + for (i = 0; i < sym->npts; i++) { + point_t p = window_scale_pt(w, sym->pts[i]); + p.x += org.x; + p.y += org.y; + if (p.x < 3) + p.x = 3; + else if (p.x > w->width - 4) + p.x = w->width - 4; + if (p.y < 3) + p.y = 3; + else if (p.y > w->height - 4) + p.y = w->height - 4; + _zbar_window_draw_marker(w, color, p); + } + } + } + } + + if (w->overlay >= 2) { + /* calculate/display frame rate */ + unsigned long time = _zbar_timer_now(); + if (w->time) { + int avg = w->time_avg = (w->time_avg + time - w->time) / 2; + point_t p = { -8, -1 }; + char text[32]; + sprintf(text, "%d.%01d fps", 1000 / avg, (10000 / avg) % 10); + _zbar_window_draw_text(w, 3, p, text); + } + w->time = time; + } + return (0); +} + +inline int zbar_window_redraw(zbar_window_t *w) +{ + int rc = 0; + zbar_image_t *img; + if (window_lock(w)) + return (-1); + if (!w->display || _zbar_window_begin(w)) { + (void)window_unlock(w); + return (-1); + } + + img = w->image; + if (w->init && w->draw_image && img) { + int format_change = + (w->src_format != img->format && w->format != img->format); + if (format_change) { + _zbar_best_format(img->format, &w->format, w->formats); + if (!w->format) + rc = err_capture_int( + w, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, __func__, + "no conversion from %x to supported formats", img->format); + w->src_format = img->format; + } + + if (!rc && (format_change || !w->scaled_size.x || !w->dst_width)) { + point_t size = { w->width, w->height }; + zprintf(24, "init: src=%.4s(%08x) %dx%d dst=%.4s(%08x) %dx%d\n", + (char *)&w->src_format, w->src_format, w->src_width, + w->src_height, (char *)&w->format, w->format, w->dst_width, + w->dst_height); + if (!w->dst_width) { + w->src_width = img->width; + w->src_height = img->height; + } + + if (size.x > w->max_width) + size.x = w->max_width; + if (size.y > w->max_height) + size.y = w->max_height; + + if (size.x * w->src_height < size.y * w->src_width) { + w->scale_num = size.x; + w->scale_den = w->src_width; + } else { + w->scale_num = size.y; + w->scale_den = w->src_height; + } + + rc = w->init(w, img, format_change); + + if (!rc) { + size.x = w->src_width; + size.y = w->src_height; + w->scaled_size = size = window_scale_pt(w, size); + w->scaled_offset.x = ((int)w->width - size.x) / 2; + w->scaled_offset.y = ((int)w->height - size.y) / 2; + zprintf(24, + "scale: src=%dx%d win=%dx%d by %d/%d => %dx%d @%d,%d\n", + w->src_width, w->src_height, w->width, w->height, + w->scale_num, w->scale_den, size.x, size.y, + w->scaled_offset.x, w->scaled_offset.y); + } else { + /* unable to display this image */ + _zbar_image_refcnt(img, -1); + w->image = img = NULL; + } + } + + if (!rc && (img->format != w->format || img->width != w->dst_width || + img->height != w->dst_height)) { + /* save *converted* image for redraw */ + zprintf(48, "convert: %.4s(%08x) %dx%d => %.4s(%08x) %dx%d\n", + (char *)&img->format, img->format, img->width, img->height, + (char *)&w->format, w->format, w->dst_width, w->dst_height); + w->image = zbar_image_convert_resize(img, w->format, w->dst_width, + w->dst_height); + w->image->syms = img->syms; + if (img->syms) + zbar_symbol_set_ref(img->syms, 1); + zbar_image_destroy(img); + img = w->image; + } + + if (!rc) { + point_t org; + rc = w->draw_image(w, img); + + org = w->scaled_offset; + if (org.x > 0) { + point_t p = { 0, org.y }; + point_t s = { org.x, w->scaled_size.y }; + _zbar_window_fill_rect(w, 0, p, s); + s.x = w->width - w->scaled_size.x - s.x; + if (s.x > 0) { + p.x = w->width - s.x; + _zbar_window_fill_rect(w, 0, p, s); + } + } + if (org.y > 0) { + point_t p = { 0, 0 }; + point_t s = { w->width, org.y }; + _zbar_window_fill_rect(w, 0, p, s); + s.y = w->height - w->scaled_size.y - s.y; + if (s.y > 0) { + p.y = w->height - s.y; + _zbar_window_fill_rect(w, 0, p, s); + } + } + } + if (!rc) + rc = window_draw_overlay(w); + } else + rc = 1; + + if (rc) + rc = _zbar_window_draw_logo(w); + + _zbar_window_end(w); + (void)window_unlock(w); + return (rc); +} + +int zbar_window_draw(zbar_window_t *w, zbar_image_t *img) +{ + if (window_lock(w)) + return (-1); + if (!w->draw_image) + img = NULL; + if (img) { + _zbar_image_refcnt(img, 1); + if (img->width != w->src_width || img->height != w->src_height) + w->dst_width = 0; + } + if (w->image) + _zbar_image_refcnt(w->image, -1); + w->image = img; + return (window_unlock(w)); +} + +void zbar_window_set_overlay(zbar_window_t *w, int lvl) +{ + if (lvl < 0) + lvl = 0; + if (lvl > 2) + lvl = 2; + if (window_lock(w)) + return; + if (w->overlay != lvl) + w->overlay = lvl; + (void)window_unlock(w); +} + +int zbar_window_get_overlay(const zbar_window_t *w) +{ + zbar_window_t *ncw = (zbar_window_t *)w; + int lvl; + if (window_lock(ncw)) + return (-1); + lvl = w->overlay; + (void)window_unlock(ncw); + return (lvl); +} + +int zbar_window_resize(zbar_window_t *w, unsigned width, unsigned height) +{ + if (window_lock(w)) + return (-1); + w->width = width; + w->height = height; + w->scaled_size.x = 0; + _zbar_window_resize(w); + return (window_unlock(w)); +} diff --git a/zbar/window.h b/zbar/window.h new file mode 100644 index 0000000..d0ecc44 --- /dev/null +++ b/zbar/window.h @@ -0,0 +1,142 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (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 + *------------------------------------------------------------------------*/ +#ifndef _WINDOW_H_ +#define _WINDOW_H_ + +#include "config.h" +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif +#include <stdlib.h> + +#include <zbar.h> +#include "error.h" +#include "mutex.h" +#include "symbol.h" + +typedef struct window_state_s window_state_t; + +struct zbar_window_s { + errinfo_t err; /* error reporting */ + zbar_image_t *image; /* last displayed image + * NB image access must be locked! + */ + unsigned overlay; /* user set overlay level */ + + uint32_t format; /* output format */ + unsigned width, height; /* current output size */ + unsigned max_width, max_height; + + uint32_t src_format; /* current input format */ + unsigned src_width; /* last displayed image size */ + unsigned src_height; + + unsigned dst_width; /* conversion target */ + unsigned dst_height; + + unsigned scale_num; /* output scaling */ + unsigned scale_den; + + point_t scaled_offset; /* output position and size */ + point_t scaled_size; + + uint32_t *formats; /* supported formats (zero terminated) */ + + zbar_mutex_t imglock; /* lock displayed image */ + + void *display; + unsigned long xwin; + unsigned long time; /* last image display in milliseconds */ + unsigned long time_avg; /* average of inter-frame times */ + + window_state_t *state; /* platform/interface specific state */ + + /* interface dependent methods */ + int (*init)(zbar_window_t *, zbar_image_t *, int); + int (*draw_image)(zbar_window_t *, zbar_image_t *); + int (*cleanup)(zbar_window_t *); +}; + +/* window.draw has to be thread safe wrt/other apis + * FIXME should be a semaphore + */ +static inline int window_lock(zbar_window_t *w) +{ + int rc = 0; + if ((rc = _zbar_mutex_lock(&w->imglock))) { + err_capture(w, SEV_FATAL, ZBAR_ERR_LOCKING, __func__, + "unable to acquire lock"); + w->err.errnum = rc; + return (-1); + } + return (0); +} + +static inline int window_unlock(zbar_window_t *w) +{ + int rc = 0; + if ((rc = _zbar_mutex_unlock(&w->imglock))) { + err_capture(w, SEV_FATAL, ZBAR_ERR_LOCKING, __func__, + "unable to release lock"); + w->err.errnum = rc; + return (-1); + } + return (0); +} + +static inline int _zbar_window_add_format(zbar_window_t *w, uint32_t fmt) +{ + int i; + for (i = 0; w->formats && w->formats[i]; i++) + if (w->formats[i] == fmt) + return (i); + + w->formats = realloc(w->formats, (i + 2) * sizeof(uint32_t)); + w->formats[i] = fmt; + w->formats[i + 1] = 0; + return (i); +} + +static inline point_t window_scale_pt(zbar_window_t *w, point_t p) +{ + p.x = ((long)p.x * w->scale_num + w->scale_den - 1) / w->scale_den; + p.y = ((long)p.y * w->scale_num + w->scale_den - 1) / w->scale_den; + return (p); +} + +/* PAL interface */ +extern int _zbar_window_attach(zbar_window_t *, void *, unsigned long); +extern int _zbar_window_expose(zbar_window_t *, int, int, int, int); +extern int _zbar_window_resize(zbar_window_t *); +extern int _zbar_window_clear(zbar_window_t *); +extern int _zbar_window_begin(zbar_window_t *); +extern int _zbar_window_end(zbar_window_t *); +extern int _zbar_window_draw_marker(zbar_window_t *, uint32_t, point_t); +extern int _zbar_window_draw_polygon(zbar_window_t *, uint32_t, const point_t *, + int); +extern int _zbar_window_draw_text(zbar_window_t *, uint32_t, point_t, + const char *); +extern int _zbar_window_fill_rect(zbar_window_t *, uint32_t, point_t, point_t); +extern int _zbar_window_draw_logo(zbar_window_t *); + +#endif diff --git a/zbar/window/dib.c b/zbar/window/dib.c new file mode 100644 index 0000000..7ae3eb2 --- /dev/null +++ b/zbar/window/dib.c @@ -0,0 +1,69 @@ +/*------------------------------------------------------------------------ + * Copyright 2009 (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 "image.h" +#include "win.h" +#include "window.h" + +static int dib_cleanup(zbar_window_t *w) +{ + return (0); +} + +static int dib_init(zbar_window_t *w, zbar_image_t *img, int new_format) +{ + window_state_t *win; + if (new_format) + _zbar_window_bih_init(w, img); + + win = w->state; + w->dst_width = win->bih.biWidth = (img->width + 3) & ~3; + w->dst_height = win->bih.biHeight = img->height; + return (0); +} + +static int dib_draw(zbar_window_t *w, zbar_image_t *img) +{ + StretchDIBits(w->state->hdc, w->scaled_offset.x, + w->scaled_offset.y + w->scaled_size.y - 1, w->scaled_size.x, + -w->scaled_size.y, 0, 0, w->src_width, w->src_height, + (void *)img->data, (BITMAPINFO *)&w->state->bih, + DIB_RGB_COLORS, SRCCOPY); + return (0); +} + +static uint32_t dib_formats[] = { fourcc('B', 'G', 'R', '3'), + fourcc('B', 'G', 'R', '4'), + fourcc('J', 'P', 'E', 'G'), 0 }; + +int _zbar_window_dib_init(zbar_window_t *w) +{ + uint32_t *fmt; + for (fmt = dib_formats; *fmt; fmt++) + _zbar_window_add_format(w, *fmt); + + w->init = dib_init; + w->draw_image = dib_draw; + w->cleanup = dib_cleanup; + return (0); +} diff --git a/zbar/window/null.c b/zbar/window/null.c new file mode 100644 index 0000000..7d30e53 --- /dev/null +++ b/zbar/window/null.c @@ -0,0 +1,88 @@ +/*------------------------------------------------------------------------ + * Copyright 2008-2009 (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 "window.h" + +static inline int null_error(void *m, const char *func) +{ + return (err_capture(m, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, func, + "not compiled with output window support")); +} + +int _zbar_window_attach(zbar_window_t *w, void *display, unsigned long win) +{ + return (null_error(w, __func__)); +} + +int _zbar_window_expose(zbar_window_t *w, int x, int y, int width, int height) +{ + return (null_error(w, __func__)); +} + +int _zbar_window_resize(zbar_window_t *w) +{ + return (0); +} + +int _zbar_window_clear(zbar_window_t *w) +{ + return (null_error(w, __func__)); +} + +int _zbar_window_begin(zbar_window_t *w) +{ + return (null_error(w, __func__)); +} + +int _zbar_window_end(zbar_window_t *w) +{ + return (null_error(w, __func__)); +} + +int _zbar_window_draw_marker(zbar_window_t *w, uint32_t rgb, point_t p) +{ + return (null_error(w, __func__)); +} + +int _zbar_window_draw_polygon(zbar_window_t *w, uint32_t rgb, + const point_t *pts, int npts) +{ + return (null_error(w, __func__)); +} + +int _zbar_window_draw_text(zbar_window_t *w, uint32_t rgb, point_t p, + const char *text) +{ + return (null_error(w, __func__)); +} + +int _zbar_window_fill_rect(zbar_window_t *w, uint32_t rgb, point_t org, + point_t size) +{ + return (null_error(w, __func__)); +} + +int _zbar_window_draw_logo(zbar_window_t *w) +{ + return (null_error(w, __func__)); +} diff --git a/zbar/window/vfw.c b/zbar/window/vfw.c new file mode 100644 index 0000000..8c485cb --- /dev/null +++ b/zbar/window/vfw.c @@ -0,0 +1,96 @@ +/*------------------------------------------------------------------------ + * Copyright 2009 (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 "window.h" + +#include <vfw.h> + +extern int _zbar_window_bih_init(zbar_window_t *w, zbar_image_t *img); + +static int vfw_cleanup(zbar_window_t *w) +{ + if (w->hdd) { + DrawDibClose(w->hdd); + w->hdd = NULL; + } + return (0); +} + +static int vfw_init(zbar_window_t *w, zbar_image_t *img, int new_format) +{ + if (new_format) + _zbar_window_bih_init(w, img); + + w->dst_width = w->bih.biWidth = (img->width + 3) & ~3; + w->dst_height = w->bih.biHeight = img->height; + + HDC hdc = GetDC(w->hwnd); + if (!hdc) + return (-1 /*FIXME*/); + + if (!DrawDibBegin(w->hdd, hdc, w->width, w->height, &w->bih, img->width, + img->height, 0)) + return (-1 /*FIXME*/); + + ReleaseDC(w->hwnd, hdc); + return (0); +} + +static int vfw_draw(zbar_window_t *w, zbar_image_t *img) +{ + HDC hdc = GetDC(w->hwnd); + if (!hdc) + return (-1 /*FIXME*/); + + zprintf(24, "DrawDibDraw(%dx%d -> %dx%d)\n", img->width, img->height, + w->width, w->height); + + DrawDibDraw(w->hdd, hdc, 0, 0, w->width, w->height, &w->bih, + (void *)img->data, 0, 0, w->src_width, w->src_height, + DDF_SAME_DRAW); + + ValidateRect(w->hwnd, NULL); + ReleaseDC(w->hwnd, hdc); + return (0); +} + +static uint32_t vfw_formats[] = { fourcc('B', 'G', 'R', '3'), + fourcc('B', 'G', 'R', '4'), + fourcc('J', 'P', 'E', 'G'), 0 }; + +int _zbar_window_vfw_init(zbar_window_t *w) +{ + w->hdd = DrawDibOpen(); + if (!w->hdd) + return (err_capture(w, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, __func__, + "unable to initialize DrawDib")); + + uint32_t *fmt; + for (fmt = vfw_formats; *fmt; fmt++) + _zbar_window_add_format(w, *fmt); + + w->init = vfw_init; + w->draw_image = vfw_draw; + w->cleanup = vfw_cleanup; + return (0); +} diff --git a/zbar/window/win.c b/zbar/window/win.c new file mode 100644 index 0000000..458e1d9 --- /dev/null +++ b/zbar/window/win.c @@ -0,0 +1,321 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (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 <ctype.h> + +#include "window.h" +#include "image.h" + +#include "win.h" + +int _zbar_window_vfw_init(zbar_window_t *w); +int _zbar_window_dib_init(zbar_window_t *w); + +int _zbar_window_resize(zbar_window_t *w) +{ + LOGBRUSH lb = { + 0, + }; + int x0, y0, by0, bh, i; + window_state_t *win = w->state; + int lbw; + + static const int bx[5] = { -6, -3, -1, 2, 5 }; + static const int bw[5] = { 1, 1, 2, 2, 1 }; + static const int zx[4] = { -7, 7, -7, 7 }; + static const int zy[4] = { -8, -8, 8, 8 }; + + if (w->height * 8 / 10 <= w->width) + lbw = w->height / 36; + else + lbw = w->width * 5 / 144; + if (lbw < 1) + lbw = 1; + win->logo_scale = lbw; + zprintf(7, "%dx%d scale=%d\n", w->width, w->height, lbw); + if (win->logo_zbars) { + DeleteObject(win->logo_zbars); + win->logo_zbars = NULL; + } + if (win->logo_zpen) + DeleteObject(win->logo_zpen); + if (win->logo_zbpen) + DeleteObject(win->logo_zbpen); + + lb.lbStyle = BS_SOLID; + lb.lbColor = RGB(0xd7, 0x33, 0x33); + win->logo_zpen = + ExtCreatePen(PS_GEOMETRIC | PS_SOLID | PS_ENDCAP_ROUND | PS_JOIN_ROUND, + lbw * 2, &lb, 0, NULL); + + lb.lbColor = RGB(0xa4, 0x00, 0x00); + win->logo_zbpen = + ExtCreatePen(PS_GEOMETRIC | PS_SOLID | PS_ENDCAP_ROUND | PS_JOIN_ROUND, + lbw * 2, &lb, 0, NULL); + + x0 = w->width / 2; + y0 = w->height / 2; + by0 = y0 - 54 * lbw / 5; + bh = 108 * lbw / 5; + + for (i = 0; i < 5; i++) { + int x = x0 + lbw * bx[i]; + HRGN bar = CreateRectRgn(x, by0, x + lbw * bw[i], by0 + bh); + if (win->logo_zbars) { + CombineRgn(win->logo_zbars, win->logo_zbars, bar, RGN_OR); + DeleteObject(bar); + } else + win->logo_zbars = bar; + } + + for (i = 0; i < 4; i++) { + win->logo_z[i].x = x0 + lbw * zx[i]; + win->logo_z[i].y = y0 + lbw * zy[i]; + } + return (0); +} + +int _zbar_window_attach(zbar_window_t *w, void *display, unsigned long unused) +{ + HDC hdc; + int height; + HFONT font; + TEXTMETRIC tm; + window_state_t *win = w->state; + if (w->display) { + /* FIXME cleanup existing resources */ + w->display = NULL; + } + + if (!display) { + if (win) { + free(win); + w->state = NULL; + } + return (0); + } + + if (!win) + win = w->state = calloc(1, sizeof(window_state_t)); + + w->display = display; + + win->bih.biSize = sizeof(win->bih); + win->bih.biPlanes = 1; + + hdc = GetDC(w->display); + if (!hdc) + return (-1 /*FIXME*/); + win->bih.biXPelsPerMeter = + 1000L * GetDeviceCaps(hdc, HORZRES) / GetDeviceCaps(hdc, HORZSIZE); + win->bih.biYPelsPerMeter = + 1000L * GetDeviceCaps(hdc, VERTRES) / GetDeviceCaps(hdc, VERTSIZE); + + height = -MulDiv(11, GetDeviceCaps(hdc, LOGPIXELSY), 96); + font = CreateFontW(height, 0, 0, 0, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, + FF_MODERN | FIXED_PITCH, NULL); + + SelectObject(hdc, font); + DeleteObject(font); + + GetTextMetrics(hdc, &tm); + win->font_height = tm.tmHeight; + + ReleaseDC(w->display, hdc); + + return (_zbar_window_dib_init(w)); +} + +int _zbar_window_begin(zbar_window_t *w) +{ + HDC hdc = w->state->hdc = GetDC(w->display); + if (!hdc || !SaveDC(hdc)) + return (-1 /*FIXME*/); + return (0); +} + +int _zbar_window_end(zbar_window_t *w) +{ + HDC hdc = w->state->hdc; + w->state->hdc = NULL; + RestoreDC(hdc, -1); + ReleaseDC(w->display, hdc); + ValidateRect(w->display, NULL); + return (0); +} + +int _zbar_window_clear(zbar_window_t *w) +{ + RECT r = { 0, 0, w->width, w->height }; + HDC hdc = GetDC(w->display); + if (!hdc) + return (-1 /*FIXME*/); + + FillRect(hdc, &r, GetStockObject(BLACK_BRUSH)); + + ReleaseDC(w->display, hdc); + ValidateRect(w->display, NULL); + return (0); +} + +static inline void win_set_rgb(HDC hdc, uint32_t rgb) +{ + SelectObject(hdc, GetStockObject(DC_PEN)); + SetDCPenColor(hdc, + RGB((rgb & 4) * 0x33, (rgb & 2) * 0x66, (rgb & 1) * 0xcc)); +} + +int _zbar_window_draw_polygon(zbar_window_t *w, uint32_t rgb, + const point_t *pts, int npts) +{ + point_t org; + POINT *gdipts; + int i; + HDC hdc = w->state->hdc; + win_set_rgb(hdc, rgb); + + org = w->scaled_offset; + gdipts = (POINT *)calloc(npts + 1, sizeof(*gdipts)); + for (i = 0; i < npts; i++) { + point_t p = window_scale_pt(w, pts[i]); + gdipts[i].x = p.x + org.x; + gdipts[i].y = p.y + org.y; + } + gdipts[npts] = gdipts[0]; + + Polyline(hdc, gdipts, npts + 1); + free(gdipts); + return (0); +} + +int _zbar_window_draw_marker(zbar_window_t *w, uint32_t rgb, point_t p) +{ + HDC hdc = w->state->hdc; + static const DWORD npolys[3] = { 5, 2, 2 }; + POINT polys[9] = { + { p.x - 2, p.y - 2 }, { p.x - 2, p.y + 2 }, { p.x + 2, p.y + 2 }, + { p.x + 2, p.y - 2 }, { p.x - 2, p.y - 2 }, + + { p.x - 3, p.y }, { p.x + 4, p.y }, + + { p.x, p.y - 3 }, { p.x, p.y + 4 }, + }; + + win_set_rgb(hdc, rgb); + + PolyPolyline(hdc, polys, npolys, 3); + return (0); +} + +int _zbar_window_draw_text(zbar_window_t *w, uint32_t rgb, point_t p, + const char *text) +{ + int n = 0; + HDC hdc = w->state->hdc; + SetTextColor(hdc, + RGB((rgb & 4) * 0x33, (rgb & 2) * 0x66, (rgb & 1) * 0xcc)); + SetBkMode(hdc, TRANSPARENT); + + while (n < 32 && text[n] && isprint(text[n])) + n++; + + if (p.x >= 0) + SetTextAlign(hdc, TA_BASELINE | TA_CENTER); + else { + SetTextAlign(hdc, TA_BASELINE | TA_RIGHT); + p.x += w->width; + } + + if (p.y < 0) + p.y = w->height + p.y * w->state->font_height * 5 / 4; + + TextOut(hdc, p.x, p.y, text, n); + return (0); +} + +int _zbar_window_fill_rect(zbar_window_t *w, uint32_t rgb, point_t org, + point_t size) +{ + RECT r = { org.x, org.y, org.x + size.x, org.y + size.y }; + HDC hdc = w->state->hdc; + SetDCBrushColor(hdc, + RGB((rgb & 4) * 0x33, (rgb & 2) * 0x66, (rgb & 1) * 0xcc)); + + FillRect(hdc, &r, GetStockObject(DC_BRUSH)); + + return (0); +} + +int _zbar_window_draw_logo(zbar_window_t *w) +{ + HDC hdc = w->state->hdc; + + window_state_t *win = w->state; + + /* FIXME buffer offscreen */ + HRGN rgn = CreateRectRgn(0, 0, w->width, w->height); + CombineRgn(rgn, rgn, win->logo_zbars, RGN_DIFF); + FillRgn(hdc, rgn, GetStockObject(WHITE_BRUSH)); + DeleteObject(rgn); + + FillRgn(hdc, win->logo_zbars, GetStockObject(BLACK_BRUSH)); + + SelectObject(hdc, win->logo_zpen); + Polyline(hdc, win->logo_z, 4); + + ExtSelectClipRgn(hdc, win->logo_zbars, RGN_AND); + SelectObject(hdc, win->logo_zbpen); + Polyline(hdc, win->logo_z, 4); + return (0); +} + +int _zbar_window_bih_init(zbar_window_t *w, zbar_image_t *img) +{ + window_state_t *win = w->state; + switch (w->format) { + case fourcc('J', 'P', 'E', 'G'): { + win->bih.biBitCount = 0; + win->bih.biCompression = BI_JPEG; + break; + } + case fourcc('B', 'G', 'R', '3'): { + win->bih.biBitCount = 24; + win->bih.biCompression = BI_RGB; + break; + } + case fourcc('B', 'G', 'R', '4'): { + win->bih.biBitCount = 32; + win->bih.biCompression = BI_RGB; + break; + } + default: + assert(0); + /* FIXME PNG? */ + } + win->bih.biSizeImage = img->datalen; + + zprintf(20, "biCompression=%d biBitCount=%d\n", (int)win->bih.biCompression, + win->bih.biBitCount); + + return (0); +} diff --git a/zbar/window/win.h b/zbar/window/win.h new file mode 100644 index 0000000..1daf65f --- /dev/null +++ b/zbar/window/win.h @@ -0,0 +1,45 @@ +/*------------------------------------------------------------------------ + * Copyright 2009 (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 + *------------------------------------------------------------------------*/ +#ifndef _WINDOW_WIN_H_ +#define _WINDOW_WIN_H_ + +#include <windows.h> + +struct window_state_s { + HDC hdc; + void *hdd; + + BITMAPINFOHEADER bih; + + /* pre-calculated logo geometries */ + int logo_scale; + HRGN logo_zbars; + HPEN logo_zpen, logo_zbpen; + POINT logo_z[4]; + + int font_height; +}; + +extern int _zbar_window_bih_init(zbar_window_t *w, zbar_image_t *img); + +#endif diff --git a/zbar/window/x.c b/zbar/window/x.c new file mode 100644 index 0000000..f4284be --- /dev/null +++ b/zbar/window/x.c @@ -0,0 +1,338 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (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 "x.h" +#include <ctype.h> +#include "image.h" +#include "window.h" + +#ifndef ZBAR_OVERLAY_FONT +#define ZBAR_OVERLAY_FONT "-*-fixed-medium-r-*-*-*-120-75-75-*-*-ISO8859-1" +#endif + +static inline unsigned long window_alloc_color(zbar_window_t *w, Colormap cmap, + unsigned short r, + unsigned short g, + unsigned short b) +{ + XColor color; + color.red = r; + color.green = g; + color.blue = b; + color.flags = 0; + XAllocColor(w->display, cmap, &color); + return (color.pixel); +} + +static inline int window_alloc_colors(zbar_window_t *w) +{ + window_state_t *x = w->state; + Colormap cmap = DefaultColormap(w->display, DefaultScreen(w->display)); + int i; + for (i = 0; i < 8; i++) + x->colors[i] = window_alloc_color(w, cmap, (i & 4) ? (0xcc * 0x101) : 0, + (i & 2) ? (0xcc * 0x101) : 0, + (i & 1) ? (0xcc * 0x101) : 0); + + x->logo_colors[0] = window_alloc_color(w, cmap, 0xd709, 0x3333, 0x3333); + x->logo_colors[1] = window_alloc_color(w, cmap, 0xa3d6, 0x0000, 0x0000); + return (0); +} + +static inline int window_hide_cursor(zbar_window_t *w) +{ + /* FIXME this seems lame...there must be a better way */ + Pixmap empty = XCreatePixmap(w->display, w->xwin, 1, 1, 1); + GC gc = XCreateGC(w->display, empty, 0, NULL); + XDrawPoint(w->display, empty, gc, 0, 0); + XColor black; + memset(&black, 0, sizeof(black)); + int screen = DefaultScreen(w->display); + black.pixel = BlackPixel(w->display, screen); + Cursor cursor = + XCreatePixmapCursor(w->display, empty, empty, &black, &black, 0, 0); + XDefineCursor(w->display, w->xwin, cursor); + XFreeCursor(w->display, cursor); + XFreeGC(w->display, gc); + XFreePixmap(w->display, empty); + return (0); +} + +int _zbar_window_resize(zbar_window_t *w) +{ + window_state_t *x = w->state; + if (!x) + return (0); + + int lbw; + if (w->height * 8 / 10 <= w->width) + lbw = w->height / 36; + else + lbw = w->width * 5 / 144; + if (lbw < 1) + lbw = 1; + x->logo_scale = lbw; + if (x->logo_zbars) + XDestroyRegion(x->logo_zbars); + x->logo_zbars = XCreateRegion(); + + int x0 = w->width / 2; + int y0 = w->height / 2; + int by0 = y0 - 54 * lbw / 5; + int bh = 108 * lbw / 5; + + static const int bx[5] = { -6, -3, -1, 2, 5 }; + static const int bw[5] = { 1, 1, 2, 2, 1 }; + + int i; + for (i = 0; i < 5; i++) { + XRectangle *bar = &x->logo_bars[i]; + bar->x = x0 + lbw * bx[i]; + bar->y = by0; + bar->width = lbw * bw[i]; + bar->height = bh; + XUnionRectWithRegion(bar, x->logo_zbars, x->logo_zbars); + } + + static const int zx[4] = { -7, 7, -7, 7 }; + static const int zy[4] = { -8, -8, 8, 8 }; + + for (i = 0; i < 4; i++) { + x->logo_z[i].x = x0 + lbw * zx[i]; + x->logo_z[i].y = y0 + lbw * zy[i]; + } + return (0); +} + +int _zbar_window_attach(zbar_window_t *w, void *display, unsigned long win) +{ + window_state_t *x = w->state; + if (w->display) { + /* cleanup existing resources */ + if (x->gc) + XFreeGC(w->display, x->gc); + assert(!x->exposed); + if (x->font) { + XFreeFont(w->display, x->font); + x->font = NULL; + } + if (x->logo_zbars) { + XDestroyRegion(x->logo_zbars); + x->logo_zbars = NULL; + } + if (x->exposed) { + XDestroyRegion(x->exposed); + x->exposed = NULL; + } + w->display = NULL; + } + w->xwin = 0; + + if (!display || !win) { + if (x) { + free(x); + w->state = NULL; + } + return (0); + } + + if (!x) + x = w->state = calloc(1, sizeof(window_state_t)); + + w->display = display; + w->xwin = win; + x->gc = XCreateGC(display, win, 0, NULL); + + XWindowAttributes attr; + XGetWindowAttributes(w->display, w->xwin, &attr); + w->width = attr.width; + w->height = attr.height; + _zbar_window_resize(w); + + window_alloc_colors(w); + window_hide_cursor(w); + + /* load overlay font */ + x->font = XLoadQueryFont(w->display, ZBAR_OVERLAY_FONT); + if (x->font) + XSetFont(w->display, x->gc, x->font->fid); + + /* FIXME add interface preference override */ +#ifdef HAVE_X11_EXTENSIONS_XVLIB_H + if (!_zbar_window_probe_xv(w)) + return (0); +#endif + + zprintf(1, "falling back to XImage\n"); + return (_zbar_window_probe_ximage(w)); +} + +int _zbar_window_expose(zbar_window_t *w, int x, int y, int width, int height) +{ + window_state_t *xs = w->state; + if (!xs->exposed) + xs->exposed = XCreateRegion(); + XRectangle r; + r.x = x; + r.y = y; + r.width = width; + r.height = height; + XUnionRectWithRegion(&r, xs->exposed, xs->exposed); + return (0); +} + +int _zbar_window_begin(zbar_window_t *w) +{ + window_state_t *xs = w->state; + if (xs->exposed) + XSetRegion(w->display, xs->gc, xs->exposed); + + return (0); +} + +int _zbar_window_end(zbar_window_t *w) +{ + window_state_t *x = w->state; + XSetClipMask(w->display, x->gc, None); + if (x->exposed) { + XDestroyRegion(x->exposed); + x->exposed = NULL; + } + XFlush(w->display); + return (0); +} + +int _zbar_window_clear(zbar_window_t *w) +{ + if (!w->display) + return (0); + window_state_t *x = w->state; + int screen = DefaultScreen(w->display); + XSetForeground(w->display, x->gc, WhitePixel(w->display, screen)); + XFillRectangle(w->display, w->xwin, x->gc, 0, 0, w->width, w->height); + return (0); +} + +int _zbar_window_draw_polygon(zbar_window_t *w, uint32_t rgb, + const point_t *pts, int npts) +{ + window_state_t *xs = w->state; + XSetForeground(w->display, xs->gc, xs->colors[rgb]); + + point_t org = w->scaled_offset; + XPoint xpts[npts + 1]; + int i; + for (i = 0; i < npts; i++) { + point_t p = window_scale_pt(w, pts[i]); + xpts[i].x = p.x + org.x; + xpts[i].y = p.y + org.y; + } + xpts[npts] = xpts[0]; + + XDrawLines(w->display, w->xwin, xs->gc, xpts, npts + 1, CoordModeOrigin); + + return (0); +} + +int _zbar_window_draw_marker(zbar_window_t *w, uint32_t rgb, point_t p) +{ + window_state_t *xs = w->state; + XSetForeground(w->display, xs->gc, xs->colors[rgb]); + XDrawRectangle(w->display, w->xwin, xs->gc, p.x - 2, p.y - 2, 4, 4); + XDrawLine(w->display, w->xwin, xs->gc, p.x, p.y - 3, p.x, p.y + 3); + XDrawLine(w->display, w->xwin, xs->gc, p.x - 3, p.y, p.x + 3, p.y); + return (0); +} + +int _zbar_window_draw_text(zbar_window_t *w, uint32_t rgb, point_t p, + const char *text) +{ + window_state_t *xs = w->state; + if (!xs->font) + return (-1); + + XSetForeground(w->display, xs->gc, xs->colors[rgb]); + + int n = 0; + while (n < 32 && text[n] && isprint(text[n])) + n++; + + int width = XTextWidth(xs->font, text, n); + if (p.x >= 0) + p.x -= width / 2; + else + p.x += w->width - width; + + int dy = xs->font->ascent + xs->font->descent; + if (p.y >= 0) + p.y -= dy / 2; + else + p.y = w->height + p.y * dy * 5 / 4; + + XDrawString(w->display, w->xwin, xs->gc, p.x, p.y, text, n); + return (0); +} + +int _zbar_window_fill_rect(zbar_window_t *w, uint32_t rgb, point_t org, + point_t size) +{ + window_state_t *xs = w->state; + XSetForeground(w->display, xs->gc, xs->colors[rgb]); + XFillRectangle(w->display, w->xwin, xs->gc, org.x, org.y, size.x, size.y); + return (0); +} + +int _zbar_window_draw_logo(zbar_window_t *w) +{ + window_state_t *x = w->state; + int screen = DefaultScreen(w->display); + + /* clear to white */ + XSetForeground(w->display, x->gc, WhitePixel(w->display, screen)); + XFillRectangle(w->display, w->xwin, x->gc, 0, 0, w->width, w->height); + + if (!x->logo_scale || !x->logo_zbars) + return (0); + + XSetForeground(w->display, x->gc, BlackPixel(w->display, screen)); + XFillRectangles(w->display, w->xwin, x->gc, x->logo_bars, 5); + + XSetLineAttributes(w->display, x->gc, 2 * x->logo_scale, LineSolid, + CapRound, JoinRound); + + XSetForeground(w->display, x->gc, x->logo_colors[0]); + XDrawLines(w->display, w->xwin, x->gc, x->logo_z, 4, CoordModeOrigin); + + if (x->exposed) { + XIntersectRegion(x->logo_zbars, x->exposed, x->exposed); + XSetRegion(w->display, x->gc, x->exposed); + } else + XSetRegion(w->display, x->gc, x->logo_zbars); + + XSetForeground(w->display, x->gc, x->logo_colors[1]); + XDrawLines(w->display, w->xwin, x->gc, x->logo_z, 4, CoordModeOrigin); + + /* reset GC */ + XSetLineAttributes(w->display, x->gc, 0, LineSolid, CapButt, JoinMiter); + return (0); +} diff --git a/zbar/window/x.h b/zbar/window/x.h new file mode 100644 index 0000000..dd7a2a7 --- /dev/null +++ b/zbar/window/x.h @@ -0,0 +1,74 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (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 + *------------------------------------------------------------------------*/ +#ifndef _WINDOW_X_H_ +#define _WINDOW_X_H_ + +#include "window.h" + +#ifdef HAVE_X +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#ifdef HAVE_X11_EXTENSIONS_XSHM_H +#include <X11/extensions/XShm.h> +#endif +#ifdef HAVE_X11_EXTENSIONS_XVLIB_H +#include <X11/extensions/Xvlib.h> +#endif +#endif + +struct window_state_s { + unsigned long colors[8]; /* pre-allocated colors */ + + GC gc; /* graphics context */ + Region exposed; /* region to redraw */ + XFontStruct *font; /* overlay font */ + + /* pre-calculated logo geometries */ + int logo_scale; + unsigned long logo_colors[2]; + Region logo_zbars; + XPoint logo_z[4]; + XRectangle logo_bars[5]; + +#ifdef HAVE_X11_EXTENSIONS_XSHM_H + XShmSegmentInfo shm; /* shared memory segment */ +#endif + + union { + XImage *x; +#ifdef HAVE_X11_EXTENSIONS_XVLIB_H + XvImage *xv; +#endif + } img; + + XID img_port; /* current format port */ + XID *xv_ports; /* best port for format */ + int num_xv_adaptors; /* number of adaptors */ + XID *xv_adaptors; /* port grabbed for each adaptor */ +}; + +extern int _zbar_window_probe_ximage(zbar_window_t *); +extern int _zbar_window_probe_xshm(zbar_window_t *); +extern int _zbar_window_probe_xv(zbar_window_t *); + +#endif diff --git a/zbar/window/ximage.c b/zbar/window/ximage.c new file mode 100644 index 0000000..17ee7e4 --- /dev/null +++ b/zbar/window/ximage.c @@ -0,0 +1,201 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (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 "image.h" +#include "window.h" +#include "x.h" + +static int ximage_cleanup(zbar_window_t *w) +{ + window_state_t *x = w->state; + if (x->img.x) + free(x->img.x); + x->img.x = NULL; + return (0); +} + +static inline int ximage_init(zbar_window_t *w, zbar_image_t *img, + int format_change) +{ + ximage_cleanup(w); + XImage *ximg = w->state->img.x = calloc(1, sizeof(XImage)); + ximg->width = img->width; + ximg->height = img->height; + ximg->format = ZPixmap; + ximg->byte_order = LSBFirst; + ximg->bitmap_unit = 8; + ximg->bitmap_bit_order = MSBFirst; + ximg->bitmap_pad = 8; + + const zbar_format_def_t *fmt = _zbar_format_lookup(w->format); + if (fmt->group == ZBAR_FMT_RGB_PACKED) { + ximg->depth = ximg->bits_per_pixel = fmt->p.rgb.bpp << 3; + ximg->red_mask = (0xff >> RGB_SIZE(fmt->p.rgb.red)) + << RGB_OFFSET(fmt->p.rgb.red); + ximg->green_mask = (0xff >> RGB_SIZE(fmt->p.rgb.green)) + << RGB_OFFSET(fmt->p.rgb.green); + ximg->blue_mask = (0xff >> RGB_SIZE(fmt->p.rgb.blue)) + << RGB_OFFSET(fmt->p.rgb.blue); + } else { + ximg->depth = ximg->bits_per_pixel = 8; + ximg->red_mask = ximg->green_mask = ximg->blue_mask = 0xff; + } + + if (!XInitImage(ximg)) + return (err_capture_int(w, SEV_ERROR, ZBAR_ERR_XPROTO, __func__, + "unable to init XImage for format %x", + w->format)); + + w->dst_width = img->width; + w->dst_height = img->height; + + /* FIXME implement some basic scaling */ + w->scale_num = w->scale_den = 1; + + zprintf(3, + "new XImage %.4s(%08" PRIx32 ") %dx%d" + " from %.4s(%08" PRIx32 ") %dx%d\n", + (char *)&w->format, w->format, ximg->width, ximg->height, + (char *)&img->format, img->format, img->width, img->height); + zprintf(4, " masks: %08lx %08lx %08lx\n", ximg->red_mask, + ximg->green_mask, ximg->blue_mask); + return (0); +} + +static int ximage_draw(zbar_window_t *w, zbar_image_t *img) +{ + window_state_t *x = w->state; + XImage *ximg = x->img.x; + assert(ximg); + ximg->data = (void *)img->data; + + point_t src = { 0, 0 }; + point_t dst = w->scaled_offset; + if (dst.x < 0) { + src.x = -dst.x; + dst.x = 0; + } + if (dst.y < 0) { + src.y = -dst.y; + dst.y = 0; + } + point_t size = w->scaled_size; + if (size.x > w->width) + size.x = w->width; + if (size.y > w->height) + size.y = w->height; + + XPutImage(w->display, w->xwin, x->gc, ximg, src.x, src.y, dst.x, dst.y, + size.x, size.y); + ximg->data = NULL; + return (0); +} + +static uint32_t ximage_formats[4][5] = { + { /* 8bpp */ + /* FIXME fourcc('Y','8','0','0'), */ + fourcc('R', 'G', 'B', '1'), fourcc('B', 'G', 'R', '1'), 0 }, + { /* 16bpp */ + fourcc('R', 'G', 'B', 'P'), fourcc('R', 'G', 'B', 'O'), + fourcc('R', 'G', 'B', 'R'), fourcc('R', 'G', 'B', 'Q'), 0 }, + { /* 24bpp */ + fourcc('R', 'G', 'B', '3'), fourcc('B', 'G', 'R', '3'), 0 }, + { /* 32bpp */ + fourcc('R', 'G', 'B', '4'), fourcc('B', 'G', 'R', '4'), 0 }, +}; + +static int ximage_probe_format(zbar_window_t *w, uint32_t format) +{ + const zbar_format_def_t *fmt = _zbar_format_lookup(format); + + XVisualInfo visreq, *visuals = NULL; + memset(&visreq, 0, sizeof(XVisualInfo)); + + visreq.depth = fmt->p.rgb.bpp << 3; + visreq.red_mask = (0xff >> RGB_SIZE(fmt->p.rgb.red)) + << RGB_OFFSET(fmt->p.rgb.red); + visreq.green_mask = (0xff >> RGB_SIZE(fmt->p.rgb.green)) + << RGB_OFFSET(fmt->p.rgb.green); + visreq.blue_mask = (0xff >> RGB_SIZE(fmt->p.rgb.blue)) + << RGB_OFFSET(fmt->p.rgb.blue); + + int n; + visuals = XGetVisualInfo(w->display, + VisualDepthMask | VisualRedMaskMask | + VisualGreenMaskMask | VisualBlueMaskMask, + &visreq, &n); + + zprintf(8, "bits=%d r=%08lx g=%08lx b=%08lx: n=%d visuals=%p\n", + visreq.depth, visreq.red_mask, visreq.green_mask, visreq.blue_mask, + n, visuals); + if (!visuals) + return (1); + XFree(visuals); + if (!n) + return (-1); + + return (0); +} + +int _zbar_window_probe_ximage(zbar_window_t *w) +{ + /* FIXME determine supported formats/depths */ + int n; + XPixmapFormatValues *formats = XListPixmapFormats(w->display, &n); + if (!formats) + return (err_capture(w, SEV_ERROR, ZBAR_ERR_XPROTO, __func__, + "unable to query XImage formats")); + + int i; + for (i = 0; i < n; i++) { + if (formats[i].depth & 0x7 || formats[i].depth > 0x20) { + zprintf(2, " [%d] depth=%d bpp=%d: not supported\n", i, + formats[i].depth, formats[i].bits_per_pixel); + continue; + } + int fmtidx = formats[i].depth / 8 - 1; + int j, n = 0; + for (j = 0; ximage_formats[fmtidx][j]; j++) + if (!ximage_probe_format(w, ximage_formats[fmtidx][j])) { + zprintf(2, " [%d] depth=%d bpp=%d: %.4s(%08" PRIx32 ")\n", i, + formats[i].depth, formats[i].bits_per_pixel, + (char *)&ximage_formats[fmtidx][j], + ximage_formats[fmtidx][j]); + _zbar_window_add_format(w, ximage_formats[fmtidx][j]); + n++; + } + if (!n) + zprintf(2, " [%d] depth=%d bpp=%d: no visuals\n", i, + formats[i].depth, formats[i].bits_per_pixel); + } + XFree(formats); + + if (!w->formats || !w->formats[0]) + return (err_capture(w, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, __func__, + "no usable XImage formats found")); + + w->init = ximage_init; + w->draw_image = ximage_draw; + w->cleanup = ximage_cleanup; + return (0); +} diff --git a/zbar/window/xv.c b/zbar/window/xv.c new file mode 100644 index 0000000..f332800 --- /dev/null +++ b/zbar/window/xv.c @@ -0,0 +1,264 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (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 <string.h> /* strcmp */ +#include "image.h" +#include "window.h" +#include "x.h" + +static int xv_cleanup(zbar_window_t *w) +{ + window_state_t *x = w->state; + if (x->img.xv) { + XFree(x->img.xv); + x->img.xv = NULL; + } + int i; + for (i = 0; i < x->num_xv_adaptors; i++) + if (x->xv_adaptors[i]) { + XvUngrabPort(w->display, x->xv_adaptors[i], CurrentTime); + x->xv_adaptors[i] = 0; + } + free(x->xv_ports); + free(x->xv_adaptors); + x->xv_ports = NULL; + x->num_xv_adaptors = 0; + x->xv_adaptors = NULL; + return (0); +} + +static inline int xv_init(zbar_window_t *w, zbar_image_t *img, + int format_change) +{ + window_state_t *x = w->state; + if (x->img.xv) { + XFree(x->img.xv); + x->img.xv = NULL; + } + if (format_change) { + /* lookup port for format */ + x->img_port = 0; + int i; + for (i = 0; w->formats[i]; i++) + if (w->formats[i] == w->format) { + x->img_port = x->xv_ports[i]; + break; + } + assert(x->img_port > 0); + } + + XvImage *xvimg = XvCreateImage(w->display, x->img_port, w->format, NULL, + img->width, img->height); + zprintf(3, + "new XvImage %.4s(%08" PRIx32 ") %dx%d(%d)" + " from %.4s(%08" PRIx32 ") %dx%d\n", + (char *)&w->format, w->format, xvimg->width, xvimg->height, + xvimg->pitches[0], (char *)&img->format, img->format, img->width, + img->height); + + /* FIXME not sure this simple check is always correct + * should lookup format to decode/sanitize target width from pitch & bpp + */ + w->dst_width = + ((xvimg->num_planes <= 1) ? xvimg->width : xvimg->pitches[0]); + w->dst_height = xvimg->height; + + /* FIXME datalen check */ + if (w->dst_width < img->width || xvimg->height < img->height) { + XFree(xvimg); + /* FIXME fallback to XImage... */ + return (err_capture(w, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, __func__, + "output image size mismatch (XvCreateImage)")); + } + x->img.xv = xvimg; + + return (0); +} + +static int xv_draw(zbar_window_t *w, zbar_image_t *img) +{ + window_state_t *x = w->state; + XvImage *xvimg = x->img.xv; + assert(xvimg); + xvimg->data = (void *)img->data; + zprintf(24, "XvPutImage(%dx%d -> %dx%d (%08lx))\n", w->src_width, + w->src_height, w->scaled_size.x, w->scaled_size.y, img->datalen); + XvPutImage(w->display, x->img_port, w->xwin, x->gc, xvimg, 0, 0, + w->src_width, w->src_height, w->scaled_offset.x, + w->scaled_offset.y, w->scaled_size.x, w->scaled_size.y); + xvimg->data = NULL; /* FIXME hold shm image until completion */ + return (0); +} + +static inline int xv_add_format(zbar_window_t *w, uint32_t fmt, XvPortID port) +{ + int i = _zbar_window_add_format(w, fmt); + + window_state_t *x = w->state; + if (!w->formats[i + 1]) + x->xv_ports = realloc(x->xv_ports, (i + 1) * sizeof(*x->xv_ports)); + + /* FIXME could prioritize by something (rate? size?) */ + x->xv_ports[i] = port; + return (i); +} + +static int xv_probe_port(zbar_window_t *w, XvPortID port) +{ + unsigned n; + XvEncodingInfo *encodings = NULL; + if (XvQueryEncodings(w->display, port, &n, &encodings)) + return (err_capture(w, SEV_ERROR, ZBAR_ERR_XPROTO, __func__, + "querying XVideo encodings")); + + zprintf(1, "probing port %u with %d encodings:\n", (unsigned)port, n); + unsigned width = 0, height = 0; + int i; + for (i = 0; i < n; i++) { + XvEncodingInfo *enc = &encodings[i]; + zprintf(2, " [%d] %lu x %lu rate=%d/%d : %s\n", i, enc->width, + enc->height, enc->rate.numerator, enc->rate.denominator, + enc->name); + if (!strcmp(enc->name, "XV_IMAGE")) { + if (width < enc->width) + width = enc->width; + if (height < enc->height) + height = enc->height; + } + } + XvFreeEncodingInfo(encodings); + encodings = NULL; + + if (!width || !height) + return (err_capture(w, SEV_ERROR, ZBAR_ERR_INVALID, __func__, + "no XV_IMAGE encodings found")); + zprintf(1, "max XV_IMAGE size %dx%d\n", width, height); + if (w->max_width > width) + w->max_width = width; + if (w->max_height > height) + w->max_height = height; + + XvImageFormatValues *formats = + XvListImageFormats(w->display, port, (int *)&n); + if (!formats) + return (err_capture(w, SEV_ERROR, ZBAR_ERR_XPROTO, __func__, + "querying XVideo image formats")); + + zprintf(1, "%d image formats\n", n); + for (i = 0; i < n; i++) { + XvImageFormatValues *fmt = &formats[i]; + zprintf(2, " [%d] %.4s(%08x) %s %s %s planes=%d bpp=%d : %.16s\n", i, + (char *)&fmt->id, fmt->id, (fmt->type == XvRGB) ? "RGB" : "YUV", + (fmt->byte_order == LSBFirst) ? "LSBFirst" : "MSBFirst", + (fmt->format == XvPacked) ? "packed" : "planar", + fmt->num_planes, fmt->bits_per_pixel, fmt->guid); + xv_add_format(w, fmt->id, port); + } + XFree(formats); + return (0); +} + +int _zbar_window_probe_xv(zbar_window_t *w) +{ + unsigned xv_major, xv_minor, xv_req, xv_ev, xv_err; + if (XvQueryExtension(w->display, &xv_major, &xv_minor, &xv_req, &xv_ev, + &xv_err)) { + zprintf(1, "XVideo extension not present\n"); + return (-1); + } + zprintf(1, "XVideo extension version %u.%u\n", xv_major, xv_minor); + + unsigned n; + XvAdaptorInfo *adaptors = NULL; + if (XvQueryAdaptors(w->display, w->xwin, &n, &adaptors)) + return (err_capture(w, SEV_ERROR, ZBAR_ERR_XPROTO, __func__, + "unable to query XVideo adaptors")); + + window_state_t *x = w->state; + x->num_xv_adaptors = 0; + x->xv_adaptors = calloc(n, sizeof(*x->xv_adaptors)); + int i; + for (i = 0; i < n; i++) { + XvAdaptorInfo *adapt = &adaptors[i]; + zprintf(2, " adaptor[%d] %lu ports %u-%u type=0x%x fmts=%lu : %s\n", + i, adapt->num_ports, (unsigned)adapt->base_id, + (unsigned)(adapt->base_id + adapt->num_ports - 1), adapt->type, + adapt->num_formats, adapt->name); + if (!(adapt->type & XvImageMask)) + continue; + + int j; + for (j = 0; j < adapt->num_ports; j++) + if (!XvGrabPort(w->display, adapt->base_id + j, CurrentTime)) { + zprintf(3, " grabbed port %u\n", + (unsigned)(adapt->base_id + j)); + x->xv_adaptors[x->num_xv_adaptors++] = adapt->base_id + j; + break; + } + + if (j == adapt->num_ports) + zprintf(3, " no available XVideo image port\n"); + } + XvFreeAdaptorInfo(adaptors); + adaptors = NULL; + + if (!x->num_xv_adaptors) { + zprintf(1, "WARNING: no XVideo adaptor supporting XvImages found\n"); + free(x->xv_adaptors); + x->xv_adaptors = NULL; + return (-1); + } + if (x->num_xv_adaptors < n) + x->xv_adaptors = + realloc(x->xv_adaptors, x->num_xv_adaptors * sizeof(int)); + + w->max_width = w->max_height = 65536; + w->formats = realloc(w->formats, sizeof(uint32_t)); + w->formats[0] = 0; + for (i = 0; i < x->num_xv_adaptors; i++) + if (xv_probe_port(w, x->xv_adaptors[i])) { + XvUngrabPort(w->display, x->xv_adaptors[i], CurrentTime); + x->xv_adaptors[i] = 0; + } + if (!w->formats[0] || w->max_width == 65536 || w->max_height == 65536) { + xv_cleanup(w); + return (-1); + } + + /* clean out any unused adaptors */ + for (i = 0; i < x->num_xv_adaptors; i++) { + int j; + for (j = 0; w->formats[j]; j++) + if (x->xv_ports[j] == x->xv_adaptors[i]) + break; + if (!w->formats[j]) { + XvUngrabPort(w->display, x->xv_adaptors[i], CurrentTime); + x->xv_adaptors[i] = 0; + } + } + + w->init = xv_init; + w->draw_image = xv_draw; + w->cleanup = xv_cleanup; + return (0); +} |