summaryrefslogtreecommitdiffstats
path: root/zbar
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-03-09 00:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-03-09 00:06:44 +0000
commit44cf8ec67278bd1ab6c7f83a9993f7a5686a9541 (patch)
tree5eec4b0d1a3f163d279c3c27c03324ba49fa235a /zbar
parentInitial commit. (diff)
downloadzbar-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')
-rw-r--r--zbar/Makefile.am137
-rw-r--r--zbar/config.c171
-rw-r--r--zbar/convert.c1229
-rw-r--r--zbar/debug.h93
-rw-r--r--zbar/decoder.c592
-rw-r--r--zbar/decoder.h292
-rw-r--r--zbar/decoder/codabar.c397
-rw-r--r--zbar/decoder/codabar.h51
-rw-r--r--zbar/decoder/code128.c582
-rw-r--r--zbar/decoder/code128.h51
-rw-r--r--zbar/decoder/code39.c336
-rw-r--r--zbar/decoder/code39.h50
-rw-r--r--zbar/decoder/code93.c387
-rw-r--r--zbar/decoder/code93.h49
-rw-r--r--zbar/decoder/databar.c1240
-rw-r--r--zbar/decoder/databar.h80
-rw-r--r--zbar/decoder/ean.c735
-rw-r--r--zbar/decoder/ean.h99
-rw-r--r--zbar/decoder/i25.c265
-rw-r--r--zbar/decoder/i25.h51
-rw-r--r--zbar/decoder/pdf417.c224
-rw-r--r--zbar/decoder/pdf417.h49
-rw-r--r--zbar/decoder/pdf417_hash.h406
-rw-r--r--zbar/decoder/qr_finder.c105
-rw-r--r--zbar/decoder/qr_finder.h23
-rw-r--r--zbar/decoder/sq_finder.c7
-rw-r--r--zbar/decoder/sq_finder.h9
-rw-r--r--zbar/error.c175
-rw-r--r--zbar/error.h236
-rw-r--r--zbar/event.h60
-rw-r--r--zbar/image.c342
-rw-r--r--zbar/image.h177
-rw-r--r--zbar/img_scanner.c1174
-rw-r--r--zbar/img_scanner.h37
-rw-r--r--zbar/jpeg.c245
-rw-r--r--zbar/libzbar.rc31
-rw-r--r--zbar/misc.c105
-rw-r--r--zbar/misc.h54
-rw-r--r--zbar/mutex.h160
-rw-r--r--zbar/processor.c727
-rw-r--r--zbar/processor.h128
-rw-r--r--zbar/processor/lock.c228
-rw-r--r--zbar/processor/null.c57
-rw-r--r--zbar/processor/posix.c325
-rw-r--r--zbar/processor/posix.h132
-rw-r--r--zbar/processor/win.c332
-rw-r--r--zbar/processor/x.c266
-rw-r--r--zbar/qrcode.h64
-rw-r--r--zbar/qrcode/bch15_5.c207
-rw-r--r--zbar/qrcode/bch15_5.h20
-rw-r--r--zbar/qrcode/binarize.c646
-rw-r--r--zbar/qrcode/binarize.h17
-rw-r--r--zbar/qrcode/isaac.c145
-rw-r--r--zbar/qrcode/isaac.h34
-rw-r--r--zbar/qrcode/qrdec.c4395
-rw-r--r--zbar/qrcode/qrdec.h171
-rw-r--r--zbar/qrcode/qrdectxt.c573
-rw-r--r--zbar/qrcode/rs.c907
-rw-r--r--zbar/qrcode/rs.h66
-rw-r--r--zbar/qrcode/util.c144
-rw-r--r--zbar/qrcode/util.h56
-rw-r--r--zbar/refcnt.c47
-rw-r--r--zbar/refcnt.h90
-rw-r--r--zbar/scanner.c311
-rw-r--r--zbar/sqcode.c578
-rw-r--r--zbar/sqcode.h21
-rw-r--r--zbar/svg.c187
-rw-r--r--zbar/svg.h68
-rw-r--r--zbar/symbol.c520
-rw-r--r--zbar/symbol.h96
-rw-r--r--zbar/thread.h123
-rw-r--r--zbar/timer.h146
-rw-r--r--zbar/video.c452
-rw-r--r--zbar/video.h183
-rw-r--r--zbar/video/dshow.c1334
-rw-r--r--zbar/video/null.c35
-rw-r--r--zbar/video/v4l.c73
-rw-r--r--zbar/video/v4l1.c404
-rw-r--r--zbar/video/v4l2.c1237
-rw-r--r--zbar/video/vfw.c511
-rw-r--r--zbar/window.c305
-rw-r--r--zbar/window.h142
-rw-r--r--zbar/window/dib.c69
-rw-r--r--zbar/window/null.c88
-rw-r--r--zbar/window/vfw.c96
-rw-r--r--zbar/window/win.c321
-rw-r--r--zbar/window/win.h45
-rw-r--r--zbar/window/x.c338
-rw-r--r--zbar/window/x.h74
-rw-r--r--zbar/window/ximage.c201
-rw-r--r--zbar/window/xv.c264
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(&centers, &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, &currentmt);
+ 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, &currentmt);
+ 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, &currentmt);
+ CHECK_COM_ERROR(hr, "couldn't query current camera format", return -1)
+
+ if (!dshow_has_vih(currentmt)) {
+ char *formattype = get_clsid_string(&currentmt->formattype);
+ char *subtype = get_clsid_string(&currentmt->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 *)&current_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);
+}