diff --git a/COPYING b/COPYING --- a/COPYING +++ b/COPYING @@ -1,4 +1,4 @@ -The ZBar Bar Code Reader is Copyright (C) 2007-2009 Jeff Brown +The ZBar Bar Code Reader is Copyright (C) 2007-2010 Jeff Brown The QR Code reader is Copyright (C) 1999-2009 Timothy B. Terriberry diff --git a/Makefile.am b/Makefile.am --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,3 @@ -AM_CPPFLAGS = -I$(srcdir)/include -AM_CFLAGS = -Wall -Wno-parentheses -AM_CXXFLAGS = $(AM_CFLAGS) ACLOCAL_AMFLAGS = -I config bin_PROGRAMS = check_PROGRAMS = @@ -9,8 +6,10 @@ lib_LTLIBRARIES = pyexec_LTLIBRARIES = CLEANFILES = DISTCLEANFILES = +MAINTAINERCLEANFILES = BUILT_SOURCES = EXTRA_DIST = +SUBDIRS = . pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = zbar.pc @@ -39,6 +38,9 @@ if HAVE_QT include $(srcdir)/qt/Makefile.am.inc pkgconfig_DATA += zbar-qt.pc endif +if HAVE_JAVA +SUBDIRS += java +endif if HAVE_NPAPI include $(srcdir)/plugin/Makefile.am.inc endif @@ -47,11 +49,8 @@ include $(srcdir)/doc/Makefile.am.inc EXTRA_DIST += zbar.ico zbar.nsi -EXTRA_DIST += examples/upcrpc.pl examples/upcrpc.py examples/paginate.pl \ - examples/barcode.png examples/processor.pl examples/processor.py \ - examples/read_one.py examples/read_one.pl \ - examples/scan_image.c examples/scan_image.cpp examples/scan_image.pl \ - examples/scan_image.py examples/scan_image.vcproj +EXTRA_DIST += examples/barcode.png examples/upcrpc.py examples/upcrpc.pl \ + examples/scan_image.c examples/scan_image.cpp examples/scan_image.vcproj EXTRA_DIST += perl/MANIFEST perl/README perl/Changes perl/COPYING.LIB \ perl/Makefile.PL perl/typemap perl/ZBar.xs perl/ppport.h \ diff --git a/configure.ac b/configure.ac --- a/configure.ac +++ b/configure.ac @@ -46,6 +46,13 @@ AC_DEFINE_UNQUOTED([LIB_VERSION_REVISION [[`echo "$LIB_VERSION" | sed -e 's/^[^:]*:\([^:]*\):.*$/\1/'`]], [Library revision]) +AM_CPPFLAGS="-I$srcdir/include" +AM_CFLAGS="-Wall -Wno-parentheses" +AM_CXXFLAGS="$AM_CFLAGS" +AC_SUBST([AM_CPPFLAGS]) +AC_SUBST([AM_CFLAGS]) +AC_SUBST([AM_CXXFLAGS]) + dnl windows build AC_CANONICAL_HOST @@ -73,13 +80,36 @@ documentation generation]) AC_ARG_VAR([XMLTOFLAGS], [additional arguments for xmlto]) AC_CHECK_PROGS([XMLTO], [xmlto], [:]) +have_java="maybe" +AC_ARG_VAR([JAVA_HOME], [root location of JDK]) +AS_IF([test "x$JAVA_HOME" = "x"], + [JAVA_PATH="$PATH"], + [JAVA_PATH="$JAVA_HOME/bin$PATH_SEPARATOR$PATH"]) + +AC_ARG_VAR([JAVAC], [location of Java language compiler]) +AC_PATH_PROGS([JAVAC], [javac jikes ecj gcj], [:], [$JAVA_PATH]) +AS_IF([test "x$JAVAC" = "x:"], [have_java="no"]) + +AC_ARG_VAR([JAVAH], [location of Java header generator]) +AC_PATH_PROGS([JAVAH], [javah], [/bin/false], [$JAVA_PATH]) + +AC_ARG_VAR([JAR], [location of Java archive tool]) +AC_PATH_PROGS([JAR], [jar], [:], [$JAVA_PATH]) +AS_IF([test "x$JAR" = "x:"], [have_java="no"]) + +AC_ARG_VAR([JAVA], [location of Java application launcher]) +AC_PATH_PROGS([JAVA], [java], [/bin/false], [$JAVA_PATH]) + +AC_ARG_VAR([CLASSPATH], [Java class path (include JUnit to run java tests)]) +AS_IF([test "x$CLASSPATH" = "x"], [CLASSPATH="."]) + dnl symbologies AC_ARG_ENABLE([codes], [AS_HELP_STRING([--enable-codes=SYMS], - [select symbologies to compile [default=ean,i25,code39,code128,qrcode]])], + [select symbologies to compile [default=ean,databar,code128,code93,code39,i25,qrcode]])], [], - [enable_codes="ean,code39,code128,i25,qrcode"]) dnl pdf417 + [enable_codes="ean,databar,code128,code93,code39,i25,qrcode"]) AC_DEFUN([ZBAR_CHK_CODE], [ AC_MSG_CHECKING([whether to build $2]) @@ -97,11 +127,13 @@ AC_DEFUN([ZBAR_CHK_CODE], [ ])dnl ZBAR_CHK_CODE([ean], [EAN symbologies]) +ZBAR_CHK_CODE([databar], [DataBar symbology]) ZBAR_CHK_CODE([code128], [Code 128 symbology]) +ZBAR_CHK_CODE([code93], [Code 93 symbology]) ZBAR_CHK_CODE([code39], [Code 39 symbology]) -ZBAR_CHK_CODE([pdf417], [PDF417 symbology]) ZBAR_CHK_CODE([i25], [Interleaved 2 of 5 symbology]) ZBAR_CHK_CODE([qrcode], [QR Code]) +ZBAR_CHK_CODE([pdf417], [PDF417 symbology]) dnl libraries @@ -255,7 +287,7 @@ AM_CONDITIONAL([HAVE_JPEG], [test "x$wit dnl ImageMagick AC_ARG_WITH([imagemagick], [AS_HELP_STRING([--without-imagemagick], - [disable support for scanning images using ImageMagick])], + [disable support for scanning images with ImageMagick])], [], [with_imagemagick="yes"]) @@ -296,7 +328,7 @@ AM_CONDITIONAL([HAVE_MAGICK], [test "x$w dnl Mozilla NPAPI AC_ARG_WITH([npapi], [AS_HELP_STRING([--with-npapi], - [enable support for Firefox/Mozilla/OpenOffice NPAPI plugin])], + [enable support for Firefox/Mozilla/OpenOffice plugin])], [], [with_npapi="no"]) @@ -383,13 +415,38 @@ AS_IF([test "x$with_qt" != "xno"], AM_CONDITIONAL([HAVE_QT], [test "x$with_qt" = "xyes"]) +dnl Java +AC_ARG_WITH([java], + [AS_HELP_STRING([--without-java], + [disable support for Java interface])], + [], + [with_java="check"]) + +AC_ARG_VAR([JAVA_CFLAGS], [compiler flags for building JNI extensions]) +AS_IF([test "x$JAVA_CFLAGS" = "x" && test "x$JAVA_HOME" != "x"], + [JAVA_CFLAGS="-I$JAVA_HOME/include"]) + +AS_IF([test "x$with_java" != "xno"], + [CPPFLAGS_save="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $JAVA_CFLAGS" + AC_CHECK_HEADER([jni.h], [], [have_java="no"]) + CPPFLAGS="$CPPFLAGS_save" + AS_IF([test "x$have_java" != "xno"], + [with_java="yes"], + [test "x$with_java" = "xyes"], + [AC_MSG_FAILURE([unable to find Java JNI! ensure CFLAGS are set +appropriately or configure --without-java])], + [with_java="no"]) +]) +AM_CONDITIONAL([HAVE_JAVA], [test "x$with_java" = "xyes"]) + dnl header files dnl FIXME switches for shm, mmap AC_HEADER_ASSERT -AC_CHECK_HEADERS([fcntl.h features.h inttypes.h stdlib.h string.h unistd.h \ - sys/ioctl.h sys/time.h sys/times.h sys/ipc.h sys/shm.h sys/mman.h]) +AC_CHECK_HEADERS([errno.h fcntl.h features.h inttypes.h stdlib.h string.h \ + unistd.h sys/ioctl.h sys/time.h sys/times.h sys/ipc.h sys/shm.h sys/mman.h]) dnl types @@ -428,6 +485,7 @@ AC_CONFIG_COMMANDS([doc/reldate.xml], AC_CONFIG_FILES([ Makefile +java/Makefile zbar.pc zbar-gtk.pc zbar-qt.pc @@ -460,6 +518,9 @@ AS_IF([test "x$with_gtk" != "xyes"], echo "Qt4 --with-qt=$with_qt" AS_IF([test "x$with_qt" != "xyes"], [echo " => the Qt4 widget will *NOT* be built"]) +echo "Java --with-java=$with_java" +AS_IF([test "x$with_java" != "xyes"], + [echo " => the Java interface will *NOT* be built"]) #echo "NPAPI Plugin --with-npapi=$with_npapi" #AS_IF([test "x$with_mozilla" != "xyes"], # [echo " => the Mozilla/Firefox/OpenOffice plugin will *NOT* be built"]) diff --git a/doc/Makefile.am.inc b/doc/Makefile.am.inc --- a/doc/Makefile.am.inc +++ b/doc/Makefile.am.inc @@ -2,6 +2,7 @@ DOCSOURCES = doc/manual.xml doc/version.xml doc/reldate.xml \ doc/ref/zbarimg.xml doc/ref/zbarcam.xml doc/ref/commonoptions.xml +MAINTAINERCLEANFILES += doc/man/man.stamp doc/version.xml doc/reldate.xml # man page targets to distribute and install dist_man_MANS = diff --git a/doc/api/footer.html b/doc/api/footer.html --- a/doc/api/footer.html +++ b/doc/api/footer.html @@ -9,9 +9,12 @@
spadix@users.sourceforge.net
-

Copyright 2008-2009 (c) Jeff Brown - All Rights Reserved.

-

Verbatim copying and distribution of this entire article are - permitted worldwide, without royalty, in any medium, provided this - notice, and the copyright notice, are preserved.

+

Copyright 2008-2010 (c) Jeff Brown

+

This documentation is part of the ZBar Barcode Reader; you can +redistribute it 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.

diff --git a/doc/manual.xml b/doc/manual.xml --- a/doc/manual.xml +++ b/doc/manual.xml @@ -29,10 +29,10 @@ 2007 2008 2009 + 2010 Jeff Brown All Rights Reserved - @@ -47,7 +47,4 @@ &refzbarimg; - - - diff --git a/doc/ref/commonoptions.xml b/doc/ref/commonoptions.xml --- a/doc/ref/commonoptions.xml +++ b/doc/ref/commonoptions.xml @@ -47,8 +47,9 @@ , , , , , , - , , - or the special value . + , , , + , + or the special value . If symbology is omitted or , the config will be set for all diff --git a/doc/ref/zbarcam.xml b/doc/ref/zbarcam.xml --- a/doc/ref/zbarcam.xml +++ b/doc/ref/zbarcam.xml @@ -60,9 +60,10 @@ /dev/video0 The underlying library currently supports EAN-13 (including - UPC and ISBN subsets), EAN-8, Code 128, Code 39, and Interleaved - 2 of 5 symbologies. The specific type of each detected symbol is - printed with the decoded data. + UPC and ISBN subsets), EAN-8, DataBar, DataBar Expanded, Code 128, + Code 93, Code 39, Interleaved 2 of 5 and QR Code symbologies. The + specific type of each detected symbol is printed with the decoded + data. @@ -160,6 +161,37 @@ + Exit Status + + zbarcam returns an exit code to indicate the + status of the program execution. Current exit codes are: + + + + 0 + + Successful program completion. + + + + + 1 + + An error occurred. This includes bad arguments and I/O + errors. + + + + + 2 + + A fatal error occurred. + + + + + + See Also diff --git a/doc/ref/zbarimg.xml b/doc/ref/zbarimg.xml --- a/doc/ref/zbarimg.xml +++ b/doc/ref/zbarimg.xml @@ -59,9 +59,10 @@ displayed to the screen. The underlying library currently supports EAN-13 (including - UPC and ISBN subsets), EAN-8, Code 128, Code 39, and Interleaved - 2 of 5 symbologies. The specific type of each detected symbol is - printed with the decoded data. + UPC and ISBN subsets), EAN-8, DataBar, DataBar Expanded, Code 128, + Code 93, Code 39, Interleaved 2 of 5 and QR Code symbologies. The + specific type of each detected symbol is printed with the decoded + data. Note that "image" @@ -166,6 +167,56 @@ + Exit Status + + zbarimg returns an exit code to indicate the + status of the program execution. Current exit codes are: + + + + 0 + + Barcodes successfully detected in all images. Warnings may + have been generated, but no errors. + + + + + 1 + + An error occurred while processing some image(s). This + includes bad arguments, I/O errors and image handling errors from + ImageMagick. + + + + + 2 + + ImageMagick fatal error. + + + + + 3 + + The user quit the program before all images were scanned. + Only applies when running in interactive mode + (with ) + + + + + 4 + + No barcode was detected in one or more of the images. No + other errors occurred. + + + + + + See Also diff --git a/examples/paginate.pl b/examples/paginate.pl deleted file mode 100755 --- a/examples/paginate.pl +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/perl -#------------------------------------------------------------------------ -# Copyright 2009 (c) Jeff Brown -# -# 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 -#------------------------------------------------------------------------ -use warnings; -use strict; - -use Barcode::ZBar; -use Image::Magick; - -warn("no input files specified?\n") if(!@ARGV); - -# running output document -my $out = undef; - -# barcode scanner -my $scanner = Barcode::ZBar::ImageScanner->new(); - -foreach my $file (@ARGV) { - print "scanning from \"$file\"\n"; - my $imseq = Image::Magick->new(); - my $err = $imseq->Read($file); - warn($err) if($err); - - foreach my $page (@$imseq) { - # convert ImageMagick page to ZBar image - my $zimg = Barcode::ZBar::Image->new(); - $zimg->set_format('Y800'); - $zimg->set_size($page->Get(qw(columns rows))); - $zimg->set_data($page->Clone()->ImageToBlob(magick => 'GRAY', depth => 8)); - - # scan for barcodes - if($scanner->scan_image($zimg)) { - # write out previous document - $out->write() if($out); - - # use first symbol found to name next image (FIXME sanitize) - my $data = ($zimg->get_symbols())[0]->get_data(); - my $idx = $page->Get('scene') + 1; - print "splitting $data from page $idx\n"; - - # create new output document - $out = Image::Magick->new(filename => $data); - } - - # append this page to current output - push(@$out, $page) if($out); - } - - # write out final document - $out->write() if($out); -} diff --git a/examples/processor.c b/examples/processor.c new file mode 100644 --- /dev/null +++ b/examples/processor.c @@ -0,0 +1,47 @@ +#include +#include + +static void my_handler (zbar_image_t *image, + const void *userdata) +{ + /* extract results */ + const zbar_symbol_t *symbol = zbar_image_first_symbol(image); + for(; symbol; symbol = zbar_symbol_next(symbol)) { + /* do something useful with results */ + zbar_symbol_type_t typ = zbar_symbol_get_type(symbol); + const char *data = zbar_symbol_get_data(symbol); + printf("decoded %s symbol \"%s\"\n", + zbar_get_symbol_name(typ), data); + } +} + +int main (int argc, char **argv) +{ + const char *device = "/dev/video0"; + + /* create a Processor */ + zbar_processor_t *proc = zbar_processor_create(1); + + /* configure the Processor */ + zbar_processor_set_config(proc, 0, ZBAR_CFG_ENABLE, 1); + + /* initialize the Processor */ + if(argc > 1) + device = argv[1]; + zbar_processor_init(proc, device, 1); + + /* setup a callback */ + zbar_processor_set_data_handler(proc, my_handler, NULL); + + /* enable the preview window */ + zbar_processor_set_visible(proc, 1); + zbar_processor_set_active(proc, 1); + + /* keep scanning until user provides key/mouse input */ + zbar_processor_user_wait(proc, -1); + + /* clean up */ + zbar_processor_destroy(proc); + + return(0); +} diff --git a/examples/processor.cpp b/examples/processor.cpp new file mode 100644 --- /dev/null +++ b/examples/processor.cpp @@ -0,0 +1,45 @@ +#include +#include + +using namespace std; +using namespace zbar; + +class MyHandler : public Image::Handler +{ + void image_callback (Image &image) + { + for(SymbolIterator symbol = image.symbol_begin(); + symbol != image.symbol_end(); + ++symbol) + cout << "decoded " << symbol->get_type_name() << " symbol " + << "\"" << symbol->get_data() << "\"" << endl; + } +}; + +int main (int argc, char **argv) +{ + // create and initialize a Processor + const char *device = "/dev/video0"; + if(argc > 1) + device = argv[1]; + Processor proc(true, device); + + // configure the Processor + proc.set_config(ZBAR_NONE, ZBAR_CFG_ENABLE, 1); + + // setup a callback + MyHandler my_handler; + proc.set_handler(my_handler); + + // enable the preview window + proc.set_visible(); + proc.set_active(); + + try { + // keep scanning until user provides key/mouse input + proc.user_wait(); + } + catch(ClosedError &e) { + } + return(0); +} diff --git a/examples/processor.pl b/examples/processor.pl deleted file mode 100755 --- a/examples/processor.pl +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env perl -use warnings; -use strict; -require Barcode::ZBar; - -# create a Processor -my $proc = Barcode::ZBar::Processor->new(); - -# configure the Processor -$proc->parse_config("enable"); - -# initialize the Processor -$proc->init($ARGV[0] || '/dev/video0'); - -# setup a callback -sub my_handler { - my ($proc, $image, $closure) = @_; - - # extract results - foreach my $symbol ($proc->get_results()) { - # do something useful with results - print('decoded ' . $symbol->get_type() . - ' symbol "' . $symbol->get_data() . "\"\n"); - } -} -$proc->set_data_handler(\&my_handler); - -# enable the preview window -$proc->set_visible(); - -# initiate scanning -$proc->set_active(); - -# keep scanning until user provides key/mouse input -$proc->user_wait(); diff --git a/examples/read_one.pl b/examples/read_one.pl deleted file mode 100755 --- a/examples/read_one.pl +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env perl -use warnings; -use strict; -require Barcode::ZBar; - -# create a Processor -my $proc = Barcode::ZBar::Processor->new(); - -# configure the Processor -$proc->parse_config("enable"); - -# initialize the Processor -$proc->init($ARGV[0] || '/dev/video0'); - -# enable the preview window -$proc->set_visible(); - -# read at least one barcode (or until window closed) -$proc->process_one(); - -# hide the preview window -$proc->set_visible(0); - -# extract results -foreach my $symbol ($proc->get_results()) { - # do something useful with results - print('decoded ' . $symbol->get_type() . - ' symbol "' . $symbol->get_data() . "\"\n"); -} diff --git a/examples/scan_image.c b/examples/scan_image.c --- a/examples/scan_image.c +++ b/examples/scan_image.c @@ -65,7 +65,7 @@ int main (int argc, char **argv) /* wrap image data */ zbar_image_t *image = zbar_image_create(); - zbar_image_set_format(image, *(int*)"Y800"); + zbar_image_set_format(image, zbar_fourcc('Y','8','0','0')); zbar_image_set_size(image, width, height); zbar_image_set_data(image, raw, width * height, zbar_image_free_data); diff --git a/examples/scan_image.pl b/examples/scan_image.pl deleted file mode 100755 --- a/examples/scan_image.pl +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/perl -use warnings; -use strict; -require Image::Magick; -require Barcode::ZBar; - -$ARGV[0] || die; - -# create a reader -my $scanner = Barcode::ZBar::ImageScanner->new(); - -# configure the reader -$scanner->parse_config("enable"); - -# obtain image data -my $magick = Image::Magick->new(); -$magick->Read($ARGV[0]) && die; -my $raw = $magick->ImageToBlob(magick => 'GRAY', depth => 8); - -# wrap image data -my $image = Barcode::ZBar::Image->new(); -$image->set_format('Y800'); -$image->set_size($magick->Get(qw(columns rows))); -$image->set_data($raw); - -# scan the image for barcodes -my $n = $scanner->scan_image($image); - -# extract results -foreach my $symbol ($image->get_symbols()) { - # do something useful with results - print('decoded ' . $symbol->get_type() . - ' symbol "' . $symbol->get_data() . "\"\n"); -} - -# clean up -undef($image); diff --git a/examples/upcrpc.pl b/examples/upcrpc.pl --- a/examples/upcrpc.pl +++ b/examples/upcrpc.pl @@ -3,7 +3,7 @@ use warnings; use strict; use Frontier::Client; use Data::Dumper; -my $s = Frontier::Client->new('url' => 'http://dev.upcdatabase.com/rpc'); +my $s = Frontier::Client->new('url' => 'http://www.upcdatabase.com/rpc'); $| = 1; # autoflush diff --git a/examples/upcrpc.py b/examples/upcrpc.py --- a/examples/upcrpc.py +++ b/examples/upcrpc.py @@ -2,7 +2,7 @@ from xmlrpclib import ServerProxy import sys, re -server = ServerProxy("http://dev.upcdatabase.com/rpc") +server = ServerProxy("http://www.upcdatabase.com/rpc") ean_re = re.compile(r'^(UPC-A:|EAN-13:)?(\d{11,13})$', re.M) def lookup(decode): diff --git a/gtk/zbargtk.c b/gtk/zbargtk.c --- a/gtk/zbargtk.c +++ b/gtk/zbargtk.c @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2008-2009 (c) Jeff Brown + * Copyright 2008-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -35,11 +35,6 @@ #define DEFAULT_WIDTH 640 #define DEFAULT_HEIGHT 480 -/* adapted from v4l2 spec */ -#define fourcc(a, b, c, d) \ - ((long)(a) | ((long)(b) << 8) | \ - ((long)(c) << 16) | ((long)(d) << 24)) - enum { DECODED, DECODED_TEXT, @@ -88,15 +83,15 @@ gboolean zbar_gtk_image_from_pixbuf (zba /* these are all guesses... */ if(nchannels == 3 && bps == 8) - type = fourcc('R','G','B','3'); + type = zbar_fourcc('R','G','B','3'); else if(nchannels == 4 && bps == 8) - type = fourcc('B','G','R','4'); /* FIXME alpha flipped?! */ + type = zbar_fourcc('B','G','R','4'); /* FIXME alpha flipped?! */ else if(nchannels == 1 && bps == 8) - type = fourcc('Y','8','0','0'); + type = zbar_fourcc('Y','8','0','0'); else if(nchannels == 3 && bps == 5) - type = fourcc('R','G','B','R'); + type = zbar_fourcc('R','G','B','R'); else if(nchannels == 3 && bps == 4) - type = fourcc('R','4','4','4'); /* FIXME maybe? */ + type = zbar_fourcc('R','4','4','4'); /* FIXME maybe? */ else { g_warning("unsupported combination: nchannels=%d bps=%d\n", nchannels, bps); @@ -142,10 +137,6 @@ static inline gboolean zbar_gtk_video_op gdk_threads_enter(); - zbar->req_width = DEFAULT_WIDTH; - zbar->req_height = DEFAULT_HEIGHT; - gtk_widget_queue_resize(GTK_WIDGET(self)); - zbar->video_opened = FALSE; if(zbar->thread) g_object_notify(G_OBJECT(self), "video-opened"); @@ -184,6 +175,10 @@ static inline gboolean zbar_gtk_video_op */ gdk_threads_enter(); + if(zbar->video_width && zbar->video_height) + zbar_video_request_size(zbar->video, + zbar->video_width, zbar->video_height); + video_opened = !zbar_negotiate_format(zbar->video, zbar->window); if(video_opened) { @@ -209,7 +204,7 @@ static inline int zbar_gtk_process_image if(!image) return(-1); - zbar_image_t *tmp = zbar_image_convert(image, fourcc('Y','8','0','0')); + zbar_image_t *tmp = zbar_image_convert(image, zbar_fourcc('Y','8','0','0')); if(!tmp) return(-1); @@ -560,6 +555,19 @@ gboolean zbar_gtk_get_video_opened (ZBar return(zbar->video_opened); } +void zbar_gtk_request_video_size (ZBarGtk *self, + int width, + int height) +{ + if(!self->_private || width < 0 || height < 0) + return; + ZBarGtkPrivate *zbar = ZBAR_GTK_PRIVATE(self->_private); + + zbar->req_width = zbar->video_width = width; + zbar->req_height = zbar->video_height = height; + gtk_widget_queue_resize(GTK_WIDGET(self)); +} + static void zbar_gtk_set_property (GObject *object, guint prop_id, const GValue *value, @@ -613,8 +621,8 @@ static void zbar_gtk_init (ZBarGtk *self zbar->window = zbar_window_create(); g_assert(zbar->window); - zbar->req_width = DEFAULT_WIDTH; - zbar->req_height = DEFAULT_HEIGHT; + zbar->req_width = zbar->video_width = DEFAULT_WIDTH; + zbar->req_height = zbar->video_width = DEFAULT_HEIGHT; /* spawn a thread to handle decoding and video */ zbar->queue = g_async_queue_new(); diff --git a/gtk/zbargtkprivate.h b/gtk/zbargtkprivate.h --- a/gtk/zbargtkprivate.h +++ b/gtk/zbargtkprivate.h @@ -71,6 +71,7 @@ typedef struct _ZBarGtkPrivate * protected by main gui lock */ unsigned req_width, req_height; + unsigned video_width, video_height; gboolean video_opened; /* window is shared: owned by main gui thread. diff --git a/include/zbar.h b/include/zbar.h --- a/include/zbar.h +++ b/include/zbar.h @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2007-2009 (c) Jeff Brown + * Copyright 2007-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -93,9 +93,12 @@ typedef enum zbar_symbol_type_e { ZBAR_EAN13 = 13, /**< EAN-13 */ ZBAR_ISBN13 = 14, /**< ISBN-13 (from EAN-13). @since 0.4 */ ZBAR_I25 = 25, /**< Interleaved 2 of 5. @since 0.4 */ + ZBAR_DATABAR = 34, /**< GS1 DataBar (RSS). @since 0.11 */ + ZBAR_DATABAR_EXP = 35, /**< GS1 DataBar Expanded. @since 0.11 */ ZBAR_CODE39 = 39, /**< Code 39. @since 0.4 */ ZBAR_PDF417 = 57, /**< PDF417. @since 0.6 */ ZBAR_QRCODE = 64, /**< QR Code. @since 0.10 */ + ZBAR_CODE93 = 93, /**< Code 93. @since 0.11 */ ZBAR_CODE128 = 128, /**< Code 128 */ ZBAR_SYMBOL = 0x00ff, /**< mask for base symbol type */ ZBAR_ADDON2 = 0x0200, /**< 2-digit add-on flag */ @@ -103,6 +106,17 @@ typedef enum zbar_symbol_type_e { ZBAR_ADDON = 0x0700, /**< add-on flag mask */ } zbar_symbol_type_t; +/** decoded symbol coarse orientation. + * @since 0.11 + */ +typedef enum zbar_orientation_e { + ZBAR_ORIENT_UNKNOWN = -1, /**< unable to determine orientation */ + ZBAR_ORIENT_UP, /**< upright, read left to right */ + ZBAR_ORIENT_RIGHT, /**< sideways, read top to bottom */ + ZBAR_ORIENT_DOWN, /**< upside-down, read right to left */ + ZBAR_ORIENT_LEFT, /**< sideways, read bottom to top */ +} zbar_orientation_t; + /** error codes. */ typedef enum zbar_error_e { ZBAR_OK = 0, /**< no error */ @@ -133,12 +147,33 @@ typedef enum zbar_config_e { ZBAR_CFG_MIN_LEN = 0x20, /**< minimum data length for valid decode */ ZBAR_CFG_MAX_LEN, /**< maximum data length for valid decode */ + ZBAR_CFG_UNCERTAINTY = 0x40,/**< required video consistency frames */ + ZBAR_CFG_POSITION = 0x80, /**< enable scanner to collect position data */ ZBAR_CFG_X_DENSITY = 0x100, /**< image scanner vertical scan density */ ZBAR_CFG_Y_DENSITY, /**< image scanner horizontal scan density */ } zbar_config_t; +/** decoder symbology modifier flags. + * @since 0.11 + */ +typedef enum zbar_modifier_e { + /** barcode tagged as GS1 (EAN.UCC) reserved + * (eg, FNC1 before first data character). + * data may be parsed as a sequence of GS1 AIs + */ + ZBAR_MOD_GS1 = 0, + + /** barcode tagged as AIM reserved + * (eg, FNC1 after first character or digit pair) + */ + ZBAR_MOD_AIM, + + /** number of modifiers */ + ZBAR_MOD_NUM, +} zbar_modifier_t; + /** retrieve runtime library version information. * @param major set to the running major version (unless NULL) * @param minor set to the running minor version (unless NULL) @@ -171,6 +206,28 @@ extern const char *zbar_get_symbol_name( */ extern const char *zbar_get_addon_name(zbar_symbol_type_t sym); +/** retrieve string name for configuration setting. + * @param config setting to name + * @returns static string name for config, + * or the empty string if value is not a known config + */ +extern const char *zbar_get_config_name(zbar_config_t config); + +/** retrieve string name for modifier. + * @param modifier flag to name + * @returns static string name for modifier, + * or the empty string if the value is not a known flag + */ +extern const char *zbar_get_modifier_name(zbar_modifier_t modifier); + +/** retrieve string name for orientation. + * @param orientation orientation encoding + * @returns the static string name for the specified orientation, + * or "UNKNOWN" if the orientation is not recognized + * @since 0.11 + */ +extern const char *zbar_get_orientation_name(zbar_orientation_t orientation); + /** parse a configuration string of the form "[symbology.]config[=value]". * the config must match one of the recognized names. * the symbology, if present, must match one of the recognized names. @@ -184,6 +241,30 @@ extern int zbar_parse_config(const char zbar_config_t *config, int *value); +/** consistently compute fourcc values across architectures + * (adapted from v4l2 specification) + * @since 0.11 + */ +#define zbar_fourcc(a, b, c, d) \ + ((unsigned long)(a) | \ + ((unsigned long)(b) << 8) | \ + ((unsigned long)(c) << 16) | \ + ((unsigned long)(d) << 24)) + +/** parse a fourcc string into its encoded integer value. + * @since 0.11 + */ +static inline unsigned long zbar_fourcc_parse (const char *format) +{ + unsigned long fourcc = 0; + if(format) { + int i; + for(i = 0; i < 4 && format[i]; i++) + fourcc |= ((unsigned long)format[i]) << (i * 8); + } + return(fourcc); +} + /** @internal type unsafe error API (don't use) */ extern int _zbar_error_spew(const void *object, int verbosity); @@ -229,6 +310,20 @@ extern void zbar_symbol_ref(const zbar_s */ extern zbar_symbol_type_t zbar_symbol_get_type(const zbar_symbol_t *symbol); +/** retrieve symbology boolean config settings. + * @returns a bitmask indicating which configs were set for the detected + * symbology during decoding. + * @since 0.11 + */ +extern unsigned int zbar_symbol_get_configs(const zbar_symbol_t *symbol); + +/** retrieve symbology modifier flag settings. + * @returns a bitmask indicating which characteristics were detected + * during decoding. + * @since 0.11 + */ +extern unsigned int zbar_symbol_get_modifiers(const zbar_symbol_t *symbol); + /** retrieve data decoded from symbol. * @returns the data string */ @@ -284,6 +379,14 @@ extern int zbar_symbol_get_loc_x(const z extern int zbar_symbol_get_loc_y(const zbar_symbol_t *symbol, unsigned index); +/** retrieve general orientation of decoded symbol. + * @returns a coarse, axis-aligned indication of symbol orientation or + * ZBAR_ORIENT_UNKNOWN if unknown + * @since 0.11 + */ +extern zbar_orientation_t +zbar_symbol_get_orientation(const zbar_symbol_t *symbol); + /** iterate the set to which this symbol belongs (there can be only one). * @returns the next symbol in the set, or * @returns NULL when no more results are available @@ -357,6 +460,14 @@ extern int zbar_symbol_set_get_size(cons extern const zbar_symbol_t* zbar_symbol_set_first_symbol(const zbar_symbol_set_t *symbols); +/** raw result iterator. + * @returns the first decoded symbol result in a set, *before* filtering + * @returns NULL if the set is empty + * @since 0.11 + */ +extern const zbar_symbol_t* +zbar_symbol_set_first_unfiltered(const zbar_symbol_set_t *symbols); + /*@}*/ /*------------------------------------------------------------*/ @@ -456,6 +567,25 @@ extern unsigned zbar_image_get_width(con */ extern unsigned zbar_image_get_height(const zbar_image_t *image); +/** retrieve both dimensions of the image. + * fills in the width and height in samples + */ +extern void zbar_image_get_size(const zbar_image_t *image, + unsigned *width, + unsigned *height); + +/** retrieve the crop rectangle. + * fills in the image coordinates of the upper left corner and size + * of an axis-aligned rectangular area of the image that will be scanned. + * defaults to the full image + * @since 0.11 + */ +extern void zbar_image_get_crop(const zbar_image_t *image, + unsigned *x, + unsigned *y, + unsigned *width, + unsigned *height); + /** return the image sample data. the returned data buffer is only * valid until zbar_image_destroy() is called */ @@ -505,12 +635,24 @@ extern void zbar_image_set_sequence(zbar unsigned sequence_num); /** specify the pixel size of the image. + * @note this also resets the crop rectangle to the full image + * (0, 0, width, height) * @note this does not affect the data! */ extern void zbar_image_set_size(zbar_image_t *image, unsigned width, unsigned height); +/** specify a rectangular region of the image to scan. + * the rectangle will be clipped to the image boundaries. + * defaults to the full image specified by zbar_image_set_size() + */ +extern void zbar_image_set_crop(zbar_image_t *image, + unsigned x, + unsigned y, + unsigned width, + unsigned height); + /** specify image sample data. when image data is no longer needed by * the library the specific data cleanup handler will be called * (unless NULL) @@ -1148,6 +1290,14 @@ static inline int zbar_decoder_parse_con zbar_decoder_set_config(decoder, sym, cfg, val)); } +/** retrieve symbology boolean config settings. + * @returns a bitmask indicating which configs are currently set for the + * specified symbology. + * @since 0.11 + */ +extern unsigned int zbar_decoder_get_configs(const zbar_decoder_t *decoder, + zbar_symbol_type_t symbology); + /** clear all decoder state. * any partial symbols are flushed */ @@ -1195,6 +1345,20 @@ zbar_decoder_get_data_length(const zbar_ extern zbar_symbol_type_t zbar_decoder_get_type(const zbar_decoder_t *decoder); +/** retrieve modifier flags for the last decoded symbol. + * @returns a bitmask indicating which characteristics were detected + * during decoding. + * @since 0.11 + */ +extern unsigned int zbar_decoder_get_modifiers(const zbar_decoder_t *decoder); + +/** retrieve last decode direction. + * @returns 1 for forward and -1 for reverse + * @returns 0 if the decode direction is unknown or does not apply + * @since 0.11 + */ +extern int zbar_decoder_get_direction(const zbar_decoder_t *decoder); + /** setup data handler callback. * the registered function will be called by the decoder * just before zbar_decode_width() returns a non-zero value. diff --git a/include/zbar/Decoder.h b/include/zbar/Decoder.h --- a/include/zbar/Decoder.h +++ b/include/zbar/Decoder.h @@ -1,5 +1,5 @@ //------------------------------------------------------------------------ -// Copyright 2007-2009 (c) Jeff Brown +// Copyright 2007-2010 (c) Jeff Brown // // This file is part of the ZBar Bar Code Reader. // @@ -149,6 +149,14 @@ class Decoder { return(zbar_decoder_get_data_length(_decoder)); } + /// retrieve last decode direction. + /// see zbar_decoder_get_direction() + /// @since 0.11 + int get_direction() const + { + return(zbar_decoder_get_direction(_decoder)); + } + /// setup callback to handle result data. void set_handler (Handler &handler) { diff --git a/include/zbar/Image.h b/include/zbar/Image.h --- a/include/zbar/Image.h +++ b/include/zbar/Image.h @@ -1,5 +1,5 @@ //------------------------------------------------------------------------ -// Copyright 2007-2009 (c) Jeff Brown +// Copyright 2007-2010 (c) Jeff Brown // // This file is part of the ZBar Bar Code Reader. // @@ -67,7 +67,12 @@ public: { if(userdata) { Image *image = (Image*)zbar_image_get_userdata(zimg); - ((Handler*)userdata)->image_callback(*image); + if(image) + ((Handler*)userdata)->image_callback(*image); + else { + Image tmp(zimg, 1); + ((Handler*)userdata)->image_callback(tmp); + } } } }; @@ -110,6 +115,8 @@ public: ~Image () { + set_data(NULL, 0); + zbar_image_set_userdata(_img, NULL); zbar_image_ref(_img, -1); } @@ -143,12 +150,7 @@ public: /// see zbar_image_set_format() void set_format (const std::string& format) { - if(format.length() != 4) - throw FormatError(); - unsigned long fourcc = ((format[0] & 0xff) | - ((format[1] & 0xff) << 8) | - ((format[2] & 0xff) << 16) | - ((format[3] & 0xff) << 24)); + unsigned long fourcc = zbar_fourcc_parse(format.c_str()); zbar_image_set_format(_img, fourcc); } @@ -183,6 +185,15 @@ public: return(zbar_image_get_height(_img)); } + /// retrieve both dimensions of the image. + /// see zbar_image_get_size() + /// @since 0.11 + void get_size (unsigned &width, + unsigned &height) const + { + zbar_image_get_size(_img, &width, &height); + } + /// specify the pixel size of the image. /// see zbar_image_set_size() void set_size (unsigned width, @@ -191,6 +202,26 @@ public: zbar_image_set_size(_img, width, height); } + /// retrieve the scan crop rectangle. + /// see zbar_image_get_crop() + void get_crop (unsigned &x, + unsigned &y, + unsigned &width, + unsigned &height) const + { + zbar_image_get_crop(_img, &x, &y, &width, &height); + } + + /// set the scan crop rectangle. + /// see zbar_image_set_crop() + void set_crop (unsigned x, + unsigned y, + unsigned width, + unsigned height) + { + zbar_image_set_crop(_img, x, y, width, height); + } + /// return the image sample data. /// see zbar_image_get_data() const void *get_data () const diff --git a/include/zbar/Processor.h b/include/zbar/Processor.h --- a/include/zbar/Processor.h +++ b/include/zbar/Processor.h @@ -1,5 +1,5 @@ //------------------------------------------------------------------------ -// Copyright 2007-2009 (c) Jeff Brown +// Copyright 2007-2010 (c) Jeff Brown // // This file is part of the ZBar Bar Code Reader. // @@ -183,8 +183,8 @@ class Processor { void force_format (std::string& input_format, std::string& output_format) { - unsigned long ifourcc = *(unsigned long*)input_format.c_str(); - unsigned long ofourcc = *(unsigned long*)output_format.c_str(); + unsigned long ifourcc = zbar_fourcc_parse(input_format.c_str()); + unsigned long ofourcc = zbar_fourcc_parse(output_format.c_str()); if(zbar_processor_force_format(_processor, ifourcc, ofourcc)) throw_exception(_processor); } diff --git a/include/zbar/QZBarImage.h b/include/zbar/QZBarImage.h --- a/include/zbar/QZBarImage.h +++ b/include/zbar/QZBarImage.h @@ -1,5 +1,5 @@ //------------------------------------------------------------------------ -// Copyright 2008-2009 (c) Jeff Brown +// Copyright 2008-2010 (c) Jeff Brown // // This file is part of the ZBar Bar Code Reader. // @@ -53,7 +53,7 @@ public: unsigned width = bpl / 4; unsigned height = qimg.height(); set_size(width, height); - set_format('B' | ('G' << 8) | ('R' << 16) | ('4' << 24)); + set_format(zbar_fourcc('B','G','R','4')); unsigned long datalen = qimg.numBytes(); set_data(qimg.bits(), datalen); diff --git a/include/zbar/Symbol.h b/include/zbar/Symbol.h --- a/include/zbar/Symbol.h +++ b/include/zbar/Symbol.h @@ -1,5 +1,5 @@ //------------------------------------------------------------------------ -// Copyright 2007-2009 (c) Jeff Brown +// Copyright 2007-2010 (c) Jeff Brown // // This file is part of the ZBar Bar Code Reader. // @@ -64,6 +64,21 @@ public: ref(-1); } + /// assignment. + SymbolSet& operator= (const SymbolSet& syms) + { + syms.ref(); + ref(-1); + _syms = syms._syms; + return(*this); + } + + /// truth testing. + bool operator! () const + { + return(!_syms || !get_size()); + } + /// manipulate reference count. void ref (int delta = 1) const { @@ -77,7 +92,7 @@ public: return(_syms); } - int get_size () + int get_size () const { return((_syms) ? zbar_symbol_set_get_size(_syms) : 0); } @@ -112,9 +127,16 @@ public: /// copy constructor. Point (const Point& pt) + : x(pt.x), + y(pt.y) + { } + + /// assignment. + Point& operator= (const Point& pt) { x = pt.x; y = pt.y; + return(*this); } }; @@ -135,7 +157,7 @@ public: _index = -1; } - /// constructor. + /// copy constructor. PointIterator (const PointIterator& iter) : _sym(iter._sym), _index(iter._index) @@ -149,6 +171,22 @@ public: _sym->ref(-1); } + /// assignment. + PointIterator& operator= (const PointIterator& iter) + { + iter._sym->ref(); + _sym->ref(-1); + _sym = iter._sym; + _index = iter._index; + return(*this); + } + + /// truth testing. + bool operator! () const + { + return(!_sym || _index < 0); + } + /// advance iterator to next Point. PointIterator& operator++ () { @@ -161,7 +199,9 @@ public: /// retrieve currently referenced Point. const Point operator* () const { - assert(_index >= 0); + assert(!!*this); + if(!*this) + return(Point()); return(Point(zbar_symbol_get_loc_x(*_sym, _index), zbar_symbol_get_loc_y(*_sym, _index))); } @@ -213,6 +253,32 @@ public: ref(-1); } + /// assignment. + Symbol& operator= (const Symbol& sym) + { + sym.ref(1); + ref(-1); + _sym = sym._sym; + _type = sym._type; + _data = sym._data; + return(*this); + } + + Symbol& operator= (const zbar_symbol_t *sym) + { + if(sym) + zbar_symbol_ref(sym, 1); + ref(-1); + init(sym); + return(*this); + } + + /// truth testing. + bool operator! () const + { + return(!_sym); + } + void ref (int delta = 1) const { if(_sym) @@ -311,6 +377,13 @@ public: return((_sym) ? zbar_symbol_get_loc_y(_sym, index) : -1); } + /// see zbar_symbol_get_orientation(). + /// @since 0.11 + int get_orientation () const + { + return(zbar_symbol_get_orientation(_sym)); + } + /// see zbar_symbol_xml(). const std::string xml () const { @@ -320,9 +393,6 @@ public: } protected: - - friend class SymbolIterator; - /// (re)initialize Symbol from C symbol object. void init (const zbar_symbol_t *sym = NULL) { @@ -361,7 +431,7 @@ public: { const zbar_symbol_set_t *zsyms = _syms; if(zsyms) - _sym.init(zbar_symbol_set_first_symbol(zsyms)); + _sym = zbar_symbol_set_first_symbol(zsyms); } /// copy constructor. @@ -370,25 +440,33 @@ public: { const zbar_symbol_set_t *zsyms = _syms; if(zsyms) - _sym.init(zbar_symbol_set_first_symbol(zsyms)); + _sym = zbar_symbol_set_first_symbol(zsyms); } ~SymbolIterator () { - _sym.init(); + } + + /// assignment. + SymbolIterator& operator= (const SymbolIterator& iter) + { + _syms = iter._syms; + _sym = iter._sym; + return(*this); + } + + bool operator! () const + { + return(!_syms || !_sym); } /// advance iterator to next Symbol. SymbolIterator& operator++ () { - const zbar_symbol_t *zsym = _sym; - if(zsym) - _sym.init(zbar_symbol_next(zsym)); - else { - const zbar_symbol_set_t *zsyms = _syms; - if(zsyms) - _sym.init(zbar_symbol_set_first_symbol(zsyms)); - } + if(!!_sym) + _sym = zbar_symbol_next(_sym); + else if(!!_syms) + _sym = zbar_symbol_set_first_symbol(_syms); return(*this); } diff --git a/include/zbar/Video.h b/include/zbar/Video.h --- a/include/zbar/Video.h +++ b/include/zbar/Video.h @@ -1,5 +1,5 @@ //------------------------------------------------------------------------ -// Copyright 2007-2009 (c) Jeff Brown +// Copyright 2007-2010 (c) Jeff Brown // // This file is part of the ZBar Bar Code Reader. // @@ -92,7 +92,7 @@ public: /// see zbar_video_init() void init (std::string& format) { - unsigned int fourcc = *(unsigned int*)format.c_str(); + unsigned int fourcc = zbar_fourcc_parse(format.c_str()); if(zbar_video_init(_video, fourcc)) throw_exception(_video); } diff --git a/include/zbar/zbargtk.h b/include/zbar/zbargtk.h --- a/include/zbar/zbargtk.h +++ b/include/zbar/zbargtk.h @@ -136,7 +136,7 @@ struct _ZBarGtkClass { GType zbar_gtk_get_type(void) G_GNUC_CONST; -/** +/** * zbar_gtk_new: * create a new barcode reader widget instance. * initially has no associated video device or image. @@ -189,7 +189,17 @@ void zbar_gtk_set_video_enabled(ZBarGtk */ gboolean zbar_gtk_get_video_opened(ZBarGtk *zbar); -/** +/** set video camera resolution. + * @width: width in pixels + * @height: height in pixels + * + * @note this call must be made before video is initialized + */ +void zbar_gtk_request_video_size(ZBarGtk *zbar, + int width, + int height); + +/** * utility function to populate a zbar_image_t from a GdkPixbuf. * @image: the zbar library image destination to populate * @pixbuf: the GdkPixbuf source diff --git a/iphone/ChangeLog b/iphone/ChangeLog new file mode 100644 --- /dev/null +++ b/iphone/ChangeLog @@ -0,0 +1,70 @@ +version 1.0.1: + * hotfix broken ZBarHelpController back button + * release updates + - update docs + * fix support for GS1 AIs + * fix simulated camera image orientation/scaling + * cleanup and expose ZBarHelpController + * expose enable for reader capture processing + * workaround iOS 4.2 hang + - update to use latest SDK + * add support for Code 93 symbology + +version 1.0: + * update to SDK 4.1, clean out LLVM warnings + * fix camera simulation gesture + +version 0.1.2: + * fix missing header dependency + * doc enhancements + * force controls to front when showsZBarControls is enabled + * fix initial zoom crop (performance bug) + * workaround iPhone quartz access past image data + +version 0.1.1: + * migrate to binary iPhone SDK distribution (NB backward incompatibilities!) + - restructure headers + +version 0.1: + * workaround for iPhone simulator builds + - refactor ZBarReaderView for capture/simulator specific implementations + - fix tracking calculations + - fix captured video frame color conversion + * fix for poor iPhone 4 performance + * enable torch for iPhone 4 + * fix iPhone circular ref bug + * add iPhone cache flush, change new libs to weak refs + * fix iPhone async ref bug + * enhance iPhone readertest w/more options + * add iPhone zoom support, integrate with crop + * add iPhone OS 4.0 video capture support + - replacement view controller for new camera reader + - separate view for use without controller + - separate capture delegate for use standalone + - add continuous autofocus + * cleanup and expose iphone help display API + * fixes to new iphone help display + * migrate iphone help display to integrated web page (NB resource updates!) + - allows easier customization + - local links open directly, external links confirm and jump out to Safari + - JavaScript hook for help context customization + - also enhanced default help (note this changes the required resources) + - fix to disable scanning during help overlay + - thanks to iafanasyev and others for detailed suggestions + * fix iphone custom overlay response (bug #2959617) + - thanks to an anonymous user for the patch! + * iphone widget performance tuning enhancements + - fix crop calculation bug in ZBarImage + - add properties to control pre-scan image cropping and scaling + - add property for scanner cache control + - enable some scanner density control (FIXME should be property) + - fix ifdef for quality control (FIXME should be property) + - add "sequence" mode test (not actually so useful) + * realtime scanning for iphone widget + - uses UIGetScreenImage() (NB private) + - ZBarImage from CGImage (instead of UIImage) + - add crop to scaling step + - expose symbol set unfiltered results + * iphone widget back compat updates, add basic test app + * add Obj-C wrapper + * first pass working iPhone "widget" diff --git a/iphone/README b/iphone/README new file mode 100644 --- /dev/null +++ b/iphone/README @@ -0,0 +1,59 @@ +ZBar iPhone SDK +=============== + +ZBar Bar Code Reader is an open source software suite for reading bar +codes from various sources, such as video streams, image files and raw +intensity sensors. It supports EAN-13/UPC-A, UPC-E, EAN-8, DataBar, +Code 128, Code 93, Code 39, Interleaved 2 of 5 and QR Code. These are +the Objective C wrappers and integrated widget for developing with the +library on the iPhone platform. + +Check the ZBar project home page for the latest release, forums, etc. + +* http://zbar.sourceforge.net/iphone + +Installation +------------ + +If you are migrating from a pre-SDK source version of the library, +first make sure you remove all of the old references to zbar.xcodeproj +and libzbar.a and revert any related build settings. + +To add the SDK to an Xcode project: + + 1. Drag ZBarSDK into your Xcode project. + 3. Add these system frameworks to your project: + * AVFoundation.framework (weak) + * CoreMedia.framework (weak) + * CoreVideo.framework (weak) + * QuartzCore.framework + * libiconv.dylib + +Documentation +------------- + +See Documentation.html for complete SDK documentation. + +Examples +-------- + +A tutorial that walks through installing and using the SDK is +available in the documentation. The SDK disk image also contains the +resulting Xcode project at Examples/ReaderSample. + +Examples/readertest demonstrates most of the configuration options +available for the reader. + +You should be able to open and build the examples directly from the +disk image. If not, please copy them to your local drive and build +from there. + +Copyright and License +--------------------- + +Licensed under the GNU Lesser General Public License, version 2.1. +http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt + +Copyright 2008-2010 © Jeff Brown et al + +See the included files COPYING and LICENSE for details diff --git a/iphone/ZBarCVImage.h b/iphone/ZBarCVImage.h new file mode 100644 --- /dev/null +++ b/iphone/ZBarCVImage.h @@ -0,0 +1,43 @@ +//------------------------------------------------------------------------ +// Copyright 2010 (c) Jeff Brown +// +// 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 +//------------------------------------------------------------------------ + +#import +#import + +// ZBarImage referring to a CVPixelBuffer. used internally to handle +// asynchronous conversion to UIImage + +@interface ZBarCVImage + : ZBarImage +{ + CVPixelBufferRef pixelBuffer; + void *rgbBuffer; + NSInvocationOperation *conversion; +} + +- (void) waitUntilConverted; + +@property (nonatomic) CVPixelBufferRef pixelBuffer; +@property (nonatomic, readonly) void *rgbBuffer; + +@end diff --git a/iphone/ZBarCVImage.m b/iphone/ZBarCVImage.m new file mode 100644 --- /dev/null +++ b/iphone/ZBarCVImage.m @@ -0,0 +1,195 @@ +//------------------------------------------------------------------------ +// Copyright 2010 (c) Jeff Brown +// +// 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 +//------------------------------------------------------------------------ + +#import "ZBarCVImage.h" +#define MODULE ZBarCVImage +#import "debug.h" + +static NSOperationQueue *conversionQueue; + +static const void* +asyncProvider_getBytePointer (void *info) +{ + // block until data is available + ZBarCVImage *image = info; + assert(image); + [image waitUntilConverted]; + void *buf = image.rgbBuffer; + assert(buf); + return(buf); +} + +static const CGDataProviderDirectCallbacks asyncProvider = { + .version = 0, + .getBytePointer = asyncProvider_getBytePointer, + .releaseBytePointer = NULL, + .getBytesAtPosition = NULL, + .releaseInfo = (void*)CFRelease, +}; + +@implementation ZBarCVImage + +@synthesize pixelBuffer, rgbBuffer; + +- (void) dealloc +{ + self.pixelBuffer = NULL; + if(rgbBuffer) { + free(rgbBuffer); + rgbBuffer = NULL; + } + [conversion release]; + conversion = nil; + [super dealloc]; +} + +- (void) setPixelBuffer: (CVPixelBufferRef) newbuf +{ + CVPixelBufferRef oldbuf = pixelBuffer; + if(newbuf) + CVPixelBufferRetain(newbuf); + pixelBuffer = newbuf; + if(oldbuf) + CVPixelBufferRelease(oldbuf); +} + +- (void) waitUntilConverted +{ + // operation will at least have been queued already + NSOperation *op = [conversion retain]; + if(!op) + return; + [op waitUntilFinished]; + [op release]; +} + +- (UIImage*) UIImageWithOrientation: (UIImageOrientation) orient +{ + if(!conversion && !rgbBuffer) { + // start format conversion in separate thread + NSOperationQueue *queue = conversionQueue; + if(!queue) { + queue = conversionQueue = [NSOperationQueue new]; + queue.maxConcurrentOperationCount = 1; + } + else + [queue waitUntilAllOperationsAreFinished]; + + conversion = [[NSInvocationOperation alloc] + initWithTarget: self + selector: @selector(convertCVtoRGB) + object: nil]; + [queue addOperation: conversion]; + [conversion release]; + } + + // create UIImage before converted data is available + CGSize size = self.size; + int w = size.width; + int h = size.height; + + CGDataProviderRef datasrc = + CGDataProviderCreateDirect([self retain], 3 * w * h, &asyncProvider); + CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB(); + CGImageRef cgimg = + CGImageCreate(w, h, 8, 24, 3 * w, cs, + kCGBitmapByteOrderDefault, datasrc, + NULL, YES, kCGRenderingIntentDefault); + CGColorSpaceRelease(cs); + CGDataProviderRelease(datasrc); + + UIImage *uiimg = + [UIImage imageWithCGImage: cgimg + scale: 1 + orientation: orient]; + CGImageRelease(cgimg); + + return(uiimg); +} + +// convert video frame to a CGImage compatible RGB format +// FIXME this is temporary until we can find the native way... +- (void) convertCVtoRGB +{ + timer_start; + unsigned long format = self.format; + assert(format == zbar_fourcc('C','V','2','P')); + if(format != zbar_fourcc('C','V','2','P')) + return; + + CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly); + int w = CVPixelBufferGetWidth(pixelBuffer); + int h = CVPixelBufferGetHeight(pixelBuffer); + int dy = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0); + int duv = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1); + uint8_t *py = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0); + uint8_t *puv = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1); + if(!py || !puv || dy < w || duv < w) + goto error; + + int datalen = 3 * w * h; + // Quartz accesses some undocumented amount past allocated data? + // ...allocate extra to compensate + uint8_t *pdst = rgbBuffer = malloc(datalen + 3 * w); + if(!pdst) + goto error; + [self setData: rgbBuffer + withLength: datalen]; + + for(int y = 0; y < h; y++) { + const uint8_t *qy = py; + const uint8_t *quv = puv; + for(int x = 0; x < w; x++) { + int Y1 = *(qy++) - 16; + int Cb = *(quv) - 128; + int Cr = *(quv + 1) - 128; + Y1 *= 4769; + quv += (x & 1) << 1; + int r = (Y1 + 6537 * Cr + 2048) / 4096; + int g = (Y1 - 1604 * Cb - 3329 * Cr + 2048) / 4096; + int b = (Y1 + 8263 * Cb + 2048) / 4096; + + r = (r | -!!(r >> 8)) & -((r >> 8) >= 0); + g = (g | -!!(g >> 8)) & -((g >> 8) >= 0); + b = (b | -!!(b >> 8)) & -((b >> 8) >= 0); + + *(pdst++) = r; + *(pdst++) = g; + *(pdst++) = b; + } + py += dy; + if(y & 1) + puv += duv; + } + +error: + CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly); + zlog(@"convert time %gs", timer_elapsed(t_start, timer_now())); + + // release buffer as soon as conversion is complete + self.pixelBuffer = NULL; + + conversion = nil; +} + +@end diff --git a/iphone/ZBarCaptureReader.m b/iphone/ZBarCaptureReader.m new file mode 100644 --- /dev/null +++ b/iphone/ZBarCaptureReader.m @@ -0,0 +1,355 @@ +//------------------------------------------------------------------------ +// Copyright 2010 (c) Jeff Brown +// +// 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 +//------------------------------------------------------------------------ + +#import +#import +#import +#import +#import +#import +#import "ZBarCVImage.h" + +#define MODULE ZBarCaptureReader +#import "debug.h" + +@implementation ZBarCaptureReader + +@synthesize captureOutput, captureDelegate, scanner, scanCrop, size, + framesPerSecond, enableCache; +@dynamic enableReader; + +- (void) initResult +{ + [result release]; + result = [ZBarCVImage new]; + result.format = [ZBarImage fourcc: @"CV2P"]; +} + +- (id) initWithImageScanner: (ZBarImageScanner*) _scanner +{ + self = [super init]; + if(!self) + return(nil); + + t_fps = t_frame = timer_now(); + enableCache = YES; + + scanner = [_scanner retain]; + scanCrop = CGRectMake(0, 0, 1, 1); + image = [ZBarImage new]; + image.format = [ZBarImage fourcc: @"Y800"]; + [self initResult]; + + captureOutput = [AVCaptureVideoDataOutput new]; + captureOutput.alwaysDiscardsLateVideoFrames = YES; + +#ifdef FIXED_8697526 + /* iOS 4.2 introduced a bug that causes [session startRunning] to + * hang if the session has a preview layer and this property is + * specified at the output. As this happens to be the default + * setting for the currently supported devices, it can be omitted + * without causing a functional problem (for now...). Of course, + * we still have no idea what the real problem is, or how robust + * this is as a workaround... + */ + captureOutput.videoSettings = + [NSDictionary + dictionaryWithObject: + [NSNumber numberWithInt: + kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange] + forKey: (NSString*)kCVPixelBufferPixelFormatTypeKey]; +#endif + + queue = dispatch_queue_create("ZBarCaptureReader", NULL); + [captureOutput setSampleBufferDelegate: + (id)self + queue: queue]; + + return(self); +} + +- (id) init +{ + self = [self initWithImageScanner: + [[ZBarImageScanner new] + autorelease]]; + if(!self) + return(nil); + + [scanner setSymbology: 0 + config: ZBAR_CFG_X_DENSITY + to: 3]; + [scanner setSymbology: 0 + config: ZBAR_CFG_Y_DENSITY + to: 3]; + return(self); +} + +- (void) dealloc +{ + captureDelegate = nil; + + // queue continues to run after stopping (NB even after DidStopRunning!); + // ensure released delegate is not called. (also NB that the queue + // may not be null, even in this case...) + [captureOutput setSampleBufferDelegate: nil + queue: queue]; + [captureOutput release]; + captureOutput = nil; + dispatch_release(queue); + + [image release]; + image = nil; + [result release]; + result = nil; + [scanner release]; + scanner = nil; + [super dealloc]; +} + +- (BOOL) enableReader +{ + return(!OSAtomicCompareAndSwap32Barrier(0, 0, &running)); +} + +- (void) setEnableReader: (BOOL) enable +{ + if(!enable) + OSAtomicAnd32OrigBarrier(0, (void*)&running); + else if(OSAtomicCompareAndSwap32Barrier(0, 1, &running)) + @synchronized(scanner) { + scanner.enableCache = enableCache; + } +} + +- (void) willStartRunning +{ + self.enableReader = YES; +} + +- (void) willStopRunning +{ + self.enableReader = NO; +} + +- (void) flushCache +{ + @synchronized(scanner) { + scanner.enableCache = enableCache; + } +} + +- (void) setCaptureDelegate: (id) delegate +{ + @synchronized(scanner) { + captureDelegate = delegate; + } +} + +- (void) cropUpdate +{ + @synchronized(scanner) { + image.crop = CGRectMake(scanCrop.origin.x * width, + scanCrop.origin.y * height, + scanCrop.size.width * width, + scanCrop.size.height * height); + } +} + +- (void) setScanCrop: (CGRect) crop +{ + if(CGRectEqualToRect(scanCrop, crop)) + return; + scanCrop = crop; + [self cropUpdate]; +} + +- (void) didTrackSymbols: (ZBarSymbolSet*) syms +{ + [captureDelegate + captureReader: self + didTrackSymbols: syms]; +} + +- (void) didReadNewSymbolsFromImage: (ZBarImage*) img +{ + timer_start; + [captureDelegate + captureReader: self + didReadNewSymbolsFromImage: img]; + OSAtomicCompareAndSwap32Barrier(2, 1, &running); + zlog(@"latency: delegate=%gs total=%gs", + timer_elapsed(t_start, timer_now()), + timer_elapsed(t_scan, timer_now())); +} + +- (void) setFramesPerSecond: (CGFloat) fps +{ + framesPerSecond = fps; +} + +- (void) updateFPS: (NSNumber*) val +{ + [self setFramesPerSecond: val.doubleValue]; +} + +- (void) setSize: (CGSize) _size +{ + size = _size; +} + +- (void) updateSize: (CFDictionaryRef) val +{ + CGSize _size; + if(CGSizeMakeWithDictionaryRepresentation(val, &_size)) + [self setSize: _size]; +} + +- (void) captureOutput: (AVCaptureOutput*) output + didOutputSampleBuffer: (CMSampleBufferRef) samp + fromConnection: (AVCaptureConnection*) conn +{ + // queue is apparently not flushed when stopping; + // only process when running + if(!OSAtomicCompareAndSwap32Barrier(1, 1, &running)) + return; + + NSAutoreleasePool *pool = [NSAutoreleasePool new]; + image.sequence = framecnt++; + + uint64_t now = timer_now(); + double dt = timer_elapsed(t_frame, now); + t_frame = now; + if(dt > 2) { + t_fps = now; + dt_frame = 0; + } + else if(!dt_frame) + dt_frame = dt; + dt_frame = (dt_frame + dt) / 2; + if(timer_elapsed(t_fps, now) >= 1) { + [self performSelectorOnMainThread: @selector(updateFPS:) + withObject: [NSNumber numberWithDouble: 1 / dt_frame] + waitUntilDone: NO]; + t_fps = now; + } + + CVImageBufferRef buf = CMSampleBufferGetImageBuffer(samp); + if(CMSampleBufferGetNumSamples(samp) != 1 || + !CMSampleBufferIsValid(samp) || + !CMSampleBufferDataIsReady(samp) || + !buf) { + zlog(@"ERROR: invalid sample"); + goto error; + } + + OSType format = CVPixelBufferGetPixelFormatType(buf); + int planes = CVPixelBufferGetPlaneCount(buf); + + if(format != kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange || + !planes) { + zlog(@"ERROR: invalid buffer format"); + goto error; + } + + int w = CVPixelBufferGetBytesPerRowOfPlane(buf, 0); + int h = CVPixelBufferGetHeightOfPlane(buf, 0); + CVReturn rc = + CVPixelBufferLockBaseAddress(buf, kCVPixelBufferLock_ReadOnly); + if(!w || !h || rc) { + zlog(@"ERROR: invalid buffer data"); + goto error; + } + + void *data = CVPixelBufferGetBaseAddressOfPlane(buf, 0); + if(data) { + [image setData: data + withLength: w * h]; + + BOOL doTrack = NO; + int ngood = 0; + ZBarSymbolSet *syms = nil; + @synchronized(scanner) { + if(width != w || height != h) { + width = w; + height = h; + CGSize _size = CGSizeMake(w, h); + CFDictionaryRef sized = + CGSizeCreateDictionaryRepresentation(_size); + if(sized) { + [self performSelectorOnMainThread: @selector(updateSize:) + withObject: (id)sized + waitUntilDone: NO]; + CFRelease(sized); + } + image.size = _size; + [self cropUpdate]; + } + + ngood = [scanner scanImage: image]; + syms = scanner.results; + doTrack = [captureDelegate respondsToSelector: + @selector(captureReader:didTrackSymbols:)]; + } + now = timer_now(); + + if(ngood >= 0) { + // return unfiltered results for tracking feedback + syms.filterSymbols = NO; + int nraw = syms.count; + if(nraw > 0) + zlog(@"scan image: %dx%d crop=%@ ngood=%d nraw=%d", + w, h, NSStringFromCGRect(image.crop), ngood, nraw); + + if(ngood) { + // copy image data so we can release the buffer + result.size = CGSizeMake(w, h); + result.pixelBuffer = buf; + result.symbols = syms; + t_scan = now; + OSAtomicCompareAndSwap32Barrier(1, 2, &running); + [self performSelectorOnMainThread: + @selector(didReadNewSymbolsFromImage:) + withObject: result + waitUntilDone: NO]; + [self initResult]; + } + + if(nraw && doTrack) + [self performSelectorOnMainThread: + @selector(didTrackSymbols:) + withObject: syms + waitUntilDone: NO]; + } + [image setData: NULL + withLength: 0]; + } + else + zlog(@"ERROR: invalid data"); + CVPixelBufferUnlockBaseAddress(buf, kCVPixelBufferLock_ReadOnly); + + error: + [pool release]; +} + +@end diff --git a/iphone/ZBarHelpController.m b/iphone/ZBarHelpController.m new file mode 100644 --- /dev/null +++ b/iphone/ZBarHelpController.m @@ -0,0 +1,271 @@ +//------------------------------------------------------------------------ +// Copyright 2009-2010 (c) Jeff Brown +// +// 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 +//------------------------------------------------------------------------ + +#import + +@implementation ZBarHelpController + +@synthesize delegate; + +- (id) initWithReason: (NSString*) _reason +{ + self = [super init]; + if(!self) + return(nil); + + if(!_reason) + _reason = @"INFO"; + reason = [_reason retain]; + orientations = ((1 << UIInterfaceOrientationPortrait) | + (1 << UIInterfaceOrientationPortraitUpsideDown) | + (1 << UIInterfaceOrientationLandscapeLeft) | + (1 << UIInterfaceOrientationLandscapeRight)); + + return(self); +} + +- (id) init +{ + return([self initWithReason: nil]); +} + +- (void) cleanup +{ + [toolbar release]; + toolbar = nil; + [webView release]; + webView = nil; + [doneBtn release]; + doneBtn = nil; + [backBtn release]; + backBtn = nil; + [space release]; + space = nil; +} + +- (void) dealloc +{ + [self cleanup]; + [reason release]; + reason = nil; + [linkURL release]; + linkURL = nil; + [super dealloc]; +} + +- (void) layoutSubviewsForOrientation: (UIInterfaceOrientation) orient +{ + CGFloat h; + if(orient == UIInterfaceOrientationPortrait || + orient == UIInterfaceOrientationPortraitUpsideDown) + h = 44; + else + h = 32; + CGRect r = self.view.bounds; + r.origin.y += r.size.height - h; + r.size.height = h; + toolbar.frame = r; + + r = self.view.bounds; + r.size.height -= h; + webView.frame = r; +} + +- (void) viewDidLoad +{ + [super viewDidLoad]; + + UIView *view = self.view; + view.backgroundColor = [UIColor colorWithWhite: .125f + alpha: 1]; + view.autoresizingMask = (UIViewAutoresizingFlexibleWidth | + UIViewAutoresizingFlexibleHeight); + + webView = [UIWebView new]; + webView.delegate = self; + webView.backgroundColor = [UIColor colorWithWhite: .125f + alpha: 1 ]; + webView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | + UIViewAutoresizingFlexibleHeight); + + toolbar = [UIToolbar new]; + toolbar.barStyle = UIBarStyleBlackOpaque; + toolbar.autoresizingMask = (UIViewAutoresizingFlexibleWidth | + UIViewAutoresizingFlexibleTopMargin); + + doneBtn = [[UIBarButtonItem alloc] + initWithBarButtonSystemItem: UIBarButtonSystemItemDone + target: self + action: @selector(dismiss)]; + + backBtn = [[UIBarButtonItem alloc] + initWithImage: [UIImage imageNamed: @"zbar-back.png"] + style: UIBarButtonItemStylePlain + target: webView + action: @selector(goBack)]; + + space = [[UIBarButtonItem alloc] + initWithBarButtonSystemItem: + UIBarButtonSystemItemFlexibleSpace + target: nil + action: nil]; + + toolbar.items = [NSArray arrayWithObjects: space, doneBtn, nil]; + + [view addSubview: toolbar]; + + UIInterfaceOrientation orient = self.interfaceOrientation; + if(!((orientations >> orient) & 1)) + for(orient = UIInterfaceOrientationPortrait; + (orientations >> orient) && !((orientations >> orient) & 1); + orient++); + [self layoutSubviewsForOrientation: orient]; + + NSString *path = [[NSBundle mainBundle] + pathForResource: @"zbar-help" + ofType: @"html"]; + + NSURLRequest *req = nil; + if(path) { + NSURL *url = [NSURL fileURLWithPath: path + isDirectory: NO]; + if(url) + req = [NSURLRequest requestWithURL: url]; + } + if(req) + [webView loadRequest: req]; + else + NSLog(@"ERROR: unable to load zbar-help.html from bundle"); +} + +- (void) viewDidUnload +{ + [self cleanup]; + [super viewDidUnload]; +} + +- (void) viewWillAppear: (BOOL) animated +{ + assert(webView); + if(webView.loading) + [webView removeFromSuperview]; + [super viewWillAppear: animated]; +} + +- (void) viewWillDisappear: (BOOL) animated +{ + [webView stopLoading]; + webView.delegate = nil; + [super viewWillDisappear: animated]; +} + +- (BOOL) shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation) orient +{ + return([self isInterfaceOrientationSupported: orient]); +} + +- (void) willAnimateRotationToInterfaceOrientation: (UIInterfaceOrientation) orient + duration: (NSTimeInterval) duration +{ + [self layoutSubviewsForOrientation: orient]; + [webView reload]; +} + +- (BOOL) isInterfaceOrientationSupported: (UIInterfaceOrientation) orient +{ + return((orientations >> orient) & 1); +} + +- (void) setInterfaceOrientation: (UIInterfaceOrientation) orient + supported: (BOOL) supported +{ + NSUInteger mask = 1 << orient; + if(supported) + orientations |= mask; + else + orientations &= ~mask; +} + +- (void) dismiss +{ + if([delegate respondsToSelector: @selector(helpControllerDidFinish:)]) + [delegate helpControllerDidFinish: self]; + else + [self dismissModalViewControllerAnimated: YES]; +} + +- (void) webViewDidFinishLoad: (UIWebView*) view +{ + if(!view.superview) { + [view stringByEvaluatingJavaScriptFromString: + [NSString stringWithFormat: + @"onZBarHelp({reason:\"%@\"});", reason]]; + [UIView beginAnimations: @"ZBarHelp" + context: nil]; + [self.view addSubview: webView]; + [UIView commitAnimations]; + } + + BOOL canGoBack = [view canGoBack]; + NSArray *items = toolbar.items; + if(canGoBack != ([items objectAtIndex: 0] == backBtn)) { + if(canGoBack) + items = [NSArray arrayWithObjects: backBtn, space, doneBtn, nil]; + else + items = [NSArray arrayWithObjects: space, doneBtn, nil]; + [toolbar setItems: items + animated: YES]; + } +} + +- (BOOL) webView: (UIWebView*) view + shouldStartLoadWithRequest: (NSURLRequest*) req + navigationType: (UIWebViewNavigationType) nav +{ + NSURL *url = [req URL]; + if([url isFileURL]) + return(YES); + + linkURL = [url retain]; + UIAlertView *alert = + [[UIAlertView alloc] + initWithTitle: @"Open External Link" + message: @"Close this application and open link in Safari?" + delegate: nil + cancelButtonTitle: @"Cancel" + otherButtonTitles: @"OK", nil]; + alert.delegate = self; + [alert show]; + [alert release]; + return(NO); +} + +- (void) alertView: (UIAlertView*) view + clickedButtonAtIndex: (NSInteger) idx +{ + if(idx) + [[UIApplication sharedApplication] + openURL: linkURL]; +} + +@end diff --git a/iphone/ZBarImage.m b/iphone/ZBarImage.m new file mode 100644 --- /dev/null +++ b/iphone/ZBarImage.m @@ -0,0 +1,306 @@ +//------------------------------------------------------------------------ +// Copyright 2009-2010 (c) Jeff Brown +// +// 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 +//------------------------------------------------------------------------ + +#import +#import +#import "debug.h" + +static void image_cleanup(zbar_image_t *zimg) +{ + ZBarImage *image = zbar_image_get_userdata(zimg); + [image cleanup]; +} + +@implementation ZBarImage + +@dynamic format, sequence, size, crop, data, dataLength, symbols, zbarImage, + UIImage; + ++ (unsigned long) fourcc: (NSString*) format +{ + return(zbar_fourcc_parse([format UTF8String])); +} + +- (id) initWithImage: (zbar_image_t*) image +{ + if(!image) { + [self release]; + return(nil); + } + if(self = [super init]) { + zimg = image; + zbar_image_ref(image, 1); + zbar_image_set_userdata(zimg, self); + } + return(self); +} + +- (id) init +{ + zbar_image_t *image = zbar_image_create(); + self = [self initWithImage: image]; + zbar_image_ref(image, -1); + return(self); +} + +- (void) dealloc +{ + if(zimg) { + zbar_image_ref(zimg, -1); + zimg = NULL; + } + [super dealloc]; +} + +- (id) initWithCGImage: (CGImageRef) image + crop: (CGRect) crop + size: (CGSize) size +{ + if(!(self = [self init])) + return(nil); + uint64_t t_start = timer_now(); + + unsigned int w = size.width + 0.5; + unsigned int h = size.height + 0.5; + + unsigned long datalen = w * h; + uint8_t *raw = malloc(datalen); + if(!raw) { + [self release]; + return(nil); + } + + zbar_image_set_data(zimg, raw, datalen, zbar_image_free_data); + zbar_image_set_format(zimg, zbar_fourcc('Y','8','0','0')); + zbar_image_set_size(zimg, w, h); + + // scale and crop simultaneously + CGFloat scale = size.width / crop.size.width; + crop.origin.x *= -scale; + crop.size.width = scale * (CGFloat)CGImageGetWidth(image); + scale = size.height / crop.size.height; + CGFloat height = CGImageGetHeight(image); + // compensate for wacky origin + crop.origin.y = height - crop.origin.y - crop.size.height; + crop.origin.y *= -scale; + crop.size.height = scale * height; + + // generate grayscale image data + CGColorSpaceRef cs = CGColorSpaceCreateDeviceGray(); + CGContextRef ctx = + CGBitmapContextCreate(raw, w, h, 8, w, cs, kCGImageAlphaNone); + CGColorSpaceRelease(cs); + CGContextSetAllowsAntialiasing(ctx, 0); + + CGContextDrawImage(ctx, crop, image); + +#if 0 + zlog(@"convert image %dx%d: crop %g,%g %gx%g size %gx%g (%dx%d)", + CGImageGetWidth(image), CGImageGetHeight(image), + crop.origin.x, crop.origin.y, crop.size.width, crop.size.height, + size.width, size.height, w, h); + CGImageRef cgdump = CGBitmapContextCreateImage(ctx); + UIImage *uidump = [[UIImage alloc] + initWithCGImage: cgdump]; + CGImageRelease(cgdump); + UIImageWriteToSavedPhotosAlbum(uidump, nil, nil, NULL); + [uidump release]; +#endif + + CGContextRelease(ctx); + + t_convert = timer_elapsed(t_start, timer_now()); + return(self); +} + +- (id) initWithCGImage: (CGImageRef) image + size: (CGSize) size +{ + CGRect crop = CGRectMake(0, 0, + CGImageGetWidth(image), + CGImageGetHeight(image)); + return([self initWithCGImage: image + crop: crop + size: size]); +} + +- (id) initWithCGImage: (CGImageRef) image +{ + CGRect crop = CGRectMake(0, 0, + CGImageGetWidth(image), + CGImageGetHeight(image)); + return([self initWithCGImage: image + crop: crop + size: crop.size]); +} + +- (zbar_image_t*) image +{ + return(zimg); +} + +- (unsigned long) format +{ + return(zbar_image_get_format(zimg)); +} + +- (void) setFormat: (unsigned long) format +{ + zbar_image_set_format(zimg, format); +} + +- (unsigned) sequence +{ + return(zbar_image_get_sequence(zimg)); +} + +- (void) setSequence: (unsigned) seq +{ + zbar_image_set_sequence(zimg, seq); +} + +- (CGSize) size +{ + unsigned w, h; + zbar_image_get_size(zimg, &w, &h); + return(CGSizeMake(w, h)); +} + +- (void) setSize: (CGSize) size +{ + zbar_image_set_size(zimg, size.width + .5, size.height + .5); +} + +- (CGRect) crop +{ + unsigned x, y, w, h; + zbar_image_get_crop(zimg, &x, &y, &w, &h); + return(CGRectMake(x, y, w, h)); +} + +- (void) setCrop: (CGRect) crop +{ + zbar_image_set_crop(zimg, crop.origin.x + .5, crop.origin.y + .5, + crop.size.width + .5, crop.size.height + .5); +} + +- (ZBarSymbolSet*) symbols +{ + return([[[ZBarSymbolSet alloc] + initWithSymbolSet: zbar_image_get_symbols(zimg)] + autorelease]); +} + +- (void) setSymbols: (ZBarSymbolSet*) symbols +{ + zbar_image_set_symbols(zimg, [symbols zbarSymbolSet]); +} + +- (const void*) data +{ + return(zbar_image_get_data(zimg)); +} + +- (unsigned long) dataLength +{ + return(zbar_image_get_data_length(zimg)); +} + +- (void) setData: (const void*) data + withLength: (unsigned long) length +{ + zbar_image_set_data(zimg, data, length, image_cleanup); +} + +- (zbar_image_t*) zbarImage +{ + return(zimg); +} + +- (UIImage*) UIImageWithOrientation: (UIImageOrientation) orient +{ + unsigned long format = self.format; + size_t bpc, bpp; + switch(format) + { + case zbar_fourcc('R','G','B','3'): + bpc = 8; + bpp = 24; + break; + case zbar_fourcc('R','G','B','4'): + bpc = 8; + bpp = 32; + break; + case zbar_fourcc('R','G','B','Q'): + bpc = 5; + bpp = 16; + break; + default: + NSLog(@"ERROR: format %.4s(%08lx) is unsupported", + (char*)&format, format); + assert(0); + return(nil); + }; + + unsigned w = zbar_image_get_width(zimg); + unsigned h = zbar_image_get_height(zimg); + const void *data = zbar_image_get_data(zimg); + size_t datalen = zbar_image_get_data_length(zimg); + CGDataProviderRef datasrc = + CGDataProviderCreateWithData(self, data, datalen, (void*)CFRelease); + CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB(); + CGImageRef cgimg = + CGImageCreate(w, h, bpc, bpp, ((bpp + 7) >> 3) * w, cs, + kCGBitmapByteOrderDefault | + kCGImageAlphaNoneSkipFirst, + datasrc, NULL, YES, kCGRenderingIntentDefault); + CGColorSpaceRelease(cs); + CGDataProviderRelease(datasrc); + + UIImage *uiimg = + [UIImage imageWithCGImage: cgimg + scale: 1 + orientation: orient]; + CGImageRelease(cgimg); + return(uiimg); +} + +- (UIImage*) UIImage +{ + return([self UIImageWithOrientation: UIImageOrientationUp]); +} + +- (void) cleanup +{ +} + +#if 0 +- (ZBarImage*) convertToFormat: (unsigned long) format +{ + zbar_image_t *zdst = zbar_image_convert(zimg, format); + ZBarImage *image = ; + return([[[ZBarImage alloc] initWithImage: zdst] autorelease]); +} +#endif + +@end diff --git a/iphone/ZBarImageScanner.m b/iphone/ZBarImageScanner.m new file mode 100644 --- /dev/null +++ b/iphone/ZBarImageScanner.m @@ -0,0 +1,85 @@ +//------------------------------------------------------------------------ +// Copyright 2009 (c) Jeff Brown +// +// 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 +//------------------------------------------------------------------------ + +#import +#import "debug.h" + +@implementation ZBarImageScanner + +@dynamic enableCache, results; + +- (id) init +{ + if(self = [super init]) { + scanner = zbar_image_scanner_create(); + } + return(self); +} + +- (void) dealloc +{ + if(scanner) { + zbar_image_scanner_destroy(scanner); + scanner = NULL; + } + [super dealloc]; +} + +- (BOOL) enableCache +{ + assert(0); // FIXME + return(NO); +} + +- (void) setEnableCache: (BOOL) enable +{ + zbar_image_scanner_enable_cache(scanner, enable); +} + +- (ZBarSymbolSet*) results +{ + const zbar_symbol_set_t *set = zbar_image_scanner_get_results(scanner); + return([[[ZBarSymbolSet alloc] initWithSymbolSet: set] autorelease]); +} + +// image scanner config wrappers +- (void) parseConfig: (NSString*) cfg +{ + zbar_image_scanner_parse_config(scanner, [cfg UTF8String]); + // FIXME throw errors +} + +- (void) setSymbology: (zbar_symbol_type_t) sym + config: (zbar_config_t) cfg + to: (int) val +{ + zbar_image_scanner_set_config(scanner, sym, cfg, val); + // FIXME throw errors +} + +- (NSInteger) scanImage: (ZBarImage*) image +{ + return(zbar_scan_image(scanner, image.zbarImage)); +} + +@end diff --git a/iphone/ZBarReaderController.m b/iphone/ZBarReaderController.m new file mode 100644 --- /dev/null +++ b/iphone/ZBarReaderController.m @@ -0,0 +1,747 @@ +//------------------------------------------------------------------------ +// Copyright 2009-2010 (c) Jeff Brown +// +// 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 +//------------------------------------------------------------------------ + +#import +#import +#import "debug.h" + +/* the use of UIGetScreenImage() may no longer be sanctioned, even + * though it was previously "allowed". define this to 0 to rip it out + * and fall back to cameraMode=Default (manual capture) + */ +#ifndef USE_PRIVATE_APIS +# define USE_PRIVATE_APIS 0 +#endif + +#ifndef MIN_QUALITY +# define MIN_QUALITY 10 +#endif + +NSString* const ZBarReaderControllerResults = @"ZBarReaderControllerResults"; + +#if USE_PRIVATE_APIS +// expose undocumented API +CF_RETURNS_RETAINED +CGImageRef UIGetScreenImage(void); +#endif + +@implementation ZBarReaderController + +@synthesize scanner, readerDelegate, cameraMode, scanCrop, maxScanDimension, + showsHelpOnFail, takesPicture, enableCache, tracksSymbols; +@dynamic showsZBarControls; + +- (id) init +{ + if(self = [super init]) { + showsHelpOnFail = YES; + hasOverlay = showsZBarControls = + [self respondsToSelector: @selector(cameraOverlayView)]; + enableCache = tracksSymbols = YES; + scanCrop = CGRectMake(0, 0, 1, 1); + maxScanDimension = 640; + + scanner = [ZBarImageScanner new]; + [scanner setSymbology: 0 + config: ZBAR_CFG_X_DENSITY + to: 2]; + [scanner setSymbology: 0 + config: ZBAR_CFG_Y_DENSITY + to: 2]; + + if([UIImagePickerController + isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera]) + self.sourceType = UIImagePickerControllerSourceTypeCamera; + +#if USE_PRIVATE_APIS + cameraMode = ZBarReaderControllerCameraModeSampling; +#else + cameraMode = ZBarReaderControllerCameraModeDefault; +#endif + } + return(self); +} + +- (void) initOverlay +{ + CGRect bounds = self.view.bounds; + overlay = [[UIView alloc] initWithFrame: bounds]; + overlay.backgroundColor = [UIColor clearColor]; + + CGRect r = bounds; + r.size.height -= 54; + boxView = [[UIView alloc] initWithFrame: r]; + + boxLayer = [CALayer new]; + boxLayer.frame = r; + boxLayer.borderWidth = 1; + boxLayer.borderColor = [UIColor greenColor].CGColor; + [boxView.layer addSublayer: boxLayer]; + + toolbar = [UIToolbar new]; + toolbar.barStyle = UIBarStyleBlackOpaque; + r.origin.y = r.size.height; + r.size.height = 54; + toolbar.frame = r; + + cancelBtn = [[UIBarButtonItem alloc] + initWithBarButtonSystemItem: UIBarButtonSystemItemCancel + target: self + action: @selector(cancel)]; + cancelBtn.width = r.size.width / 4 - 16; + + scanBtn = [[UIBarButtonItem alloc] + initWithTitle: @"Scan!" + style: UIBarButtonItemStyleDone + target: self + action: @selector(scan)]; + scanBtn.width = r.size.width / 2 - 16; + + for(int i = 0; i < 2; i++) + space[i] = [[UIBarButtonItem alloc] + initWithBarButtonSystemItem: + UIBarButtonSystemItemFlexibleSpace + target: nil + action: nil]; + + space[2] = [[UIBarButtonItem alloc] + initWithBarButtonSystemItem: + UIBarButtonSystemItemFixedSpace + target: nil + action: nil]; + space[2].width = r.size.width / 4 - 16; + + infoBtn = [[UIButton buttonWithType: UIButtonTypeInfoLight] retain]; + r.origin.x = r.size.width - 54; + r.size.width = 54; + infoBtn.frame = r; + [infoBtn addTarget: self + action: @selector(info) + forControlEvents: UIControlEventTouchUpInside]; +} + +- (void) viewDidLoad +{ + [super viewDidLoad]; + [super setDelegate: self]; + if(hasOverlay) + [self initOverlay]; +} + +- (void) cleanup +{ + [overlay release]; + overlay = nil; + [boxView release]; + boxView = nil; + [boxLayer release]; + boxLayer = nil; + [toolbar release]; + toolbar = nil; + [cancelBtn release]; + cancelBtn = nil; + [scanBtn release]; + scanBtn = nil; + for(int i = 0; i < 3; i++) { + [space[i] release]; + space[i] = nil; + } + [infoBtn release]; + infoBtn = nil; + [help release]; + help = nil; +} + +- (void) viewDidUnload +{ + [self cleanup]; + [super viewDidUnload]; +} + +- (void) dealloc +{ + [self cleanup]; + [scanner release]; + scanner = nil; + [super dealloc]; +} + +- (void) scan +{ + scanBtn.enabled = NO; + self.view.userInteractionEnabled = NO; + [self takePicture]; +} + +- (void) cancel +{ + [self performSelector: @selector(imagePickerControllerDidCancel:) + withObject: self + afterDelay: 0.1]; +} + +- (void) reenable +{ + scanBtn.enabled = YES; + self.view.userInteractionEnabled = YES; +} + +- (void) initScanning +{ + if(hasOverlay && + self.sourceType == UIImagePickerControllerSourceTypeCamera) { + if(showsZBarControls || ![self cameraOverlayView]) + [self setCameraOverlayView: overlay]; + + UIView *activeOverlay = [self cameraOverlayView]; + + if(showsZBarControls) { + if(!toolbar.superview) { + [overlay addSubview: toolbar]; + [overlay addSubview: infoBtn]; + } + [self setShowsCameraControls: NO]; + } + else { + [toolbar removeFromSuperview]; + [infoBtn removeFromSuperview]; + if(activeOverlay == overlay) + [self setShowsCameraControls: YES]; + } + + self.view.userInteractionEnabled = YES; + + sampling = (cameraMode == ZBarReaderControllerCameraModeSampling || + cameraMode == ZBarReaderControllerCameraModeSequence); + + if(sampling) { + toolbar.items = [NSArray arrayWithObjects: + cancelBtn, space[0], nil]; + + t_frame = timer_now(); + dt_frame = 0; + boxLayer.opacity = 0; + if(boxView.superview != activeOverlay) + [boxView removeFromSuperview]; + if(!boxView.superview) + [activeOverlay insertSubview: boxView atIndex:0]; + scanner.enableCache = enableCache; + + SEL meth = nil; + if(cameraMode == ZBarReaderControllerCameraModeSampling) { + // ensure crop rect does not include controls + if(scanCrop.origin.x + scanCrop.size.width > .8875) + scanCrop.size.width = .8875 - scanCrop.origin.x; + + meth = @selector(scanScreen); + } + else + meth = @selector(takePicture); + + [self performSelector: meth + withObject: nil + afterDelay: 2]; +#ifdef DEBUG_OBJC + [self performSelector: @selector(dumpFPS) + withObject: nil + afterDelay: 4]; +#endif + } + else { + scanBtn.enabled = NO; + toolbar.items = [NSArray arrayWithObjects: + cancelBtn, space[0], scanBtn, space[1], space[2], nil]; + + [self performSelector: @selector(reenable) + withObject: nil + afterDelay: .5]; + + [boxView removeFromSuperview]; + } + } +} + +- (void) viewWillAppear: (BOOL) animated +{ + [self initScanning]; + [super viewWillAppear: animated]; +} + +- (void) viewWillDisappear: (BOOL) animated +{ + sampling = NO; + scanner.enableCache = NO; + [super viewWillDisappear: animated]; +} + +- (BOOL) showsZBarControls +{ + return(showsZBarControls); +} + +- (void) setCameraMode: (ZBarReaderControllerCameraMode) mode +{ +#if !USE_PRIVATE_APIS + if(mode == ZBarReaderControllerCameraModeSampling) + [NSException raise: NSInvalidArgumentException + format: @"ZBarReaderController cannot set cameraMode=Sampling" + @" when USE_PRIVATE_APIS=0"]; +#endif + cameraMode = mode; +} + +- (void) setShowsZBarControls: (BOOL) show +{ + if(show && !hasOverlay) + [NSException raise: NSInvalidArgumentException + format: @"ZBarReaderController cannot set showsZBarControls=YES for OS<3.1"]; + + showsZBarControls = show; +} + +// intercept delegate as readerDelegate + +- (void) setDelegate: (id ) delegate +{ + self.readerDelegate = (id )delegate; +} + + +#ifdef DEBUG_OBJC +- (void) dumpFPS +{ + if(!sampling) + return; + [self performSelector: @selector(dumpFPS) + withObject: nil + afterDelay: 2]; + zlog(@"fps=%g", 1 / dt_frame); +} +#endif + +- (NSInteger) scanImage: (CGImageRef) image + withScaling: (CGFloat) scale +{ + uint64_t now = timer_now(); + if(dt_frame) + dt_frame = (dt_frame + timer_elapsed(t_frame, now)) / 2; + else + dt_frame = timer_elapsed(t_frame, now); + t_frame = now; + + int w = CGImageGetWidth(image); + int h = CGImageGetHeight(image); + CGRect crop; + if(w >= h) + crop = CGRectMake(scanCrop.origin.x * w, scanCrop.origin.y * h, + scanCrop.size.width * w, scanCrop.size.height * h); + else + crop = CGRectMake(scanCrop.origin.y * w, scanCrop.origin.x * h, + scanCrop.size.height * w, scanCrop.size.width * h); + + CGSize size; + if(crop.size.width >= crop.size.height && + crop.size.width > maxScanDimension) + size = CGSizeMake(maxScanDimension, + crop.size.height * maxScanDimension / crop.size.width); + else if(crop.size.height > maxScanDimension) + size = CGSizeMake(crop.size.width * maxScanDimension / crop.size.height, + maxScanDimension); + else + size = crop.size; + + if(scale) { + size.width *= scale; + size.height *= scale; + } + + if(self.sourceType != UIImagePickerControllerSourceTypeCamera || + cameraMode == ZBarReaderControllerCameraModeDefault) { + // limit the maximum number of scan passes + int density; + if(size.width > 720) + density = (size.width / 240 + 1) / 2; + else + density = 1; + [scanner setSymbology: 0 + config: ZBAR_CFG_X_DENSITY + to: density]; + + if(size.height > 720) + density = (size.height / 240 + 1) / 2; + else + density = 1; + [scanner setSymbology: 0 + config: ZBAR_CFG_Y_DENSITY + to: density]; + } + + ZBarImage *zimg = [[ZBarImage alloc] + initWithCGImage: image + crop: crop + size: size]; + int nsyms = [scanner scanImage: zimg]; + [zimg release]; + + return(nsyms); +} + +- (ZBarSymbol*) extractBestResult: (BOOL) filter +{ + ZBarSymbol *sym = nil; + ZBarSymbolSet *results = scanner.results; + results.filterSymbols = filter; + for(ZBarSymbol *s in results) + if(!sym || sym.quality < s.quality) + sym = s; + return(sym); +} + +- (void) updateBox: (ZBarSymbol*) sym + imageSize: (CGSize) size +{ + [CATransaction begin]; + [CATransaction setAnimationDuration: .3]; + [CATransaction setAnimationTimingFunction: + [CAMediaTimingFunction functionWithName: + kCAMediaTimingFunctionLinear]]; + + CGFloat alpha = boxLayer.opacity; + if(sym) { + CGRect r = sym.bounds; + if(r.size.width > 16 && r.size.height > 16) { + r.origin.x += scanCrop.origin.y * size.width; + r.origin.y += scanCrop.origin.x * size.height; + r = CGRectInset(r, -16, -16); + if(alpha > .25) { + CGRect frame = boxLayer.frame; + r.origin.x = (r.origin.x * 3 + frame.origin.x) / 4; + r.origin.y = (r.origin.y * 3 + frame.origin.y) / 4; + r.size.width = (r.size.width * 3 + frame.size.width) / 4; + r.size.height = (r.size.height * 3 + frame.size.height) / 4; + } + boxLayer.frame = r; + boxLayer.opacity = 1; + } + } + else { + if(alpha > .1) + boxLayer.opacity = alpha / 2; + else if(alpha) + boxLayer.opacity = 0; + } + [CATransaction commit]; +} + +#if USE_PRIVATE_APIS + +- (void) scanScreen +{ + if(!sampling) + return; + + // FIXME ugly hack: use private API to sample screen + CGImageRef image = UIGetScreenImage(); + + [self scanImage: image + withScaling: 0]; + CGSize size = CGSizeMake(CGImageGetWidth(image), CGImageGetHeight(image)); + CGImageRelease(image); + + ZBarSymbol *sym = [self extractBestResult: NO]; + + if(sym && !sym.count) { + SEL cb = @selector(imagePickerController:didFinishPickingMediaWithInfo:); + if(takesPicture) { + symbol = [sym retain]; + [self takePicture]; + } + else if([readerDelegate respondsToSelector: cb]) { + symbol = [sym retain]; + + [CATransaction begin]; + [CATransaction setDisableActions: YES]; + boxLayer.opacity = 0; + [CATransaction commit]; + + // capture preview image and send to delegate + // after box has been hidden + [self performSelector: @selector(captureScreen) + withObject: nil + afterDelay: 0.001]; + return; + } + } + + // reschedule + [self performSelector: @selector(scanScreen) + withObject: nil + afterDelay: 0.001]; + + if(tracksSymbols) + [self updateBox: sym + imageSize: size]; +} + +- (void) captureScreen +{ + CGImageRef screen = UIGetScreenImage(); + + CGRect r = CGRectMake(0, 0, + CGImageGetWidth(screen), CGImageGetHeight(screen)); + if(r.size.width > r.size.height) + r.size.width -= 54; + else + r.size.height -= 54; + CGImageRef preview = CGImageCreateWithImageInRect(screen, r); + CGImageRelease(screen); + + UIImage *image = [UIImage imageWithCGImage: preview]; + CGImageRelease(preview); + + [readerDelegate + imagePickerController: self + didFinishPickingMediaWithInfo: + [NSDictionary dictionaryWithObjectsAndKeys: + image, UIImagePickerControllerOriginalImage, + [NSArray arrayWithObject: symbol], + ZBarReaderControllerResults, + nil]]; + [symbol release]; + symbol = nil; + + // continue scanning until dismissed + [self performSelector: @selector(scanScreen) + withObject: nil + afterDelay: 0.001]; +} + +#endif /* USE_PRIVATE_APIS */ + +- (void) scanSequence: (UIImage*) image +{ + if(!sampling) { + [image release]; + return; + } + + int nsyms = [self scanImage: image.CGImage + withScaling: 0]; + + ZBarSymbol *sym = nil; + if(nsyms) + [self extractBestResult: NO]; + + SEL cb = @selector(imagePickerController:didFinishPickingMediaWithInfo:); + if(sym && !sym.count && + [readerDelegate respondsToSelector: cb]) + [readerDelegate + imagePickerController: self + didFinishPickingMediaWithInfo: + [NSDictionary dictionaryWithObjectsAndKeys: + image, UIImagePickerControllerOriginalImage, + [NSArray arrayWithObject: sym], + ZBarReaderControllerResults, + nil]]; + CGSize size = image.size; + [image release]; + + // reschedule + [self performSelector: @selector(takePicture) + withObject: nil + afterDelay: 0.001]; + + if(tracksSymbols) + [self updateBox: sym + imageSize: size]; +} + +- (void) showHelpWithReason: (NSString*) reason +{ + if(help) { + [help.view removeFromSuperview]; + [help release]; + } + help = [[ZBarHelpController alloc] + initWithReason: reason]; + help.delegate = (id)self; + + if(self.sourceType != UIImagePickerControllerSourceTypeCamera) { + [self presentModalViewController: help + animated: YES]; + return; + } + + // show help as overlay view to workaround controller bugs + sampling = NO; + scanner.enableCache = NO; + help.wantsFullScreenLayout = YES; + help.view.alpha = 0; + + UIView *activeOverlay = [self cameraOverlayView]; + help.view.frame = [activeOverlay + convertRect: CGRectMake(0, 0, 320, 480) + fromView: nil]; + [activeOverlay addSubview: help.view]; + [UIView beginAnimations: @"ZBarHelp" + context: nil]; + help.view.alpha = 1; + [UIView commitAnimations]; +} + +- (void) info +{ + [self showHelpWithReason: @"INFO"]; +} + +- (void) imagePickerController: (UIImagePickerController*) picker + didFinishPickingMediaWithInfo: (NSDictionary*) info +{ + UIImage *img = [info objectForKey: UIImagePickerControllerOriginalImage]; + + id results = nil; + if(self.sourceType == UIImagePickerControllerSourceTypeCamera && + cameraMode == ZBarReaderControllerCameraModeSequence) { + if(sampling) + [self performSelector: @selector(scanSequence:) + withObject: [img retain] + afterDelay: 0.001]; + return; + } + else if(!sampling) + results = [self scanImage: img.CGImage]; + else { + results = [NSArray arrayWithObject: symbol]; + [symbol release]; + symbol = nil; + } + + [self performSelector: @selector(reenable) + withObject: nil + afterDelay: .25]; + + if(results) { + NSMutableDictionary *newinfo = [info mutableCopy]; + [newinfo setObject: results + forKey: ZBarReaderControllerResults]; + SEL cb = @selector(imagePickerController:didFinishPickingMediaWithInfo:); + if([readerDelegate respondsToSelector: cb]) + [readerDelegate imagePickerController: self + didFinishPickingMediaWithInfo: newinfo]; + else + [self dismissModalViewControllerAnimated: YES]; + [newinfo release]; + return; + } + + BOOL camera = (self.sourceType == UIImagePickerControllerSourceTypeCamera); + BOOL retry = !camera || (hasOverlay && ![self showsCameraControls]); + if(showsHelpOnFail && retry) + [self showHelpWithReason: @"FAIL"]; + + SEL cb = @selector(readerControllerDidFailToRead:withRetry:); + if([readerDelegate respondsToSelector: cb]) + // assume delegate dismisses controller if necessary + [readerDelegate readerControllerDidFailToRead: self + withRetry: retry]; + else if(!retry) + // must dismiss stock controller + [self dismissModalViewControllerAnimated: YES]; +} + +- (void) imagePickerControllerDidCancel: (UIImagePickerController*) picker +{ + SEL cb = @selector(imagePickerControllerDidCancel:); + if([readerDelegate respondsToSelector: cb]) + [readerDelegate imagePickerControllerDidCancel: self]; + else + [self dismissModalViewControllerAnimated: YES]; +} + +// ZBarHelpDelegate + +- (void) helpControllerDidFinish: (ZBarHelpController*) hlp +{ + if(self.sourceType == UIImagePickerControllerSourceTypeCamera) { + [UIView beginAnimations: @"ZBarHelp" + context: nil]; + hlp.view.alpha = 0; + [UIView commitAnimations]; + [self initScanning]; + } + else + [hlp dismissModalViewControllerAnimated: YES]; +} + +- (id ) scanImage: (CGImageRef) image +{ + timer_start; + + int nsyms = [self scanImage: image + withScaling: 0]; + + if(!nsyms && + CGImageGetWidth(image) >= 640 && + CGImageGetHeight(image) >= 640) + // make one more attempt for close up, grainy images + nsyms = [self scanImage: image + withScaling: .5]; + + NSMutableArray *syms = nil; + if(nsyms) { + // quality/type filtering + int max_quality = MIN_QUALITY; + for(ZBarSymbol *sym in scanner.results) { + zbar_symbol_type_t type = sym.type; + int quality; + if(type == ZBAR_QRCODE) + quality = INT_MAX; + else + quality = sym.quality; + + if(quality < max_quality) { + zlog(@" type=%d quality=%d < %d\n", + type, quality, max_quality); + continue; + } + + if(max_quality < quality) { + max_quality = quality; + if(syms) + [syms removeAllObjects]; + } + zlog(@" type=%d quality=%d\n", type, quality); + if(!syms) + syms = [NSMutableArray arrayWithCapacity: 1]; + + [syms addObject: sym]; + } + } + + zlog(@"read %d filtered symbols in %gs total\n", + (!syms) ? 0 : [syms count], timer_elapsed(t_start, timer_now())); + return(syms); +} + +@end diff --git a/iphone/ZBarReaderView.m b/iphone/ZBarReaderView.m new file mode 100644 --- /dev/null +++ b/iphone/ZBarReaderView.m @@ -0,0 +1,408 @@ +//------------------------------------------------------------------------ +// Copyright 2010 (c) Jeff Brown +// +// 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 +//------------------------------------------------------------------------ + +#import + +#define MODULE ZBarReaderView +#import "debug.h" + +// silence warning +@interface ZBarReaderViewImpl : NSObject +@end + +@implementation ZBarReaderView + +@synthesize readerDelegate, tracksSymbols, torchMode, showsFPS, zoom, scanCrop, + previewTransform; +@dynamic scanner, allowsPinchZoom, enableCache, device, session, captureReader; + ++ (id) alloc +{ + if(self == [ZBarReaderView class]) { + // this is an abstract wrapper for implementation selected + // at compile time. replace with concrete subclass. + return([ZBarReaderViewImpl alloc]); + } + return([super alloc]); +} + +- (void) initSubviews +{ + assert(preview); + + CGRect r = preview.bounds; + overlay = [CALayer new]; + overlay.frame = r; + overlay.backgroundColor = [UIColor clearColor].CGColor; + [preview addSublayer: overlay]; + + tracking = [CALayer new]; + tracking.frame = r; + tracking.opacity = 0; + tracking.borderWidth = 1; + tracking.backgroundColor = [UIColor clearColor].CGColor; + tracking.borderColor = [UIColor greenColor].CGColor; + [overlay addSublayer: tracking]; + + r.origin.x = 3 * r.size.width / 4; + r.origin.y = r.size.height - 32; + r.size.width = r.size.width - r.origin.x + 12; + r.size.height = 32 + 12; + fpsView = [[UIView alloc] + initWithFrame: r]; + fpsView.backgroundColor = [UIColor colorWithWhite: 0 + alpha: .333]; + fpsView.layer.cornerRadius = 12; + fpsView.hidden = YES; + [self addSubview: fpsView]; + + fpsLabel = [[UILabel alloc] + initWithFrame: CGRectMake(0, 0, + r.size.width - 12, + r.size.height - 12)]; + fpsLabel.backgroundColor = [UIColor clearColor]; + fpsLabel.textColor = [UIColor colorWithRed: .333 + green: .666 + blue: 1 + alpha: 1]; + fpsLabel.font = [UIFont systemFontOfSize: 18]; + fpsLabel.textAlignment = UITextAlignmentRight; + [fpsView addSubview: fpsLabel]; + + self.zoom = 1.25; +} + +- (id) initWithImageScanner: (ZBarImageScanner*) scanner +{ + self = [super initWithFrame: CGRectMake(0, 0, 320, 426)]; + if(!self) + return(nil); + + self.backgroundColor = [UIColor blackColor]; + self.contentMode = UIViewContentModeScaleAspectFill; + self.autoresizingMask = + UIViewAutoresizingFlexibleWidth | + UIViewAutoresizingFlexibleHeight; + + tracksSymbols = YES; + torchMode = 2; // AVCaptureTorchModeAuto + scanCrop = zoomCrop = CGRectMake(0, 0, 1, 1); + imageScale = 1; + previewTransform = CGAffineTransformIdentity; + + pinch = [[UIPinchGestureRecognizer alloc] + initWithTarget: self + action: @selector(handlePinch)]; + [self addGestureRecognizer: pinch]; + + return(self); +} + +- (id) init +{ + ZBarImageScanner *scanner = + [[ZBarImageScanner new] + autorelease]; + self = [self initWithImageScanner: scanner]; + if(!self) + return(nil); + + [scanner setSymbology: 0 + config: ZBAR_CFG_X_DENSITY + to: 3]; + [scanner setSymbology: 0 + config: ZBAR_CFG_Y_DENSITY + to: 3]; + return(self); +} + +- (void) dealloc +{ + [preview removeFromSuperlayer]; + [preview release]; + preview = nil; + [overlay release]; + overlay = nil; + [tracking release]; + tracking = nil; + [fpsLabel release]; + fpsLabel = nil; + [fpsView release]; + fpsView = nil; + [pinch release]; + pinch = nil; + [super dealloc]; +} + +- (void) resetTracking +{ + [tracking removeAllAnimations]; + [CATransaction begin]; + [CATransaction setDisableActions: YES]; + CGSize size = overlay.bounds.size; + CGRect crop = zoomCrop; + tracking.frame = CGRectMake(crop.origin.x * size.width, + crop.origin.y * size.height, + crop.size.width * size.width, + crop.size.height * size.height); + tracking.opacity = 0; + [CATransaction commit]; +} + +- (void) setImageSize: (CGSize) size +{ + CGSize psize = preview.bounds.size; + CGFloat scalex = size.width / psize.height; + CGFloat scaley = size.height / psize.width; + imageScale = (scalex > scaley) ? scalex : scaley; + + // match overlay to preview image + overlay.bounds = CGRectMake(0, 0, size.width, size.height); + CGFloat scale = 1 / imageScale; + CATransform3D xform = CATransform3DMakeRotation(M_PI / 2, 0, 0, 1); + overlay.transform = CATransform3DScale(xform, scale, scale, 1); + tracking.borderWidth = imageScale / zoom; + + zlog(@"scaling: layer=%@ image=%@ scale=%g %c %g = 1/%g", + NSStringFromCGSize(psize), NSStringFromCGSize(size), + scalex, (scalex > scaley) ? '>' : '<', scaley, scale); + [self resetTracking]; +} + +- (void) cropUpdate +{ + CGAffineTransform xfrm = + CGAffineTransformMakeTranslation(.5, .5); + CGFloat z = 1 / zoom; + xfrm = CGAffineTransformScale(xfrm, z, z); + xfrm = CGAffineTransformTranslate(xfrm, -.5, -.5); + zoomCrop = CGRectApplyAffineTransform(scanCrop, xfrm); + [self resetTracking]; +} + +- (void) setScanCrop: (CGRect) r +{ + if(CGRectEqualToRect(scanCrop, r)) + return; + scanCrop = r; + [self cropUpdate]; +} + +- (void) setTracksSymbols: (BOOL) track +{ + if(track == tracksSymbols) + return; + tracksSymbols = track; + [self resetTracking]; +} + +- (BOOL) allowsPinchZoom +{ + return(pinch.enabled); +} + +- (void) setAllowsPinchZoom: (BOOL) enabled +{ + pinch.enabled = enabled; +} + +- (void) setShowsFPS: (BOOL) show +{ + if(show == showsFPS) + return; + fpsView.hidden = !show; +} + +- (void) setZoom: (CGFloat) z +{ + if(z < 1.0) + z = 1.0; + if(z > 2.0) + z = 2.0; + if(z == zoom) + return; + zoom = z; + + [CATransaction begin]; + [CATransaction setAnimationDuration: .1]; + [CATransaction setAnimationTimingFunction: + [CAMediaTimingFunction functionWithName: + kCAMediaTimingFunctionLinear]]; + [preview setAffineTransform: + CGAffineTransformScale(previewTransform, z, z)]; + tracking.borderWidth = imageScale / zoom; + [CATransaction commit]; + + [self cropUpdate]; +} + +- (void) setPreviewTransform: (CGAffineTransform) xfrm +{ + previewTransform = xfrm; + [preview setAffineTransform: + CGAffineTransformScale(previewTransform, zoom, zoom)]; +} + +- (void) start +{ + if(started) + return; + started = YES; + + [self resetTracking]; + fpsLabel.text = @"--- fps "; + + [[UIDevice currentDevice] + beginGeneratingDeviceOrientationNotifications]; +} + +- (void) stop +{ + if(!started) + return; + started = NO; + + [[UIDevice currentDevice] + endGeneratingDeviceOrientationNotifications]; +} + +- (void) flushCache +{ +} + +// UIGestureRecognizer callback + +- (void) handlePinch +{ + if(pinch.state == UIGestureRecognizerStateBegan) + zoom0 = zoom; + CGFloat z = zoom0 * pinch.scale; + self.zoom = z; + + if((zoom < 1.5) != (z < 1.5)) { + int d = (z < 1.5) ? 3 : 2; + ZBarImageScanner *scanner = self.scanner; + @synchronized(scanner) { + [scanner setSymbology: 0 + config: ZBAR_CFG_X_DENSITY + to: d]; + [scanner setSymbology: 0 + config: ZBAR_CFG_Y_DENSITY + to: d]; + } + } +} + +- (void) updateTracking: (CALayer*) trk + withSymbol: (ZBarSymbol*) sym +{ + if(!sym) + return; + + CGRect r = sym.bounds; + if(r.size.width <= 32 && r.size.height <= 32) + return; + r = CGRectInset(r, -24, -24); + + CALayer *current = trk.presentationLayer; + CGPoint cp = current.position; + CGPoint p = CGPointMake(CGRectGetMidX(r), CGRectGetMidY(r)); + p = CGPointMake((p.x * 3 + cp.x) / 4, (p.y * 3 + cp.y) / 4); + + CGRect cr = current.bounds; + r.origin = cr.origin; + r.size.width = (r.size.width * 3 + cr.size.width) / 4; + r.size.height = (r.size.height * 3 + cr.size.height) / 4; + + CAMediaTimingFunction *linear = + [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionLinear]; + + CABasicAnimation *resize = + [CABasicAnimation animationWithKeyPath: @"bounds"]; + resize.fromValue = [NSValue valueWithCGRect: cr]; + resize.toValue = [NSValue valueWithCGRect: r]; + resize.duration = .2; + resize.timingFunction = linear; + resize.fillMode = kCAFillModeForwards; + resize.removedOnCompletion = NO; + + CABasicAnimation *move = + [CABasicAnimation animationWithKeyPath: @"position"]; + move.fromValue = [NSValue valueWithCGPoint: cp]; + move.toValue = [NSValue valueWithCGPoint: p]; + move.duration = .2; + move.timingFunction = linear; + move.fillMode = kCAFillModeForwards; + move.removedOnCompletion = NO; + + CABasicAnimation *on = + [CABasicAnimation animationWithKeyPath: @"opacity"]; + on.fromValue = [NSNumber numberWithDouble: current.opacity]; + on.toValue = [NSNumber numberWithDouble: 1]; + on.duration = .2; + on.timingFunction = linear; + on.fillMode = kCAFillModeForwards; + on.removedOnCompletion = NO; + + CABasicAnimation *off = nil; + if(!TARGET_IPHONE_SIMULATOR) { + off = [CABasicAnimation animationWithKeyPath: @"opacity"]; + off.fromValue = [NSNumber numberWithDouble: 1]; + off.toValue = [NSNumber numberWithDouble: 0]; + off.beginTime = .5; + off.duration = .5; + off.timingFunction = linear; + } + + CAAnimationGroup *group = [CAAnimationGroup animation]; + group.animations = [NSArray arrayWithObjects: resize, move, on, off, nil]; + group.duration = 1; + group.fillMode = kCAFillModeForwards; + group.removedOnCompletion = !TARGET_IPHONE_SIMULATOR; + [trk addAnimation: group + forKey: @"tracking"]; +} + +- (void) didTrackSymbols: (ZBarSymbolSet*) syms +{ + if(!tracksSymbols) + return; + + int n = syms.count; + assert(n); + if(!n) + return; + + ZBarSymbol *sym = nil; + for(ZBarSymbol *s in syms) + if(!sym || s.type == ZBAR_QRCODE || s.quality > sym.quality) + sym = s; + assert(sym); + if(!sym) + return; + + [self updateTracking: tracking + withSymbol: sym]; +} + +@end diff --git a/iphone/ZBarReaderViewController.m b/iphone/ZBarReaderViewController.m new file mode 100644 --- /dev/null +++ b/iphone/ZBarReaderViewController.m @@ -0,0 +1,383 @@ +//------------------------------------------------------------------------ +// Copyright 2010 (c) Jeff Brown +// +// 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 +//------------------------------------------------------------------------ + +#import +#import +#import + +#define MODULE ZBarReaderViewController +#import "debug.h" + +@implementation ZBarReaderViewController + +@synthesize scanner, readerDelegate, showsZBarControls, tracksSymbols, + enableCache, cameraOverlayView, cameraViewTransform, readerView, scanCrop; +@dynamic sourceType, allowsEditing, allowsImageEditing, showsCameraControls, + showsHelpOnFail, cameraMode, takesPicture, maxScanDimension; + ++ (BOOL) isSourceTypeAvailable: (UIImagePickerControllerSourceType) sourceType +{ + if(sourceType != UIImagePickerControllerSourceTypeCamera) + return(NO); + return(TARGET_IPHONE_SIMULATOR || + [UIImagePickerController isSourceTypeAvailable: sourceType]); +} + +- (id) init +{ + if(!TARGET_IPHONE_SIMULATOR && + !NSClassFromString(@"AVCaptureSession")) { + // fallback to old interface + zlog(@"Falling back to ZBarReaderController"); + [self release]; + return([ZBarReaderController new]); + } + + self = [super init]; + if(!self) + return(nil); + + self.wantsFullScreenLayout = YES; + + showsZBarControls = tracksSymbols = enableCache = YES; + scanCrop = CGRectMake(0, 0, 1, 1); + cameraViewTransform = CGAffineTransformIdentity; + + // create our own scanner to store configuration, + // independent of whether view is loaded + scanner = [ZBarImageScanner new]; + [scanner setSymbology: 0 + config: ZBAR_CFG_X_DENSITY + to: 3]; + [scanner setSymbology: 0 + config: ZBAR_CFG_Y_DENSITY + to: 3]; + + return(self); +} + +- (void) cleanup +{ + readerView.readerDelegate = nil; + [readerView release]; + readerView = nil; + [controls release]; + controls = nil; +} + +- (void) dealloc +{ + [self cleanup]; + [cameraOverlayView release]; + cameraOverlayView = nil; + [scanner release]; + scanner = nil; + [super dealloc]; +} + +- (void) initControls +{ + if(!showsZBarControls && controls) { + [controls removeFromSuperview]; + [controls release]; + controls = nil; + } + if(!showsZBarControls) + return; + + UIView *view = self.view; + if(controls) { + assert(controls.superview == view); + [view bringSubviewToFront: controls]; + return; + } + + CGRect r = view.bounds; + r.origin.y = r.size.height - 54; + r.size.height = 54; + controls = [[UIView alloc] + initWithFrame: r]; + controls.backgroundColor = [UIColor blackColor]; + + UIToolbar *toolbar = + [UIToolbar new]; + r.origin.y = 0; + toolbar.frame = r; + toolbar.barStyle = UIBarStyleBlackOpaque; + + toolbar.items = + [NSArray arrayWithObjects: + [[[UIBarButtonItem alloc] + initWithBarButtonSystemItem: UIBarButtonSystemItemCancel + target: self + action: @selector(cancel)] + autorelease], + [[[UIBarButtonItem alloc] + initWithBarButtonSystemItem: UIBarButtonSystemItemFlexibleSpace + target: nil + action: nil] + autorelease], + nil]; + [controls addSubview: toolbar]; + [toolbar release]; + + UIButton *info = + [UIButton buttonWithType: UIButtonTypeInfoLight]; + r.origin.x = r.size.width - 54; + r.size.width = 54; + info.frame = r; + [info addTarget: self + action: @selector(info) + forControlEvents: UIControlEventTouchUpInside]; + [controls addSubview: info]; + + [view addSubview: controls]; +} + +- (void) initSimulator +{ + // simulator specific hooks +} + +- (void) loadView +{ + self.view = [[UIView alloc] + initWithFrame: CGRectMake(0, 0, 320, 480)]; +} + +- (void) viewDidLoad +{ + [super viewDidLoad]; + UIView *view = self.view; + view.backgroundColor = [UIColor blackColor]; + + readerView = [[ZBarReaderView alloc] + initWithImageScanner: scanner]; + readerView.readerDelegate = (id)self; + readerView.scanCrop = scanCrop; + readerView.previewTransform = cameraViewTransform; + readerView.tracksSymbols = tracksSymbols; + readerView.enableCache = enableCache; + [view addSubview: readerView]; + + if(cameraOverlayView) { + assert(!cameraOverlayView.superview); + [cameraOverlayView removeFromSuperview]; + [view addSubview: cameraOverlayView]; + } + + [self initControls]; + [self initSimulator]; +} + +- (void) viewDidUnload +{ + [cameraOverlayView removeFromSuperview]; + [self cleanup]; + [super viewDidUnload]; +} + +- (void) viewWillAppear: (BOOL) animated +{ + [self initControls]; + [super viewWillAppear: animated]; + + [readerView start]; + + UIApplication *app = [UIApplication sharedApplication]; + BOOL willHideStatusBar = + !didHideStatusBar && self.wantsFullScreenLayout && !app.statusBarHidden; + if(willHideStatusBar) + [app setStatusBarHidden: YES + withAnimation: UIStatusBarAnimationFade]; + didHideStatusBar = didHideStatusBar || willHideStatusBar; +} + +- (void) dismissModalViewControllerAnimated: (BOOL) animated +{ + if(didHideStatusBar) { + [[UIApplication sharedApplication] + setStatusBarHidden: NO + withAnimation: UIStatusBarAnimationFade]; + didHideStatusBar = NO; + } + [super dismissModalViewControllerAnimated: animated]; +} + +- (void) viewWillDisappear: (BOOL) animated +{ + [readerView stop]; + + if(didHideStatusBar) { + [[UIApplication sharedApplication] + setStatusBarHidden: NO + withAnimation: UIStatusBarAnimationFade]; + didHideStatusBar = NO; + } + + [super viewWillDisappear: animated]; +} + +- (ZBarReaderView*) readerView +{ + // force view to load + (void)self.view; + assert(readerView); + return(readerView); +} + +- (void) setTracksSymbols: (BOOL) track +{ + tracksSymbols = track; + if(readerView) + readerView.tracksSymbols = track; +} + +- (void) setEnableCache: (BOOL) enable +{ + enableCache = enable; + if(readerView) + readerView.enableCache = enable; +} + +- (void) setScanCrop: (CGRect) r +{ + scanCrop = r; + if(readerView) + readerView.scanCrop = r; +} + +- (void) setCameraOverlayView: (UIView*) newview +{ + UIView *oldview = cameraOverlayView; + [oldview removeFromSuperview]; + + cameraOverlayView = [newview retain]; + if([self isViewLoaded] && newview) + [self.view addSubview: newview]; + + [oldview release]; +} + +- (void) setCameraViewTransform: (CGAffineTransform) xfrm +{ + cameraViewTransform = xfrm; + if(readerView) + readerView.previewTransform = xfrm; +} + +- (void) cancel +{ + if(!readerDelegate) + return; + SEL cb = @selector(imagePickerControllerDidCancel:); + if([readerDelegate respondsToSelector: cb]) + [readerDelegate + imagePickerControllerDidCancel: (UIImagePickerController*)self]; + else + [self dismissModalViewControllerAnimated: YES]; +} + +- (void) info +{ + [self showHelpWithReason: @"INFO"]; +} + +- (void) showHelpWithReason: (NSString*) reason +{ + ZBarHelpController *help = + [[ZBarHelpController alloc] + initWithReason: reason]; + help.delegate = (id)self; + help.wantsFullScreenLayout = YES; + UIView *helpView = help.view; + helpView.alpha = 0; + [self.view addSubview: helpView]; + [UIView beginAnimations: @"ZBarHelp" + context: nil]; + help.view.alpha = 1; + [UIView commitAnimations]; +} + +// ZBarHelpDelegate + +- (void) helpControllerDidFinish: (ZBarHelpController*) help +{ + [UIView beginAnimations: @"ZBarHelp" + context: help]; + [UIView setAnimationDelegate: self]; + [UIView setAnimationDidStopSelector: @selector(removeHelp:done:context:)]; + help.view.alpha = 0; + [UIView commitAnimations]; +} + +- (void) removeHelp: (NSString*) id + done: (NSNumber*) done + context: (void*) ctx +{ + if([id isEqualToString: @"ZBarHelp"]) { + ZBarHelpController *help = ctx; + [help.view removeFromSuperview]; + [help release]; + } +} + +// ZBarReaderViewDelegate + +- (void) readerView: (ZBarReaderView*) view + didReadSymbols: (ZBarSymbolSet*) syms + fromImage: (UIImage*) image +{ + [readerDelegate + imagePickerController: (UIImagePickerController*)self + didFinishPickingMediaWithInfo: + [NSDictionary dictionaryWithObjectsAndKeys: + image, UIImagePickerControllerOriginalImage, + syms, ZBarReaderControllerResults, + nil]]; +} + +// "deprecated" properties + +#define DEPRECATED_PROPERTY(getter, setter, type, val, ignore) \ + - (type) getter \ + { \ + return(val); \ + } \ + - (void) setter: (type) v \ + { \ + NSAssert2(ignore || v == val, \ + @"attempt to set unsupported value (%d)" \ + @" for %@ property", val, @#getter); \ + } + +DEPRECATED_PROPERTY(sourceType, setSourceType, UIImagePickerControllerSourceType, UIImagePickerControllerSourceTypeCamera, NO) +DEPRECATED_PROPERTY(allowsEditing, setAllowsEditing, BOOL, NO, NO) +DEPRECATED_PROPERTY(allowsImageEditing, setAllowsImageEditing, BOOL, NO, NO) +DEPRECATED_PROPERTY(showsCameraControls, setShowsCameraControls, BOOL, NO, NO) +DEPRECATED_PROPERTY(showsHelpOnFail, setShowsHelpOnFail, BOOL, NO, YES) +DEPRECATED_PROPERTY(cameraMode, setCameraMode, ZBarReaderControllerCameraMode, ZBarReaderControllerCameraModeSampling, NO) +DEPRECATED_PROPERTY(takesPicture, setTakesPicture, BOOL, NO, NO) +DEPRECATED_PROPERTY(maxScanDimension, setMaxScanDimension, NSInteger, 640, YES) + +@end diff --git a/iphone/ZBarReaderViewImpl_Capture.m b/iphone/ZBarReaderViewImpl_Capture.m new file mode 100644 --- /dev/null +++ b/iphone/ZBarReaderViewImpl_Capture.m @@ -0,0 +1,355 @@ +//------------------------------------------------------------------------ +// Copyright 2010 (c) Jeff Brown +// +// 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 +//------------------------------------------------------------------------ + +#import +#import +#import +#import +#import + +#define MODULE ZBarReaderView +#import "debug.h" + +// protected APIs +@interface ZBarReaderView() +- (void) initSubviews; +- (void) cropUpdate; +- (void) setImageSize: (CGSize) size; +- (void) didTrackSymbols: (ZBarSymbolSet*) syms; +@end + +@interface ZBarReaderViewImpl + : ZBarReaderView +{ + AVCaptureSession *session; + AVCaptureDevice *device; + AVCaptureInput *input; + ZBarCaptureReader *captureReader; +} + +@end + +@implementation ZBarReaderViewImpl + +@synthesize device, session, captureReader; + +- (id) initWithImageScanner: (ZBarImageScanner*) scanner +{ + self = [super initWithImageScanner: scanner]; + if(!self) + return(nil); + + session = [AVCaptureSession new]; + NSNotificationCenter *notify = + [NSNotificationCenter defaultCenter]; + [notify addObserver: self + selector: @selector(onVideoError:) + name: AVCaptureSessionRuntimeErrorNotification + object: session]; + [notify addObserver: self + selector: @selector(onVideoStart:) + name: AVCaptureSessionDidStartRunningNotification + object: session]; + [notify addObserver: self + selector: @selector(onVideoStop:) + name: AVCaptureSessionDidStopRunningNotification + object: session]; + [notify addObserver: self + selector: @selector(onVideoStop:) + name: AVCaptureSessionWasInterruptedNotification + object: session]; + [notify addObserver: self + selector: @selector(onVideoStart:) + name: AVCaptureSessionInterruptionEndedNotification + object: session]; + + self.device = [AVCaptureDevice + defaultDeviceWithMediaType: AVMediaTypeVideo]; + + captureReader = [[ZBarCaptureReader alloc] + initWithImageScanner: scanner]; + captureReader.captureDelegate = (id)self; + [session addOutput: captureReader.captureOutput]; + + if([session canSetSessionPreset: AVCaptureSessionPreset640x480]) + session.sessionPreset = AVCaptureSessionPreset640x480; + + [captureReader addObserver: self + forKeyPath: @"size" + options: 0 + context: NULL]; + + [self initSubviews]; + [self cropUpdate]; + return(self); +} + +- (void) initSubviews +{ + AVCaptureVideoPreviewLayer *videoPreview = + [[AVCaptureVideoPreviewLayer + layerWithSession: session] + retain]; + videoPreview.videoGravity = AVLayerVideoGravityResizeAspectFill; + preview = videoPreview; + preview.frame = self.bounds; + [self.layer addSublayer: preview]; + + [super initSubviews]; + + // camera image is rotated + overlay.transform = CATransform3DMakeRotation(M_PI / 2, 0, 0, 1); +} + +- (void) dealloc +{ + [[NSNotificationCenter defaultCenter] + removeObserver: self]; + if(showsFPS) { + @try { + [captureReader removeObserver: self + forKeyPath: @"framesPerSecond"]; + } + @catch(...) { } + } + @try { + [captureReader removeObserver: self + forKeyPath: @"size"]; + } + @catch(...) { } + captureReader.captureDelegate = nil; + [captureReader release]; + captureReader = nil; + [device release]; + device = nil; + [input release]; + input = nil; + [session release]; + session = nil; + [super dealloc]; +} + +- (void) cropUpdate +{ + [super cropUpdate]; + captureReader.scanCrop = zoomCrop; +} + +- (ZBarImageScanner*) scanner +{ + return(captureReader.scanner); +} + +- (void) setDevice: (AVCaptureDevice*) newdev +{ + id olddev = device; + AVCaptureInput *oldinput = input; + assert(!olddev == !oldinput); + + NSError *error = nil; + device = [newdev retain]; + if(device) { + assert([device hasMediaType: AVMediaTypeVideo]); + input = [[AVCaptureDeviceInput alloc] + initWithDevice: newdev + error: &error]; + assert(input); + } + else + input = nil; + + [session beginConfiguration]; + if(oldinput) + [session removeInput: input]; + if(input) + [session addInput: input]; + [session commitConfiguration]; + + [olddev release]; + [oldinput release]; +} + +- (BOOL) enableCache +{ + return(captureReader.enableCache); +} + +- (void) setEnableCache: (BOOL) enable +{ + captureReader.enableCache = enable; +} + +- (void) setTorchMode: (NSInteger) mode +{ + [super setTorchMode: mode]; + if(running && [device isTorchModeSupported: mode]) + @try { + device.torchMode = mode; + } + @catch(...) { } +} + +- (void) setShowsFPS: (BOOL) show +{ + [super setShowsFPS: show]; + @try { + if(show) + [captureReader addObserver: self + forKeyPath: @"framesPerSecond" + options: 0 + context: NULL]; + else + [captureReader removeObserver: self + forKeyPath: @"framesPerSecond"]; + } + @catch(...) { } +} + +- (void) start +{ + if(started) + return; + [super start]; + + [session startRunning]; + captureReader.enableReader = YES; +} + +- (void) stop +{ + if(!started) + return; + [super stop]; + + captureReader.enableReader = NO; + [session stopRunning]; +} + +- (void) flushCache +{ + [captureReader flushCache]; +} + +// AVCaptureSession notifications + +- (void) onVideoStart: (NSNotification*) note +{ + zlog(@"onVideoStart: running=%d %@", running, note); + if(running) + return; + running = YES; + + // lock device and set focus mode + NSError *error = nil; + if([device lockForConfiguration: &error]) { + if([device isFocusModeSupported: AVCaptureFocusModeContinuousAutoFocus]) + device.focusMode = AVCaptureFocusModeContinuousAutoFocus; + if([device isTorchModeSupported: torchMode]) + device.torchMode = torchMode; + } + else + zlog(@"failed to lock device: %@", error); +} + +- (void) onVideoStop: (NSNotification*) note +{ + zlog(@"onVideoStop: %@", note); + if(!running) + return; + + [device unlockForConfiguration]; + running = NO; +} + +- (void) onVideoError: (NSNotification*) note +{ + zlog(@"onVideoError: %@", note); + if(running) { + // FIXME does session always stop on error? + running = started = NO; + [device unlockForConfiguration]; + } + NSError *err = + [note.userInfo objectForKey: AVCaptureSessionErrorKey]; + NSLog(@"ZBarReaderView: ERROR during capture: %@: %@", + [err localizedDescription], + [err localizedFailureReason]); +} + +// NSKeyValueObserving + +- (void) observeValueForKeyPath: (NSString*) path + ofObject: (id) obj + change: (NSDictionary*) info + context: (void*) ctx +{ + if(obj == captureReader && + [path isEqualToString: @"size"]) + // adjust preview to match image size + [self setImageSize: captureReader.size]; + else if(obj == captureReader && + [path isEqualToString: @"framesPerSecond"]) + fpsLabel.text = [NSString stringWithFormat: @"%.2ffps ", + captureReader.framesPerSecond]; +} + +// ZBarCaptureDelegate + +- (void) captureReader: (ZBarCaptureReader*) reader + didTrackSymbols: (ZBarSymbolSet*) syms +{ + [self didTrackSymbols: syms]; +} + +- (void) captureReader: (ZBarCaptureReader*) reader + didReadNewSymbolsFromImage: (ZBarImage*) zimg +{ + zlog(@"scanned %d symbols: %@", zimg.symbols.count, zimg); + if(!readerDelegate) + return; + + UIImageOrientation orient; + switch([UIDevice currentDevice].orientation) + { + case UIDeviceOrientationPortraitUpsideDown: + orient = UIImageOrientationLeft; + break; + case UIDeviceOrientationLandscapeLeft: + orient = UIImageOrientationUp; + break; + case UIDeviceOrientationLandscapeRight: + orient = UIImageOrientationDown; + break; + default: + orient = UIImageOrientationRight; + break; + } + + UIImage *uiimg = [zimg UIImageWithOrientation: orient]; + [readerDelegate + readerView: self + didReadSymbols: zimg.symbols + fromImage: uiimg]; +} + +@end diff --git a/iphone/ZBarReaderViewImpl_Simulator.m b/iphone/ZBarReaderViewImpl_Simulator.m new file mode 100644 --- /dev/null +++ b/iphone/ZBarReaderViewImpl_Simulator.m @@ -0,0 +1,223 @@ +//------------------------------------------------------------------------ +// Copyright 2010 (c) Jeff Brown +// +// 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 +//------------------------------------------------------------------------ + +#import +#import + +#define MODULE ZBarReaderView +#import "debug.h" + +// hack around missing simulator support for AVCapture interfaces + +@interface ZBarReaderViewController(Simulator) +@end + +@implementation ZBarReaderViewController(Simulator) + +- (void) initSimulator +{ + UILongPressGestureRecognizer *press = + [[UILongPressGestureRecognizer alloc] + initWithTarget: self + action: @selector(didLongPress:)]; + [self.view addGestureRecognizer: press]; + press.numberOfTouchesRequired = 2; + [press release]; +} + +- (void) takePicture +{ + UIImagePickerController *picker = + [UIImagePickerController new]; + picker.delegate = (id)self; + [self presentModalViewController: picker + animated: YES]; + [picker release]; +} + +- (void) didLongPress: (UIGestureRecognizer*) press +{ + if(press.state == UIGestureRecognizerStateBegan) + [self takePicture]; +} + +- (void) imagePickerController: (UIImagePickerController*) picker + didFinishPickingMediaWithInfo: (NSDictionary*) info +{ + UIImage *image = [info objectForKey: UIImagePickerControllerOriginalImage]; + [picker dismissModalViewControllerAnimated: YES]; + [readerView performSelector: @selector(scanImage:) + withObject: image + afterDelay: .1]; +} + +- (void) imagePickerControllerDidCancel: (UIImagePickerController*) picker +{ + [picker dismissModalViewControllerAnimated: YES]; +} + +@end + +// protected APIs +@interface ZBarReaderView() +- (void) initSubviews; +- (void) setImageSize: (CGSize) size; +- (void) didTrackSymbols: (ZBarSymbolSet*) syms; +@end + +@interface ZBarReaderViewImpl + : ZBarReaderView +{ + ZBarImageScanner *scanner; + UIImage *scanImage; + CALayer *previewImage; + BOOL enableCache; +} +@end + +@implementation ZBarReaderViewImpl + +@synthesize scanner, enableCache; + +- (id) initWithImageScanner: (ZBarImageScanner*) _scanner +{ + self = [super initWithImageScanner: _scanner]; + if(!self) + return(nil); + + scanner = [_scanner retain]; + + [self initSubviews]; + return(self); +} + +- (void) initSubviews +{ + UILabel *label = + [[UILabel alloc] + initWithFrame: CGRectMake(16, 165, 288, 96)]; + label.backgroundColor = [UIColor clearColor]; + label.textColor = [UIColor whiteColor]; + label.font = [UIFont boldSystemFontOfSize: 20]; + label.numberOfLines = 4; + label.textAlignment = UITextAlignmentCenter; + label.text = @"Camera Simulation\n\n" + @"Tap and hold with two \"fingers\" to select image"; + [self addSubview: label]; + [label release]; + + preview = [CALayer new]; + preview.frame = self.bounds; + [self.layer addSublayer: preview]; + + previewImage = [CALayer new]; + previewImage.frame = self.bounds; + [preview addSublayer: previewImage]; + + [super initSubviews]; +} + +- (void) dealloc +{ + [scanner release]; + scanner = nil; + [previewImage release]; + previewImage = nil; + [super dealloc]; +} + +- (void) start +{ + if(started) + return; + [super start]; + running = YES; +} + +- (void) stop +{ + if(!started) + return; + [super stop]; + running = NO; +} + +- (void) scanImage: (UIImage*) image +{ + // strip EXIF info + CGImageRef cgimage = image.CGImage; + image = [[UIImage alloc] + initWithCGImage: cgimage + scale: 1.0 + orientation: UIImageOrientationUp]; + + [self setImageSize: image.size]; + + [CATransaction begin]; + [CATransaction setDisableActions: YES]; + previewImage.contentsScale = imageScale; + previewImage.contentsGravity = kCAGravityCenter; + previewImage.transform = CATransform3DMakeRotation(M_PI_2, 0, 0, 1); + previewImage.contents = (id)cgimage; + [CATransaction commit]; + + ZBarImage *zimg = + [[ZBarImage alloc] + initWithCGImage: cgimage]; + + CGSize size = zimg.size; + zimg.crop = CGRectMake(zoomCrop.origin.x * size.width, + zoomCrop.origin.y * size.height, + zoomCrop.size.width * size.width, + zoomCrop.size.height * size.height); + + int nsyms = [scanner scanImage: zimg]; + zlog(@"scan image: %@ crop=%@ nsyms=%d", + NSStringFromCGSize(size), NSStringFromCGRect(zimg.crop), nsyms); + [zimg release]; + + if(nsyms > 0) { + scanImage = [image retain]; + ZBarSymbolSet *syms = scanner.results; + [self performSelector: @selector(didReadSymbols:) + withObject: syms + afterDelay: .4]; + [self performSelector: @selector(didTrackSymbols:) + withObject: syms + afterDelay: .001]; + } + [image release]; +} + +- (void) didReadSymbols: (ZBarSymbolSet*) syms +{ + [readerDelegate + readerView: self + didReadSymbols: syms + fromImage: scanImage]; + [scanImage release]; + scanImage = nil; +} + +@end diff --git a/iphone/ZBarSymbol.m b/iphone/ZBarSymbol.m new file mode 100644 --- /dev/null +++ b/iphone/ZBarSymbol.m @@ -0,0 +1,197 @@ +//------------------------------------------------------------------------ +// Copyright 2009-2010 (c) Jeff Brown +// +// 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 +//------------------------------------------------------------------------ + +#import + +@implementation ZBarSymbol + +@dynamic type, typeName, configMask, modifierMask, data, quality, count, + zbarSymbol; + ++ (NSString*) nameForType: (zbar_symbol_type_t) type +{ + return([NSString stringWithUTF8String: zbar_get_symbol_name(type)]); +} + +- (id) initWithSymbol: (const zbar_symbol_t*) sym +{ + if(self = [super init]) { + symbol = sym; + zbar_symbol_ref(sym, 1); + } + return(self); +} + +- (void) dealloc +{ + if(symbol) { + zbar_symbol_ref(symbol, -1); + symbol = NULL; + } + [super dealloc]; +} + +- (zbar_symbol_type_t) type +{ + return(zbar_symbol_get_type(symbol)); +} + +- (NSString*) typeName +{ + return([[self class] nameForType: zbar_symbol_get_type(symbol)]); +} + +- (NSUInteger) configMask +{ + return(zbar_symbol_get_configs(symbol)); +} + +- (NSUInteger) modifierMask +{ + return(zbar_symbol_get_modifiers(symbol)); +} + +- (NSString*) data +{ + return([NSString stringWithUTF8String: zbar_symbol_get_data(symbol)]); +} + +- (int) quality +{ + return(zbar_symbol_get_quality(symbol)); +} + +- (int) count +{ + return(zbar_symbol_get_count(symbol)); +} + +- (zbar_orientation_t) orientation +{ + return(zbar_symbol_get_orientation(symbol)); +} + +- (const zbar_symbol_t*) zbarSymbol +{ + return(symbol); +} + +- (ZBarSymbolSet*) components +{ + return([[[ZBarSymbolSet alloc] + initWithSymbolSet: zbar_symbol_get_components(symbol)] + autorelease]); +} + +- (CGRect) bounds +{ + int n = zbar_symbol_get_loc_size(symbol); + if(!n) + return(CGRectNull); + + int xmin = INT_MAX, xmax = INT_MIN; + int ymin = INT_MAX, ymax = INT_MIN; + + for(int i = 0; i < n; i++) { + int t = zbar_symbol_get_loc_x(symbol, i); + if(xmin > t) xmin = t; + if(xmax < t) xmax = t; + t = zbar_symbol_get_loc_y(symbol, i); + if(ymin > t) ymin = t; + if(ymax < t) ymax = t; + } + return(CGRectMake(xmin, ymin, xmax - xmin, ymax - ymin)); +} + +@end + + +@implementation ZBarSymbolSet + +@dynamic count, zbarSymbolSet; +@synthesize filterSymbols; + +- (id) initWithSymbolSet: (const zbar_symbol_set_t*) s +{ + if(!s) { + [self release]; + return(nil); + } + if(self = [super init]) { + set = s; + zbar_symbol_set_ref(s, 1); + filterSymbols = YES; + } + return(self); +} + +- (void) dealloc +{ + if(set) { + zbar_symbol_set_ref(set, -1); + set = NULL; + } + [super dealloc]; +} + +- (int) count +{ + if(filterSymbols) + return(zbar_symbol_set_get_size(set)); + + int n = 0; + const zbar_symbol_t *sym = zbar_symbol_set_first_unfiltered(set); + for(; sym; sym = zbar_symbol_next(sym)) + n++; + return(n); +} + +- (const zbar_symbol_set_t*) zbarSymbolSet +{ + return(set); +} + +- (NSUInteger) countByEnumeratingWithState: (NSFastEnumerationState*) state + objects: (id*) stackbuf + count: (NSUInteger) len +{ + const zbar_symbol_t *sym = (void*)state->state; // FIXME + if(sym) + sym = zbar_symbol_next(sym); + else if(set && filterSymbols) + sym = zbar_symbol_set_first_symbol(set); + else if(set) + sym = zbar_symbol_set_first_unfiltered(set); + + if(sym) + *stackbuf = [[[ZBarSymbol alloc] + initWithSymbol: sym] + autorelease]; + + state->state = (unsigned long)sym; // FIXME + state->itemsPtr = stackbuf; + state->mutationsPtr = (void*)self; + return((sym) ? 1 : 0); +} + +@end diff --git a/iphone/bin/BuildUniversal.sh b/iphone/bin/BuildUniversal.sh new file mode 100755 --- /dev/null +++ b/iphone/bin/BuildUniversal.sh @@ -0,0 +1,23 @@ +#!/bin/sh +set -ux +SUBTARGET=${1:?} +OUTDIR=${2:-$TARGET_BUILD_DIR} + +# build library for device and simulator +xcodebuild -target $SUBTARGET -configuration $CONFIGURATION -sdk iphoneos \ + || exit 1 +xcodebuild -target $SUBTARGET -configuration $CONFIGURATION -sdk iphonesimulator \ + || exit 1 + +mkdir -p $OUTDIR + +# combine device and simulator libs into single fat lib. +# others have indicated that this approach is "wrong", but for us +# the ease of including the universal lib in a project without complicated +# changes to build settings outweighs any lack of purity in the approach +# ...we can always fix things later, if necessary +lipo -create \ + $BUILD_DIR/$CONFIGURATION-iphoneos/$SUBTARGET.a \ + $BUILD_DIR/$CONFIGURATION-iphonesimulator/$SUBTARGET.a \ + -output $OUTDIR/$SUBTARGET.a \ + || exit 1 diff --git a/iphone/bin/CreateDMG.sh b/iphone/bin/CreateDMG.sh new file mode 100755 --- /dev/null +++ b/iphone/bin/CreateDMG.sh @@ -0,0 +1,35 @@ +#!/bin/sh +set -ux +VOLNAME=${1:?} +shift +RES=$SOURCE_ROOT/res +BUDDY=$(xcrun -find PlistBuddy) \ + || exit 1 +VERSION=$($BUDDY -c 'Print :CFBundleVersion' $RES/$VOLNAME-Info.plist) \ + || exit 1 +DMG=$VOLNAME-$VERSION + +mkdir -p $TARGET_BUILD_DIR/.background \ + || exit 1 +cp -af $RES/$VOLNAME.DS_Store $TARGET_BUILD_DIR/.DS_Store +cp -af $RES/$VOLNAME-bg.png $TARGET_BUILD_DIR/.background/ + +# copy remaining arguments to image directly +for content +do + cp -af $content $TARGET_BUILD_DIR/ \ + || exit 1 +done + +# prepare examples for distribution +for example in $(find $TARGET_BUILD_DIR/Examples -depth 1) +do + rm -rf $example/{*.xcodeproj/*.{mode1v3,pbxuser},ZBarSDK} + cp -af $BUILT_PRODUCTS_DIR/ZBarSDK $example/ +done + +hdiutil create -ov -fs HFS+ -format UDZO -imagekey zlib-level=9 \ + -volname $VOLNAME \ + -srcdir $TARGET_BUILD_DIR \ + $BUILT_PRODUCTS_DIR/$DMG.dmg \ + || exit 1 diff --git a/iphone/bin/CreateDSStore.pl b/iphone/bin/CreateDSStore.pl new file mode 100755 --- /dev/null +++ b/iphone/bin/CreateDSStore.pl @@ -0,0 +1,72 @@ +#!/usr/bin/perl + +# Quick hack script to generate the .DS_Store for the DMG, which +# * allows us to precisely position the window and icons +# * is more usefully versioned +# * avoids references to my local HD(!?) + +use warnings; +use strict; + +BEGIN { + use File::Spec::Functions qw(rel2abs splitpath); + use lib (splitpath(rel2abs($0)))[1]; +} + +use Data::Plist::BinaryWriter; +use Mac::Finder::DSStore qw(writeDSDBEntries makeEntries); +use Mac::Finder::AliasRecord; + +$Mac::Finder::DSStore::Entry::types{bwsp} = 'blob'; +$Mac::Finder::DSStore::Entry::types{icvp} = 'blob'; + +writeDSDBEntries($ARGV[0] || "DS_Store", + makeEntries(".", + bwsp => Data::Plist::BinaryWriter->new(serialize => 0)->write([ + dict => { + WindowBounds => [ + string => sprintf('{{%d, %d}, {%d, %d}}', + 512, 128, 512, 608 + 22) + ], + SidebarWidth => [integer => 0], + ShowToolbar => [false => 0], + ShowSidebar => [false => 0], + ShowPathbar => [false => 0], + ShowStatusBar => [false => 0], + } + ]), + icvp => Data::Plist::BinaryWriter->new(serialize => 0)->write([ + dict => { + viewOptionsVersion => [integer => 0], + arrangeBy => [string => "none"], + iconSize => [real => 64], + textSize => [real => 12], + labelOnBottom => [true => 1], + gridSpacing => [real => 100], + gridOffsetX => [real => 0], + gridOffsetY => [real => 0], + showItemInfo => [false => 0], + showIconPreview => [false => 0], + backgroundType => [integer => 2], + backgroundColorRed => [real => 0], + backgroundColorGreen => [real => 0], + backgroundColorBlue => [real => .5], + backgroundImageAlias => [ + data => Mac::Finder::AliasRecord->new( + path => 'ZBarSDK:.background:ZBarSDK-bg.png', + volumeFS => 'HFS+')->write() + ], + }, + ]), + vstl => "icnv", + ), + makeEntries("README", Iloc_xy => [ 4.5 * 32, 2.5 * 32 ]), + makeEntries("ZBarSDK", Iloc_xy => [ 4.5 * 32, 7.5 * 32 ]), + makeEntries("ChangeLog", Iloc_xy => [ 4 * 32, 12.5 * 32 ]), + makeEntries("Documentation.html", + Iloc_xy => [ 8 * 32, 12.5 * 32 ]), + makeEntries("Examples", Iloc_xy => [ 12 * 32, 12.5 * 32 ]), + makeEntries("COPYING", Iloc_xy => [ 4 * 32, 16 * 32 ]), + makeEntries("LICENSE", Iloc_xy => [ 8 * 32, 16 * 32 ]), + makeEntries("Documentation",Iloc_xy => [ 12 * 32, 16 * 32 ]), +); diff --git a/iphone/bin/Mac/Finder/AliasRecord.pm b/iphone/bin/Mac/Finder/AliasRecord.pm new file mode 100644 --- /dev/null +++ b/iphone/bin/Mac/Finder/AliasRecord.pm @@ -0,0 +1,169 @@ +package Mac::Finder::AliasRecord; + +# Generate(/Parse) a Mac "alias record" binary string/file. +# +# Currently just enough is implemented to satisfy immediate requirements +# (ie, write backgroundImageAlias to .DS_Store for DMG) +# +# based on these documents: +# http://www.geocities.com/xhelmboyx/quicktime/formats/alias-layout.txt +# http://sebastien.kirche.free.fr/python_stuff/MacOS-aliases.txt +# +# FIXME interface is very poor... + +use warnings; +use strict; +use DateTime; +use File::Spec; +use File::Spec::Mac; +use Encode qw(encode); +require Exporter; + +our $VERSION = '0.1'; +our @ISA = qw(Exporter); + +my %FSEncodings = ( + MacFS => ['RW', ''], + MFS => ['RW', ''], + HFS => ['BD', ''], + 'HFS+' => ['H+', ''], + + AudioCD => ['', 'JH'], + ISO9660 => ['', 'AG'], + FAT => ['', 'IS'], + Joliet => ['', 'Jo'], + 'ISO9660+Joliet' => ['', 'Jo'], +); + +my %DiskEncodings = ( + HD => 0, + FixedHD => 0, + Network => 1, + NetworkDisk => 1, + Floppy => 4, + Floppy1440 => 4, + Other => 5, + OtherDisk => 5, +); + +my %RecordEncodings = ( + parentDir => 0x00, + absolutePath => 0x02, + unicodeFile => 0x0e, + unicodeVolume => 0x0f, + volumePath => 0x12, +); + +sub new { + my $class = shift || __PACKAGE__; + my $self = { + aliasCreator => '', + aliasVersion => 2, + aliasType => 'file', + volume => '', + volumeCreated => 0, + volumeFS => 'HFS', + volumeDisk => undef, + volumeAttrs => 0, + directoryID => 0, + file => '', + fileID => 0, + fileCreated => 0, + fileType => '', + fileCreator => '', + nlvlFrom => -1, + nlvlTo => -1, + records => { }, + @_ + }; + if(exists($self->{path})) { + my $path = $self->{path}; + my ($vol, $dir, $file) = File::Spec::Mac->splitpath($path); + $vol =~ s/:$//; + my @dir = File::Spec::Mac->splitdir($dir); + while(@dir && !$dir[0]) { + shift(@dir); + } + while(@dir && !$dir[-1]) { + pop(@dir); + } + $self->{volume} ||= $vol; + $self->{records}{unicodeVolume} ||= + pack('na*', length($vol), encode('utf-16be', $vol)); + + $self->{file} ||= $file; + $self->{records}{parentDir} ||= $dir[-1] + if(@dir); + $self->{records}{absolutePath} ||= $path; + $self->{records}{volumePath} ||= File::Spec->catfile('', @dir, $file); + $self->{records}{unicodeFile} ||= + pack('na*', length($file), encode('utf-16be', $file)); + } + return(bless($self, ref($class) || $class)); +} + +sub toFSTime { + my $val = shift; + if(ref($val) && $val->isa("DateTime")) { + $val = $val->epoch - DateTime->new(year => 1904)->epoch(); + } + return($val); +} + +sub write { + my ($self, $out) = @_; + + my $aliasType = $self->{aliasType}; + $aliasType = (($aliasType =~ /^d(ir(ectory)?)?$/i && 1) || + ($aliasType !~ /^f(ile)?$/ && $aliasType) || 0); + + my $volumeCreated = toFSTime($self->{volumeCreated}); + my $volumeFS = $self->{volumeFS}; + if(ref($volumeFS) ne 'ARRAY') { + $volumeFS = $FSEncodings{$volumeFS} || ['', '']; + } + + my $volumeDisk = $self->{volumeDisk}; + if(!defined($volumeDisk)) { + if($volumeFS->[0] eq 'H+') { + $volumeDisk = 'Floppy'; + } + elsif($volumeFS->[0]) { + $volumeDisk = 'HD'; + } + else { + $volumeDisk = 'Other'; + } + } + $volumeDisk = (exists($DiskEncodings{$volumeDisk}) + ? $DiskEncodings{$volumeDisk} + : $volumeDisk); + + my $fileCreated = toFSTime($self->{fileCreated}); + + my $buf = + pack('nn (C/a @28)Na2n N(C/a @64)NNa4a4 n!n!Na2 x10 (n!n/ax!2)*', + $self->{aliasVersion}, $aliasType, + $self->{volume}, $volumeCreated, $volumeFS->[0], $volumeDisk, + $self->{directoryID}, $self->{file}, $self->{fileID}, $fileCreated, + $self->{fileType}, $self->{fileCreator}, $self->{nlvlFrom}, + $self->{nlvlTo}, $self->{volumeAttrs}, $volumeFS->[1], + map(((exists($RecordEncodings{$_}) ? $RecordEncodings{$_} : $_) + => $self->{records}{$_}), + keys(%{$self->{records}})), + (-1, '')); + $buf = pack('a4n', $self->{aliasCreator}, length($buf) + 6) . $buf; + + if(!$out) { + return($buf); + } + elsif(ref($out) eq 'GLOB') { + print $out $buf; + } + else { + open(my $outfh, '>', $out) || die; + print $outfh $buf; + } +} + +1; diff --git a/iphone/debug.h b/iphone/debug.h new file mode 100644 --- /dev/null +++ b/iphone/debug.h @@ -0,0 +1,53 @@ +//------------------------------------------------------------------------ +// Copyright 2009 (c) Jeff Brown +// +// 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 +#define xNSSTR(s) @#s +#define NSSTR(s) xNSSTR(s) + +#ifdef DEBUG_OBJC +# ifndef MODULE +# define MODULE ZBarReaderController +# endif +# define zlog(fmt, ...) \ + NSLog(NSSTR(MODULE) @": " fmt , ##__VA_ARGS__) + +#define timer_start \ + uint64_t t_start = timer_now(); + +#else +# define zlog(...) while(0) +# define timer_start +#endif + +static inline uint64_t timer_now () +{ + return(mach_absolute_time()); +} + +static inline double timer_elapsed (uint64_t start, uint64_t end) +{ + mach_timebase_info_data_t info; + mach_timebase_info(&info); + return((double)(end - start) * info.numer / (info.denom * 1000000000.)); +} diff --git a/iphone/doc/Documentation.html b/iphone/doc/Documentation.html new file mode 100644 --- /dev/null +++ b/iphone/doc/Documentation.html @@ -0,0 +1,11 @@ + + + + + + + +

redirecting to Documentation/index.html

+ + diff --git a/iphone/doc/ReaderSample.png b/iphone/doc/ReaderSample.png new file mode 100644 index 0000000000000000000000000000000000000000..a71b5a4af2dbf6a6122b24e085b868d07b5ec8bb GIT binary patch literal 131381 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 z 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 diff --git a/iphone/doc/ZBarImage.rst b/iphone/doc/ZBarImage.rst new file mode 100644 --- /dev/null +++ b/iphone/doc/ZBarImage.rst @@ -0,0 +1,150 @@ +ZBarImage Class Reference +========================= + +.. class:: ZBarImage + + :Inherits from: :class:`NSObject` + + A :class:`ZBarImage` is a wrapper for images passed to the barcode reader. + It encapsulates raw image data with the format and size metadata necessary + to interpret it. + + An image must be wrapped in a :class:`ZBarImage` in order to be scanned by + the library. At least the format, size and data must be set. There are + also initialization methods for automatically extracting the data and + format from a `CGImage`. + + This class is a wrapper around a :type:`zbar_image_t` C object (q.v.) + + +Properties +---------- + + .. member:: unsigned long format + + The image format four-charcter code (fourcc) as a 4-byte integer. Use + :ref:`fourcc:` to create a fourcc value from a string. + + .. member:: unsigned sequence + + A "sequence number" associated with the image. This reference value is + unused by the library. + + .. member:: CGSize size + + The size of the image in pixels. + + .. note:: + + There is no separate "bytesPerLine" property, the width must match + the image data (which is not always the logical image width). + + .. member:: CGRect crop + + Optionally limit the scan region to this rectangle without having to + generate a cropped image. + + .. member:: const void *data + + Obtain a pointer to the raw image data. This property is read-only, use + :ref:`setData:withLength:` to set the image data. + + .. member:: unsigned long dataLength + + Byte length of the raw image data. This property is read-only, use + :ref:`setData:withLength:` to set the image data. + + .. member:: ZBarSymbolSet *symbols + + Barcode results from the last scan. + + .. member:: zbar_image_t *zbarImage + + Retrieve the underlying C object instance. (read-only) + + .. member:: UIImage *UIImage + + Convert the image to a UIImage. Only certain image formats are + supported for conversion (read-only) + + :See also: :ref:`UIImageWithOrientation:` + + +Class Methods +------------- + + .. _`fourcc:`: + .. describe:: + (unsigned long) fourcc:(NSString*)format + + Parse the integer four-character code from a string. Alternatively use + the :func:`zbar_fourcc` macro to create a constant expression. + + :format: A four character string representing an image format. + :Returns: The corresponding 4-byte integer format code. + + +Instance Methods +---------------- + + .. _`initWithImage:`: + .. describe:: - (id) initWithImage:(zbar_image_t*)image + + Initialize an image wrapper, given the C object to wrap. + + :image: The C object to wrap. + :Returns: The initialized :class:`ZBarImage`. + + .. _`initWithCGImage:`: + .. describe:: - (id) initWithCGImage:(CGImageRef)image + + Initialize a :class:`ZBarImage` from the data and metadata extracted + from a `CGImage`. The image is converted to `Y800` (grayscale) format. + + :image: A `CGImage` to source the data and metadata. + :Returns: The initialized :class:`ZBarImage`. + :See also: :ref:`initWithCGImage:size:` + + .. _`initWithCGImage:size:`: + .. describe:: - (id) initWithCGImage:(CGImageRef)image size:(CGSize)size + + Initialize a :class:`ZBarImage` from the data and metadata extracted + from a `CGImage`. The image is converted to `Y800` (grayscale) format + and scaled to the specified size. + + :image: A `CGImage` to source the data and metadata. + :size: The pixel size of the resulting ZBarImage. + :Returns: The initialized :class:`ZBarImage`. + :See also: :ref:`initWithCGImage:crop:size:` + + .. _`initWithCGImage:crop:size:`: + .. describe:: - (id) initWithCGImage:(CGImageRef)image crop:(CGRect)crop size:(CGSize)size + + Initialize a :class:`ZBarImage` from the data and metadata extracted + from a `CGImage`. The image is simultaneously converted to `Y800` + (grayscale) format, cropped and scaled to the specified size. + + :image: A `CGImage` to source the data and metadata. + :crop: The region to convert, in image coordinates. + :size: The pixel size of the resulting ZBarImage. + :Returns: The initialized :class:`ZBarImage`. + + .. _`setData:withLength:`: + .. describe:: - (void) setData:(const void*)data withLength:(unsigned long)length + + Specify a pointer to the raw image data, for the image format and size. + The length of the data must also be provided. Note that the data must + remain valid as long as the image has a reference to it. Set data to + ``NULL`` to clear a previous reference. + + :data: A pointer to a raw image data buffer. + :length: The size of the image data buffer. + + .. _`UIImageWithOrientation:`: + .. describe:: - (UIImage*) UIImageWithOrientation:(UIImageOrientation)orient + + Convert the image to a UIImage with the specified orientation. Only + certain image formats are supported for conversion. (currently + ``RGB3``, ``RGB4``, ``RGBQ``) + + :orient: Desired orientation of the image. + :Returns: A new :class:`UIImage`, or ``nil`` in case of error. diff --git a/iphone/doc/ZBarImageScanner.rst b/iphone/doc/ZBarImageScanner.rst new file mode 100644 --- /dev/null +++ b/iphone/doc/ZBarImageScanner.rst @@ -0,0 +1,99 @@ +ZBarImageScanner Class Reference +================================ + +.. class:: ZBarImageScanner + + :Inherits from: :class:`NSObject` + + This is a low-level interface for programmatically scanning images without + a user interface. If you want to scan images manually selected by the user + (from the photo library or using the camera), you may prefer to use a + :class:`ZBarReaderController` instead. + + This class is a wrapper around a :type:`zbar_image_scanner_t` C object + (q.v.) + + +Properties +---------- + + .. member:: BOOL enableCache + + Enable the inter-frame consistency cache. Set to ``YES`` for scanning + video or ``NO`` for scanning images. + + .. member:: ZBarSymbolSet results + + Decoded symbols resulting from the last scan. + + +Instance Methods +---------------- + + .. _`parseConfig:`: + .. describe:: - (void) parseConfig:(NSString*)config + + Apply scanner/decoder configuration parsed from a string. + + :config: A configuration setting of the form: `symbology.config[=value]`. + + .. _`setSymbology:config:to:`: + .. describe:: - (void) setSymbology:(zbar_symbol_type_t)symbology config:(zbar_config_t)config to:(int)value + + Apply generic scanner/decoder configuration. + + :symbology: The symbology to effect, or 0 for all. + :config: The configuration setting to adjust. + :value: The value to set for the specific configuration/symbology. + + .. _`scanImage:`: + .. describe:: - (NSInteger) scanImage:(ZBarImage*)image + + Scan an image for barcodes using the current configuration. The image + must be in ``Y800`` format (8-bpp graysale). + + :image: The :class:`ZBarImage` to scan. + :Returns: The number of barcode symbols decoded in the image. + + +Constants +--------- + +.. type:: zbar_config_t + + ZBAR_CFG_ENABLE + Control whether specific symbologies will be recognized. Disabling + unused symbologies improves performance and prevents bad scans. + + ZBAR_CFG_EMIT_CHECK + Whether to include the check digit in the result data string. This + value may be set individually for symbologies where it makes sense. + + ZBAR_CFG_MIN_LEN + The minimum data length for a symbol to be valid, set to 0 to disable. + Use with eg, I2/5 to avoid short scans. This value may be set + individually for variable-length symbologies. + + ZBAR_CFG_MAX_LEN + The maximum data length for which a symbol is valid, set to 0 to + disable. Use with eg, I2/5 to enforce a specific range of data lengths. + This value may be set individually for variable-length symbologies. + + ZBAR_CFG_UNCERTAINTY + Number of "nearby" frames that must contain a symbol before it will be + considered valid. This value may be set for individual symbologies. + + ZBAR_CFG_POSITION + Whether to track position information. + + ZBAR_CFG_X_DENSITY + The stride to use for scanning vertical columns of the image. This many + pixel columns will be skipped between vertical scan passes. Useful for + trading off between resolution and performance. This is a scanner + setting (use 0 for the symbology). + + ZBAR_CFG_Y_DENSITY + The stride to use for scanning horizontal columns of the image. This + many pixel rows will be skipped between horizontal scan passes. Useful + for trading off between resolution and performance. This is a scanner + setting (use 0 for the symbology). diff --git a/iphone/doc/ZBarReaderController.rst b/iphone/doc/ZBarReaderController.rst new file mode 100644 --- /dev/null +++ b/iphone/doc/ZBarReaderController.rst @@ -0,0 +1,156 @@ +ZBarReaderController Class Reference +==================================== + +.. class:: ZBarReaderController + + :Inherits from: :class:`UIImagePickerController` + + This is the controller to use for scanning images selected by a + :class:`UIImagePickerController` either captured manually using the camera, + or selected from the Photo Library. For more information, see + :doc:`picker`. + + It can support automatic capture from the camera only if the library is + re-built to use private APIs (see :doc:`compat`). + + +Properties +---------- + + .. member:: ZBarImageScanner *scanner + + Access to the image scanner for configuration. (read-only) + + .. member:: id readerDelegate + + The delegate that will be notified when new barcode results are + available. + + .. member:: BOOL showsZBarControls + + Whether to display a default control set consisting of cancel, scan and + info buttons. Disable these if you provide your own controls using the + :member:`cameraOverlayView`. Enabling this automatically disables the + system controls :member:`showsCameraControls`. (Default ``YES``). + + .. member:: BOOL showsHelpOnFail + + Whether to automatically display the integrated help viewer when an + image fails to decode. Even if this is disabled, the integrated help + may still be presented manually using ``showHelpWithReason:``. + (Default ``YES``) + + .. member:: ZBarReaderControllerCameraMode cameraMode + + Scanning mode to use with the camera. It is generally appropriate to + leave this at the default. + + .. member:: BOOL tracksSymbols + + Whether to display the tracking rectangle around detected barcodes. + + .. member:: BOOL takesPicture + + Whether to take a full picture (with ``takePicture``) when a barcode + is detected with ``ZBarReaderControllerCameraModeSampling``. The + resulting image will be delayed from the actual decode. + + .. member:: BOOL enableCache + + This property is deprecated and should not be modified. + + .. member:: CGRect scanCrop + + Crop images before scanning. The original image will be cropped to this + rectangle, which should be in normalized image coordinates, x-axis + major. Defaults to the full image ``{{0, 0}, {1, 1}}``. + + .. member:: NSInteger maxScanDimension + + Scale image to scan. After cropping, the image will be scaled if + necessary, such that neither of its dimensions exceed this value. + Defaults to 640. + + .. note:: + + The remaining properties are inherited from + :class:`UIImagePickerController`. + + .. member:: UIImagePickerControllerSourceType sourceType + + Image source. Use to select between the camera and photo library. + + .. member:: BOOL showsCameraControls + + Whether to display the system camera controls. Overridden to ``NO`` + when :member:`showsZBarControls` is ``YES``. + + .. member:: UIView *cameraOverlayView + + A custom view to display over the camera preview. The tracking layer + and default controls will be added to this view if they are enabled. + + .. member:: CGAffineTransform cameraViewTransform + + A transform to apply to the camera preview. Ignored by the reader. + Possibly useful for eg, a digital zoom effect. + + .. member:: BOOL allowsEditing + + Whether to enable the system image editing dialog after a picture is + taken. Possibly useful to improve reader results in some cases using + manual intervention. + + +Instance Methods +---------------- + + .. _`showHelpWithReason:`: + .. describe:: - (void) showHelpWithReason:(NSString*)reason + + Display the integrated help browser. Use this with custom overlays if + you don't also want to create your own help view. Should only be called + when the reader is displayed. The ``reason`` argument will be passed to + the :func:`onZBarHelp` javascript function. + + :reason: A string parameter passed to javascript. + + .. _`scanImage:`: + .. describe:: - (id ) scanImage:(CGImageRef)image + + Scan an image for barcodes. This is a wrapper around + ``scanner.scanImage`` that applies scanCrop and maxScanDimension. Some + additional result filtering is also performed. + + :image: A :class:`CGImage` to scan. + :Returns: The result set containing :class:`ZBarSymbol` objects. + + +Constants +--------- + +.. type:: ZBarReaderControllerCameraMode + + The scanning mode to use with the camera. + + ZBarReaderControllerCameraModeDefault + The standard mode provided by UIImagePickerController - the user + manually captures an image by tapping a control. This is the default + unless private APIs are enabled. + + ZBarReaderControllerCameraModeSampling + Automatically capture by taking screenshots with + :func:`UIGetScreenImage`. Resolution is limited to the screen + resolution, so this mode is inappropriate for longer codes. Only + available when private APIs are enabled, and becomes the default mode in + that case. + + ZBarReaderControllerCameraModeSequence + Experimental mode that automatically scans by "rapidly" scanning + pictures captured with ``takePicture``. Not recommended for serious + use. + +.. c:var:: NSString *ZBarReaderControllerResults + + The info dictionary key used to return decode results to + ``imagePickerController:didFinishPickingMediaWithInfo:`` diff --git a/iphone/doc/ZBarReaderDelegate.rst b/iphone/doc/ZBarReaderDelegate.rst new file mode 100644 --- /dev/null +++ b/iphone/doc/ZBarReaderDelegate.rst @@ -0,0 +1,70 @@ +ZBarReaderDelegate Protocol Reference +===================================== + +.. class:: ZBarReaderDelegate + + :Inherits from: :class:`UIImagePickerControllerDelegate` + + This protocol must be implemented by the + :member:`~ZBarReaderViewController::readerDelegate` provided to a + :class:`ZBarReaderViewController` or :class:`ZBarReaderController`. It is + used to notify the delegate of new decode results, when an image fails to + decode, or when the user dismisses the reader with the built-in controls. + + +Instance Methods +---------------- + + .. describe:: - (void) imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary*)info + + This inherited delegate method is called when a barcode is successfully + decoded. The decoded symbols are available from the dictionary as a + :class:`ZBarSymbolSet` using the :c:data:`ZBarReaderControllerResults` + key. The image from which the barcodes were scanned is available using + the :c:data:`UIImagePickerControllerOriginalImage` key. No other keys + are guaranteed to be valid. + + .. note:: + + The ``picker`` parameter will be the reader controller instance that + read the barcodes - not necessarily a + :class:`UIImagePickerController` instance. You should cast it to the + correct type for anything other than basic view controller access. + + :picker: The reader controller that scanned the barcode(s). + :info: A dictionary containing the image and decode results. + + .. describe:: - (void) imagePickerControllerDidCancel:(UIImagePickerController*)picker + + Called when the user taps the "Cancel" button provided by the built-in + controls (when :member:`showsZBarControls`\ ``=YES``). The default + implementation dismisses the reader. If this method is implemented, it + should do the same. + + .. note:: + + The ``picker`` parameter will be the reader controller instance that + read the barcodes - not necessarily a + :class:`UIImagePickerController` instance. You should cast it to the + correct type for anything other than basic view controller access. + + :picker: The reader controller that scanned the barcode(s). + + .. describe:: - (void) readerControllerDidFailToRead:(ZBarReaderController*)reader withRetry:(BOOL)retry + + Called when an image, manually captured or selected from the photo + library, is scanned and no barcodes were detected. + + If the ``retry`` parameter is ``NO``, the controller must be dismissed + before this method returns. Otherwise, another scan may be attempted + without re-presenting the controller. + + If the :member:`~ZBarReaderController::showsHelpOnFail` is ``YES`` *and* + ``retry`` is ``YES``, the integrated help viewer will already be + presenting. + + If this method is not implemented, the controller will be dismissed iff + ``retry`` is ``NO``. + + :reader: The :class:`ZBarReaderController` that scanned the barcode(s). + :retry: Whether another scan may be attempted. diff --git a/iphone/doc/ZBarReaderView.rst b/iphone/doc/ZBarReaderView.rst new file mode 100644 --- /dev/null +++ b/iphone/doc/ZBarReaderView.rst @@ -0,0 +1,104 @@ +ZBarReaderView Class Reference +============================== + +.. class:: ZBarReaderView + + :Inherits from: :class:`UIView` + + This is a barcode reader encapsulted in a UIView. It manages an + :class:`AVCaptureSession` with a camera device and a + :class:`ZBarCaptureReader`, presents the video preview and optionally + tracks detected barcode symbols. A delegate will usually be assigned for + notification of new decode results. + + +Properties +---------- + + .. member:: id readerDelegate + + The delegate that will be notified of new decode results. + + .. member:: ZBarImageScanner *scanner + + Access to the image scanner is provided for configuration. (read-only) + + .. member:: BOOL tracksSymbols + + Whether to display the tracking annotation (default ``YES``). + + .. member:: BOOL allowsPinchZoom + + Enable pinch gesture recognition for manually zooming the preview/decode + (default ``YES``). + + .. member:: NSInteger torchMode + + An :type:`AVCaptureTorchMode` value that will be applied if/when + appropriate. (default Auto) + + .. member:: BOOL showsFPS + + Overlay the decode frame rate on the preview to help with performance + optimization. This is for *debug only* and should not be set for + production. (default ``NO``) + + .. member:: CGFloat zoom + + Zoom scale factor applied to the video preview *and* scanCrop. This + value is also updated by the pinch-zoom gesture. Valid values are in + the range [1,2]. (default 1.25) + + .. member:: CGRect scanCrop + + The region of the video image that will be scanned, in normalized image + coordinates. Note that the video image is in landscape mode (default + {{0, 0}, {1, 1}}) + + .. member:: CGAffineTransform previewTransform + + Additional transform that will be applied to the video preview. Note + that this transform is *not* applied to scanCrop. + + .. member:: AVCaptureDevice *device + + The capture device may be manipulated or replaced. + + .. member:: AVCaptureSession *session + + Direct access to the capture session. Warranty void if opened. + (read-only) + + .. member:: ZBarCaptureReader *captureReader + + Direct access to the capture reader. Warranty void if opened. + (read-only) + + .. member:: BOOL enableCache + + :Deprecated: + + Whether to use the inter-frame consistency cache. This should always be + set to ``YES``. + + +Instance Methods +---------------- + + .. describe:: - (id) initWithImageScanner:(ZBarImageScanner*)imageScanner + + :imageScanner: A pre-configured :class:`ZBarImageScanner` to use for scanning + :Returns: The initialized :class:`ZBarReaderView` + + .. describe:: - (void) start + + Begin/resume scanning after a call to ``stop``. + + .. describe:: - (void) stop + + Stop scanning and pause the video feed. + + .. describe:: - (void) flushCache + + Flush the inter-frame consistency cache. Any barcodes in the frame will + be re-recognized in subsequent frames. diff --git a/iphone/doc/ZBarReaderViewController.rst b/iphone/doc/ZBarReaderViewController.rst new file mode 100644 --- /dev/null +++ b/iphone/doc/ZBarReaderViewController.rst @@ -0,0 +1,128 @@ +ZBarReaderViewController Class Reference +======================================== + +.. class:: ZBarReaderViewController + + :Inherits from: :class:`UIViewController` + + This is the controller to use for live scanning from the camera feed with + automatic capture. For scanning from image files or with manual capture, + see :class:`ZBarReaderController`. + + +Properties +---------- + + .. member:: ZBarImageScanner *scanner + + Access to the image scanner for configuration. (read-only) + + .. member:: id readerDelegate + + The delegate that will be notified when new barcode results are + available. + + .. member:: BOOL showsZBarControls + + Whether to display a default control set consisting of cancel, scan and + info buttons. Disable these if you provide your own controls using the + :member:`cameraOverlayView`. (Default ``YES``). + + .. member:: BOOL tracksSymbols + + Whether to display the tracking rectangle around detected barcodes. + + .. member:: CGRect scanCrop + + Crop images before scanning. The original image will be cropped to this + rectangle, which should be in normalized image coordinates (NB the + camera image x-axis is *vertical* on the screen). Defaults to the full + image ``{{0, 0}, {1, 1}}``. + + .. member:: UIView *cameraOverlayView + + A custom view to display over the camera preview. + + .. member:: CGAffineTransform cameraViewTransform + + A transform to apply to the camera preview. Ignored by the reader. + + .. member:: ZBarReaderView *readerView + + View that presents the camera preview and performs the scanning. This + view has other properties you may use to control the appearance and + behavior of the reader. + + Note that this view may be released when it is not displayed (eg, under + low memory conditions). You should apply any configuration just before + you present the reader. + + .. member:: BOOL enableCache + + This property is deprecated and should not be modified. + + .. warning:: + + The remaining properties are deprecated, they are only present for + backward compatibility with :class:`ZBarReaderController` and will raise + an exception if inappropriate/unsupported values are set. + + .. member:: UIImagePickerControllerSourceType sourceType + + Raises an exception if anything other than + ``UIImagePickerControllerSourceTypeCamera`` is set. If you want to scan + images, use a :class:`ZBarReaderController` instead of this class. + + .. member:: BOOL allowsEditing + + Raises an exception if anything other than ``NO`` is set. + + .. member:: BOOL showsCameraControls + + Raises an exception if anything other than ``NO`` is set. Use + :member:`showsZBarControls` to disable the buit-in overlay. + + .. member:: BOOL showsHelpOnFail + + Any value set to this property is ignored. It is only useful for + scanning images, for which you should use :class:`ZBarReaderController`. + + .. member:: ZBarReaderControllerCameraMode cameraMode + + This reader only supports scanning from the camera feed. If you want to + scan manually captured images, use a :class:`ZBarReaderController` + instead of this class. + + .. member:: BOOL takesPicture + + Raises an exception if anything other than ``NO`` is set. This + controller automatically returns the scanned camera frame and does not + support capturing a separate image. + + .. member:: NSInteger maxScanDimension + + Any value set to this property is ignored. It is only useful for + scanning images, for which you should use :class:`ZBarReaderController`. + + +Class Methods +------------- + + .. describe:: + (BOOL) isSourceTypeAvailable:(UIImagePickerControllerSourceType)source + + Returns ``YES`` only if ``source`` is ``Camera`` and the + :class:`UImagePickerController` method of the same name also returns + ``YES``. + +Instance Methods +---------------- + + .. _`showHelpWithReason:`: + .. describe:: - (void) showHelpWithReason:(NSString*)reason + + Display the integrated help browser. Use this with custom overlays if + you don't also want to create your own help view. Should only be called + when the reader is displayed. The ``reason`` argument will be passed to + the :func:`onZBarHelp` javascript function. + + :reason: A string parameter passed to javascript. diff --git a/iphone/doc/ZBarReaderViewDelegate.rst b/iphone/doc/ZBarReaderViewDelegate.rst new file mode 100644 --- /dev/null +++ b/iphone/doc/ZBarReaderViewDelegate.rst @@ -0,0 +1,26 @@ +ZBarReaderViewDelegate Protocol Reference +========================================= + +.. class:: ZBarReaderViewDelegate + + :Inherits from: :class:`NSObject` + + This protocol, which must be implemented by the `readerDelegate` provided + to a :class:`ZBarReaderView`, is used to notify the delegate of new decode + results. + + +Instance Methods +---------------- + + .. describe:: - (void) readerView:(ZBarReaderView*)readerView didReadSymbols:(ZBarSymbolSet*)symbols fromImage:(UIImage*)image + + Called to notify the delegate of new decode results. + + Note that the referenced image is a proxy for a video buffer that is + asynchronously being converted to a :class:`UIImage`, attempting to + access the data will block until the conversion is complete. + + :readerView: :class:`ZBarReaderView` that scanned the barcode(s). + :symbols: :class:`ZBarSymbolSet` containing the decode results. + :image: :class:`UIImage` from which the barcode(s) were scanned. diff --git a/iphone/doc/ZBarSymbol.rst b/iphone/doc/ZBarSymbol.rst new file mode 100644 --- /dev/null +++ b/iphone/doc/ZBarSymbol.rst @@ -0,0 +1,166 @@ +ZBarSymbol Class Reference +========================== + +.. class:: ZBarSymbol + + :Inherits from: :class:`NSObject` + + A symbol wraps all of the information the library has about a decoded + barcode. Use the available properties to retrieve the barcode data, the + symbology (type of barcode), location and more. + + This class is a simple wrapper around a :type:`zbar_symbol_t` C object + (q.v.) + + +Properties +---------- + + .. member:: zbar_symbol_type_t type + + The type of symbology that was decoded. (read-only) + + .. member:: NSString *typeName + + The canonical name used by the library to represent the symbology. + (read-only) + + .. member:: NSUInteger configMask + + Bitmask of symbology config settings used during decode. + + .. member:: NSUInteger modifierMask + + Bitmask of symbology characteristics detected during decode. + + .. member:: NSString *data + + The raw decoded barcode data. (read-only) + + .. member:: int quality + + A relative metric indicating rough confidence in the decoded value. + Larger values are better than smaller values. (read-only) + + .. member:: zbar_orientation_t orientation + + The general, axis-aligned orientation of the symbol, or + ZBAR_ORIENT_UNKNOWN if unknown. (read-only) + + .. member:: ZBarSymbolSet *components + + The components of a composite symbol. (read-only) + + .. member:: const zbar_symbol_t *zbarSymbol + + Retrieve the underlying C object instance. (read-only) + + .. member:: CGRect bounds + + Calculate a rough bounding box for the symbol. (read-only) + + .. note:: + + Coordinates are relative to the image *data*, which may not match a + displayed UIImage. Make sure to account for the UIImage orientation + when using these values. + + +Class Methods +------------- + + .. _`nameForType:`: + .. describe:: + (NSString*) nameForType:(zbar_symbol_type_t)type + + Retrieve the canonical name for a symbology used by the library, given + its enumerated value. + + :type: The :type:`zbar_symbol_type_t` enumerated symbology value. + :Returns: A short string name for the symbology. + + +Instance Methods +---------------- + + .. _`initWithSymbol:`: + .. describe:: - (id) initWithSymbol:(const zbar_symbol_t*)symbol + + Initialize a symbol wrapper, given the C object to wrap. + + :symbol: The C object to wrap. + :Returns: The initialized symbol, or nil if an error occurred. + + +Constants +--------- + +.. type:: zbar_symbol_type_t + + Symbology identifiers. + + ZBAR_NONE + No symbol was decoded. + + ZBAR_PARTIAL + Intermediate status. + + ZBAR_EAN8 + EAN-8 + + ZBAR_UPCE + UPC-E + + ZBAR_ISBN10 + ISBN-10, converted from EAN-13 + + ZBAR_UPCA + UPC-A + + ZBAR_EAN13 + EAN-13 + + ZBAR_ISBN13 + ISBN-13, converted from EAN-13 + + ZBAR_I25 + Interleaved 2 of 5 + + ZBAR_DATABAR + GS1 DataBar (RSS) + + ZBAR_DATABAR_EXP + GS1 DataBar Expanded + + ZBAR_CODE39 + Code 39 (3 of 9) + + ZBAR_QRCODE + QR Code + + ZBAR_CODE128 + Code 128 + +.. type:: zbar_orientation_t + + The coarse orientation of a symbol. + + .. note:: + + Orientation is relative to the image *data*, which may not match a + displayed UIImage. Make sure to account for the UIImage orientation + when using these values. + + ZBAR_ORIENT_UNKNOWN + Unable to determine orientation. + + ZBAR_ORIENT_UP + Upright, read left to right + + ZBAR_ORIENT_RIGHT + Sideways, read top to bottom + + ZBAR_ORIENT_DOWN + Upside-down, read right to left + + ZBAR_ORIENT_LEFT + Sideways, read bottom to top diff --git a/iphone/doc/ZBarSymbolSet.rst b/iphone/doc/ZBarSymbolSet.rst new file mode 100644 --- /dev/null +++ b/iphone/doc/ZBarSymbolSet.rst @@ -0,0 +1,43 @@ +ZBarSymbolSet Class Reference +============================= + +.. class:: ZBarSymbolSet + + :Inherits from: :class:`NSObject` + :Conforms to: :class:`NSFastEnumeration` + + A symbol set is a simple container for the symbols scanned from an image. + It supports :class:`NSFastEnumeration`, and not much else... Use it to + iterate through the :class:`ZBarSymbol` objects in a decode result set:: + + ZBarSymbolSet *symbols = image.symbols; + for(ZBarSymbol *symbol in symbols) { + // process result + } + + This class is a simple wrapper around a :type:`zbar_symbol_set_t` C object + (q.v.) + + +Properties +---------- + + .. member:: int count + + The number of symbols in the set. (read-only) + + .. member:: const zbar_symbol_set_t *zbarSymbolSet + + Retrieve the underlying C object instance. (read-only) + + +Instance Methods +---------------- + + .. _`initWithSymbolSet:`: + .. describe:: - (id) initWithSymbolSet:(const zbar_symbol_set_t*)set + + Initialize a symbol set wrapper, given the C object to wrap. + + :set: The C object to wrap. + :Returns: The initialized symbol set, or nil if an error occurred. diff --git a/iphone/doc/apiref.rst b/iphone/doc/apiref.rst new file mode 100644 --- /dev/null +++ b/iphone/doc/apiref.rst @@ -0,0 +1,16 @@ +******************* + API Reference +******************* + +.. toctree:: + :maxdepth: 1 + + ZBarImage + ZBarImageScanner + ZBarReaderController + ZBarReaderDelegate + ZBarReaderView + ZBarReaderViewController + ZBarReaderViewDelegate + ZBarSymbol + ZBarSymbolSet diff --git a/iphone/doc/camera.rst b/iphone/doc/camera.rst new file mode 100644 --- /dev/null +++ b/iphone/doc/camera.rst @@ -0,0 +1,129 @@ +Scanning From the Camera Feed +============================= + +Many iPhone developers want their application to support automatic recognition +of barcodes from the camera feed in real-time. ZBar makes this easy! + +There are three levels that you may choose to integrate at, from least complex +(recommended) to most complex these are: + +* Use the fully integrated view controller - this is very easy to implement + and is the recommended approach. +* Use the reader view with your own controller - this is not recommended, it + does not give you much more flexibility than using the full controller. +* Use the capture component with your own AVCapture session - this is not + supported and only provided for advanced developers with special needs who + are already familiar with AVCapture. + + +Using a ZBarReaderViewController +-------------------------------- + +This is the fastest, easiest and recommend way to get the barcode reader into +your application. It is also the only way to support :doc:`automatic fallback +for iOS 3.1 `. The procedure is the same as using a +UIImagePickerController to take a picture with the camera, so it will help if +you are familiar with that. Basically you: + +1. Create the reader. + + This is as simple as creating a new :class:`ZBarReaderViewController`:: + + ZBarReaderViewController *reader = [ZBarReaderViewController new]; + +2. Setup a delegate to receive the results. + + The delegate should implement the :class:`ZBarReaderDelegate` protocol, + which inherits from :class:`UIImagePickerControllerDelegate`:: + + reader.readerDelegate = self; + +3. Configure the reader. + + Aside from the properties of the reader itself, you can configure the + decoder via the :member:`~ZBarReaderViewController::scanner` property and + further customize the view via the + :member:`~ZBarReaderViewController::readerView` property:: + + [reader.scanner setSymbology: ZBAR_QRCODE + config: ZBAR_CFG_ENABLE + to: 0]; + reader.readerView.zoom = 1.0; + + See :doc:`custom` and :doc:`optimizing` for more details. + +4. Present the reader to the user. + + Typically the controller is presented modally, although the new controller + does not require it (note that modal presentation is the only option if you + want to support :doc:`iOS 3.1 fallback `):: + + [self presentModalViewController: reader + animated: YES]; + +5. Process the results. + + The controller will call the + :member:`imagePickerController:didFinishPickingMediaWithInfo:` method of + your delegate every time new results become available. The barcode data + can be obtained using the :c:data:`ZBarReaderControllerResults` key of the + info dictionary. This key will return "something enumerable"; keep in mind + that there may be multiple results. You may also retrieve the + corresponding image with :c:data:`UIImagePickerControllerOriginalImage` as + usual:: + + - (void) imagePickerController: (UIImagePickerController*) reader + didFinishPickingMediaWithInfo: (NSDictionary*) info + { + id results = + [info objectForKey: ZBarReaderControllerResults]; + UIImage *image = + [info objectForKey: UIImagePickerControllerOriginalImage]; + ... + + The ``reader`` parameter will be the actual type of the reader (not + necessarily a :class:`UIImagePickerController`). + + .. note:: + + The delegate method should queue the interface response and return as + soon as possible; any processing of the results should be deferred until + later, otherwise the user will experience unacceptable latency between + the actual scan completion and the visual interface feedback. + +6. Dismiss the reader (or not). + + Once you have the results you may dismiss the reader:: + + [reader dismissModalViewControllerAnimated: YES]; + + .. warning:: + + It is very important to dismiss from the *reader* (not the presenting + controller) to avoid corrupting the interface. + + Alternatively, you may choose to continue scanning and provide visual + feedback another way (eg, maybe by updating your custom overlay with the + results). The "continuous" mode of the readertest example does this. + + +Using a ZBarReaderView +---------------------- + +:class:`ZBarReaderViewController` is a relatively thin wrapper around a +:class:`ZBarReaderView`; it is possible to use the view directly. You will +lose the automatic fallback for iOS 3.1 and some of the simulator hooks, so +this approach is not currently recommended (or well tested). + + +Using the ZBarCaptureReader +--------------------------- + +If you have special requirements for the capture session or just want to use +your own preview, you can add your own :class:`ZBarCaptureReader` to your +session. You must have a solid understanding of the AVCapture infrastructure +if you plan to use this approach. + +.. admonition:: TBD + + sorry, you're on your own here - UTSL :) diff --git a/iphone/doc/compat.rst b/iphone/doc/compat.rst new file mode 100644 --- /dev/null +++ b/iphone/doc/compat.rst @@ -0,0 +1,183 @@ +Backward Compatibility +====================== + +Generally speaking, we take great care to ensure that each release of the +library is backward compatible with previous versions - upgrading the library +should not require any changes to your code and will continue to provide +equivalent functionality. The notable exception to this is the iOS 4 upgrade +and associated "deprecation" of the former automatic capture method by our +vendor. + + +The Private API +--------------- + +The API that we use for automatic capture with iOS 3.x (namely +:func:`UIGetScreenImage`) has an interesting history. It has changed status +several times, starting with "Private, unless we like you" moving to +"reluctantly Public but undocumeted" by popular demand and reverting to +"strictly Private" as of iOS 4. The current story: if you want to distribute +on the App Store, you had better not be using it - IOW, no automatic capture +for you with iOS 3.x. + +Since App Store distribution is the most common use for the library, the +default configuration, and thus the binary SDK, does *not* use any private +APIs. + +Users targeting ad-hoc or enterprise distribution may not care about the +status of the API and may prefer to continue supporting automatic capture for +iOS 3.x. To do this you will need to rebuild the library with the following +define set for all configurations: + +.. sourcecode:: sh + + USE_PRIVATE_APIS=1 + +For reference, you can check whether your app refers to the offensive function +with this command: + +.. sourcecode:: sh + + $ otool -vI MyApp.app/MyApp | grep UIGetScreenImage + +If there is any output, then the executable includes the private API and is +bound to be rejected if submitted for review. Otherwise it is "clean" as far +as this library is concerned. + + +Upgrading to iOS 4 +------------------ + +If you were using the reader before iOS 4 was introduced, you will want to +upgrade to the new reader controller. The performance has improved quite a +bit, and you can continue to support automatic capture on the App Store. + +.. note:: + + This discussion only applies to automatic capture from the camera. If you + are only scanning image files, or prefer/need to use manual capture, you + should not change anything. + +Basically just replace your old :class:`ZBarReaderController` with a new +:class:`ZBarReaderViewController` and you're done! See the reference and the +next section for compatibility between the two classes. + +Also see the :doc:`install` instructions for details about upgrading the +header references to use the SDK. + + +Supporting iOS 3.x +------------------ + +The new :class:`ZBarReaderViewController` is intentionally designed to be +compatible with the old :class:`ZBarReaderController` in most aspects that +relate to reading barcodes. When a :class:`ZBarReaderViewController` is +initialized under iOS 3.x, it will *replace* itself with a +:class:`ZBarReaderController`. You can leverage the compatibility of these +controllers to continue supporting iOS 3.x. + +The following properties and methods should be equivalent across +implementations. You may use them without regard for the actual instance +type. + +======================================================== ==== +Equivalent Members +======================================================== ==== +:member:`~ZBarReaderViewController::cameraOverlayView` +:member:`~ZBarReaderViewController::cameraViewTransform` +:member:`~ZBarReaderViewController::enableCache` +:member:`~ZBarReaderViewController::scanner` +:member:`~ZBarReaderViewController::readerDelegate` +:member:`~ZBarReaderViewController::scanCrop` +``showHelpWithReason:`` +:member:`~ZBarReaderViewController::showsZBarControls` +:member:`~ZBarReaderViewController::tracksSymbols` +======================================================== ==== + +Some properties are available with :class:`ZBarReaderViewController` only for +backward compatibility. If these are configured, they must be set as +indicated; attempts to set another value will raise an exception. + +==================================================== ======================================= +:class:`ZBarReaderController` Property :class:`ZBarReaderViewController` Value +==================================================== ======================================= +:member:`~ZBarReaderController::allowsEditing` ``NO`` +:member:`~ZBarReaderController::cameraMode` ``Sampling`` +:member:`~ZBarReaderController::maxScanDimension` (ignored) +:member:`~ZBarReaderController::showsCameraControls` ``NO`` +:member:`~ZBarReaderController::showsHelpOnFail` (ignored) +:member:`~ZBarReaderController::sourceType` ``Camera`` +:member:`~ZBarReaderController::takesPicture` ``NO`` +==================================================== ======================================= + +Also, the ``isSourceTypeAvailable:`` class method of +:class:`ZBarReaderViewController` will return ``YES`` only for the ``Camera`` +source. + +All other members of :class:`ZBarReaderController`, including those inherited +from :class:`UIImagePickerController` are not supported by +:class:`ZBarReaderViewController`. This includes ``takePicture`` and +``scanImage:``, among others. + +Remaining members of :class:`ZBarReaderViewController`: are only available +with the new implementation. At the moment this is only +:member:`~ZBarReaderViewController::readerView`, but any new properties or +methods not listed here will also fall in this category. + +To access settings that may not be available in a potential fallback +environment, you must verify that they exist and may be set as desired - eg, +by testing the specific reader subtype. + +Weak Linking +^^^^^^^^^^^^ + +When leveraging fallbacks to iOS 3.x, it is important that features introduced +in iOS 4 are referenced using *weak* links. You must configure your project +correctly to support this: + +* Make sure the iOS 4 frameworks are set to *Weak*. Specifically, these are + AVCapture, CoreMedia and CoreVideo. + +* Build with the latest SDK - do *not* use the "Base SDK" setting to target + earlier devices. + +* Set the correct iOS 3.x version for the "iPhone OS Deployment Target" + build setting. + + +Example: Fallback to Manual Capture +----------------------------------- + +This code example will configure the reader for automatic capture from the +camera for iOS 4 and fall back to manual or automatic capture for iOS 3.x, +depending on whether the library was compiled to use private APIs:: + + if(![ZBarReaderController isSourceTypeAvailable: + UIImagePickerControllerSoureTypeCamera]) { + // camera unavailable: display warning and abort + // or resort to keypad entry, etc... + return; + } + + ZBarReaderViewController *reader = [ZBarReaderViewController new]; + // reader will be a ZBarReaderController for iOS 3.x + // or a ZBarReaderViewController for iOS 4 + + reader.readerDelegate = self; + reader.sourceType = UIImagePickerControllerSoureTypeCamera; + reader.showsZBarControls = YES; + + if(reader.cameraMode == ZBarReaderControllerCameraModeSampling) { + // additional automatic capture configuration here + } + else { + // additional manual capture configuration here + } + + [self presentModalViewController: reader + animated: YES]; + +If you are using a custom control set +(:member:`~ZBarReaderViewController::showsZBarControls`\ ``=NO``), you will +want to provide a button attached to ``takePicture`` for the manual capture +case. The built-in controls do this automatically. diff --git a/iphone/doc/conf.py b/iphone/doc/conf.py new file mode 100644 --- /dev/null +++ b/iphone/doc/conf.py @@ -0,0 +1,77 @@ +import sys, os +from plistlib import readPlist + +# General configuration + +extensions = [] +templates_path = ['ext'] +source_suffix = '.rst' +master_doc = 'index' +exclude_patterns = ['.#*'] + +project = u'ZBar iPhone SDK' +copyright = u'2010, Jeff Brown et al' + +today_fmt = '%Y-%m-%d' +info = readPlist('../res/ZBarSDK-Info.plist') +version = 'X.Y' +if info: + version = info['CFBundleVersion'] +release = version + +#add_module_names = False + +pygments_style = 'sphinx' +highlight_language = 'objc' +primary_domain = 'cpp' + +# Options for HTML output + +html_theme = 'default' +html_theme_options = { + 'bgcolor': 'white', + 'textcolor': 'black', + 'linkcolor': '#247', + 'headbgcolor': '#edeff0', + 'headtextcolor': '#247', + 'headlinkcolor': '#c11', + 'sidebarbgcolor': '#247', + 'sidebartextcolor': 'white', + 'sidebarlinkcolor': '#cde', + 'relbarbgcolor': '#247', + 'relbartextcolor': '#ccc', + 'relbarlinkcolor': 'white', + 'footerbgcolor': 'white', + 'footertextcolor': 'black', + 'codebgcolor': '#dfe', + 'codetextcolor': 'black', +} + +html_short_title = 'ZBarSDK ' + version +html_title = 'ZBar iPhone SDK Documentation' +html_static_path = ['static'] +html_favicon = '../../zbar.ico' +html_style = 'style.css' +html_use_modindex = False +html_use_index = False +html_copy_source = False +html_show_sourcelink = False +htmlhelp_basename = 'doc' + +# Options for LaTeX output + +latex_paper_size = 'letter' +latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]) +latex_documents = [ + ('index', 'ZBarSDK.tex', u'ZBar iPhone SDK Documentation', + u'Jeff Brown', 'manual'), +] + +#latex_logo = '' +#latex_use_parts = False +#latex_preamble = '' +#latex_appendices = [] +#latex_use_modindex = False diff --git a/iphone/doc/custom.rst b/iphone/doc/custom.rst new file mode 100644 --- /dev/null +++ b/iphone/doc/custom.rst @@ -0,0 +1,72 @@ +Customizing the Interface +========================= + +The reader supports customization of the camera overlay and the integrated +help that is displayed. + + +Customizing the Overlay +----------------------- + +If you are scanning with the camera, whether using a +:class:`ZBarReaderViewController` for automatic capture or manually with +:class:`ZBarReaderController`, you may want to customize the appearance of the +reader. You do this mainly by setting a +:member:`~ZBarReaderViewController::cameraOverlayView`. + +Note that if you are scanning images from the photo library, there is no +customization - you are limited to the system picker interface provided by the +:class:`UIImagePickerController`. + +If you are using a :class:`ZBarReaderViewController`, are *only* planning to +support iOS 4 and just want to add to the existing controls, you can simply +set your overlay to include the additional view hierarchy:: + + reader.cameraOverlayView = myLogoImageView; + +Otherwise, if you are using a :class:`ZBarReaderController`, still need the +iOS 3.1 fallback or prefer to completely replace the default controls, you +should disable those first. Note that you will need to provide your own +controls, which should at least include a way to dismiss the reader:: + + reader.showsCameraControls = NO; // for UIImagePickerController + reader.showsZBarControls = NO; + reader.cameraOverlayView = myControlView; + +For manual capture with :class:`ZBarReaderController`, you should also include +a control connected to :member:`~ZBarReaderController::takePicture`. + +See :doc:`compat` for more information about iOS 3.1 fallbacks. + +In either case, the overlay view may be loaded from a NIB, or simply created +programmatically. + +You can also disable the tracking rectangle that highlights barcodes with +:member:`~ZBarReaderViewController::tracksSymbols`. + + +Presenting Help +--------------- + +If you have set ``showsZBarControls = NO`` and replaced the default controls, +you may still present the built-in help viewer. Just hook your custom control +to the ``showsHelpWithReason:`` method of the controller. You should only +call this method when the reader is actually presented. + +The default reader controls invoke ``showsHelpWithReason:`` with a reason +parameter of ``"INFO"`` when the info button is tapped. + + +Customizing the Help Content +---------------------------- + +Whether you use the default controls or provide your own, you can still +customize the content of the help that is displayed. The integrated viewer +uses a UIWebView to display the contents of :file:`zbar-help.html` that we +copied into your Resources. You should hack this up as you see fit to give +your users the best help experience. + +To allow for runtime customization based on the reason for presenting help, +the javascript function ``onZBarHelp`` will be called just before the page is +displayed, with the ``reason`` argument set as provided to +``showsHelpWithReason:``. diff --git a/iphone/doc/devguide.rst b/iphone/doc/devguide.rst new file mode 100644 --- /dev/null +++ b/iphone/doc/devguide.rst @@ -0,0 +1,13 @@ +*********************** + Developer's Guide +*********************** + +.. toctree:: + :maxdepth: 2 + + camera + picker + custom + optimizing + compat + licensing diff --git a/iphone/doc/faq.rst b/iphone/doc/faq.rst new file mode 100644 --- /dev/null +++ b/iphone/doc/faq.rst @@ -0,0 +1,113 @@ +Frequently Asked Questions (FAQ) +================================ + +This is the ever-growing list of answers to commonly asked questions. Please +feel free to post you question in our `iPhone Developers forum`_ if you do not +find the information you need in this documentation. + +.. _`iPhone Developers Forum`: + http://sourceforge.net/projects/zbar/forums/forum/1072195 + + +General +------- + +This looks great... Where can I get it? + You can download the latest version of the SDK from + http://zbar.sf.net/iphone + + +Compatibility +------------- + +Which iPhone devices does this library support? + The library works *only* with the iPhone 3GS and iPhone 4. + +Will you make it work with the iPhone 3G? + *No* - the 3G it is not supported and is unlikely to ever be supported. + + To be fair, you *can* use the 3G to scan image files, as long as they're in + focus (ie, *not* images taken by the built-in fixed-focus camera). There + is at least one application that found a use for this... + +What target iOS versions does this library work with? + iOS 4 is fully supported, including the latest video streaming interfaces. + Since Apple has dropped support for earlier versions of iOS on the App + Store, we recommend that you target only iOS 4 for reading barcodes. + + iOS 3.1 is also supported, but you will have to resort to manual capture if + you intend to distribute on the App Store. + + The library should work all the way back to iOS 3.0, although we no longer + test with it. For most cases we recommend you use at least iOS 3.1, which + introduced several must-have enhancements for the UIImagePickerController + (ie, :member:`~ZBarReaderController::cameraOverlayView` and ``takePicture``) + + In all cases you will need at least a 4.0 SDK to build. + + See :doc:`compat` for details about iOS version fallbacks. + +Are any private APIs in use? + No - the binary release of the SDK does not use any private APIs. + + OTOH, if you do not care about private APIs (for instance, you are + targeting AdHoc or enterprise distribution) and you want to enable + automatic capture with iOS 3.1, you can build a version that uses + UIGetScreenImage (which is again a private API). + + See :doc:`compat` for details. + +Does this support "automatic" barcode capture? + Yes - if you use iOS 4, the default configuration will capture barcodes + automatically from the video stream. + + Again, automatic capture is no longer supported with iOS 3.1 for apps + distributed on the App Store. + + +Building +-------- + +I get "Undefined symbols" errors when I try to build? + Most likely you did not add all of the necessary framework dependencies. + See :doc:`tutorial` or :doc:`install` for the list of frameworks you need + to link against. + + +Licensing +--------- + +Please refer to :doc:`licensing` for questions about licensing. + + +Barcodes +-------- + +Why do my UPC barcodes have an extra 0 at the front? + The UPC-A_ symbology is the subset of EAN-13_ that starts with a leading 0. + The ZBar decoder enables only EAN-13_ by default, so GTIN-13_ product codes + are consistently reported. You can choose to receive the 12-digit results + instead by explicitly enabling UPC-A_. + + The :member:`~ZBarSymbol::type` property of the symbol can be used to see + which type of barcode is reported. + + See EAN-13_ and UPC-A_ for more information. + +Why does my UPC-E (short version) barcode data look completely wrong? + UPC-E_ is a "zero compressed" version of UPC-A_; certain of the zeros are + removed from the UPC-A_ data to generate the UPC-E_ barcode. The ZBar + decoder *expands* this compression by default, again to consistently report + GTIN-13_ product codes. You can choose to receive the compressed 8-digit + results instead by explicitly enabling UPC-E_. + + The :member:`~ZBarSymbol::type` property of the symbol can be used to see + which type of barcode is reported. + + See UPC-E_ for more information. + +.. _GTIN-13: +.. _GTIN: http://wikipedia.org/wiki/GTIN +.. _EAN-13: http://wikipedia.org/wiki/EAN-13 +.. _UPC-A: http://wikipedia.org/wiki/UPC-A +.. _UPC-E: http://wikipedia.org/wiki/UPC-E#Zero-compressed_UPC-E diff --git a/iphone/doc/getstarted.rst b/iphone/doc/getstarted.rst new file mode 100644 --- /dev/null +++ b/iphone/doc/getstarted.rst @@ -0,0 +1,12 @@ +********************* + Getting Started +********************* + +.. toctree:: + :maxdepth: 2 + :numbered: + + install + tutorial + faq + support diff --git a/iphone/doc/index.rst b/iphone/doc/index.rst new file mode 100644 --- /dev/null +++ b/iphone/doc/index.rst @@ -0,0 +1,20 @@ +################### + ZBar iPhone SDK +################### + +Welcome to the ZBar SDK for iPhone! + +This documentation covers all aspects of developing with the SDK: from adding +the SDK to your project, to writing code that uses it, even licensing the +library with your app. + +Please let us know if you find anything inaccurate or lacking (even better, +send doc patches!) + +.. toctree:: + :maxdepth: 2 + :numbered: + + getstarted + devguide + apiref diff --git a/iphone/doc/install.rst b/iphone/doc/install.rst new file mode 100644 --- /dev/null +++ b/iphone/doc/install.rst @@ -0,0 +1,135 @@ +Installing the SDK +================== + +These are the basic instructions for obtaining the SDK and adding it to an +Xcode project. + +You may want to try things out with the :doc:`tutorial` before hacking at your +own project. + + +Requirements +------------ + +You will need *all* of the following to develop iPhone applications +using this SDK: + +* Mac OS X >= 10.6.x (Snow Leopard) +* Xcode >= 3.2.3 +* iPhone SDK >= 4.0 +* An iPhone 3GS or iPhone 4 +* iOS >= 3.1 running on the device (>= 4.0 is preferred) + +.. warning:: + + *Only* the iPhone 3GS and iPhone 4 are supported, as they have a camera + with auto-focus. The ZBar library does not support the iPhone 3G and is + unlikely to ever support it. + + +Downloading +----------- + +Download the latest binary release of the ZBar SDK from + +http://zbar.sourceforge.net/iphone + + +Integration +----------- + +The recommended installation method is to simply copy the SDK into your +Xcode project: + +1. Open ZBarSDK-|version|.dmg in the Finder. + +2. Drag the :file:`ZBarSDK` folder into your Xcode project. In the dialog + that appears, you should choose to **copy** the SDK into your project by + checking the box. The target that you want to link with the library should + also be selected in the target list. + +4. Link the following additional frameworks to any targets that link with the + ZBarSDK. You should set the first three to use weak references and + configure an appropriate deployment target if you still need to support + iOS 3.1: + + * :file:`AVFoundation.framework` (weak) + * :file:`CoreMedia.framework` (weak) + * :file:`CoreVideo.framework` (weak) + * :file:`QuartzCore.framework` + * :file:`libiconv.dylib` + + If you check "Link Binary With Libraries" for the target(s), you should see + all of these frameworks along with :file:`libzbar.a`. + +5. Import the SDK header from your prefix header to make the barcode reader + APIs available:: + + #import "ZBarSDK.h" + +Proceed to :doc:`camera` or :doc:`picker` to learn about using the reader APIs +to scan barcodes. Use the :doc:`apiref` for specific interface details. + + +Upgrading from an Older Version +------------------------------- + +If you are using an older version of the *SDK* (NB, skip to the next section +if you are currently using Mercurial), upgrading is straightforward: + +1. Delete the current ZBarSDK group from your project. You should choose + to delete the files if you copied them into your project. +2. Drag the new ZBarSDK from the DMG into your project. + +Clean out and rebuild your project with the new version. + + +Upgrading a Pre-SDK Integration +------------------------------- + +If your project was using the library directly from the Mercurial repository, +before the SDK was introduced, there are a few incompatibilities that +you must resolve in order to upgrade. Don't worry - all of you source stays +the same, you just need to update how the library is included in the project +and how the headers are imported. + +Switching to the Binary Distribution +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This approach is recommended - the binary releases provide you with a stable +development platform, isolating you from temporary instability and transient +problems that may occur at the bleeding edge. + +The first task is to reverse the previous ZBar integration: + +1. Remove the reference to zbar.xcodeproj from your project. +2. Remove any :file:`zbar-*` files from your Resources. +3. In the target build settings, remove any "Header Search Paths" that + reference zbar. +4. Remove any references to zbar headers in your :file:`prefix.pch` or source + files. + +Now just continue with the `integration`_ instructions above and you should be +back up and running! + +Continuing with Mercurial +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Alternatively, you may still prefer to select Mercurial revisions. You have a +few choices for this: + +* You may build your own ZBarSDK and copy/link it into your project. This is + the same as `Switching to the Binary Distribution`_, except that you use + your own version of the SDK. In this case you need to manually rebuild the + SDK when you update it. +* You may leave zbar.xcodeproj as a project dependency and pull the SDK into + your project. This is not well tested, so ymmv. +* You may leave zbar.xcodeproj as a project dependency and just link libzbar.a + into your project, as before. You will need to update the target dependency + (the library target changed names to libzbar) and add the + :file:`iphone/include/ZBarSDK` directory to "Header Search Paths" + +In any case, you should remove the references to the zbar headers from +:file:`prefix.pch` (or your source files) and replace them with:: + + #import "ZBarSDK.h" diff --git a/iphone/doc/licensing.rst b/iphone/doc/licensing.rst new file mode 100644 --- /dev/null +++ b/iphone/doc/licensing.rst @@ -0,0 +1,187 @@ +Licensing the Library +===================== + +First of all, the big disclaimer: + +.. warning:: + + We are *not* lawyers; we cannot help you decide if you should use the + library or how to apply the license, only your lawyer can advise you + concerning legal matters. + +That said, it should also be noted that we have neither the resources (time, +cash, etc) nor the patience to enforce the license (at all); the reality is +that all of this is left to your discretion. + +If you prefer to leave the lawyers out of it, the rest of this section will +help you apply the license to your application. + + +Licensing FAQ +------------- + +Can I use this library with my proprietary (closed source) application? + Yes, that is our intent and we do not believe there is any problem + regarding the license. + +Will I need to open source my entire app? + No, it is not required by the license. + +Will I need to distribute all of my "object files" on the App Store? + No, this is also not required by the license, although you should offer to + provide them upon request. See below for more detail. + +But I read somewhere that "iPhone apps may not use LGPL code"? + That statement is an over-generalization that does not apply in this case. + Most likely your source is either: + + * referring to the GPL, which is significantly different from the + *L*\ GPL + * referring to a different version of the LGPL; we intentionally use + version 2.1, which has specific static linking exceptions. + * not a lawyer either and too lazy to read the whole license + + Basically, if you leverage the appropriate sections of the license, it + should be fully compatible with the App Store restrictions and + requirements. + +This is too complicated, can I just pay for an easier license? + No, it is not possible. There are multiple problems with this approach, + some brief highlights: + + * Most open source projects (including this one) do not have a single + author. Tracking down every contributor and getting their approval could + be quite a challenge. + * The license is meant to protect users' rights to the software. Giving + you special treatment bypasses the protection we offered them, + effectively revoking their rights. This would be a violation of their + trust and completely defeats the purpose of the license. + + You may think of this as the "price" you pay for using our open source + library. If you want to make your life easier, you should be petitioning + Apple for shared library support... + +What if you add a clause that lets me do whatever I want? + No, also not possible. In addition to the problems mentioned above, there + are even more problems with this: + + * Sourceforge requires an OSI approved license for hosting our project; + an altered license would no longer be approved. + * Again we are not lawyers and therefore not qualified to change the + license, we would have to pay one of those slimy buggers to do it. + +Do I need to add an "about" dialog to display your copyright/license? + No, not as such. We won't discourage you from plugging the library if you + like, but it is not a requirement. You should think of our license as a + supplement to your own software license, therefore it is appropriate to + display it where (and only where) you display your own: + + * If you follow Apple's recommendation, the App Store is the only place + that the user accesses your license, so it should also be the only place + where the library supplement is available. + * If your app already has some kind of "about" view that displays your + copyright/license information, it is also appropriate to display the same + information for the library. + +Do I need to include the entire library in my application bundle? + No, it is not necessary: + + * If you have not modified the library, it is sufficient to provide a link + to the project and the version information. + * If you are using a modified version, you may provide a link to download + that instead of including it in the bundle. + + +Modifications +------------- + +What is a "modification"? Again, we leave it to your discretion with this +guidance: + +* If you use the distributed binary SDK you have certainly not modified the + library. +* If you are working from Mercurial, *any* change you have made to the + "source code" of the library is a modification, it does not matter how + trivial. You can see what changes have been made by running + ``hg status -mard``; if this command outputs anything, you have modified + the library. + +If you find that you have made changes to the library, you should carefully +consider how far you want to proceed down that path. Once you publish your +changes you have created a "fork" of the project which you now need to +maintain. Are you prepared to merge every time the library is updated? + +If your change adds a useful feature to the library, we absolutely encourage +you to submit patches. Assuming you can get your patch into the project, then +you will no longer need to use a modified version! When submitting patches, +ensure that your changes are appropriate for all of our users. Specifically, +we are not interested in patches that simply hack up the library to work the +way you want. Compare a patch that changes the default behavior to your +preference (probably not acceptable), to a patch that adds a new configuration +to support the feature you have added (probably fine). + + +Object File Distribution +------------------------ + +Section 6 of the LGPL v2.1 specifically permits static linking with the +library. If your project is not open source, this section does require that +you make your object files available to your users. The intent, as indicated +in the license, is that a user who has obtained your software may exercise +their right to modify the library and then re-link their modified version into +your application. + +We recommend that you apply Subsection 6c, which only requires that you make a +written offer to provide the object files. Now...if you consider the actual +utility of this mechanism - that it is only applicable to developers, and only +those with in depth knowledge of the tools, the time required for development +- all to have a new barcode reader in a specific version of your application +that only they can use, the reality is that no one is going to request this. +You probably should not even waste time preparing for it until a request is +made. + +Additionally, to avoid "casual requests" from nefarious types that just want +to inconvenience you, also consider charging a fee for the distribution of +this material (as permitted by the license); just add up the cost of burning +and shipping a disk. If this cost is "large" compared to the price of your +app, the likelyhood of a request is reduced even further. + + +Using the Unmodified Library +---------------------------- + +Applying the license in this case is somewhat simpler. These are the basic +steps you can follow: + +1. Verify that the rest of your software license is compatible with the LGPL. + You cannot use the library if they are incompatible. + + For those using the default App Store license, we have reviewed this and + believe it is compatible with the LGPL. + +2. At the end of your license text, in an annex or supplement, start by + declaring your use of the library and offering a link to the project. + Something like this: + + This software uses the open source ZBar Barcode Reader library, version + |version|, which is available from http://zbar.sourceforge.net/iphone + + If you built your own version of the library, replace the version callout + with eg, "cloned from Mercurial revision xxxxxxxx" + +3. Then append the contents of the text file COPYING, included with the + library. This is all of the copyright information for the library. + +4. Then append the contents of the text file LICENSE, also included with the + library. This is just the LGPL version 2.1 which you may also obtain from + http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html + +5. You may choose to make the written offer for the object files explicit. + Provide some text and whatever link or email address is appropriate. + + +Using a Modified Library +------------------------ + +We intentionally leave this option vague and force you to refer to the license +as an underhanded way of encouraging you to contribute back to the project ;) diff --git a/iphone/doc/optimizing.rst b/iphone/doc/optimizing.rst new file mode 100644 --- /dev/null +++ b/iphone/doc/optimizing.rst @@ -0,0 +1,437 @@ +Optimizing the Reader +===================== + +As good as the iPhone performance is for a mobile device, the reality from an +image processing perspective is that it represents a lower performance target. +While the default configuration of the iPhone reader has been carefully tuned +for the general case, you can often obtain much better results if you optimize +for your specific application. + +.. note:: + + Performance is a complex topic. The only way to tune performance is by + changing settings and comparing measured results. If you are not + comfortable with the concepts presented here, it is recommended that you + leave the settings at the defaults and avoid compromising reliability. + +Performance of the barcode reader is generally evaluated by two factors: + +* The **latency** - how quickly it "recognizes" a barcode. Specifically this + is the time from when the user puts a barcode in the frame or selects an + image until a response is indicated back to the them. + +* The **reliability** - it does not matter how quickly an image is scanned if + an incorrect result is returned. That may seem obvious, but bad decodes + *are* possible and you need to keep this in mind when changing settings that + affect the reliability of the reader. + +Basically our goal is to optimize the latency without sacrificing reliability. +There are several factors that contribue to latency: + +* The **quality** of the barcode image. Quality includes the available + resolution, focus, lighting, noise, etc. We have more control over some of + these than others. + +* The **camera**. When scanning from the camera, the time for the + autoexposure and autofocus to converge on an image that can be decoded is a + significant contribution to the overall latency. + +* The **frame rate** of the reader - this translates to the time it takes the + scanner to process an image. + +* The **effort level** of the reader - some of the available settings control + "how hard" the reader tries to find barcodes in each frame. + +* The **delegate latency** - the time spent in your application after a + barcode has been detected until the user is notified. + +Most of these factors are interrelated. We will discuss those we can control +in detail, as well the settings you use to affect them. Then we will provide +a few specific case examples. + + +Measuring Performance +--------------------- + +Subjective response times are a good place to start (does it "feel" responsive +to you?), and possibly the only way to evaluate the overall experience, but to +compare incremental changes to interrelated settings and have meaningful +performance discussions with others, we need a more quantitative approach. + +The :func:`mach_absolute_time` function is a good tool for accurately +measuring small delays. Research this function and learn how to apply it. As +when measuring any real-world value, keep in mind that some variance is to be +expected - even if you perform exactly the same operation multiple times, you +will not see exactly the same measurement. You should collect several +samples, discard any obvious outliers, and average the remaining measurements. + +One way that the overall reader latency may be evaluated is by manually +marking the time when the barcode is presented to the reader. Add a control +to your overlay that captures the start time when tapped and compare this to +the end time, just before your delegate returns. + +The reader continually monitors the frame rate at which it is running. The +measured value may be displayed for debugging purposes by enabling the +:member:`~ZBarReaderView::showsFPS` property. The readertest example does +this and also provides control over many of the available settings, so you can +quickly test how each setting affects the frame rate. You should target your +optimization efforts to achieve a frame rate of at least 8-10fps, although +12-15fps is preferable. + +You can measure the latency of your delegate using :func:`mach_absolute_time`. +The measured value should be less than about 100ms, the smaller the better, to +avoid noticeable lag. + +The readertest is a good tool for testing the performance of the reader. You +can tune the settings appropriately for your application and evaluate the +effect each change has on the performance. + + +Delegate Latency +---------------- + +This latency contributor is the easiest for you to effect (and sometimes the +easiest to overlook). You delegate method should update the interface - +dismiss the controller or update your overlay to indicate success - and +*nothing* else. All other processing should be deferred until after the +animations have started. + + +Image Quality +------------- + +Resolution +^^^^^^^^^^ + +One might think that "more is better" in terms of resolution, but this is not +necessarily the case. Given average image quality, the ideal resolution for +scanning is right around three pixels per barcode "module" (the width of the +smallest bar or space). Note that this measure is not an absolute image size +or even a measure of the physical dimensions represented by a pixel sample, it +*only* describes the sampled size of the barcode in the image. + +As the resolution decreases below about two pixels per module, edge fidelity +is lost and the bars and spaces start to merge together, making it impossible +(for this library) to scan. This affects the density (feature size) and +maximum size (data capacity) of the barcodes that can be detected. +Conversely, as the resolution increases above about 4 pixels per module, noise +can interfere with the edge detection and images will take longer to process. + +Other quality factors, such as poor focus, bad lighting or even excessive +noise, can increase (or decrease) the resolution requirement. + +When scanning from the camera, the reader defaults to 640x480, which is good +for most applications. On the iPhone 4, you can increase this using a capture +:member:`~ZBarReaderView::session` preset. The iPhone 3GS does not have a +higher resolution option available. + +For scanning images, you can use +:member:`~ZBarReaderController::maxScanDimension` to control the scaled size +of the converted image, or resort to converting them yourself. + +If you want to read long linear barcodes or dense 2-D symbols, you will +probably want to increase the resolution by adjusting these settings. + +Keep in mind that more pixels will take longer to scan, refer to the `frame +rate`_ discussion for ways to compensate. + +Focus +^^^^^ + +Ideally we would fix the focus at a calculated optimum distance and optimize +the aperture selection to maximize the depth of field. Unfortunately the APIs +do not currently give us control over any of these settings, the best we can +do (as of iOS 4) is continuous auto-focus mode - this mode is configured by +the reader automatically. It can still take the device as long as 1-2 seconds +to find the appropriate macro focus setting, but there just isn't much we can +do about that. + +Lighting and Exposure +^^^^^^^^^^^^^^^^^^^^^ + +An image that is too bright or overexposed can completely wash out any +barcodes. An image that is too dark or underexposed will not provide +sufficient contrast for the scanner. Low light levels also tend to produce +noisier images, possibly because the driver uses a faster "ISO" setting to +compensate for the lighting. + +The camera defaults to continuous automatic exposure and white balance. Since +there are no other useful values, the reader leaves these unchanged from their +default setting. + +For the iPhone 4 device, the "torch" can be enabled to provide additional +illumination for the camera in low-light conditions. The reader sets the +torch to automatic by default, so it should turn on only when needeed... +There have been some reports that the torch turns on inappropriately, washing +out the image. If you find that this occurs, you should instead set the +:member:`~ZBarReaderView::torchMode` property of the :class:`ZBarReaderView` +to ``Off``. + +For scanning images from another source, you are again stuck with the +available image quality. If you have any control over the image source, you +should do what you can to fix quality problems there. + +Noise +^^^^^ + +Some level of noise is filtered by the reader, but excessive noise levels +create additional edges in the image which corrupt barcodes and increase +scanning time (decreasing the frame rate). + +As mentioned with `lighting and exposure`_, noise mostly becomes a problem +when the light-level is too low, but high-resolution images may also increase +exposure to sensor noise. + +We compensate for noise by *reducing* the `resolution`_ from the sensor +maximum. Scaling the image down has the effect of averaging several pixels +into one value, filtering out the high-frequency noise component. + + +Frame Rate +---------- + +The time it takes to scan and decode an image/frame is roughly proportional to +the number of pixels that are processed. The number and type of enabled +symbologies and image noise can also affect the processing time. + +We have several knobs available that affect the frame rate. Most of these are +geared toward reducing the number of image pixels that are scanned. + +Decrease the Resolution +^^^^^^^^^^^^^^^^^^^^^^^ + +Adjusting the resolution of the image is an easy way to quickly reduce the +number of pixels. Smaller images also mean there is less data to carry +around, which helps performance in other ways. For example, reducing each +image dimension by 30% (eg, from 640x480 to 448x336) will about double the +speed of the reader (to a point). [FIXME verify!] + +Adjusting the resolution is `described above `_. As mentioned +there, reducing the resolution will negatively impact the minimum feature size +and maximum barcode size that can be scanned, but it will help filter noise. + +Crop the Scan Region +^^^^^^^^^^^^^^^^^^^^ + +It may not always be necessary for an application to scan all the way to the +edges of the image. By cropping the scan area, you can get most of the +benefits of reduced resolution without sacrificing the minimum feature size. +Cropping will also not affect image noise, but similar to decreasing the +resolution, it does affect the maximum size barcode that can be scanned. + +For all cases you set the crop rectangle +:class:`~ZBarReaderViewController::scanCrop` property. Note that the +rectangle provided to the controller is *normalized* across image size and +rotation. This means that the coordinates range from 0 to 1 and the axes will +be arranged such that the x-axis of the crop rectangle corresponds to the +major (longer) image axis. + +Your interface will typically need to indicate the cropped scan area to the +user with visual queues. Use the +:class:`~ZBarReaderViewController::cameraOverlayView` to provide this. + +By default, the :class:`ZBarReaderView` recognizes a pinch gesture to +digitally zoom the preview around the center of the image. This zoom does not +affect the resolution of the image, but it does crop the scan region to the +visible area. You can also disable the pinch gesture and set the +:class:`~ZBarReaderView::zoom` programmatically. + +Limit the Scan Density +^^^^^^^^^^^^^^^^^^^^^^ + +The scanner works by making scan passes across the pixel rows and colums of +the image. The density of the passes is configured at the scanner as a pixel +stride for each axis. ``ZBAR_CFG_Y_DENSITY`` (``ZBAR_CFG_X_DENSITY``) +controls the number of pixel rows (columns) that are skipped between +successive horizontal (vertical) scan passes. (Note that "density" is really +not a good name for the configuation settings... "stride" might be more +appropriate.) + +Decreasing the scan density (by increasing the stride setting) is a great way +to limit the processing (increasing the frame rate) without sacrificing scan +resolution - each scan pass is still made at full image resolution, there are +just fewer passes (less redundancy). + +Setting the stride value to 0 completely disables scanning in that direction. +This is very useful when reading linear codes with a visual alignment guide - +scanning parallel to the bars is a waste of cycles which may be better applied +to support higher resolution or increased density of scans across the symbol. +Note that some 2-D symbologies (QR Code) require scans in both directions. + +Setting the stride to a very large value will generate a single scan pass +through the center of the image. Note that some symbologies will not be +detected without multiple successful passes; it is usually better to combine +this setting with cropping to generate a number of closely clustered scan +passes in the target area. + +Note that the density also affects the aspect ratio and rotation that can be +tolerated. If you set it too large, some barcodes will become more difficult +to read. + +In general, 2 to 4 is a good target for the stride setting, unless you have +very high or low resolution images. + +Disable unused symbologies +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Limiting the symbologies to the set of interest should provide a small +performance boost. It also improves decode reliability - it is impossible to +receive an incorrect or unexpected decode result from a symbology that is +disabled. + +The reader does support full auto-discrimination among the supported +symbologies, but with all of them enabled you may need to compensate elsewhere +to get a good frame rate. + +For example, if you are only interested in QR codes, disable the others. The +robust way to do this is by disabling *all* symbologies and then reenabling +only those you want. This helps isolate you from encountering new symbologies +that may be added in future versions of the library until you are ready to +handle them:: + + [scanner setSymbology: 0 + config: ZBAR_CFG_ENABLE + to: 0]; + [scanner setSymbology: ZBAR_QRCODE + config: ZBAR_CFG_ENABLE + to: 1]; + +Even if you would like your application to support multiple symbologies, you +may consider if there is a way to limit the enabled subset based on the +scanning context, etc... + + +Examples +-------- + +These examples demonstrate several scenarios for scanning from the camera with +automatic capture using iOS 4. You can try them yourself using the +readertest. For each example, start with the default settings (by tapping the +``ZBarReaderViewController`` class), then enable continuous mode and the +custom overlay (by disabling +:member:`~ZBarReaderViewController::showsZBarControls`). You should also use +a release build and avoid running in the debugger. + +Frame rates are approximate, measured on an iPhone 3GS running iOS 4.0.1 in a +well lit room. Two measurements are taken for each sample: the rate with the +camera pointed at a blank white page such that it fills the frame, and the +rate while continuously decoding the provided example. For best results, it +is recommended that you print the examples rather than scanning them from the +screen. + +For reference, the base frame rates with default settings are 12fps for a +blank white page, 7.5fps for this `basic EAN symbol`_ and 2.2fps for this +`basic QR symbol`_. + +.. _`basic EAN symbol`: + http://zbar.sf.net/test/ean13/9876543210128.png +.. _`basic QR symbol`: + http://chart.apis.google.com/chart?cht=qr&chs=512x512&chl=http://zbar.sf.net/iphone + +Long Linear Symbols +^^^^^^^^^^^^^^^^^^^ + +For this example, we will use a relatively `long Code 128 barcode`_. + +.. _`long Code 128 barcode`: + http://zbar.sf.net/test/code128/ALPHA.png + +While it should be possible to read this symbol with the default settings, you +may notice that it is not very reliable. You will have to stretch the symbol +across the entire screen, and even then the default settings will only give +you about 1.6 pixels per module, well below the ideal target of 3. To improve +these results, we want to maximize scanning resolution for the long image +axis. + +1. Disable the default zoom/crop - zoom all the way out by hitting "Scan" and + pinching the preview; the frame rate immediately drops to 8fps / 4.8fps. + +We should compensate for this reduction in the frame rate: + +2. Crop the image to a long, skinny rectangle - set the + :member:`~ZBarReaderViewController::scanCrop` setting to + ``{{0, 0.3}, {1, 0.4}}``; The frame rate jumps up to 18fps / 8.7fps. + +3. Disable scans across the short image axis - set the ``CFG_X_DENSITY`` + setting to 0. The frame rate goes all the way to 30fps / 13fps. + +Since we have plenty of margin with the frame rate, we can minimize the total +decode latency by performing more scan passes through the symbol: + +4. Increase the scan density - set the ``CFG_Y_DENSITY`` setting to 1 (13.5fps + / 5fps) or 2 (24fps / 9fps). + +You should now be able to quickly and reliably decode long linear symbols. + +If have an iPhone 4, you may also try increasing the resolution to support +even longer symbols (NB there is no readertest setting for resolution). You +may have to compensate elsewhere to bring the frame rate back to a reasonable +level. + +High Density QR Symbols +^^^^^^^^^^^^^^^^^^^^^^^ + +For this example we will use a `version 29 QR Code symbol`_. + +.. _`version 29 QR Code symbol`: + http://www.qrcomic.com/images/5.png + +In this case we still want to maximize the resolution, but we also need to +increase the scan density to reliably pick up the small finder patterns: + +1. Maximize scan density in both directions - set the ``CFG_X_DENSITY`` and + ``CFG_Y_DENSITY`` settings both to 1. You should be able to scan the symbol + now, although the frame rate drops to 4.5fps / 1fps + +2. Disable the default zoom/crop - zoom all the way out by hitting "Scan" and + pinching the preview; the frame rate drops further to 3fps / 0.7fps + +We can compensate somewhat for the reduced frame rate: + +3. Crop the image to a square - set ``scanCrop`` to ``{{0.125, 0}, {.75, 1}}``. + This boosts the frame rate slightly to 3.7fps / 0.75fps. + +4. Disable linear symbologies - set the symbologies such that only QR Code is + enabled (4fps / 1fps) + +Even though the frame rate is still pretty bad, the QR recognition latency +should be acceptable. + +If have an iPhone 4, you may also try increasing the resolution to support +even denser QR symbols (NB there is no readertest setting for resolution). +You may have to compensate elsewhere to bring the frame rate back to a +reasonable level. + +Small DataBar Symbols +^^^^^^^^^^^^^^^^^^^^^ + +For this example we will use a `DataBar symbol`_ printed with a small feature +size, typical of the stickers used to tag produce. Scale it when printing +such that the printed dimensions are about 1cm square. This symbol should +scan with the default settings, but we will attempt to optimize the scan +latency for this case. + +.. _`DataBar symbol`: + http://zbar.sf.net/test/databar/0109876543210128-so.png + +As well as high barcode resolution, we also want high density passes in both +directions to minimize sensitivity to rotation: + +1. Maximize scan density in both directions - set the ``CFG_X_DENSITY`` and + ``CFG_Y_DENSITY`` settings both to 1. The frame rate drops to 4.5fps / + 3fps. + +Compensate for the reduction in frame rate by zooming in on the small symbol, +which crops the scanned image. Zooming also helps the user see the small +barcode: + +2. Zoom all the way in - hit "Scan" and un-pinch the preview. The frame rate + recovers to 11fps / 6.2fps. + +3. Crop the image to a square - set ``scanCrop`` to ``{{0.125, 0}, {0.75, 1}}`` + (14fps / 7.5fps) + +4. Disable all symbologies except DataBar and DataBar Expanded (14.5fps / 9fps) + +The reader should now be very sensitive to DataBar, even when scanned at an +angle. diff --git a/iphone/doc/picker.rst b/iphone/doc/picker.rst new file mode 100644 --- /dev/null +++ b/iphone/doc/picker.rst @@ -0,0 +1,104 @@ +Scanning a User-Selected Image +============================== + +Some applications may need the full resolution offered by camera snapshots, or +need to scan an image from the user's photo library. In these cases you use a +:class:`ZBarReaderController`. This reader is a *subclass* of +:class:`UIImagePickerController`, and you use it the same way. See the +documentation for :class:`UIImagePickerController` for more detais. + +1. Create the reader. + + This is as simple as creating a new :class:`ZBarReaderController`:: + + ZBarReaderController *reader = [ZBarReaderController new]; + +2. Setup a delegate to receive the results. + + The delegate should implement the :class:`ZBarReaderDelegate` protocol, + which inherits from :class:`UIImagePickerControllerDelegate`:: + + reader.readerDelegate = self; + +3. Configure the reader. + + You will need to set the :member:`~ZBarReaderController::sourceType` + appropriately. Aside from the properties of the reader itself, you can + configure the decoder via the :member:`~ZBarReaderController::scanner` + property:: + + if([ZBarReaderController isSourceTypeAvailable: + UIImagePickerControllerSoureTypeCamera]) + reader.sourceType = UIImagePickerControllerSoureTypeCamera; + [reader.scanner setSymbology: ZBAR_I25 + config: ZBAR_CFG_ENABLE + to: 0]; + + See :doc:`custom` and :doc:`optimizing` for more details. + +4. Present the reader to the user. + + As the reader is a UIImagePickerController, it must be presented modally:: + + [self presentModalViewController: reader + animated: YES]; + +5. Process the results. + + The controller will call the + :member:`imagePickerController:didFinishPickingMediaWithInfo:` method of + your delegate for a successful decode (NB *not* every time the user takes a + picture or selects an image). The barcode data can be obtained using the + :c:data:`ZBarReaderControllerResults` key of the info dictionary. This key + will return "something enumerable"; keep in mind that there may be multiple + results. You may also retrieve the corresponding image with + :c:data:`UIImagePickerControllerOriginalImage` as usual:: + + - (void) imagePickerController: (UIImagePickerController*) reader + didFinishPickingMediaWithInfo: (NSDictionary*) info + { + id results = + [info objectForKey: ZBarReaderControllerResults]; + UIImage *image = + [info objectForKey: UIImagePickerControllerOriginalImage]; + ... + + The ``reader`` parameter will be the actual :class:`ZBarReaderController` + (again, a subclass :class:`UIImagePickerController`). + + .. note:: + + The delegate method should dismiss the reader and return as soon as + possible; any processing of the results should be deferred until later, + otherwise the user will experience unacceptable latency between the + actual scan completion and the visual interface feedback. + +6. Dismiss the reader. + + Once you have the results you should dismiss the reader:: + + [reader dismissModalViewControllerAnimated: YES]; + + .. warning:: + + It is very important to dismiss from the *reader* (not the presenting + controller) to avoid corrupting the interface. + + +Handling Failure +---------------- + +It is always possible the user selects/takes an image that does not contain +barcodes, or that the image quality is not sufficient for the ZBar library to +scan successfully. + +In this case, and if :member:`~ZBarReaderController::showsHelpOnFail` is +``YES``, the integrated help controller will automatically be displayed with +reason set to ``"FAIL"``. + +Your delegate may also choose to implement the optional +``readerControllerDidFailToRead:withRetry:`` method to explicitly handle +failures. If the ``retry`` parameter is ``NO``, you *must* dismiss the reader +before returning, otherwise you may continue and allow the user to retry the +operation. Note that, if it is enabled, the integrated help will be displayed +when this delegate method is invoked. diff --git a/iphone/doc/static/style.css b/iphone/doc/static/style.css new file mode 100644 --- /dev/null +++ b/iphone/doc/static/style.css @@ -0,0 +1,36 @@ +@import url("default.css"); + +h1, h2, h3, h4, h5, h6 { + clear: both; +} + +img.logo { + vertical-align: middle; +} + +img.floatright { + float: right; + clear: both; + margin-left: 2em; + margin-right: 2em; +} + +dl.docutils dt { + font-weight: bold; +} +dl.docutils dd { + margin-top: 1em; + margin-bottom: 1em; +} + +table.docutils { + margin-left: auto; + margin-right: auto; +} +table.docutils th, table.docutils td { + padding: .25em .5em; +} + +table.docutils.field-list { + margin-left: 0; +} diff --git a/iphone/doc/support.rst b/iphone/doc/support.rst new file mode 100644 --- /dev/null +++ b/iphone/doc/support.rst @@ -0,0 +1,19 @@ +Obtaining Support +================= + +If this documentation does not address your question/problem and you need +support, please feel free to post in our `iPhone Developers Forum`_. + +.. _`iPhone Developers Forum`: + http://sourceforge.net/projects/zbar/forums/forum/1072195 + +When posting, please: + +* Check the :doc:`faq` and the rest of this documentation first. +* Start a new thread for a new question - do not "hijack" an unrelated thread. +* Post complete details of your problem - complete error messages as well as + relevant source code and images. +* Log-in to receive email, or check back within a day or so - you will almost + always get a response. + +For the latest support information, please see http://zbar.sf.net/iphone diff --git a/iphone/doc/tutorial.rst b/iphone/doc/tutorial.rst new file mode 100644 --- /dev/null +++ b/iphone/doc/tutorial.rst @@ -0,0 +1,216 @@ +ZBar SDK Integration Tutorial +============================= + +.. image:: ReaderSample.png + :alt: Screenshot of the ReaderSample app + :width: 414 + :height: 770 + :scale: 40 + :class: floatright + +This tutorial will quickly get you up and running with the ZBar iPhone SDK. + +We will develop a very simple app that presents a button the user can tap to +invoke the barcode reader and then displays the results. Interface Builder +will be used to create the interface. + +The completed project is also available with the distributed SDK under +:file:`Examples/ReaderSample`. + + +Create the App +-------------- + +1. Open Xcode; you must have version 3.2.3 or later. + +2. Create a new project using the "View-based Application" template. Name the + project "ReaderSample". Save it wherever you like. + +3. Open :file:`ReaderSampleViewController.xib` + +4. Drag a Round Rect Button onto the view and title it "Scan". Customize the + placement and appearance as you like. + +5. Drag an Image View onto the view. Size it to fill about half of the + remaining space. Change the view mode to Aspect Fit. + +6. Drag a Text View onto the view and size it to fill the remaining space. + Change the default text to "No barcode scanned" or something. De-select + "Editable" + +7. Add connections to the interface elements in the code; open + :file:`ReaderSampleViewController.h` and change the interface to:: + + @interface ReaderSampleViewController : UIViewController + { + UIImageView *resultImage; + UITextView *resultText; + } + @property (nonatomic, retain) IBOutlet UIImageView *resultImage; + @property (nonatomic, retain) IBOutlet UITextView *resultText; + - (IBAction) scanButtonTapped; + @end + +8. Now we can finish the interface connections - open + :file:`ReaderSampleViewController.xib` and make these connections: + + * Connect ReaderSampleViewController ``resultImage`` outlet to the + ImageView. + * Connect ReaderSampleViewController ``resultText`` outlet to the TextView. + * Connect ReaderSampleViewController ``scanButtonTapped`` action to the + RoundedRectButton(Scan) event ``TouchUpInside``. + + Consult the Xcode documentation if you need help making these connections. + Make sure you save the XIB once they are finished. + +9. Finish the implementation in :file:`ReaderSampleViewController.m`:: + + @synthesize resultImage, resultText; + + - (IBAction) scanButtonTapped + { + NSLog(@"TBD: scan barcode here..."); + } + + - (void) dealloc { + self.resultImage = nil; + self.resultText = nil; + [super dealloc]; + } + + This stub for scanButtonTapped is temporary, we'll fix it in a minute... + +Although it doesn't do much yet, you should now have a working skeleton app +that you can build and run. + + +Integrate the Reader +-------------------- + +Now for the exciting part - let's add a barcode reader! + +1. If you have not done so already, download the latest SDK from + http://zbar.sourceforge.net/iphone + +2. Double-click the disk image, ZBarSDK-|version|.dmg in the Finder to open it. + +3. Drag the :file:`ZBarSDK` folder into your Xcode project. Make sure that + the "Copy Items into destination group's folder" checkbox is checked. + +4. Right-click the ``Frameworks`` group in your project. Select + ``Add -> Existing Frameworks...`` Make sure "All" are listed and add each + of these from the ``Device`` list (NB hold down command for multiple + selection): + + * AVFoundation.framework + * CoreMedia.framework + * CoreVideo.framework + * QuartzCore.framework + * libiconv.dylib + +5. Import the SDK header. You will usually want to prefix it, so add it to + :file:`ReaderSample_prefix.pch`:: + + // ADD: import barcode reader APIs + #import "ZBarSDK.h" + +6. Declare support for the delegate protocol in + :file:`ReaderSampleViewController.h`:: + + @interface ReaderSampleViewController : UIViewController + // ADD: delegate protocol + < ZBarReaderDelegate > + { + ... + +7. Re-implement scanButtonTapped to present a barcode reader when the user + taps the Scan button. In :file:`ReaderSampleViewController.m`:: + + - (IBAction) scanButtonTapped + { + // ADD: present a barcode reader that scans from the camera feed + ZBarReaderViewController *reader = [ZBarReaderViewController new]; + reader.readerDelegate = self; + + ZBarImageScanner *scanner = reader.scanner; + // TODO: (optional) additional reader configuration here + + // EXAMPLE: disable rarely used I2/5 to improve performance + [scanner setSymbology: ZBAR_I25 + config: ZBAR_CFG_ENABLE + to: 0]; + + // present and release the controller + [self presentModalViewController: reader + animated: YES]; + [reader release]; + } + +8. Finally, implement the delegate method to do something useful with the + results. Still in :file:`ReaderSampleViewController.m`:: + + - (void) imagePickerController: (UIImagePickerController*) reader + didFinishPickingMediaWithInfo: (NSDictionary*) info + { + // ADD: get the decode results + id results = + [info objectForKey: ZBarReaderControllerResults]; + ZBarSymbol *symbol = nil; + for(symbol in results) + // EXAMPLE: just grab the first barcode + break; + + // EXAMPLE: do something useful with the barcode data + resultText.text = symbol.data; + + // EXAMPLE: do something useful with the barcode image + resultImage.image = + [info objectForKey: UIImagePickerControllerOriginalImage]; + + // ADD: dismiss the controller (NB dismiss from the *reader*!) + [reader dismissModalViewControllerAnimated: YES]; + } + +And that's it! + + +Testing +------- + +1. Save everything (don't forget to save MyAppViewController.xib). + +2. Select "Build and Run". + +3. Tap the Scan button. + +4. Aim at barcode. + +5. Enjoy the sweet fruits of your minimal labor + + +Where to go from here +--------------------- + +You can learn more about using the reader APIs to scan barcodes from +:doc:`camera` or :doc:`picker`. Use the :doc:`apiref` to find details about a +particular interface. + + +Troubleshooting +--------------- + +We take great care to ensure this tutorial is working as described. However, +if you do have a problem + +1. Make sure you followed the instructions exactly - every detail is + important. +2. Start from scratch with a new project and follow the instructions + *exactly*. +3. Try the ReaderSample distributed with the SDK and compare your work with + that. +4. If you are unable to get things working, you may post your frustrations in + the project `iPhone Developers Forum`_. Please be very specific about your + problem, post the complete text of any errors, etc. + +.. _`iPhone Developers Forum`: + http://sourceforge.net/projects/zbar/forums/forum/1072195 diff --git a/iphone/examples/ReaderSample/Classes/ReaderSampleAppDelegate.h b/iphone/examples/ReaderSample/Classes/ReaderSampleAppDelegate.h new file mode 100644 --- /dev/null +++ b/iphone/examples/ReaderSample/Classes/ReaderSampleAppDelegate.h @@ -0,0 +1,21 @@ +// +// ReaderSampleAppDelegate.h +// ReaderSample +// +// Created by spadix on 8/4/10. +// + +#import + +@class ReaderSampleViewController; + +@interface ReaderSampleAppDelegate : NSObject { + UIWindow *window; + ReaderSampleViewController *viewController; +} + +@property (nonatomic, retain) IBOutlet UIWindow *window; +@property (nonatomic, retain) IBOutlet ReaderSampleViewController *viewController; + +@end + diff --git a/iphone/examples/ReaderSample/Classes/ReaderSampleAppDelegate.m b/iphone/examples/ReaderSample/Classes/ReaderSampleAppDelegate.m new file mode 100644 --- /dev/null +++ b/iphone/examples/ReaderSample/Classes/ReaderSampleAppDelegate.m @@ -0,0 +1,86 @@ +// +// ReaderSampleAppDelegate.m +// ReaderSample +// +// Created by spadix on 8/4/10. +// + +#import "ReaderSampleAppDelegate.h" +#import "ReaderSampleViewController.h" + +@implementation ReaderSampleAppDelegate + +@synthesize window; +@synthesize viewController; + + +#pragma mark - +#pragma mark Application lifecycle + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // Override point for customization after application launch. + + // Add the view controller's view to the window and display. + [window addSubview:viewController.view]; + [window makeKeyAndVisible]; + + return YES; +} + + +- (void)applicationWillResignActive:(UIApplication *)application { + /* + Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. + */ +} + + +- (void)applicationDidEnterBackground:(UIApplication *)application { + /* + Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + If your application supports background execution, called instead of applicationWillTerminate: when the user quits. + */ +} + + +- (void)applicationWillEnterForeground:(UIApplication *)application { + /* + Called as part of transition from the background to the inactive state: here you can undo many of the changes made on entering the background. + */ +} + + +- (void)applicationDidBecomeActive:(UIApplication *)application { + /* + Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + */ +} + + +- (void)applicationWillTerminate:(UIApplication *)application { + /* + Called when the application is about to terminate. + See also applicationDidEnterBackground:. + */ +} + + +#pragma mark - +#pragma mark Memory management + +- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application { + /* + Free up as much memory as possible by purging cached data objects that can be recreated (or reloaded from disk) later. + */ +} + + +- (void)dealloc { + [viewController release]; + [window release]; + [super dealloc]; +} + + +@end diff --git a/iphone/examples/ReaderSample/Classes/ReaderSampleViewController.h b/iphone/examples/ReaderSample/Classes/ReaderSampleViewController.h new file mode 100644 --- /dev/null +++ b/iphone/examples/ReaderSample/Classes/ReaderSampleViewController.h @@ -0,0 +1,21 @@ +// +// ReaderSampleViewController.h +// ReaderSample +// +// Created by spadix on 8/4/10. +// + +#import + +@interface ReaderSampleViewController + : UIViewController + // ADD: delegate protocol + < ZBarReaderDelegate > +{ + UIImageView *resultImage; + UITextView *resultText; +} +@property (nonatomic, retain) IBOutlet UIImageView *resultImage; +@property (nonatomic, retain) IBOutlet UITextView *resultText; +- (IBAction) scanButtonTapped; +@end diff --git a/iphone/examples/ReaderSample/Classes/ReaderSampleViewController.m b/iphone/examples/ReaderSample/Classes/ReaderSampleViewController.m new file mode 100644 --- /dev/null +++ b/iphone/examples/ReaderSample/Classes/ReaderSampleViewController.m @@ -0,0 +1,68 @@ +// +// ReaderSampleViewController.m +// ReaderSample +// +// Created by spadix on 8/4/10. +// + +#import "ReaderSampleViewController.h" + +@implementation ReaderSampleViewController + +@synthesize resultImage, resultText; + +- (IBAction) scanButtonTapped +{ + // ADD: present a barcode reader that scans from the camera feed + ZBarReaderViewController *reader = [ZBarReaderViewController new]; + reader.readerDelegate = self; + + ZBarImageScanner *scanner = reader.scanner; + // TODO: (optional) additional reader configuration here + + // EXAMPLE: disable rarely used I2/5 to improve performance + [scanner setSymbology: ZBAR_I25 + config: ZBAR_CFG_ENABLE + to: 0]; + + // present and release the controller + [self presentModalViewController: reader + animated: YES]; + [reader release]; +} + +- (void) imagePickerController: (UIImagePickerController*) reader + didFinishPickingMediaWithInfo: (NSDictionary*) info +{ + // ADD: get the decode results + id results = + [info objectForKey: ZBarReaderControllerResults]; + ZBarSymbol *symbol = nil; + for(symbol in results) + // EXAMPLE: just grab the first barcode + break; + + // EXAMPLE: do something useful with the barcode data + resultText.text = symbol.data; + + // EXAMPLE: do something useful with the barcode image + resultImage.image = + [info objectForKey: UIImagePickerControllerOriginalImage]; + + // ADD: dismiss the controller (NB dismiss from the *reader*!) + [reader dismissModalViewControllerAnimated: YES]; +} + +- (void)didReceiveMemoryWarning { + // Releases the view if it doesn't have a superview. + [super didReceiveMemoryWarning]; + // Release any cached data, images, etc that aren't in use. +} + +- (void)dealloc { + self.resultImage = nil; + self.resultText = nil; + [super dealloc]; +} + +@end diff --git a/iphone/examples/ReaderSample/MainWindow.xib b/iphone/examples/ReaderSample/MainWindow.xib new file mode 100644 --- /dev/null +++ b/iphone/examples/ReaderSample/MainWindow.xib @@ -0,0 +1,444 @@ + + + + 1024 + 10D571 + 786 + 1038.29 + 460.00 + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + 112 + + + YES + + + + YES + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + YES + + YES + + + YES + + + + YES + + IBFilesOwner + IBCocoaTouchFramework + + + IBFirstResponder + IBCocoaTouchFramework + + + IBCocoaTouchFramework + + + ReaderSampleViewController + + + 1 + + IBCocoaTouchFramework + NO + + + + 292 + {320, 480} + + 1 + MSAxIDEAA + + NO + NO + + IBCocoaTouchFramework + YES + + + + + YES + + + delegate + + + + 4 + + + + viewController + + + + 11 + + + + window + + + + 14 + + + + + YES + + 0 + + + + + + -1 + + + File's Owner + + + 3 + + + ReaderSample App Delegate + + + -2 + + + + + 10 + + + + + 12 + + + + + + + YES + + YES + -1.CustomClassName + -2.CustomClassName + 10.CustomClassName + 10.IBEditorWindowLastContentRect + 10.IBPluginDependency + 12.IBEditorWindowLastContentRect + 12.IBPluginDependency + 3.CustomClassName + 3.IBPluginDependency + + + YES + UIApplication + UIResponder + ReaderSampleViewController + {{234, 376}, {320, 480}} + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + {{525, 346}, {320, 480}} + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + ReaderSampleAppDelegate + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + YES + + + YES + + + + + YES + + + YES + + + + 15 + + + + YES + + UIWindow + UIView + + IBUserSource + + + + + ReaderSampleAppDelegate + NSObject + + YES + + YES + viewController + window + + + YES + ReaderSampleViewController + UIWindow + + + + YES + + YES + viewController + window + + + YES + + viewController + ReaderSampleViewController + + + window + UIWindow + + + + + IBProjectSource + Classes/ReaderSampleAppDelegate.h + + + + ReaderSampleAppDelegate + NSObject + + IBUserSource + + + + + ReaderSampleViewController + UIViewController + + IBProjectSource + Classes/ReaderSampleViewController.h + + + + + YES + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSError.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSFileManager.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSKeyValueCoding.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSKeyValueObserving.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSKeyedArchiver.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSObject.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSRunLoop.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSThread.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSURL.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSURLConnection.h + + + + NSObject + + IBFrameworkSource + UIKit.framework/Headers/UIAccessibility.h + + + + NSObject + + IBFrameworkSource + UIKit.framework/Headers/UINibLoading.h + + + + NSObject + + IBFrameworkSource + UIKit.framework/Headers/UIResponder.h + + + + UIApplication + UIResponder + + IBFrameworkSource + UIKit.framework/Headers/UIApplication.h + + + + UIResponder + NSObject + + + + UISearchBar + UIView + + IBFrameworkSource + UIKit.framework/Headers/UISearchBar.h + + + + UISearchDisplayController + NSObject + + IBFrameworkSource + UIKit.framework/Headers/UISearchDisplayController.h + + + + UIView + + IBFrameworkSource + UIKit.framework/Headers/UITextField.h + + + + UIView + UIResponder + + IBFrameworkSource + UIKit.framework/Headers/UIView.h + + + + UIViewController + + IBFrameworkSource + UIKit.framework/Headers/UINavigationController.h + + + + UIViewController + + IBFrameworkSource + UIKit.framework/Headers/UIPopoverController.h + + + + UIViewController + + IBFrameworkSource + UIKit.framework/Headers/UISplitViewController.h + + + + UIViewController + + IBFrameworkSource + UIKit.framework/Headers/UITabBarController.h + + + + UIViewController + UIResponder + + IBFrameworkSource + UIKit.framework/Headers/UIViewController.h + + + + UIWindow + UIView + + IBFrameworkSource + UIKit.framework/Headers/UIWindow.h + + + + + 0 + IBCocoaTouchFramework + + com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS + + + + com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 + + + YES + ReaderSample.xcodeproj + 3 + 112 + + diff --git a/iphone/examples/ReaderSample/ReaderSample-Info.plist b/iphone/examples/ReaderSample/ReaderSample-Info.plist new file mode 100644 --- /dev/null +++ b/iphone/examples/ReaderSample/ReaderSample-Info.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + + CFBundleIdentifier + com.yourcompany.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + LSRequiresIPhoneOS + + NSMainNibFile + MainWindow + + diff --git a/iphone/examples/ReaderSample/ReaderSample.xcodeproj/project.pbxproj b/iphone/examples/ReaderSample/ReaderSample.xcodeproj/project.pbxproj new file mode 100755 --- /dev/null +++ b/iphone/examples/ReaderSample/ReaderSample.xcodeproj/project.pbxproj @@ -0,0 +1,386 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 45; + objects = { + +/* Begin PBXBuildFile section */ + 1D3623260D0F684500981E51 /* ReaderSampleAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D3623250D0F684500981E51 /* ReaderSampleAppDelegate.m */; }; + 1D60589B0D05DD56006BFB54 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; }; + 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; }; + 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; }; + 288765A50DF7441C002DB57D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 288765A40DF7441C002DB57D /* CoreGraphics.framework */; }; + 2899E5220DE3E06400AC0155 /* ReaderSampleViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2899E5210DE3E06400AC0155 /* ReaderSampleViewController.xib */; }; + 28AD733F0D9D9553002E5188 /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28AD733E0D9D9553002E5188 /* MainWindow.xib */; }; + 28D7ACF80DDB3853001CB0EB /* ReaderSampleViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 28D7ACF70DDB3853001CB0EB /* ReaderSampleViewController.m */; }; + DC3CEC791209F83E00D7A786 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC3CEC781209F83E00D7A786 /* AVFoundation.framework */; }; + DC3CEC7B1209F83E00D7A786 /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC3CEC7A1209F83E00D7A786 /* CoreMedia.framework */; }; + DC3CEC7D1209F83E00D7A786 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC3CEC7C1209F83E00D7A786 /* CoreVideo.framework */; }; + DC3CEC7F1209F83E00D7A786 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC3CEC7E1209F83E00D7A786 /* QuartzCore.framework */; }; + DC3CEC811209F83E00D7A786 /* libiconv.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = DC3CEC801209F83E00D7A786 /* libiconv.dylib */; }; + DC48C40C1219E1A20047193B /* libzbar.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC48C4061219E1A20047193B /* libzbar.a */; }; + DC48C40D1219E1A20047193B /* zbar-back.png in Resources */ = {isa = PBXBuildFile; fileRef = DC48C4081219E1A20047193B /* zbar-back.png */; }; + DC48C40E1219E1A20047193B /* zbar-help.html in Resources */ = {isa = PBXBuildFile; fileRef = DC48C4091219E1A20047193B /* zbar-help.html */; }; + DC48C40F1219E1A20047193B /* zbar-helpicons.png in Resources */ = {isa = PBXBuildFile; fileRef = DC48C40A1219E1A20047193B /* zbar-helpicons.png */; }; + DC48C4101219E1A20047193B /* zbar-samples.png in Resources */ = {isa = PBXBuildFile; fileRef = DC48C40B1219E1A20047193B /* zbar-samples.png */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 1D3623240D0F684500981E51 /* ReaderSampleAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReaderSampleAppDelegate.h; sourceTree = ""; }; + 1D3623250D0F684500981E51 /* ReaderSampleAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReaderSampleAppDelegate.m; sourceTree = ""; }; + 1D6058910D05DD3D006BFB54 /* ReaderSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ReaderSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 288765A40DF7441C002DB57D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 2899E5210DE3E06400AC0155 /* ReaderSampleViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ReaderSampleViewController.xib; sourceTree = ""; }; + 28AD733E0D9D9553002E5188 /* MainWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainWindow.xib; sourceTree = ""; }; + 28D7ACF60DDB3853001CB0EB /* ReaderSampleViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReaderSampleViewController.h; sourceTree = ""; }; + 28D7ACF70DDB3853001CB0EB /* ReaderSampleViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReaderSampleViewController.m; sourceTree = ""; }; + 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 32CA4F630368D1EE00C91783 /* ReaderSample_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReaderSample_Prefix.pch; sourceTree = ""; }; + 8D1107310486CEB800E47090 /* ReaderSample-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "ReaderSample-Info.plist"; plistStructureDefinitionIdentifier = "com.apple.xcode.plist.structure-definition.iphone.info-plist"; sourceTree = ""; }; + DC3CEC781209F83E00D7A786 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; + DC3CEC7A1209F83E00D7A786 /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; }; + DC3CEC7C1209F83E00D7A786 /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; }; + DC3CEC7E1209F83E00D7A786 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + DC3CEC801209F83E00D7A786 /* libiconv.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libiconv.dylib; path = usr/lib/libiconv.dylib; sourceTree = SDKROOT; }; + DC48C3F41219E1A20047193B /* Decoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Decoder.h; sourceTree = ""; }; + DC48C3F51219E1A20047193B /* Exception.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Exception.h; sourceTree = ""; }; + DC48C3F61219E1A20047193B /* Image.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Image.h; sourceTree = ""; }; + DC48C3F71219E1A20047193B /* ImageScanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageScanner.h; sourceTree = ""; }; + DC48C3F81219E1A20047193B /* Processor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Processor.h; sourceTree = ""; }; + DC48C3F91219E1A20047193B /* Scanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Scanner.h; sourceTree = ""; }; + DC48C3FA1219E1A20047193B /* Symbol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Symbol.h; sourceTree = ""; }; + DC48C3FB1219E1A20047193B /* Video.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Video.h; sourceTree = ""; }; + DC48C3FC1219E1A20047193B /* Window.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Window.h; sourceTree = ""; }; + DC48C3FD1219E1A20047193B /* zbar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = zbar.h; sourceTree = ""; }; + DC48C3FE1219E1A20047193B /* ZBarCaptureReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarCaptureReader.h; sourceTree = ""; }; + DC48C3FF1219E1A20047193B /* ZBarImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarImage.h; sourceTree = ""; }; + DC48C4001219E1A20047193B /* ZBarImageScanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarImageScanner.h; sourceTree = ""; }; + DC48C4011219E1A20047193B /* ZBarReaderController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarReaderController.h; sourceTree = ""; }; + DC48C4021219E1A20047193B /* ZBarReaderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarReaderView.h; sourceTree = ""; }; + DC48C4031219E1A20047193B /* ZBarReaderViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarReaderViewController.h; sourceTree = ""; }; + DC48C4041219E1A20047193B /* ZBarSDK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarSDK.h; sourceTree = ""; }; + DC48C4051219E1A20047193B /* ZBarSymbol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarSymbol.h; sourceTree = ""; }; + DC48C4061219E1A20047193B /* libzbar.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libzbar.a; sourceTree = ""; }; + DC48C4081219E1A20047193B /* zbar-back.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "zbar-back.png"; sourceTree = ""; }; + DC48C4091219E1A20047193B /* zbar-help.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "zbar-help.html"; sourceTree = ""; }; + DC48C40A1219E1A20047193B /* zbar-helpicons.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "zbar-helpicons.png"; sourceTree = ""; }; + DC48C40B1219E1A20047193B /* zbar-samples.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "zbar-samples.png"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1D60588F0D05DD3D006BFB54 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */, + 288765A50DF7441C002DB57D /* CoreGraphics.framework in Frameworks */, + 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */, + DC3CEC791209F83E00D7A786 /* AVFoundation.framework in Frameworks */, + DC3CEC7B1209F83E00D7A786 /* CoreMedia.framework in Frameworks */, + DC3CEC7D1209F83E00D7A786 /* CoreVideo.framework in Frameworks */, + DC3CEC7F1209F83E00D7A786 /* QuartzCore.framework in Frameworks */, + DC3CEC811209F83E00D7A786 /* libiconv.dylib in Frameworks */, + DC48C40C1219E1A20047193B /* libzbar.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 080E96DDFE201D6D7F000001 /* Classes */ = { + isa = PBXGroup; + children = ( + 1D3623240D0F684500981E51 /* ReaderSampleAppDelegate.h */, + 1D3623250D0F684500981E51 /* ReaderSampleAppDelegate.m */, + 28D7ACF60DDB3853001CB0EB /* ReaderSampleViewController.h */, + 28D7ACF70DDB3853001CB0EB /* ReaderSampleViewController.m */, + ); + path = Classes; + sourceTree = ""; + }; + 19C28FACFE9D520D11CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 1D6058910D05DD3D006BFB54 /* ReaderSample.app */, + ); + name = Products; + sourceTree = ""; + }; + 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { + isa = PBXGroup; + children = ( + 080E96DDFE201D6D7F000001 /* Classes */, + 29B97315FDCFA39411CA2CEA /* Other Sources */, + 29B97317FDCFA39411CA2CEA /* Resources */, + DC48C3F01219E1A20047193B /* ZBarSDK */, + 29B97323FDCFA39411CA2CEA /* Frameworks */, + 19C28FACFE9D520D11CA2CBB /* Products */, + ); + name = CustomTemplate; + sourceTree = ""; + }; + 29B97315FDCFA39411CA2CEA /* Other Sources */ = { + isa = PBXGroup; + children = ( + 32CA4F630368D1EE00C91783 /* ReaderSample_Prefix.pch */, + 29B97316FDCFA39411CA2CEA /* main.m */, + ); + name = "Other Sources"; + sourceTree = ""; + }; + 29B97317FDCFA39411CA2CEA /* Resources */ = { + isa = PBXGroup; + children = ( + 2899E5210DE3E06400AC0155 /* ReaderSampleViewController.xib */, + 28AD733E0D9D9553002E5188 /* MainWindow.xib */, + 8D1107310486CEB800E47090 /* ReaderSample-Info.plist */, + ); + name = Resources; + sourceTree = ""; + }; + 29B97323FDCFA39411CA2CEA /* Frameworks */ = { + isa = PBXGroup; + children = ( + 1D30AB110D05D00D00671497 /* Foundation.framework */, + 288765A40DF7441C002DB57D /* CoreGraphics.framework */, + 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */, + DC3CEC7E1209F83E00D7A786 /* QuartzCore.framework */, + DC3CEC781209F83E00D7A786 /* AVFoundation.framework */, + DC3CEC7A1209F83E00D7A786 /* CoreMedia.framework */, + DC3CEC7C1209F83E00D7A786 /* CoreVideo.framework */, + DC3CEC801209F83E00D7A786 /* libiconv.dylib */, + ); + name = Frameworks; + sourceTree = ""; + }; + DC48C3F01219E1A20047193B /* ZBarSDK */ = { + isa = PBXGroup; + children = ( + DC48C3F11219E1A20047193B /* Headers */, + DC48C4061219E1A20047193B /* libzbar.a */, + DC48C4071219E1A20047193B /* Resources */, + ); + path = ZBarSDK; + sourceTree = ""; + }; + DC48C3F11219E1A20047193B /* Headers */ = { + isa = PBXGroup; + children = ( + DC48C3F21219E1A20047193B /* ZBarSDK */, + ); + path = Headers; + sourceTree = ""; + }; + DC48C3F21219E1A20047193B /* ZBarSDK */ = { + isa = PBXGroup; + children = ( + DC48C3F31219E1A20047193B /* zbar */, + DC48C3FD1219E1A20047193B /* zbar.h */, + DC48C3FE1219E1A20047193B /* ZBarCaptureReader.h */, + DC48C3FF1219E1A20047193B /* ZBarImage.h */, + DC48C4001219E1A20047193B /* ZBarImageScanner.h */, + DC48C4011219E1A20047193B /* ZBarReaderController.h */, + DC48C4021219E1A20047193B /* ZBarReaderView.h */, + DC48C4031219E1A20047193B /* ZBarReaderViewController.h */, + DC48C4041219E1A20047193B /* ZBarSDK.h */, + DC48C4051219E1A20047193B /* ZBarSymbol.h */, + ); + path = ZBarSDK; + sourceTree = ""; + }; + DC48C3F31219E1A20047193B /* zbar */ = { + isa = PBXGroup; + children = ( + DC48C3F41219E1A20047193B /* Decoder.h */, + DC48C3F51219E1A20047193B /* Exception.h */, + DC48C3F61219E1A20047193B /* Image.h */, + DC48C3F71219E1A20047193B /* ImageScanner.h */, + DC48C3F81219E1A20047193B /* Processor.h */, + DC48C3F91219E1A20047193B /* Scanner.h */, + DC48C3FA1219E1A20047193B /* Symbol.h */, + DC48C3FB1219E1A20047193B /* Video.h */, + DC48C3FC1219E1A20047193B /* Window.h */, + ); + path = zbar; + sourceTree = ""; + }; + DC48C4071219E1A20047193B /* Resources */ = { + isa = PBXGroup; + children = ( + DC48C4081219E1A20047193B /* zbar-back.png */, + DC48C4091219E1A20047193B /* zbar-help.html */, + DC48C40A1219E1A20047193B /* zbar-helpicons.png */, + DC48C40B1219E1A20047193B /* zbar-samples.png */, + ); + path = Resources; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 1D6058900D05DD3D006BFB54 /* ReaderSample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "ReaderSample" */; + buildPhases = ( + 1D60588D0D05DD3D006BFB54 /* Resources */, + 1D60588E0D05DD3D006BFB54 /* Sources */, + 1D60588F0D05DD3D006BFB54 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ReaderSample; + productName = ReaderSample; + productReference = 1D6058910D05DD3D006BFB54 /* ReaderSample.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 29B97313FDCFA39411CA2CEA /* Project object */ = { + isa = PBXProject; + buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "ReaderSample" */; + compatibilityVersion = "Xcode 3.1"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + English, + Japanese, + French, + German, + ); + mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 1D6058900D05DD3D006BFB54 /* ReaderSample */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 1D60588D0D05DD3D006BFB54 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 28AD733F0D9D9553002E5188 /* MainWindow.xib in Resources */, + 2899E5220DE3E06400AC0155 /* ReaderSampleViewController.xib in Resources */, + DC48C40D1219E1A20047193B /* zbar-back.png in Resources */, + DC48C40E1219E1A20047193B /* zbar-help.html in Resources */, + DC48C40F1219E1A20047193B /* zbar-helpicons.png in Resources */, + DC48C4101219E1A20047193B /* zbar-samples.png in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 1D60588E0D05DD3D006BFB54 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1D60589B0D05DD56006BFB54 /* main.m in Sources */, + 1D3623260D0F684500981E51 /* ReaderSampleAppDelegate.m in Sources */, + 28D7ACF80DDB3853001CB0EB /* ReaderSampleViewController.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1D6058940D05DD3E006BFB54 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = ReaderSample_Prefix.pch; + INFOPLIST_FILE = "ReaderSample-Info.plist"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/ZBarSDK\"", + ); + PRODUCT_NAME = ReaderSample; + SYMROOT = /tmp/ReaderSample.build; + }; + name = Debug; + }; + 1D6058950D05DD3E006BFB54 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = ReaderSample_Prefix.pch; + INFOPLIST_FILE = "ReaderSample-Info.plist"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/ZBarSDK\"", + ); + PRODUCT_NAME = ReaderSample; + SYMROOT = /tmp/ReaderSample.build; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + C01FCF4F08A954540054247B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 4.0; + PREBINDING = NO; + SDKROOT = iphoneos; + }; + name = Debug; + }; + C01FCF5008A954540054247B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 4.0; + OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; + PREBINDING = NO; + SDKROOT = iphoneos; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "ReaderSample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1D6058940D05DD3E006BFB54 /* Debug */, + 1D6058950D05DD3E006BFB54 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C01FCF4E08A954540054247B /* Build configuration list for PBXProject "ReaderSample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C01FCF4F08A954540054247B /* Debug */, + C01FCF5008A954540054247B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; +} diff --git a/iphone/examples/ReaderSample/ReaderSampleViewController.xib b/iphone/examples/ReaderSample/ReaderSampleViewController.xib new file mode 100644 --- /dev/null +++ b/iphone/examples/ReaderSample/ReaderSampleViewController.xib @@ -0,0 +1,521 @@ + + + + 1024 + 10F569 + 788 + 1038.29 + 461.00 + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + 117 + + + YES + + + + YES + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + YES + + YES + + + YES + + + + YES + + IBFilesOwner + IBCocoaTouchFramework + + + IBFirstResponder + IBCocoaTouchFramework + + + + 274 + + YES + + + 292 + {{20, 392}, {280, 48}} + + NO + IBCocoaTouchFramework + 0 + 0 + + Helvetica-Bold + 18 + 16 + + 1 + Scan + + 3 + MQA + + + 1 + MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA + + + 3 + MC41AA + + + + + 292 + {{20, 20}, {280, 210}} + + 1 + NO + IBCocoaTouchFramework + + + + 292 + {{20, 238}, {280, 146}} + + + 3 + MCAwAA + + NO + YES + YES + IBCocoaTouchFramework + NO + No barcode scanned + + 2 + IBCocoaTouchFramework + + + + {320, 460} + + + 3 + MC43NQA + + 2 + + + NO + + IBCocoaTouchFramework + + + + + YES + + + view + + + + 7 + + + + resultImage + + + + 11 + + + + resultText + + + + 12 + + + + scanButtonTapped + + + 7 + + 13 + + + + + YES + + 0 + + + + + + -1 + + + File's Owner + + + -2 + + + + + 6 + + + YES + + + + + + + + 8 + + + + + 9 + + + + + 10 + + + + + + + YES + + YES + -1.CustomClassName + -2.CustomClassName + 10.IBPluginDependency + 6.IBEditorWindowLastContentRect + 6.IBPluginDependency + 8.IBPluginDependency + 9.IBPluginDependency + + + YES + ReaderSampleViewController + UIResponder + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + {{239, 276}, {320, 480}} + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + YES + + + YES + + + + + YES + + + YES + + + + 13 + + + + YES + + ReaderSampleViewController + UIViewController + + scanButtonTapped + id + + + scanButtonTapped + + scanButtonTapped + id + + + + YES + + YES + resultImage + resultText + + + YES + UIImageView + UITextView + + + + YES + + YES + resultImage + resultText + + + YES + + resultImage + UIImageView + + + resultText + UITextView + + + + + IBProjectSource + Classes/ReaderSampleViewController.h + + + + + YES + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSError.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSFileManager.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSKeyValueCoding.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSKeyValueObserving.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSKeyedArchiver.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSObject.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSRunLoop.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSThread.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSURL.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSURLConnection.h + + + + NSObject + + IBFrameworkSource + UIKit.framework/Headers/UIAccessibility.h + + + + NSObject + + IBFrameworkSource + UIKit.framework/Headers/UINibLoading.h + + + + NSObject + + IBFrameworkSource + UIKit.framework/Headers/UIResponder.h + + + + UIButton + UIControl + + IBFrameworkSource + UIKit.framework/Headers/UIButton.h + + + + UIControl + UIView + + IBFrameworkSource + UIKit.framework/Headers/UIControl.h + + + + UIImageView + UIView + + IBFrameworkSource + UIKit.framework/Headers/UIImageView.h + + + + UIResponder + NSObject + + + + UIScrollView + UIView + + IBFrameworkSource + UIKit.framework/Headers/UIScrollView.h + + + + UISearchBar + UIView + + IBFrameworkSource + UIKit.framework/Headers/UISearchBar.h + + + + UISearchDisplayController + NSObject + + IBFrameworkSource + UIKit.framework/Headers/UISearchDisplayController.h + + + + UITextView + UIScrollView + + IBFrameworkSource + UIKit.framework/Headers/UITextView.h + + + + UIView + + IBFrameworkSource + UIKit.framework/Headers/UITextField.h + + + + UIView + UIResponder + + IBFrameworkSource + UIKit.framework/Headers/UIView.h + + + + UIViewController + + IBFrameworkSource + UIKit.framework/Headers/UINavigationController.h + + + + UIViewController + + IBFrameworkSource + UIKit.framework/Headers/UIPopoverController.h + + + + UIViewController + + IBFrameworkSource + UIKit.framework/Headers/UISplitViewController.h + + + + UIViewController + + IBFrameworkSource + UIKit.framework/Headers/UITabBarController.h + + + + UIViewController + UIResponder + + IBFrameworkSource + UIKit.framework/Headers/UIViewController.h + + + + + 0 + IBCocoaTouchFramework + + com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS + + + + com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 + + + YES + ReaderSample.xcodeproj + 3 + 117 + + diff --git a/iphone/examples/ReaderSample/ReaderSample_Prefix.pch b/iphone/examples/ReaderSample/ReaderSample_Prefix.pch new file mode 100644 --- /dev/null +++ b/iphone/examples/ReaderSample/ReaderSample_Prefix.pch @@ -0,0 +1,11 @@ +// +// Prefix header for all source files of the 'ReaderSample' target in the 'ReaderSample' project +// + +#ifdef __OBJC__ +# import +# import + +// ADD: barcode reader APIs +# import "ZBarSDK.h" +#endif diff --git a/iphone/examples/ReaderSample/ZBarSDK b/iphone/examples/ReaderSample/ZBarSDK new file mode 120000 --- /dev/null +++ b/iphone/examples/ReaderSample/ZBarSDK @@ -0,0 +1,1 @@ +../../build/Release-iphoneos/ZBarSDK \ No newline at end of file diff --git a/iphone/examples/ReaderSample/build b/iphone/examples/ReaderSample/build new file mode 120000 --- /dev/null +++ b/iphone/examples/ReaderSample/build @@ -0,0 +1,1 @@ +/tmp/ReaderSample.build \ No newline at end of file diff --git a/iphone/examples/ReaderSample/main.m b/iphone/examples/ReaderSample/main.m new file mode 100644 --- /dev/null +++ b/iphone/examples/ReaderSample/main.m @@ -0,0 +1,15 @@ +// +// main.m +// ReaderSample +// +// Created by spadix on 8/4/10. +// + +#import + +int main(int argc, char *argv[]) { + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + int retVal = UIApplicationMain(argc, argv, nil, nil); + [pool release]; + return retVal; +} diff --git a/iphone/examples/readertest/ZBarSDK b/iphone/examples/readertest/ZBarSDK new file mode 120000 --- /dev/null +++ b/iphone/examples/readertest/ZBarSDK @@ -0,0 +1,1 @@ +../../build/Release-iphoneos/ZBarSDK \ No newline at end of file diff --git a/iphone/examples/readertest/build b/iphone/examples/readertest/build new file mode 120000 --- /dev/null +++ b/iphone/examples/readertest/build @@ -0,0 +1,1 @@ +/tmp/readertest.build \ No newline at end of file diff --git a/iphone/examples/readertest/entitlements.plist b/iphone/examples/readertest/entitlements.plist new file mode 100644 --- /dev/null +++ b/iphone/examples/readertest/entitlements.plist @@ -0,0 +1,8 @@ + + + + + get-task-allow + + + diff --git a/iphone/examples/readertest/prefix.pch b/iphone/examples/readertest/prefix.pch new file mode 100644 --- /dev/null +++ b/iphone/examples/readertest/prefix.pch @@ -0,0 +1,11 @@ +#ifdef __OBJC__ +# import +# import +# import +# import +# import +# import +# import +# import +# import "ZBarSDK.h" +#endif diff --git a/iphone/examples/readertest/readertest.m b/iphone/examples/readertest/readertest.m new file mode 100644 --- /dev/null +++ b/iphone/examples/readertest/readertest.m @@ -0,0 +1,829 @@ +#if 0 +# define VALGRIND "/usr/local/bin/valgrind" +#endif + +enum { + CLASS_SECTION = 0, + SOURCE_SECTION, + CAMODE_SECTION, + CONFIG_SECTION, + CUSTOM_SECTION, + SYMBOL_SECTION, + RESULT_SECTION, + NUM_SECTIONS +}; + +static NSString* const section_titles[] = { + @"Classes", + @"SourceType", + @"CameraMode", + @"Reader Configuration", + nil, + @"Enabled Symbologies", + @"Decode Results", +}; + +static const CGRect const crop_choices[] = { + { { 0, 0 }, { 1, 1 } }, + { { .125, 0 }, { .75, 1 } }, + { { 0, .3 }, { 1, .4 } }, + { { 0, 0 }, { 0, 0 } } +}; + +static const NSInteger const density_choices[] = { + 3, 2, 1, 0, 4, -1 +}; + +@interface AppDelegate + : UITableViewController + < UIApplicationDelegate, + UINavigationControllerDelegate, + UITableViewDelegate, + UITableViewDataSource, + UIActionSheetDelegate, + ZBarReaderDelegate > +{ + UIWindow *window; + UINavigationController *nav; + + NSMutableArray *sections, *symbolEnables; + + BOOL found, paused, continuous; + NSInteger dataHeight; + UILabel *typeLabel, *dataLabel; + UIImageView *imageView; + + ZBarReaderViewController *reader; + UIView *overlay; + UIBarButtonItem *manualBtn; + UILabel *typeOvl, *dataOvl; + NSArray *masks; +} + +@end + + +@implementation AppDelegate + +- (id) init +{ + // force these classes to load + [ZBarReaderViewController class]; + [ZBarReaderController class]; + + return([super initWithStyle: UITableViewStyleGrouped]); +} + +- (void) initReader: (NSString*) clsName +{ + [reader release]; + reader = [NSClassFromString(clsName) new]; + reader.readerDelegate = self; +} + +- (void) initOverlay +{ + overlay = [[UIView alloc] + initWithFrame: CGRectMake(0, 426, 320, 54)]; + overlay.backgroundColor = [UIColor clearColor]; + + masks = [[NSArray alloc] + initWithObjects: + [[[UIView alloc] + initWithFrame: CGRectMake(0, -426, 320, 0)] + autorelease], + [[[UIView alloc] + initWithFrame: CGRectMake(0, -426, 0, 426)] + autorelease], + [[[UIView alloc] + initWithFrame: CGRectMake(0, 0, 320, 0)] + autorelease], + [[[UIView alloc] + initWithFrame: CGRectMake(320, -426, 0, 426)] + autorelease], + nil]; + for(UIView *mask in masks) { + mask.backgroundColor = [UIColor colorWithWhite: 0 + alpha: .5]; + [overlay addSubview: mask]; + } + + UILabel *label = + [[UILabel alloc] + initWithFrame: CGRectMake(0, -426, 320, 48)]; + label.backgroundColor = [UIColor clearColor]; + label.textColor = [UIColor whiteColor]; + label.font = [UIFont boldSystemFontOfSize: 24]; + label.text = @"Custom Overlay"; + [overlay addSubview: label]; + [label release]; + + typeOvl = [[UILabel alloc] + initWithFrame: CGRectMake(0, -378, 80, 24)]; + typeOvl.backgroundColor = [UIColor clearColor]; + typeOvl.textColor = [UIColor whiteColor]; + typeOvl.font = [UIFont systemFontOfSize: 16]; + typeOvl.textAlignment = UITextAlignmentCenter; + [overlay addSubview: typeOvl]; + + dataOvl = [[UILabel alloc] + initWithFrame: CGRectMake(96, -378, 224, 24)]; + dataOvl.backgroundColor = [UIColor clearColor]; + dataOvl.textColor = [UIColor whiteColor]; + dataOvl.font = [UIFont systemFontOfSize: 16]; + [overlay addSubview: dataOvl]; + + UIToolbar *toolbar = + [[UIToolbar alloc] + initWithFrame: CGRectMake(0, 0, 320, 54)]; + toolbar.tintColor = [UIColor colorWithRed: .5 + green: 0 + blue: 0 + alpha: 1]; + [manualBtn release]; + manualBtn = [[UIBarButtonItem alloc] + initWithBarButtonSystemItem: UIBarButtonSystemItemCamera + target: self + action: @selector(manualCapture)]; + + toolbar.items = + [NSArray arrayWithObjects: + [[[UIBarButtonItem alloc] + initWithTitle: @"X" + style: UIBarButtonItemStylePlain + target: self + action: @selector(imagePickerControllerDidCancel:)] + autorelease], + [[[UIBarButtonItem alloc] + initWithBarButtonSystemItem: UIBarButtonSystemItemFlexibleSpace + target: nil + action: nil] + autorelease], + manualBtn, + [[[UIBarButtonItem alloc] + initWithBarButtonSystemItem: UIBarButtonSystemItemFlexibleSpace + target: nil + action: nil] + autorelease], + [[[UIBarButtonItem alloc] + initWithBarButtonSystemItem: UIBarButtonSystemItemPause + target: self + action: @selector(pause)] + autorelease], + [[[UIBarButtonItem alloc] + initWithBarButtonSystemItem: UIBarButtonSystemItemFlexibleSpace + target: nil + action: nil] + autorelease], + nil]; + [overlay addSubview: toolbar]; + [toolbar release]; + + + UIButton *info = + [UIButton buttonWithType: UIButtonTypeInfoLight]; + info.frame = CGRectMake(266, 0, 54, 54); + [info addTarget: self + action: @selector(info) + forControlEvents: UIControlEventTouchUpInside]; + [overlay addSubview: info]; +} + +- (void) setCheck: (BOOL) state + forCell: (UITableViewCell*) cell +{ + cell.accessoryType = + ((state) + ? UITableViewCellAccessoryCheckmark + : UITableViewCellAccessoryNone); +} + +- (void) setCheckForTag: (int) tag + inSection: (int) section +{ + for(UITableViewCell *cell in [sections objectAtIndex: section]) + [self setCheck: (cell.tag == tag) + forCell: cell]; +} + +- (void) setCheckForName: (NSString*) name + inSection: (int) section +{ + for(UITableViewCell *cell in [sections objectAtIndex: section]) + [self setCheck: [name isEqualToString: cell.textLabel.text] + forCell: cell]; +} + +- (void) applicationDidFinishLaunching: (UIApplication*) application +{ + self.title = @"ZBar Reader Test"; + + nav = [[UINavigationController alloc] + initWithRootViewController: self]; + nav.delegate = self; + + window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]]; + [window addSubview: nav.view]; + [window makeKeyAndVisible]; + + [self initReader: @"ZBarReaderViewController"]; +} + +- (UITableViewCell*) cellWithTitle: (NSString*) title + tag: (NSInteger) tag + checked: (BOOL) checked +{ + UITableViewCell *cell = [UITableViewCell new]; + cell.textLabel.text = title; + cell.tag = tag; + [self setCheck: checked + forCell: cell]; + return([cell autorelease]); +} + +- (void) initControlCells +{ + // NB don't need SourceTypeSavedPhotosAlbum + static NSString* const sourceNames[] = { + @"Library", @"Camera", @"Album", nil + }; + NSMutableArray *sources = [NSMutableArray array]; + for(int i = 0; sourceNames[i]; i++) + if([[reader class] isSourceTypeAvailable: i]) + [sources addObject: + [self cellWithTitle: sourceNames[i] + tag: i + checked: (reader.sourceType == i)]]; + [sections replaceObjectAtIndex: SOURCE_SECTION + withObject: sources]; + + static NSString* const modeNames[] = { + @"Default", @"Sampling", @"Sequence", nil + }; + NSMutableArray *modes = [NSMutableArray array]; + for(int i = 0; modeNames[i]; i++) + [modes addObject: + [self cellWithTitle: modeNames[i] + tag: i + checked: (reader.cameraMode == i)]]; + [sections replaceObjectAtIndex: CAMODE_SECTION + withObject: modes]; + + static NSString* const configNames[] = { + @"showsCameraControls", @"showsZBarControls", @"tracksSymbols", + @"enableCache", @"showsHelpOnFail", @"takesPicture", + nil + }; + NSMutableArray *configs = [NSMutableArray array]; + for(int i = 0; configNames[i]; i++) + @try { + BOOL checked = [[reader valueForKey: configNames[i]] boolValue]; + [configs addObject: + [self cellWithTitle: configNames[i] + tag: i + checked: checked]]; + } + @catch(...) { } + [sections replaceObjectAtIndex: CONFIG_SECTION + withObject: configs]; + + UITableViewCell *cropCell = + [[[UITableViewCell alloc] + initWithStyle: UITableViewCellStyleValue1 + reuseIdentifier: nil] + autorelease]; + cropCell.textLabel.text = @"scanCrop"; + cropCell.detailTextLabel.text = NSStringFromCGRect(crop_choices[0]); + + UITableViewCell *xDensityCell = + [[[UITableViewCell alloc] + initWithStyle: UITableViewCellStyleValue1 + reuseIdentifier: nil] + autorelease]; + xDensityCell.textLabel.text = @"CFG_X_DENSITY"; + xDensityCell.detailTextLabel.tag = ZBAR_CFG_X_DENSITY; + xDensityCell.detailTextLabel.text = + [NSString stringWithFormat: @"%d", density_choices[0]]; + + UITableViewCell *yDensityCell = + [[[UITableViewCell alloc] + initWithStyle: UITableViewCellStyleValue1 + reuseIdentifier: nil] + autorelease]; + yDensityCell.textLabel.text = @"CFG_Y_DENSITY"; + yDensityCell.detailTextLabel.tag = ZBAR_CFG_Y_DENSITY; + yDensityCell.detailTextLabel.text = + [NSString stringWithFormat: @"%d", density_choices[0]]; + + [sections replaceObjectAtIndex: CUSTOM_SECTION + withObject: [NSArray arrayWithObjects: + xDensityCell, + yDensityCell, + cropCell, + [self cellWithTitle: @"continuous" + tag: 1 + checked: continuous], + nil]]; + + static const int symbolValues[] = { + ZBAR_QRCODE, ZBAR_CODE128, ZBAR_CODE93, ZBAR_CODE39, ZBAR_I25, + ZBAR_DATABAR, ZBAR_DATABAR_EXP, + ZBAR_EAN13, ZBAR_EAN8, ZBAR_UPCA, ZBAR_UPCE, ZBAR_ISBN13, ZBAR_ISBN10, + 0 + }; + NSMutableArray *symbols = [NSMutableArray array]; + [symbolEnables release]; + symbolEnables = [[NSMutableArray alloc] init]; + BOOL en = YES; + for(int i = 0; symbolValues[i]; i++) { + en = en && (symbolValues[i] != ZBAR_UPCA); + [symbols addObject: + [self cellWithTitle: [ZBarSymbol nameForType: symbolValues[i]] + tag: symbolValues[i] + checked: en]]; + [symbolEnables addObject: [NSNumber numberWithBool: en]]; + } + [sections replaceObjectAtIndex: SYMBOL_SECTION + withObject: symbols]; + + [self.tableView reloadData]; +} + +- (void) viewDidLoad +{ + [super viewDidLoad]; + + UITableView *view = self.tableView; + view.delegate = self; + view.dataSource = self; + + [self initOverlay]; + + sections = [[NSMutableArray alloc] + initWithCapacity: NUM_SECTIONS]; + for(int i = 0; i < NUM_SECTIONS; i++) + [sections addObject: [NSNull null]]; + + NSArray *classes = + [NSArray arrayWithObjects: + [self cellWithTitle: @"ZBarReaderViewController" + tag: 0 + checked: YES], + [self cellWithTitle: @"ZBarReaderController" + tag: 1 + checked: NO], + nil]; + [sections replaceObjectAtIndex: CLASS_SECTION + withObject: classes]; + + UITableViewCell *typeCell = [UITableViewCell new]; + typeLabel = [typeCell.textLabel retain]; + UITableViewCell *dataCell = [UITableViewCell new]; + dataLabel = [dataCell.textLabel retain]; + dataLabel.numberOfLines = 0; + dataLabel.lineBreakMode = UILineBreakModeCharacterWrap; + UITableViewCell *imageCell = [UITableViewCell new]; + imageView = [UIImageView new]; + imageView.contentMode = UIViewContentModeScaleAspectFit; + imageView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | + UIViewAutoresizingFlexibleHeight); + UIView *content = imageCell.contentView; + imageView.frame = content.bounds; + [content addSubview: imageView]; + [imageView release]; + NSArray *results = + [NSArray arrayWithObjects: typeCell, dataCell, imageCell, nil]; + [sections replaceObjectAtIndex: RESULT_SECTION + withObject: results]; + + [self initControlCells]; +} + +- (void) viewDidUnload +{ + [sections release]; + sections = nil; + [symbolEnables release]; + symbolEnables = nil; + [manualBtn release]; + manualBtn = nil; + [typeLabel release]; + typeLabel = nil; + [dataLabel release]; + dataLabel = nil; + [imageView release]; + imageView = nil; + [typeOvl release]; + typeOvl = nil; + [dataOvl release]; + dataOvl = nil; + [overlay release]; + overlay = nil; + [masks release]; + masks = nil; + [super viewDidUnload]; +} + +- (void) dealloc +{ + [reader release]; + reader = nil; + [nav release]; + nav = nil; + [window release]; + window = nil; + [super dealloc]; +} + +- (void) scan +{ + found = paused = NO; + imageView.image = nil; + typeLabel.text = nil; + dataLabel.text = nil; + typeOvl.text = nil; + dataOvl.text = nil; + [self.tableView reloadData]; + if([reader respondsToSelector: @selector(readerView)]) + reader.readerView.showsFPS = YES; + if(reader.sourceType == UIImagePickerControllerSourceTypeCamera) + reader.cameraOverlayView = (reader.showsZBarControls) ? nil : overlay; + manualBtn.enabled = TARGET_IPHONE_SIMULATOR || + (reader.cameraMode == ZBarReaderControllerCameraModeDefault); + [self presentModalViewController: reader + animated: YES]; +} + +- (void) help +{ + ZBarHelpController *help = + [[ZBarHelpController alloc] + initWithReason: @"TEST"]; + [self presentModalViewController: help + animated: YES]; + [help release]; +} + +- (void) info +{ + [reader showHelpWithReason: @"INFO"]; +} + +- (void) pause +{ + if(![reader respondsToSelector: @selector(readerView)]) + return; + paused = !paused; + if(paused) + [reader.readerView stop]; + else + [reader.readerView start]; +} + +- (void) manualCapture +{ + [(UIImagePickerController*)reader takePicture]; +} + +// UINavigationControllerDelegate + +- (void) navigationController: (UINavigationController*) _nav + willShowViewController: (UIViewController*) vc + animated: (BOOL) animated +{ + self.navigationItem.leftBarButtonItem = + [[[UIBarButtonItem alloc] + initWithTitle: @"Help" + style: UIBarButtonItemStyleDone + target: self + action: @selector(help)] + autorelease]; + self.navigationItem.rightBarButtonItem = + [[[UIBarButtonItem alloc] + initWithTitle: @"Scan!" + style: UIBarButtonItemStyleDone + target: self + action: @selector(scan)] + autorelease]; +} + +// UITableViewDataSource + +- (NSInteger) numberOfSectionsInTableView: (UITableView*) view +{ + return(sections.count - !found); +} + +- (NSInteger) tableView: (UITableView*) view + numberOfRowsInSection: (NSInteger) idx +{ + NSArray *section = [sections objectAtIndex: idx]; + return(section.count); +} + +- (UITableViewCell*) tableView: (UITableView*) view + cellForRowAtIndexPath: (NSIndexPath*) path +{ + return([[sections objectAtIndex: path.section] + objectAtIndex: path.row]); +} + +- (NSString*) tableView: (UITableView*) view + titleForHeaderInSection: (NSInteger) idx +{ + assert(idx < NUM_SECTIONS); + return(section_titles[idx]); +} + +// UITableViewDelegate + +- (NSIndexPath*) tableView: (UITableView*) view + willSelectRowAtIndexPath: (NSIndexPath*) path +{ + if(path.section == RESULT_SECTION && path.row != 2) + return(nil); + return(path); +} + +- (void) alertUnsupported +{ + UIAlertView *alert = + [[UIAlertView alloc] + initWithTitle: @"Unsupported" + message: @"Setting not available for this reader" + @" (or with this OS on this device)" + delegate: nil + cancelButtonTitle: @"Cancel" + otherButtonTitles: nil]; + [alert show]; + [alert release]; +} + +- (void) advanceCrop: (UILabel*) label +{ + CGRect r = CGRectFromString(label.text); + int i; + for(i = 0; crop_choices[i].size.width;) + if(CGRectEqualToRect(r, crop_choices[i++])) + break; + if(!crop_choices[i].size.width) + i = 0; + r = crop_choices[i]; + reader.scanCrop = r; + label.text = NSStringFromCGRect(r); + + r.origin.x *= 426; + r.origin.y *= 320; + r.size.width *= 426; + r.size.height *= 320; + UIView *mask = [masks objectAtIndex: 0]; + mask.frame = CGRectMake(0, -426, 320, r.origin.x); + mask = [masks objectAtIndex: 1]; + mask.frame = CGRectMake(0, r.origin.x - 426, r.origin.y, r.size.width); + + r.origin.y += r.size.height; + mask = [masks objectAtIndex: 2]; + mask.frame = CGRectMake(r.origin.y, r.origin.x - 426, + 320 - r.origin.y, r.size.width); + + r.origin.x += r.size.width; + mask = [masks objectAtIndex: 3]; + mask.frame = CGRectMake(0, r.origin.x - 426, 320, 426 - r.origin.x); +} + +- (void) advanceDensity: (UILabel*) label +{ + NSInteger d = [label.text integerValue]; + int i; + for(i = 0; density_choices[i] >= 0;) + if(d == density_choices[i++]) + break; + if(density_choices[i] < 0) + i = 0; + d = density_choices[i]; + assert(d >= 0); + [reader.scanner setSymbology: 0 + config: label.tag + to: d]; + label.text = [NSString stringWithFormat: @"%d", d]; +} + +- (void) tableView: (UITableView*) view + didSelectRowAtIndexPath: (NSIndexPath*) path +{ + [view deselectRowAtIndexPath: path + animated: YES]; + + UITableViewCell *cell = [view cellForRowAtIndexPath: path]; + + switch(path.section) + { + case CLASS_SECTION: { + NSString *name = cell.textLabel.text; + [self initReader: name]; + [self initControlCells]; + [self setCheckForName: name + inSection: CLASS_SECTION]; + break; + } + + case SOURCE_SECTION: + [self setCheckForTag: reader.sourceType = cell.tag + inSection: SOURCE_SECTION]; + break; + + case CAMODE_SECTION: + @try { + reader.cameraMode = cell.tag; + } + @catch (...) { + [self alertUnsupported]; + } + [self setCheckForTag: reader.cameraMode + inSection: CAMODE_SECTION]; + break; + + case CONFIG_SECTION: { + BOOL state; + NSString *key = cell.textLabel.text; + state = ![[reader valueForKey: key] boolValue]; + @try { + [reader setValue: [NSNumber numberWithBool: state] + forKey: key]; + } + @catch (...) { + [self alertUnsupported]; + } + + // read back and update current state + state = [[reader valueForKey: key] boolValue]; + [self setCheck: state + forCell: cell]; + break; + } + + case CUSTOM_SECTION: + switch(path.row) + { + case 0: + case 1: + [self advanceDensity: cell.detailTextLabel]; + break; + case 2: + [self advanceCrop: cell.detailTextLabel]; + break; + case 3: + [self setCheck: continuous = !continuous + forCell: cell]; + break; + default: + assert(0); + } + break; + + case SYMBOL_SECTION: { + BOOL state = ![[symbolEnables objectAtIndex: path.row] boolValue]; + [symbolEnables replaceObjectAtIndex: path.row + withObject: [NSNumber numberWithBool: state]]; + [reader.scanner setSymbology: cell.tag + config: ZBAR_CFG_ENABLE + to: state]; + [self setCheck: state + forCell: cell]; + break; + } + case RESULT_SECTION: + if(path.row == 2) + [[[[UIActionSheet alloc] + initWithTitle: nil + delegate: self + cancelButtonTitle: @"Cancel" + destructiveButtonTitle: nil + otherButtonTitles: @"Save Image", nil] + autorelease] + showInView: self.view]; + break; + default: + assert(0); + } +} + +- (CGFloat) tableView: (UITableView*) view + heightForRowAtIndexPath: (NSIndexPath*) path +{ + if(path.section < RESULT_SECTION) + return(44); + + switch(path.row) { + case 0: return(44); + case 1: return(dataHeight); + case 2: return(300); + default: assert(0); + } + return(44); +} + +// UIActionSheetDelegate + +- (void) actionSheet: (UIActionSheet*) sheet + clickedButtonAtIndex: (NSInteger) idx +{ + if(idx == sheet.cancelButtonIndex) + return; + idx -= sheet.firstOtherButtonIndex; + if(!idx) + UIImageWriteToSavedPhotosAlbum(imageView.image, nil, NULL, NULL); +} + +// ZBarReaderDelegate + +- (void) imagePickerController: (UIImagePickerController*) picker + didFinishPickingMediaWithInfo: (NSDictionary*) info +{ + id results = + [info objectForKey: ZBarReaderControllerResults]; + assert(results); + + UIImage *image = [info objectForKey: UIImagePickerControllerOriginalImage]; + assert(image); + if(image) + imageView.image = image; + + int quality = 0; + ZBarSymbol *bestResult = nil; + for(ZBarSymbol *sym in results) + if(sym.quality > quality) + bestResult = sym; + assert(!!bestResult); + + [self performSelector: @selector(presentResult:) + withObject: bestResult + afterDelay: .001]; + if(!continuous) + [picker dismissModalViewControllerAnimated: YES]; +} + +- (void) presentResult: (ZBarSymbol*) sym +{ + found = !!sym; + NSString *typeName = sym.typeName; + typeLabel.text = typeName; + NSString *data = sym.data; + dataLabel.text = data; + + if(continuous) { + typeOvl.text = typeName; + dataOvl.text = data; + } + + NSLog(@"imagePickerController:didFinishPickingMediaWithInfo:\n"); + NSLog(@" type=%@ data=%@\n", sym.typeName, data); + + CGSize size = [data sizeWithFont: [UIFont systemFontOfSize: 17] + constrainedToSize: CGSizeMake(288, 2000) + lineBreakMode: UILineBreakModeCharacterWrap]; + dataHeight = size.height + 26; + if(dataHeight > 2000) + dataHeight = 2000; + + [self.tableView reloadData]; + [self.tableView scrollToRowAtIndexPath: + [NSIndexPath indexPathForRow: 0 + inSection: RESULT_SECTION] + atScrollPosition:UITableViewScrollPositionTop + animated: NO]; +} + +- (void) imagePickerControllerDidCancel: (UIImagePickerController*) picker +{ + NSLog(@"imagePickerControllerDidCancel:\n"); + [reader dismissModalViewControllerAnimated: YES]; +} + +- (void) readerControllerDidFailToRead: (ZBarReaderController*) _reader + withRetry: (BOOL) retry +{ + NSLog(@"readerControllerDidFailToRead: retry=%s\n", + (retry) ? "YES" : "NO"); + if(!retry) + [_reader dismissModalViewControllerAnimated: YES]; +} + +@end + + +int main (int argc, char *argv[]) +{ +#ifdef VALGRIND + if(argc < 2 || (argc >= 2 && strcmp(argv[1], "-valgrind"))) + execl(VALGRIND, VALGRIND, + "--log-file=/tmp/memcheck.log", "--leak-check=full", + argv[0], "-valgrind", + NULL); +#endif + + NSAutoreleasePool *pool = [NSAutoreleasePool new]; + int rc = UIApplicationMain(argc, argv, nil, @"AppDelegate"); + [pool release]; + return(rc); +} diff --git a/iphone/examples/readertest/readertest.plist b/iphone/examples/readertest/readertest.plist new file mode 100644 --- /dev/null +++ b/iphone/examples/readertest/readertest.plist @@ -0,0 +1,28 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + + CFBundleIdentifier + net.sourceforge.zbar.test.readertest + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + LSRequiresIPhoneOS + + + diff --git a/iphone/examples/readertest/readertest.xcodeproj/project.pbxproj b/iphone/examples/readertest/readertest.xcodeproj/project.pbxproj new file mode 100755 --- /dev/null +++ b/iphone/examples/readertest/readertest.xcodeproj/project.pbxproj @@ -0,0 +1,345 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 45; + objects = { + +/* Begin PBXBuildFile section */ + 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; }; + 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; }; + 288765FD0DF74451002DB57D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 288765FC0DF74451002DB57D /* CoreGraphics.framework */; }; + DC48C4D61219E5F70047193B /* libzbar.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC48C4D01219E5F70047193B /* libzbar.a */; }; + DC48C4D71219E5F70047193B /* zbar-back.png in Resources */ = {isa = PBXBuildFile; fileRef = DC48C4D21219E5F70047193B /* zbar-back.png */; }; + DC48C4D81219E5F70047193B /* zbar-help.html in Resources */ = {isa = PBXBuildFile; fileRef = DC48C4D31219E5F70047193B /* zbar-help.html */; }; + DC48C4D91219E5F70047193B /* zbar-helpicons.png in Resources */ = {isa = PBXBuildFile; fileRef = DC48C4D41219E5F70047193B /* zbar-helpicons.png */; }; + DC48C4DA1219E5F70047193B /* zbar-samples.png in Resources */ = {isa = PBXBuildFile; fileRef = DC48C4D51219E5F70047193B /* zbar-samples.png */; }; + DCB9118510BC5DA200B907F0 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCB9118410BC5DA200B907F0 /* QuartzCore.framework */; }; + DCB9118810BC5DB500B907F0 /* libiconv.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = DCD6E13B10B0AFD4002005CD /* libiconv.dylib */; }; + DCD6E0D010B0AD41002005CD /* readertest.m in Sources */ = {isa = PBXBuildFile; fileRef = DCD6E0CF10B0AD41002005CD /* readertest.m */; }; + DCDC6D9B11ACA23000021380 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCDC6D9A11ACA23000021380 /* CoreVideo.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + DCDC6D9F11ACA23900021380 /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCDC6D9E11ACA23900021380 /* CoreMedia.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + DCDC6DEC11ACA5B400021380 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCDC6DEB11ACA5B400021380 /* AVFoundation.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 1D6058910D05DD3D006BFB54 /* readertest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = readertest.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 288765FC0DF74451002DB57D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + DC48C4BE1219E5F70047193B /* Decoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Decoder.h; sourceTree = ""; }; + DC48C4BF1219E5F70047193B /* Exception.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Exception.h; sourceTree = ""; }; + DC48C4C01219E5F70047193B /* Image.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Image.h; sourceTree = ""; }; + DC48C4C11219E5F70047193B /* ImageScanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageScanner.h; sourceTree = ""; }; + DC48C4C21219E5F70047193B /* Processor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Processor.h; sourceTree = ""; }; + DC48C4C31219E5F70047193B /* Scanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Scanner.h; sourceTree = ""; }; + DC48C4C41219E5F70047193B /* Symbol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Symbol.h; sourceTree = ""; }; + DC48C4C51219E5F70047193B /* Video.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Video.h; sourceTree = ""; }; + DC48C4C61219E5F70047193B /* Window.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Window.h; sourceTree = ""; }; + DC48C4C71219E5F70047193B /* zbar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = zbar.h; sourceTree = ""; }; + DC48C4C81219E5F70047193B /* ZBarCaptureReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarCaptureReader.h; sourceTree = ""; }; + DC48C4C91219E5F70047193B /* ZBarImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarImage.h; sourceTree = ""; }; + DC48C4CA1219E5F70047193B /* ZBarImageScanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarImageScanner.h; sourceTree = ""; }; + DC48C4CB1219E5F70047193B /* ZBarReaderController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarReaderController.h; sourceTree = ""; }; + DC48C4CC1219E5F70047193B /* ZBarReaderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarReaderView.h; sourceTree = ""; }; + DC48C4CD1219E5F70047193B /* ZBarReaderViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarReaderViewController.h; sourceTree = ""; }; + DC48C4CE1219E5F70047193B /* ZBarSDK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarSDK.h; sourceTree = ""; }; + DC48C4CF1219E5F70047193B /* ZBarSymbol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarSymbol.h; sourceTree = ""; }; + DC48C4D01219E5F70047193B /* libzbar.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libzbar.a; sourceTree = ""; }; + DC48C4D21219E5F70047193B /* zbar-back.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "zbar-back.png"; sourceTree = ""; }; + DC48C4D31219E5F70047193B /* zbar-help.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "zbar-help.html"; sourceTree = ""; }; + DC48C4D41219E5F70047193B /* zbar-helpicons.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "zbar-helpicons.png"; sourceTree = ""; }; + DC48C4D51219E5F70047193B /* zbar-samples.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "zbar-samples.png"; sourceTree = ""; }; + DCB9118410BC5DA200B907F0 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + DCD6E0CE10B0AD41002005CD /* prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = prefix.pch; sourceTree = ""; }; + DCD6E0CF10B0AD41002005CD /* readertest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = readertest.m; sourceTree = ""; }; + DCD6E0D810B0AD55002005CD /* readertest.plist */ = {isa = PBXFileReference; explicitFileType = text.plist.info; fileEncoding = 4; path = readertest.plist; sourceTree = ""; }; + DCD6E13B10B0AFD4002005CD /* libiconv.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libiconv.dylib; path = usr/lib/libiconv.dylib; sourceTree = SDKROOT; }; + DCDC6D9A11ACA23000021380 /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; }; + DCDC6D9E11ACA23900021380 /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; }; + DCDC6DEB11ACA5B400021380 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1D60588F0D05DD3D006BFB54 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */, + 288765FD0DF74451002DB57D /* CoreGraphics.framework in Frameworks */, + 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */, + DCB9118510BC5DA200B907F0 /* QuartzCore.framework in Frameworks */, + DCDC6DEC11ACA5B400021380 /* AVFoundation.framework in Frameworks */, + DCDC6D9F11ACA23900021380 /* CoreMedia.framework in Frameworks */, + DCDC6D9B11ACA23000021380 /* CoreVideo.framework in Frameworks */, + DCB9118810BC5DB500B907F0 /* libiconv.dylib in Frameworks */, + DC48C4D61219E5F70047193B /* libzbar.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 19C28FACFE9D520D11CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 1D6058910D05DD3D006BFB54 /* readertest.app */, + ); + name = Products; + sourceTree = ""; + }; + 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { + isa = PBXGroup; + children = ( + DCD6E0CE10B0AD41002005CD /* prefix.pch */, + DCD6E0CF10B0AD41002005CD /* readertest.m */, + DC48C4BA1219E5F70047193B /* ZBarSDK */, + DC3CEAC61209C07400D7A786 /* Resources */, + 29B97323FDCFA39411CA2CEA /* Frameworks */, + 19C28FACFE9D520D11CA2CBB /* Products */, + ); + name = CustomTemplate; + sourceTree = ""; + }; + 29B97323FDCFA39411CA2CEA /* Frameworks */ = { + isa = PBXGroup; + children = ( + 1D30AB110D05D00D00671497 /* Foundation.framework */, + 288765FC0DF74451002DB57D /* CoreGraphics.framework */, + 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */, + DCB9118410BC5DA200B907F0 /* QuartzCore.framework */, + DCDC6DEB11ACA5B400021380 /* AVFoundation.framework */, + DCDC6D9A11ACA23000021380 /* CoreVideo.framework */, + DCDC6D9E11ACA23900021380 /* CoreMedia.framework */, + DCD6E13B10B0AFD4002005CD /* libiconv.dylib */, + ); + name = Frameworks; + sourceTree = ""; + }; + DC3CEAC61209C07400D7A786 /* Resources */ = { + isa = PBXGroup; + children = ( + DCD6E0D810B0AD55002005CD /* readertest.plist */, + ); + name = Resources; + sourceTree = ""; + }; + DC48C4BA1219E5F70047193B /* ZBarSDK */ = { + isa = PBXGroup; + children = ( + DC48C4BB1219E5F70047193B /* Headers */, + DC48C4D01219E5F70047193B /* libzbar.a */, + DC48C4D11219E5F70047193B /* Resources */, + ); + path = ZBarSDK; + sourceTree = ""; + }; + DC48C4BB1219E5F70047193B /* Headers */ = { + isa = PBXGroup; + children = ( + DC48C4BC1219E5F70047193B /* ZBarSDK */, + ); + path = Headers; + sourceTree = ""; + }; + DC48C4BC1219E5F70047193B /* ZBarSDK */ = { + isa = PBXGroup; + children = ( + DC48C4BD1219E5F70047193B /* zbar */, + DC48C4C71219E5F70047193B /* zbar.h */, + DC48C4C81219E5F70047193B /* ZBarCaptureReader.h */, + DC48C4C91219E5F70047193B /* ZBarImage.h */, + DC48C4CA1219E5F70047193B /* ZBarImageScanner.h */, + DC48C4CB1219E5F70047193B /* ZBarReaderController.h */, + DC48C4CC1219E5F70047193B /* ZBarReaderView.h */, + DC48C4CD1219E5F70047193B /* ZBarReaderViewController.h */, + DC48C4CE1219E5F70047193B /* ZBarSDK.h */, + DC48C4CF1219E5F70047193B /* ZBarSymbol.h */, + ); + path = ZBarSDK; + sourceTree = ""; + }; + DC48C4BD1219E5F70047193B /* zbar */ = { + isa = PBXGroup; + children = ( + DC48C4BE1219E5F70047193B /* Decoder.h */, + DC48C4BF1219E5F70047193B /* Exception.h */, + DC48C4C01219E5F70047193B /* Image.h */, + DC48C4C11219E5F70047193B /* ImageScanner.h */, + DC48C4C21219E5F70047193B /* Processor.h */, + DC48C4C31219E5F70047193B /* Scanner.h */, + DC48C4C41219E5F70047193B /* Symbol.h */, + DC48C4C51219E5F70047193B /* Video.h */, + DC48C4C61219E5F70047193B /* Window.h */, + ); + path = zbar; + sourceTree = ""; + }; + DC48C4D11219E5F70047193B /* Resources */ = { + isa = PBXGroup; + children = ( + DC48C4D21219E5F70047193B /* zbar-back.png */, + DC48C4D31219E5F70047193B /* zbar-help.html */, + DC48C4D41219E5F70047193B /* zbar-helpicons.png */, + DC48C4D51219E5F70047193B /* zbar-samples.png */, + ); + path = Resources; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 1D6058900D05DD3D006BFB54 /* readertest */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "readertest" */; + buildPhases = ( + 1D60588D0D05DD3D006BFB54 /* Resources */, + 1D60588E0D05DD3D006BFB54 /* Sources */, + 1D60588F0D05DD3D006BFB54 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = readertest; + productName = readertest; + productReference = 1D6058910D05DD3D006BFB54 /* readertest.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 29B97313FDCFA39411CA2CEA /* Project object */ = { + isa = PBXProject; + buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "readertest" */; + compatibilityVersion = "Xcode 3.1"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + English, + Japanese, + French, + German, + ); + mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 1D6058900D05DD3D006BFB54 /* readertest */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 1D60588D0D05DD3D006BFB54 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DC48C4D71219E5F70047193B /* zbar-back.png in Resources */, + DC48C4D81219E5F70047193B /* zbar-help.html in Resources */, + DC48C4D91219E5F70047193B /* zbar-helpicons.png in Resources */, + DC48C4DA1219E5F70047193B /* zbar-samples.png in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 1D60588E0D05DD3D006BFB54 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DCD6E0D010B0AD41002005CD /* readertest.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1D6058940D05DD3E006BFB54 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = prefix.pch; + INFOPLIST_FILE = readertest.plist; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/ZBarSDK\"", + ); + PRODUCT_NAME = readertest; + }; + name = Debug; + }; + 1D6058950D05DD3E006BFB54 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = prefix.pch; + INFOPLIST_FILE = readertest.plist; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/ZBarSDK\"", + ); + PRODUCT_NAME = readertest; + }; + name = Release; + }; + C01FCF4F08A954540054247B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 3.1; + PREBINDING = NO; + SDKROOT = iphoneos; + SYMROOT = /tmp/readertest.build; + }; + name = Debug; + }; + C01FCF5008A954540054247B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 3.1; + PREBINDING = NO; + SDKROOT = iphoneos; + SYMROOT = /tmp/readertest.build; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "readertest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1D6058940D05DD3E006BFB54 /* Debug */, + 1D6058950D05DD3E006BFB54 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C01FCF4E08A954540054247B /* Build configuration list for PBXProject "readertest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C01FCF4F08A954540054247B /* Debug */, + C01FCF5008A954540054247B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; +} diff --git a/iphone/include/ZBarSDK/ZBarCaptureReader.h b/iphone/include/ZBarSDK/ZBarCaptureReader.h new file mode 100644 --- /dev/null +++ b/iphone/include/ZBarSDK/ZBarCaptureReader.h @@ -0,0 +1,105 @@ +//------------------------------------------------------------------------ +// Copyright 2010 (c) Jeff Brown +// +// 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 +//------------------------------------------------------------------------ + +#import +#import "ZBarImageScanner.h" + +@class AVCaptureVideoDataOutput, AVCaptureOutput; +@class ZBarCaptureReader, ZBarCVImage; + +@protocol ZBarCaptureDelegate + +// called when a new barcode is detected. the image refers to the +// video buffer and must not be retained for long +- (void) captureReader: (ZBarCaptureReader*) captureReader + didReadNewSymbolsFromImage: (ZBarImage*) image; + +@optional +// called when a potential/uncertain barcode is detected. will also +// be called *after* captureReader:didReadNewSymbolsFromImage: +// when good barcodes are detected +- (void) captureReader: (ZBarCaptureReader*) captureReader + didTrackSymbols: (ZBarSymbolSet*) symbols; + +@end + +@interface ZBarCaptureReader + : NSObject +{ + AVCaptureVideoDataOutput *captureOutput; + id captureDelegate; + ZBarImageScanner *scanner; + CGRect scanCrop; + CGSize size; + CGFloat framesPerSecond; + BOOL enableCache; + + dispatch_queue_t queue; + ZBarImage *image; + ZBarCVImage *result; + int32_t running; + int framecnt; + unsigned width, height; + uint64_t t_frame, t_fps, t_scan; + CGFloat dt_frame; +} + +// supply a pre-configured image scanner +- (id) initWithImageScanner: (ZBarImageScanner*) imageScanner; + +// this must be called before the session is started +- (void) willStartRunning; + +// this must be called *before* the session is stopped +- (void) willStopRunning; + +// clear the internal result cache +- (void) flushCache; + +// the capture output. add this to an instance of AVCaptureSession +@property (nonatomic, readonly) AVCaptureOutput *captureOutput; + +// delegate is notified of decode results and symbol tracking. +@property (nonatomic, assign) id captureDelegate; + +// access to image scanner for configuration. +@property (nonatomic, readonly) ZBarImageScanner *scanner; + +// region of image to scan in normalized coordinates. +// NB horizontal crop currently ignored... +@property (nonatomic, assign) CGRect scanCrop; + +// size of video frames. +@property (nonatomic, readonly) CGSize size; + +// (quickly) gate the reader function without interrupting the video +// stream. also flushes the cache when enabled. defaults to *NO* +@property (nonatomic) BOOL enableReader; + +// current frame rate (for debug/optimization). +// only valid when running +@property (nonatomic, readonly) CGFloat framesPerSecond; + +@property (nonatomic) BOOL enableCache; + +@end diff --git a/iphone/include/ZBarSDK/ZBarHelpController.h b/iphone/include/ZBarSDK/ZBarHelpController.h new file mode 100644 --- /dev/null +++ b/iphone/include/ZBarSDK/ZBarHelpController.h @@ -0,0 +1,60 @@ +//------------------------------------------------------------------------ +// Copyright 2009-2010 (c) Jeff Brown +// +// 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 +//------------------------------------------------------------------------ + +#import + +@class ZBarHelpController; + +@protocol ZBarHelpDelegate +@optional + +- (void) helpControllerDidFinish: (ZBarHelpController*) help; + +@end + + +// failure dialog w/a few useful tips + +@interface ZBarHelpController : UIViewController + < UIWebViewDelegate, + UIAlertViewDelegate > +{ + NSString *reason; + id delegate; + UIWebView *webView; + UIToolbar *toolbar; + UIBarButtonItem *doneBtn, *backBtn, *space; + NSURL *linkURL; + NSUInteger orientations; +} + +@property (nonatomic, assign) id delegate; + +// designated initializer +- (id) initWithReason: (NSString*) reason; + +- (BOOL) isInterfaceOrientationSupported: (UIInterfaceOrientation) orientation; +- (void) setInterfaceOrientation: (UIInterfaceOrientation) orientation + supported: (BOOL) supported; + +@end diff --git a/iphone/include/ZBarSDK/ZBarImage.h b/iphone/include/ZBarSDK/ZBarImage.h new file mode 100644 --- /dev/null +++ b/iphone/include/ZBarSDK/ZBarImage.h @@ -0,0 +1,69 @@ +//------------------------------------------------------------------------ +// Copyright 2009 (c) Jeff Brown +// +// 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 +//------------------------------------------------------------------------ + +#import +#import "zbar.h" +#import "ZBarSymbol.h" + +#ifdef __cplusplus +using namespace zbar; +#endif + +// Obj-C wrapper for ZBar image + +@interface ZBarImage : NSObject +{ + zbar_image_t *zimg; + double t_convert; +} + +@property (nonatomic) unsigned long format; +@property (nonatomic) unsigned sequence; +@property (nonatomic) CGSize size; +@property (nonatomic) CGRect crop; +@property (readonly, nonatomic) const void *data; +@property (readonly, nonatomic) unsigned long dataLength; +@property (copy, nonatomic) ZBarSymbolSet *symbols; +@property (readonly, nonatomic) zbar_image_t *zbarImage; +@property (readonly, nonatomic) UIImage *UIImage; + +- (id) initWithImage: (zbar_image_t*) image; +- (id) initWithCGImage: (CGImageRef) image; +- (id) initWithCGImage: (CGImageRef) image + size: (CGSize) size; +- (id) initWithCGImage: (CGImageRef) image + crop: (CGRect) crop + size: (CGSize) size; + +- (void) setData: (const void*) data + withLength: (unsigned long) length; +- (UIImage*) UIImageWithOrientation: (UIImageOrientation) imageOrientation; +- (void) cleanup; + ++ (unsigned long) fourcc: (NSString*) format; + +#if 0 +- convertToFormat: (unsigned long) format; +#endif + +@end diff --git a/iphone/include/ZBarSDK/ZBarImageScanner.h b/iphone/include/ZBarSDK/ZBarImageScanner.h new file mode 100644 --- /dev/null +++ b/iphone/include/ZBarSDK/ZBarImageScanner.h @@ -0,0 +1,51 @@ +//------------------------------------------------------------------------ +// Copyright 2009 (c) Jeff Brown +// +// 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 +//------------------------------------------------------------------------ + +#import +#import "zbar.h" +#import "ZBarImage.h" + +#ifdef __cplusplus +using namespace zbar; +#endif + +// Obj-C wrapper for ZBar image scanner + +@interface ZBarImageScanner : NSObject +{ + zbar_image_scanner_t *scanner; +} + +@property (nonatomic) BOOL enableCache; +@property (readonly, nonatomic) ZBarSymbolSet *results; + +// decoder configuration +- (void) parseConfig: (NSString*) configStr; +- (void) setSymbology: (zbar_symbol_type_t) symbology + config: (zbar_config_t) config + to: (int) value; + +// image scanning interface +- (NSInteger) scanImage: (ZBarImage*) image; + +@end diff --git a/iphone/include/ZBarSDK/ZBarReaderController.h b/iphone/include/ZBarSDK/ZBarReaderController.h new file mode 100644 --- /dev/null +++ b/iphone/include/ZBarSDK/ZBarReaderController.h @@ -0,0 +1,142 @@ +//------------------------------------------------------------------------ +// Copyright 2009-2010 (c) Jeff Brown +// +// 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 +//------------------------------------------------------------------------ + +#import +#import "ZBarImageScanner.h" + +#ifdef __cplusplus +using namespace zbar; +#endif + +typedef enum { + // default interface provided by UIImagePickerController - user manually + // captures an image by pressing a button + ZBarReaderControllerCameraModeDefault = 0, + + // automatically scan by taking screenshots with UIGetScreenImage(). + // resolution is limited by the screen, so this is inappropriate for + // longer codes + ZBarReaderControllerCameraModeSampling, + + // automatically scan by rapidly taking pictures with takePicture. + // tradeoff resolution with frame rate by adjusting the crop, and size + // properties of the reader along with the density configs of the image + // scanner + ZBarReaderControllerCameraModeSequence, + +} ZBarReaderControllerCameraMode; + + +@class ZBarReaderController, ZBarHelpController; + +@protocol ZBarReaderDelegate +@optional + +// called when no barcode is found in an image selected by the user. +// if retry is NO, the delegate *must* dismiss the controller +- (void) readerControllerDidFailToRead: (ZBarReaderController*) reader + withRetry: (BOOL) retry; + +@end + + +@interface ZBarReaderController + : UIImagePickerController + < UINavigationControllerDelegate, + UIImagePickerControllerDelegate > +{ + ZBarImageScanner *scanner; + ZBarHelpController *help; + UIView *overlay, *boxView; + CALayer *boxLayer; + + UIToolbar *toolbar; + UIBarButtonItem *cancelBtn, *scanBtn, *space[3]; + UIButton *infoBtn; + + id readerDelegate; + BOOL showsZBarControls, showsHelpOnFail, takesPicture, enableCache; + ZBarReaderControllerCameraMode cameraMode; + CGRect scanCrop; + NSInteger maxScanDimension; + + BOOL hasOverlay, sampling; + uint64_t t_frame; + double dt_frame; + + ZBarSymbol *symbol; +} + +// access to configure image scanner +@property (readonly, nonatomic) ZBarImageScanner *scanner; + +// barcode result recipient (NB don't use delegate) +@property (nonatomic, assign) id readerDelegate; + +// whether to use alternate control set +@property (nonatomic) BOOL showsZBarControls; + +// whether to display helpful information when decoding fails +@property (nonatomic) BOOL showsHelpOnFail; + +// how to use the camera (when sourceType == Camera) +@property (nonatomic) ZBarReaderControllerCameraMode cameraMode; + +// whether to outline symbols with the green tracking box. +@property (nonatomic) BOOL tracksSymbols; + +// whether to automatically take a full picture when a barcode is detected +// (when cameraMode == Sampling) +@property (nonatomic) BOOL takesPicture; + +// whether to use the "cache" for realtime modes (default YES). this can be +// used to safely disable the inter-frame consistency and duplicate checks, +// speeding up recognition, iff: +// 1. the controller is dismissed when a barcode is read and +// 2. unreliable symbologies are disabled (all EAN/UPC variants and I2/5) +@property (nonatomic) BOOL enableCache; + +// crop images for scanning. the original image will be cropped to this +// rectangle before scanning. the rectangle is normalized to the image size +// and aspect ratio; useful values will place the rectangle between 0 and 1 +// on each axis, where the x-axis corresponds to the image major axis. +// defaults to the full image (0, 0, 1, 1). +@property (nonatomic) CGRect scanCrop; + +// scale image to scan. after cropping, the image will be scaled if +// necessary, such that neither of its dimensions exceed this value. +// defaults to 640. +@property (nonatomic) NSInteger maxScanDimension; + +// display the built-in help browser. for use with custom overlays if +// you don't also want to create your own help view. only send this +// message when the reader is displayed. the argument will be passed +// to the onZBarHelp() javascript function. +- (void) showHelpWithReason: (NSString*) reason; + +// direct scanner interface - scan UIImage and return something enumerable +- (id ) scanImage: (CGImageRef) image; + +@end + +extern NSString* const ZBarReaderControllerResults; diff --git a/iphone/include/ZBarSDK/ZBarReaderView.h b/iphone/include/ZBarSDK/ZBarReaderView.h new file mode 100644 --- /dev/null +++ b/iphone/include/ZBarSDK/ZBarReaderView.h @@ -0,0 +1,118 @@ +//------------------------------------------------------------------------ +// Copyright 2010 (c) Jeff Brown +// +// 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 +//------------------------------------------------------------------------ + +#import +#import "ZBarImageScanner.h" + +@class AVCaptureSession, AVCaptureDevice; +@class CALayer; +@class ZBarImageScanner, ZBarCaptureReader, ZBarReaderView; + +// delegate is notified of decode results. + +@protocol ZBarReaderViewDelegate < NSObject > + +- (void) readerView: (ZBarReaderView*) readerView + didReadSymbols: (ZBarSymbolSet*) symbols + fromImage: (UIImage*) image; + +@end + +// read barcodes from the displayed video preview. the view maintains +// a complete video capture session feeding a ZBarCaptureReader and +// presents the associated preview with symbol tracking annotations. + +@interface ZBarReaderView + : UIView +{ + id readerDelegate; + CGRect scanCrop, zoomCrop; + CGAffineTransform previewTransform; + CGFloat zoom, zoom0; + BOOL tracksSymbols, showsFPS; + NSInteger torchMode; + + CALayer *preview, *overlay, *tracking; + UIView *fpsView; + UILabel *fpsLabel; + UIPinchGestureRecognizer *pinch; + CGFloat imageScale; + BOOL started, running; +} + +// supply a pre-configured image scanner. +- (id) initWithImageScanner: (ZBarImageScanner*) imageScanner; + +// start the video stream and barcode reader. +- (void) start; + +// stop the video stream and barcode reader. +- (void) stop; + +// clear the internal result cache +- (void) flushCache; + +// delegate is notified of decode results. +@property (nonatomic, assign) id readerDelegate; + +// access to image scanner for configuration. +@property (nonatomic, readonly) ZBarImageScanner *scanner; + +// whether to display the tracking annotation for uncertain barcodes +// (default YES). +@property (nonatomic) BOOL tracksSymbols; + +// enable pinch gesture recognition for zooming the preview/decode +// (default YES). +@property (nonatomic) BOOL allowsPinchZoom; + +// torch mode to set automatically (default Auto). +@property (nonatomic) NSInteger torchMode; + +// whether to display the frame rate for debug/configuration +// (default NO). +@property (nonatomic) BOOL showsFPS; + +// zoom scale factor applied to video preview *and* scanCrop. +// also updated by pinch-zoom gesture. clipped to range [1,2], +// defaults to 1.25 +@property (nonatomic) CGFloat zoom; + +// the region of the image that will be scanned. normalized coordinates. +@property (nonatomic) CGRect scanCrop; + +// additional transform applied to video preview. +// (NB *not* applied to scan crop) +@property (nonatomic) CGAffineTransform previewTransform; + +// specify an alternate capture device. +@property (nonatomic, retain) AVCaptureDevice *device; + +// direct access to the capture session. warranty void if opened... +@property (nonatomic, readonly) AVCaptureSession *session; +@property (nonatomic, readonly) ZBarCaptureReader *captureReader; + +// this flag still works, but its use is deprecated +@property (nonatomic) BOOL enableCache; + +@end diff --git a/iphone/include/ZBarSDK/ZBarReaderViewController.h b/iphone/include/ZBarSDK/ZBarReaderViewController.h new file mode 100644 --- /dev/null +++ b/iphone/include/ZBarSDK/ZBarReaderViewController.h @@ -0,0 +1,100 @@ +//------------------------------------------------------------------------ +// Copyright 2010 (c) Jeff Brown +// +// 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 +//------------------------------------------------------------------------ + +#import +#import "ZBarReaderController.h" + +@class ZBarReaderView; + +// drop in video scanning replacement for ZBarReaderController. +// this is a thin controller around a ZBarReaderView that adds the UI +// controls and select functionality offered by ZBarReaderController. +// Automatically falls back to a ZBarReaderController if video APIs +// are unavailable (eg for OS < 4.0) + +@interface ZBarReaderViewController + : UIViewController +{ + ZBarImageScanner *scanner; + id readerDelegate; + ZBarReaderView *readerView; + UIView *cameraOverlayView; + CGAffineTransform cameraViewTransform; + CGRect scanCrop; + BOOL showsZBarControls, tracksSymbols, enableCache; + + UIView *controls; + BOOL didHideStatusBar; +} + +// access to configure image scanner +@property (nonatomic, readonly) ZBarImageScanner *scanner; + +// barcode result recipient +@property (nonatomic, assign) id readerDelegate; + +// whether to use alternate control set +@property (nonatomic) BOOL showsZBarControls; + +// whether to show the green tracking box. note that, even when +// enabled, the box will only be visible when scanning EAN and I2/5. +@property (nonatomic) BOOL tracksSymbols; + +// crop images for scanning. the image will be cropped to this +// rectangle before scanning. the rectangle is normalized to the +// image size and aspect ratio; useful values will place the rectangle +// between 0 and 1 on each axis, where the x-axis corresponds to the +// image major axis. defaults to the full image (0, 0, 1, 1). +@property (nonatomic) CGRect scanCrop; + +// provide a custom overlay. note that this can be used with +// showsZBarControls enabled (but not if you want backward compatibility) +@property (nonatomic, retain) UIView *cameraOverlayView; + +// transform applied to the preview image. +@property (nonatomic) CGAffineTransform cameraViewTransform; + +// display the built-in help browser. the argument will be passed to +// the onZBarHelp() javascript function. +- (void) showHelpWithReason: (NSString*) reason; + +// direct access to the ZBarReaderView +@property (nonatomic, readonly) ZBarReaderView *readerView; + +// this flag still works, but its use is deprecated +@property (nonatomic) BOOL enableCache; + +// these are present only for backward compatibility. +// they will error if inappropriate/unsupported values are set +@property (nonatomic) UIImagePickerControllerSourceType sourceType; // Camera +@property (nonatomic) BOOL allowsEditing; // NO +@property (nonatomic) BOOL allowsImageEditing; // NO +@property (nonatomic) BOOL showsCameraControls; // NO +@property (nonatomic) BOOL showsHelpOnFail; // ignored +@property (nonatomic) ZBarReaderControllerCameraMode cameraMode; // Sampling +@property (nonatomic) BOOL takesPicture; // NO +@property (nonatomic) NSInteger maxScanDimension; // ignored + ++ (BOOL) isSourceTypeAvailable: (UIImagePickerControllerSourceType) sourceType; + +@end diff --git a/iphone/include/ZBarSDK/ZBarSDK.h b/iphone/include/ZBarSDK/ZBarSDK.h new file mode 100644 --- /dev/null +++ b/iphone/include/ZBarSDK/ZBarSDK.h @@ -0,0 +1,33 @@ +/*------------------------------------------------------------------------ + * Copyright 2010 (c) Jeff Brown + * + * 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 + *------------------------------------------------------------------------*/ + +#import "zbar.h" + +#import "ZBarSymbol.h" +#import "ZBarImage.h" +#import "ZBarImageScanner.h" +#import "ZBarReaderView.h" +#import "ZBarReaderViewController.h" +#import "ZBarReaderController.h" +#import "ZBarCaptureReader.h" +#import "ZBarHelpController.h" diff --git a/iphone/include/ZBarSDK/ZBarSymbol.h b/iphone/include/ZBarSDK/ZBarSymbol.h new file mode 100644 --- /dev/null +++ b/iphone/include/ZBarSDK/ZBarSymbol.h @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------ +// Copyright 2009-2010 (c) Jeff Brown +// +// 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 +//------------------------------------------------------------------------ + +#import +#import +#import "zbar.h" + +#ifdef __cplusplus +using namespace zbar; +#endif + +// Obj-C wrapper for ZBar result types + +@interface ZBarSymbolSet + : NSObject +{ + const zbar_symbol_set_t *set; + BOOL filterSymbols; +} + +@property (readonly, nonatomic) int count; +@property (readonly, nonatomic) const zbar_symbol_set_t *zbarSymbolSet; +@property (nonatomic) BOOL filterSymbols; + +- (id) initWithSymbolSet: (const zbar_symbol_set_t*) set; + +@end + + +@interface ZBarSymbol : NSObject +{ + const zbar_symbol_t *symbol; +} + +@property (readonly, nonatomic) zbar_symbol_type_t type; +@property (readonly, nonatomic) NSString *typeName; +@property (readonly, nonatomic) NSUInteger configMask; +@property (readonly, nonatomic) NSUInteger modifierMask; +@property (readonly, nonatomic) NSString *data; +@property (readonly, nonatomic) int quality; +@property (readonly, nonatomic) int count; +@property (readonly, nonatomic) zbar_orientation_t orientation; +@property (readonly, nonatomic) ZBarSymbolSet *components; +@property (readonly, nonatomic) const zbar_symbol_t *zbarSymbol; +@property (readonly, nonatomic) CGRect bounds; + +- (id) initWithSymbol: (const zbar_symbol_t*) symbol; + ++ (NSString*) nameForType: (zbar_symbol_type_t) type; + +@end diff --git a/iphone/include/config.h b/iphone/include/config.h new file mode 100644 --- /dev/null +++ b/iphone/include/config.h @@ -0,0 +1,231 @@ +/* manually customized for iPhone platform */ + +/* whether to build support for Code 128 symbology */ +#define ENABLE_CODE128 1 + +/* whether to build support for Code 93 symbology */ +#define ENABLE_CODE93 1 + +/* whether to build support for Code 39 symbology */ +#define ENABLE_CODE39 1 + +/* whether to build support for DataBar symbology */ +#define ENABLE_DATABAR 1 + +/* whether to build support for EAN symbologies */ +#define ENABLE_EAN 1 + +/* whether to build support for Interleaved 2 of 5 symbology */ +#define ENABLE_I25 1 + +/* whether to build support for PDF417 symbology */ +#undef ENABLE_PDF417 + +/* whether to build support for QR Code */ +#define ENABLE_QRCODE 1 + +/* Define to 1 if you have the `atexit' function. */ +#undef HAVE_ATEXIT + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_FEATURES_H + +/* Define to 1 if you have the `getpagesize' function. */ +#undef HAVE_GETPAGESIZE + +/* Define if you have the iconv() function and it works. */ +#undef HAVE_ICONV + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_JPEGLIB_H + +/* Define to 1 if you have the `jpeg' library (-ljpeg). */ +#undef HAVE_LIBJPEG + +/* Define to 1 if you have the `pthread' library (-lpthread). */ +#undef HAVE_LIBPTHREAD + +/* Define to 1 if you have the header file. */ +#undef HAVE_LINUX_VIDEODEV2_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_LINUX_VIDEODEV_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `memset' function. */ +#define HAVE_MEMSET 1 + +/* Define to 1 if you have a working `mmap' system call. */ +#undef HAVE_MMAP + +/* Define to 1 if you have the header file. */ +#undef HAVE_POLL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_PTHREAD_H + +/* Define to 1 if you have the `setenv' function. */ +#undef HAVE_SETENV + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_IPC_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_MMAN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SHM_H + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIMES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if the system has the type `uintptr_t'. */ +#define HAVE_UINTPTR_T 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_VFW_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_X11_EXTENSIONS_XSHM_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_X11_EXTENSIONS_XVLIB_H + +/* Define as const if the declaration of iconv() needs const. */ +#undef ICONV_CONST + +/* Library major version */ +#define LIB_VERSION_MAJOR 0 + +/* Library minor version */ +#define LIB_VERSION_MINOR 2 + +/* Library revision */ +#define LIB_VERSION_REVISION 0 + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#undef LT_OBJDIR + +/* Define to 1 if assertions should be disabled. */ +//#undef NDEBUG + +/* Define to 1 if your C compiler doesn't accept -c and -o together. */ +#undef NO_MINUS_C_MINUS_O + +/* Name of package */ +#define PACKAGE "zbar" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "spadix@users.sourceforge.net" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "zbar" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "zbar 0.10" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "zbar" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "0.10" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "0.10" + +/* Define to 1 if the X Window System is missing or not being used. */ +#define X_DISPLAY_MISSING 1 + +/* Program major version (before the '.') as a number */ +#define ZBAR_VERSION_MAJOR 0 + +/* Program minor version (after '.') as a number */ +#define ZBAR_VERSION_MINOR 10 + +/* Define for Solaris 2.5.1 so the uint32_t typedef from , + , or is not used. If the typedef were allowed, the + #define below would cause a syntax error. */ +#undef _UINT32_T + +/* Define for Solaris 2.5.1 so the uint8_t typedef from , + , or is not used. If the typedef were allowed, the + #define below would cause a syntax error. */ +#undef _UINT8_T + +/* Minimum Windows API version */ +#undef _WIN32_WINNT + +/* used only for pthread debug attributes */ +#undef __USE_UNIX98 + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#undef inline +#endif + +/* Define to the type of a signed integer type of width exactly 32 bits if + such a type exists and the standard includes do not define it. */ +#undef int32_t + +/* Define to the type of an unsigned integer type of width exactly 32 bits if + such a type exists and the standard includes do not define it. */ +#undef uint32_t + +/* Define to the type of an unsigned integer type of width exactly 8 bits if + such a type exists and the standard includes do not define it. */ +#undef uint8_t + +/* Define to the type of an unsigned integer type wide enough to hold a + pointer, if such a type exists, and if the system does not define it. */ +#undef uintptr_t + +#ifndef X_DISPLAY_MISSING +# define HAVE_X +#endif + diff --git a/iphone/include/prefix.pch b/iphone/include/prefix.pch new file mode 100644 --- /dev/null +++ b/iphone/include/prefix.pch @@ -0,0 +1,10 @@ +#ifdef __OBJC__ +# import +# import +# import +# import +# import +# import +# import +# import +#endif diff --git a/iphone/res/ZBarSDK-Info.plist b/iphone/res/ZBarSDK-Info.plist new file mode 100644 --- /dev/null +++ b/iphone/res/ZBarSDK-Info.plist @@ -0,0 +1,16 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleName + ZBarSDK + CFBundleVersion + 1.0.1 + CFBundleSignature + ???? + NSHumanReadableCopyright + Copyright 2010 © Jeff Brown et al + + diff --git a/iphone/res/ZBarSDK-bg.png b/iphone/res/ZBarSDK-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..5c633ace7cfc63684aed03aa53c71c7b752eb317 GIT binary patch literal 23569 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 VH=rocyi2gz*3&|1Rv_)e{|ElIn@s=! diff --git a/iphone/res/ZBarSDK-bg.svg b/iphone/res/ZBarSDK-bg.svg new file mode 100644 --- /dev/null +++ b/iphone/res/ZBarSDK-bg.svg @@ -0,0 +1,78 @@ + + + + + + + + ZBar iPhone SDK DMG Folder Background Image + + + + + + + + + + + + + + + + + + + + + + + Read this first + + + Then drag this + into your + Xcode project + + + + + + diff --git a/iphone/res/ZBarSDK.DS_Store b/iphone/res/ZBarSDK.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..6e71b2f4e8aa91b36e4a25535611f49c4e466a9a GIT binary patch literal 8196 zc%1E5TTc@~6h1Spbh$_u6fcmNMwFPS2pElGjHT2_1JcG4X%Qv6?Xcaj-D!5W;3Y)j zi?93*6JLGx5BO?)Gx`&Z@g2{0r_yf47hg2YmrTE%IhQ#z=j==u07$A{o&-=}zo~ur z^BN%03jwHLzfVvO5)u*;5)u;sJt6@q{{4Pl-2W015{C;Un$D+F(D;*ouoAYU;GKYp z7^i}xA^n8lq~M)^iI7vlQNfB}Rd7OZQt(c|B1l4;!3$Odi_a925S;wOq9h~^H-c~b zaed3J=@!?q_eQT-rt3{itS*~&nQx_d-7dRp>Zunmu1!u|N=#1O*uInqp08h>*xp{v zn`NeJ&azqdDk?n6SNN6~QW~8l&MoXbZU{WhtGg-9X-IOKS3$`qpwX_FLLx^H%Fx8^ zJM#;Lbtj0h`NBhw;BLV$gV4>=rU;#WXokL~#$9xuDRDdJuuYR~DQoI+T`O%Ej=!8) zZgJ{Ag+RZ}U#6-VY{oJ*H#6M5FLXQ&WdTK$M-{@4ow}$^EU=7 zkOB=H$U_?Dp&!m*YahD|Iv6kxHL#HfVdP4S(<`0p>1`2%A6>!3>;zkyYi{YWP>NDk zosHi4+7u7^natKaliTh?=D5gMZ-!%*v+L_F^NNoxO=nitPI1rU)$WEUU(-saZHy+1 zzubgodBDm=&2co_V5w(IHn&-e&%zfqj^%kI*4=wFJ}`Lv#L3enLb{ZmzJ4`wY$$Rn zf*02?jKCD!f;m`*B5uqEydrPOJMx}7k&|X|j|d(X z<8^2h_onMv9#)`PvNypm{aL7B68tS^umBv4jKxdh&Gm1-9W>kB+xt}jF|^1+4x`Gz zBHZiLrqH5&6jRHg1a+vQe;YiU%L5a~>~>2Mwj4az5{smQ&sDHOJ|GmU0(apl+EuZr zEcD`b&KX+U0 + + + + + + + rounded button overlay + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iphone/res/buttonmask.svg b/iphone/res/buttonmask.svg new file mode 100644 --- /dev/null +++ b/iphone/res/buttonmask.svg @@ -0,0 +1,26 @@ + + + + + + + + mask for button overlays + + + + + + + diff --git a/iphone/res/buttonup.svg b/iphone/res/buttonup.svg new file mode 100644 --- /dev/null +++ b/iphone/res/buttonup.svg @@ -0,0 +1,57 @@ + + + + + + + + rounded button overlay + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iphone/res/lightbulb.svg b/iphone/res/lightbulb.svg new file mode 100644 --- /dev/null +++ b/iphone/res/lightbulb.svg @@ -0,0 +1,108 @@ + + + + + + + + lit light bulb icon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iphone/res/shakyhand.svg b/iphone/res/shakyhand.svg new file mode 100644 --- /dev/null +++ b/iphone/res/shakyhand.svg @@ -0,0 +1,108 @@ + + + + + + + + unstable hand icon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iphone/res/shakyphone.svg b/iphone/res/shakyphone.svg new file mode 100644 --- /dev/null +++ b/iphone/res/shakyphone.svg @@ -0,0 +1,51 @@ + + + + + + + + iPhone shake gesture icon + + + + + + + + + + + + + + + + + + + + + + diff --git a/iphone/res/stopwatch.svg b/iphone/res/stopwatch.svg new file mode 100644 --- /dev/null +++ b/iphone/res/stopwatch.svg @@ -0,0 +1,88 @@ + + + + + + + + stopwatch icon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iphone/res/zbar-back.png b/iphone/res/zbar-back.png new file mode 100644 index 0000000000000000000000000000000000000000..6a0681d0deeaa2cfbeffa620ea31a3cc279fd0da GIT binary patch literal 319 zc%17D@N?(olHy`uVBq!ia0vp^vOp}s!VDz86uj;SQVPi)LB0$ORcZ_j4J`}|zkosw zFBlj~4Hy_+B``2p&0t^Lma2&CZ(T{bIf_VD(OCd&>FVdQ&MBb@08P+*g8%>k diff --git a/iphone/res/zbar-back.svg b/iphone/res/zbar-back.svg new file mode 100644 --- /dev/null +++ b/iphone/res/zbar-back.svg @@ -0,0 +1,31 @@ + + + + + + + + left pointing arrow toolbar icon + + + + + + + + + + + diff --git a/iphone/res/zbar-help.html b/iphone/res/zbar-help.html new file mode 100644 --- /dev/null +++ b/iphone/res/zbar-help.html @@ -0,0 +1,88 @@ + + + + + +Barcode Reader Help + + + +

Barcode Reader Help

+
+

Recognized barcodes should look like

+
+
+ +

QR Codes

+
+

Also recognized, but not shown: +Code 128, +DataBar (RSS), +Code 93, +Code 39 and +Interleaved 2 of 5

+
+

Hints for successful scanning

+
+
+

Ensure there is plenty of

+

Light

+
+
+
4"
+

Distance

+

should be about 3 to 5 inches

+
+
+
+

Shake

+

to force the camera to focus

+
+
+
+

Wait

+

for the autofocus to finish

+
+
+
+

Hold Still

+

while the barcode is scanned

+
+ + + diff --git a/iphone/res/zbar-helpicons.png b/iphone/res/zbar-helpicons.png new file mode 100644 index 0000000000000000000000000000000000000000..2a07e7c44478cd30a1e297395de2f25c8a7bb52a GIT binary patch literal 18350 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 JPDHLkV1mE0dd2_% diff --git a/iphone/res/zbar-samples.png b/iphone/res/zbar-samples.png new file mode 100644 index 0000000000000000000000000000000000000000..7c805e2ca96ad96e391803e6ffb0bdb35f9696ed GIT binary patch literal 1181 zc$@*91Y-M%P)tqZ00009a7bBm0000; z0000;07l7cJ^%m&Pf0{URCwC$oIhylTok}Fbu14$35AS>4!PR{LFm#Y&_(F>aH)e^ z=_DDl+`*-|38jm;b$GZ44&8L<^gwWUn?n#XxP{>6@_R#K%%>);{qM&)keHi$PjY`= z@9~`DjRyZqF$7Dn1WT|4OR(N7c4&vT8QP{9VuxUdtCL`d*rplUp>0Ml%hnCqV@$!w6KMjOb8vV0}}pt&!8S`~3_f1?xLvWy!N1$BRX<1Zy`hBg?s9TQcCeStVG4 zwZq)n+PkeC-#W>1$plNVcJMOR5I19~twDd*yCPVEwQIcfY~ZyS=x;U_EWvu6f!`X? z=i_GOp9D*=-k5oMNv@sjOn_hs*4yK)O{Zh)@8SF7WeC>3Vr_C{Wu9J^krFJy+Reb{ zpyX%kzxVf+V0~+>EXm15W%|fveqquxki)w%=+P%j(MKZo-^ zU3EGDE1-9d-KTE@R-giCUQb>cZ$0~KPkxjJjXF_JqmFvTdf5R`6E5#6b{|yn;5=ku z^$dWX8k2MsV0;2247jaBy~*E2X&Aj>ReB}2)J?7(c1johPr@1%em@j+Y#rdqa>`dg zuMB6Z_fQy99WJVMZmJkScv*amY>6L#h^<~c0@?+wfzklr&+(MIqFZdITThDR&%vT= z-_%-3sr(yG$8gz&i(WjC49@-@+-SoE@Q(gta8!Hw!D>dM1!n_bXpZ2=jMW6x2} z^mCr+_wV5BDJfob;dBH?Avi6#x}W@l_SMwYuA#9e%ns8ACmJhQ&8-OQ&*=m>4ARY8 zR(rlSw?0hfX039zDn1_9%O#kc08{g(Y6cW9>&N4`JC1TyBc)wd+SPI0!mLzI77jEoe3;+gMi$Hkx{b%QvevG4mvysda|Y1y vn7Z249q8-c<{jm4Km<#$1WT|4YoFvFczj9D2djhT00000NkvXXu0mjf>ajvZ diff --git a/iphone/zbar.xcodeproj/project.pbxproj b/iphone/zbar.xcodeproj/project.pbxproj new file mode 100644 --- /dev/null +++ b/iphone/zbar.xcodeproj/project.pbxproj @@ -0,0 +1,993 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 45; + objects = { + +/* Begin PBXAggregateTarget section */ + DC1A4A4E11FF5D0500BCDA30 /* ZBarSDK.dmg */ = { + isa = PBXAggregateTarget; + buildConfigurationList = DC1A4A5311FF5D3D00BCDA30 /* Build configuration list for PBXAggregateTarget "ZBarSDK.dmg" */; + buildPhases = ( + DC3CF2061218355900D7A786 /* Copy SDK */, + DC48C585121AC7C20047193B /* Build Documentation */, + DC3CF025121720B600D7A786 /* Copy Examples */, + DC1A4A4D11FF5D0500BCDA30 /* Make Disk Image */, + ); + dependencies = ( + DC3CF01F1216366200D7A786 /* PBXTargetDependency */, + ); + name = ZBarSDK.dmg; + productName = Package; + }; + DC3CEE821215C7EF00D7A786 /* ZBarSDK */ = { + isa = PBXAggregateTarget; + buildConfigurationList = DC3CEE851215C83500D7A786 /* Build configuration list for PBXAggregateTarget "ZBarSDK" */; + buildPhases = ( + DC3CEE891215C88000D7A786 /* Build Universal Library */, + DC3CEE811215C7EF00D7A786 /* Copy Headers */, + DC3CEE9E1215C9B800D7A786 /* Copy Headers */, + DC3CEE9F1215C9B800D7A786 /* Copy Resources */, + ); + dependencies = ( + DC3CEE871215C85400D7A786 /* PBXTargetDependency */, + ); + name = ZBarSDK; + productName = ZBarSDK; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + DC26004C118631C200FA987B /* ZBarCaptureReader.m in Sources */ = {isa = PBXBuildFile; fileRef = DC26004B118631C200FA987B /* ZBarCaptureReader.m */; }; + DC299A9D1208B5E8006A023C /* libzbar.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D2AAC07E0554694100DB518D /* libzbar.a */; }; + DC299AA01208B61C006A023C /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC50453D1203396B009FF359 /* AVFoundation.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + DC299AA11208B61C006A023C /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC50453F1203396B009FF359 /* CoreMedia.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + DC299AA21208B61C006A023C /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC5045411203396B009FF359 /* CoreVideo.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + DC299AA31208B61C006A023C /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC1A49C111FF537000BCDA30 /* QuartzCore.framework */; }; + DC299AA41208B61C006A023C /* libiconv.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = DC5045431203396B009FF359 /* libiconv.dylib */; }; + DC299AA51208B63C006A023C /* zbar-help.html in Resources */ = {isa = PBXBuildFile; fileRef = DC1A49A211FF33B300BCDA30 /* zbar-help.html */; }; + DC299AA61208B63C006A023C /* zbar-helpicons.png in Resources */ = {isa = PBXBuildFile; fileRef = DC1A49A311FF33B300BCDA30 /* zbar-helpicons.png */; }; + DC299AA71208B63C006A023C /* zbar-samples.png in Resources */ = {isa = PBXBuildFile; fileRef = DC1A49A411FF33B300BCDA30 /* zbar-samples.png */; }; + DC299B061208FC11006A023C /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC299B051208FC11006A023C /* CoreGraphics.framework */; }; + DC299B311208FCA3006A023C /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC67C08210A079BE0033B702 /* UIKit.framework */; }; + DC299B841208FCAB006A023C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AACBBE490F95108600F1A2B1 /* Foundation.framework */; }; + DC3AAB4F11B71A040021C7B1 /* ZBarCVImage.m in Sources */ = {isa = PBXBuildFile; fileRef = DC3AAB4D11B71A040021C7B1 /* ZBarCVImage.m */; }; + DC3CE47811FA1622008FAF88 /* ZBarReaderViewImpl_Capture.m in Sources */ = {isa = PBXBuildFile; fileRef = DC3CE47611FA1622008FAF88 /* ZBarReaderViewImpl_Capture.m */; }; + DC3CE47911FA1622008FAF88 /* ZBarReaderViewImpl_Simulator.m in Sources */ = {isa = PBXBuildFile; fileRef = DC3CE47711FA1622008FAF88 /* ZBarReaderViewImpl_Simulator.m */; }; + DC3CEE921215C93200D7A786 /* zbar.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = DC67C11710A07A810033B702 /* zbar.h */; }; + DC3CEE941215C97F00D7A786 /* Decoder.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = DC50463512034A4C009FF359 /* Decoder.h */; }; + DC3CEE951215C97F00D7A786 /* Exception.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = DC50463612034A4C009FF359 /* Exception.h */; }; + DC3CEE961215C97F00D7A786 /* Image.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = DC50463712034A4C009FF359 /* Image.h */; }; + DC3CEE971215C97F00D7A786 /* ImageScanner.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = DC50463812034A4C009FF359 /* ImageScanner.h */; }; + DC3CEE981215C97F00D7A786 /* Processor.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = DC50463912034A4C009FF359 /* Processor.h */; }; + DC3CEE991215C97F00D7A786 /* Scanner.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = DC50463A12034A4C009FF359 /* Scanner.h */; }; + DC3CEE9A1215C97F00D7A786 /* Symbol.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = DC50463B12034A4C009FF359 /* Symbol.h */; }; + DC3CEE9B1215C97F00D7A786 /* Video.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = DC50463C12034A4C009FF359 /* Video.h */; }; + DC3CEE9C1215C97F00D7A786 /* Window.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = DC50463D12034A4C009FF359 /* Window.h */; }; + DC3CEEA01215C9C000D7A786 /* zbar-help.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = DC1A49A211FF33B300BCDA30 /* zbar-help.html */; }; + DC3CEEA11215C9C000D7A786 /* zbar-helpicons.png in Copy Resources */ = {isa = PBXBuildFile; fileRef = DC1A49A311FF33B300BCDA30 /* zbar-helpicons.png */; }; + DC3CEEA21215C9C000D7A786 /* zbar-samples.png in Copy Resources */ = {isa = PBXBuildFile; fileRef = DC1A49A411FF33B300BCDA30 /* zbar-samples.png */; }; + DC3CEFB51216349700D7A786 /* ZBarCaptureReader.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = DC3CEFA51216347100D7A786 /* ZBarCaptureReader.h */; }; + DC3CEFB61216349700D7A786 /* ZBarImage.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = DC3CEFA61216347100D7A786 /* ZBarImage.h */; }; + DC3CEFB71216349700D7A786 /* ZBarImageScanner.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = DC3CEFA71216347100D7A786 /* ZBarImageScanner.h */; }; + DC3CEFB81216349700D7A786 /* ZBarReaderController.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = DC3CEFA81216347100D7A786 /* ZBarReaderController.h */; }; + DC3CEFB91216349700D7A786 /* ZBarReaderView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = DC3CEFA91216347100D7A786 /* ZBarReaderView.h */; }; + DC3CEFBA1216349700D7A786 /* ZBarReaderViewController.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = DC3CEFAA1216347100D7A786 /* ZBarReaderViewController.h */; }; + DC3CEFBB1216349700D7A786 /* ZBarSDK.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = DC3CEFAB1216347100D7A786 /* ZBarSDK.h */; }; + DC3CEFBC1216349700D7A786 /* ZBarSymbol.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = DC3CEFAC1216347100D7A786 /* ZBarSymbol.h */; }; + DC3CF2021218353B00D7A786 /* README in Copy SDK */ = {isa = PBXBuildFile; fileRef = DC50467B12034D60009FF359 /* README */; }; + DC3CF2031218353B00D7A786 /* ChangeLog in Copy SDK */ = {isa = PBXBuildFile; fileRef = DC3CF141121721A100D7A786 /* ChangeLog */; }; + DC3CF2041218353B00D7A786 /* COPYING in Copy SDK */ = {isa = PBXBuildFile; fileRef = DC50467C12034D71009FF359 /* COPYING */; }; + DC3CF2051218353B00D7A786 /* LICENSE in Copy SDK */ = {isa = PBXBuildFile; fileRef = DC50467D12034D71009FF359 /* LICENSE */; }; + DC3CF29A1218359400D7A786 /* ReaderSample in Copy Examples */ = {isa = PBXBuildFile; fileRef = DC3CF2081218358C00D7A786 /* ReaderSample */; }; + DC3CF29B1218359400D7A786 /* readertest in Copy Examples */ = {isa = PBXBuildFile; fileRef = DC3CF2651218358C00D7A786 /* readertest */; }; + DC3CF2A312197A8C00D7A786 /* zbar-back.png in Copy Resources */ = {isa = PBXBuildFile; fileRef = DC3CF2A212197A7200D7A786 /* zbar-back.png */; }; + DC3EBB2D119DDB2100107EE9 /* ZBarReaderViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DC3EBB2C119DDB2100107EE9 /* ZBarReaderViewController.m */; }; + DC48C5341219FDDE0047193B /* zbar-back.png in Resources */ = {isa = PBXBuildFile; fileRef = DC3CF2A212197A7200D7A786 /* zbar-back.png */; }; + DC48C5411219FE550047193B /* readertest.m in Sources */ = {isa = PBXBuildFile; fileRef = DC48C5401219FE550047193B /* readertest.m */; }; + DC48C57D121A1F410047193B /* ZBarSDK in Copy SDK */ = {isa = PBXBuildFile; fileRef = DC48C55F121A1E7F0047193B /* ZBarSDK */; }; + DC48C5A8121B1F910047193B /* Documentation.html in Copy SDK */ = {isa = PBXBuildFile; fileRef = DC48C5A7121B1F840047193B /* Documentation.html */; }; + DC4920EE10A70475000E4D43 /* ZBarImage.m in Sources */ = {isa = PBXBuildFile; fileRef = DC4920EC10A70475000E4D43 /* ZBarImage.m */; }; + DC4920EF10A70475000E4D43 /* ZBarImageScanner.m in Sources */ = {isa = PBXBuildFile; fileRef = DC4920ED10A70475000E4D43 /* ZBarImageScanner.m */; }; + DC67C17110A07AD30033B702 /* config.c in Sources */ = {isa = PBXBuildFile; fileRef = DC67C11F10A07AD30033B702 /* config.c */; }; + DC67C17410A07AD30033B702 /* code128.c in Sources */ = {isa = PBXBuildFile; fileRef = DC67C12310A07AD30033B702 /* code128.c */; }; + DC67C17610A07AD30033B702 /* code39.c in Sources */ = {isa = PBXBuildFile; fileRef = DC67C12510A07AD30033B702 /* code39.c */; }; + DC67C17810A07AD30033B702 /* ean.c in Sources */ = {isa = PBXBuildFile; fileRef = DC67C12710A07AD30033B702 /* ean.c */; }; + DC67C17A10A07AD30033B702 /* i25.c in Sources */ = {isa = PBXBuildFile; fileRef = DC67C12910A07AD30033B702 /* i25.c */; }; + DC67C17F10A07AD30033B702 /* qr_finder.c in Sources */ = {isa = PBXBuildFile; fileRef = DC67C12E10A07AD30033B702 /* qr_finder.c */; }; + DC67C18110A07AD30033B702 /* decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = DC67C13010A07AD30033B702 /* decoder.c */; }; + DC67C18310A07AD30033B702 /* error.c in Sources */ = {isa = PBXBuildFile; fileRef = DC67C13210A07AD30033B702 /* error.c */; }; + DC67C18610A07AD30033B702 /* image.c in Sources */ = {isa = PBXBuildFile; fileRef = DC67C13510A07AD30033B702 /* image.c */; }; + DC67C18810A07AD30033B702 /* img_scanner.c in Sources */ = {isa = PBXBuildFile; fileRef = DC67C13710A07AD30033B702 /* img_scanner.c */; }; + DC67C19510A07AD30033B702 /* bch15_5.c in Sources */ = {isa = PBXBuildFile; fileRef = DC67C14710A07AD30033B702 /* bch15_5.c */; }; + DC67C19710A07AD30033B702 /* binarize.c in Sources */ = {isa = PBXBuildFile; fileRef = DC67C14910A07AD30033B702 /* binarize.c */; }; + DC67C19910A07AD30033B702 /* isaac.c in Sources */ = {isa = PBXBuildFile; fileRef = DC67C14B10A07AD30033B702 /* isaac.c */; }; + DC67C19B10A07AD30033B702 /* qrdec.c in Sources */ = {isa = PBXBuildFile; fileRef = DC67C14D10A07AD30033B702 /* qrdec.c */; }; + DC67C19D10A07AD30033B702 /* qrdectxt.c in Sources */ = {isa = PBXBuildFile; fileRef = DC67C14F10A07AD30033B702 /* qrdectxt.c */; }; + DC67C19E10A07AD30033B702 /* rs.c in Sources */ = {isa = PBXBuildFile; fileRef = DC67C15010A07AD30033B702 /* rs.c */; }; + DC67C1A010A07AD30033B702 /* util.c in Sources */ = {isa = PBXBuildFile; fileRef = DC67C15210A07AD30033B702 /* util.c */; }; + DC67C1A310A07AD30033B702 /* refcnt.c in Sources */ = {isa = PBXBuildFile; fileRef = DC67C15510A07AD30033B702 /* refcnt.c */; }; + DC67C1A510A07AD30033B702 /* scanner.c in Sources */ = {isa = PBXBuildFile; fileRef = DC67C15710A07AD30033B702 /* scanner.c */; }; + DC67C1A810A07AD30033B702 /* symbol.c in Sources */ = {isa = PBXBuildFile; fileRef = DC67C15A10A07AD30033B702 /* symbol.c */; }; + DC67C1C210A07B6B0033B702 /* ZBarHelpController.m in Sources */ = {isa = PBXBuildFile; fileRef = DC67C1BE10A07B6B0033B702 /* ZBarHelpController.m */; }; + DC67C1C310A07B6B0033B702 /* ZBarReaderController.m in Sources */ = {isa = PBXBuildFile; fileRef = DC67C1BF10A07B6B0033B702 /* ZBarReaderController.m */; }; + DC67C1C410A07B6B0033B702 /* ZBarSymbol.m in Sources */ = {isa = PBXBuildFile; fileRef = DC67C1C010A07B6B0033B702 /* ZBarSymbol.m */; }; + DCB3789B12A43CAC0059B07B /* ZBarHelpController.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = DCB3789A12A43C7E0059B07B /* ZBarHelpController.h */; }; + DCDC6E3011ADCA8E00021380 /* ZBarReaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = DCDC6E2F11ADCA8E00021380 /* ZBarReaderView.m */; }; + DCE9900D129719F100D2655C /* code93.c in Sources */ = {isa = PBXBuildFile; fileRef = DCE9900B129719F100D2655C /* code93.c */; }; + DCF5C9AD11EA3AD100E7DC21 /* databar.c in Sources */ = {isa = PBXBuildFile; fileRef = DCF5C9AB11EA3AD100E7DC21 /* databar.c */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + DC299A9E1208B5F1006A023C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = D2AAC07D0554694100DB518D; + remoteInfo = libzbar; + }; + DC3CEE861215C85400D7A786 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = D2AAC07D0554694100DB518D; + remoteInfo = libzbar; + }; + DC3CF01E1216366200D7A786 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DC3CEE821215C7EF00D7A786; + remoteInfo = ZBarSDK; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + DC3CEE811215C7EF00D7A786 /* Copy Headers */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = Headers/ZBarSDK; + dstSubfolderSpec = 1; + files = ( + DC3CEFB51216349700D7A786 /* ZBarCaptureReader.h in Copy Headers */, + DCB3789B12A43CAC0059B07B /* ZBarHelpController.h in Copy Headers */, + DC3CEFB61216349700D7A786 /* ZBarImage.h in Copy Headers */, + DC3CEFB71216349700D7A786 /* ZBarImageScanner.h in Copy Headers */, + DC3CEFB81216349700D7A786 /* ZBarReaderController.h in Copy Headers */, + DC3CEFB91216349700D7A786 /* ZBarReaderView.h in Copy Headers */, + DC3CEFBA1216349700D7A786 /* ZBarReaderViewController.h in Copy Headers */, + DC3CEFBB1216349700D7A786 /* ZBarSDK.h in Copy Headers */, + DC3CEFBC1216349700D7A786 /* ZBarSymbol.h in Copy Headers */, + DC3CEE921215C93200D7A786 /* zbar.h in Copy Headers */, + ); + name = "Copy Headers"; + runOnlyForDeploymentPostprocessing = 0; + }; + DC3CEE9E1215C9B800D7A786 /* Copy Headers */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = Headers/ZBarSDK/zbar; + dstSubfolderSpec = 1; + files = ( + DC3CEE941215C97F00D7A786 /* Decoder.h in Copy Headers */, + DC3CEE951215C97F00D7A786 /* Exception.h in Copy Headers */, + DC3CEE961215C97F00D7A786 /* Image.h in Copy Headers */, + DC3CEE971215C97F00D7A786 /* ImageScanner.h in Copy Headers */, + DC3CEE981215C97F00D7A786 /* Processor.h in Copy Headers */, + DC3CEE991215C97F00D7A786 /* Scanner.h in Copy Headers */, + DC3CEE9A1215C97F00D7A786 /* Symbol.h in Copy Headers */, + DC3CEE9B1215C97F00D7A786 /* Video.h in Copy Headers */, + DC3CEE9C1215C97F00D7A786 /* Window.h in Copy Headers */, + ); + name = "Copy Headers"; + runOnlyForDeploymentPostprocessing = 0; + }; + DC3CEE9F1215C9B800D7A786 /* Copy Resources */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = Resources; + dstSubfolderSpec = 1; + files = ( + DC3CF2A312197A8C00D7A786 /* zbar-back.png in Copy Resources */, + DC3CEEA01215C9C000D7A786 /* zbar-help.html in Copy Resources */, + DC3CEEA11215C9C000D7A786 /* zbar-helpicons.png in Copy Resources */, + DC3CEEA21215C9C000D7A786 /* zbar-samples.png in Copy Resources */, + ); + name = "Copy Resources"; + runOnlyForDeploymentPostprocessing = 0; + }; + DC3CF025121720B600D7A786 /* Copy Examples */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = Examples; + dstSubfolderSpec = 1; + files = ( + DC3CF29A1218359400D7A786 /* ReaderSample in Copy Examples */, + DC3CF29B1218359400D7A786 /* readertest in Copy Examples */, + ); + name = "Copy Examples"; + runOnlyForDeploymentPostprocessing = 0; + }; + DC3CF2061218355900D7A786 /* Copy SDK */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 1; + files = ( + DC48C57D121A1F410047193B /* ZBarSDK in Copy SDK */, + DC3CF2021218353B00D7A786 /* README in Copy SDK */, + DC3CF2031218353B00D7A786 /* ChangeLog in Copy SDK */, + DC3CF2041218353B00D7A786 /* COPYING in Copy SDK */, + DC3CF2051218353B00D7A786 /* LICENSE in Copy SDK */, + DC48C5A8121B1F910047193B /* Documentation.html in Copy SDK */, + ); + name = "Copy SDK"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + AACBBE490F95108600F1A2B1 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + D2AAC07E0554694100DB518D /* libzbar.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libzbar.a; sourceTree = BUILT_PRODUCTS_DIR; }; + DC1A49A211FF33B300BCDA30 /* zbar-help.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; name = "zbar-help.html"; path = "res/zbar-help.html"; sourceTree = ""; }; + DC1A49A311FF33B300BCDA30 /* zbar-helpicons.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "zbar-helpicons.png"; path = "res/zbar-helpicons.png"; sourceTree = ""; }; + DC1A49A411FF33B300BCDA30 /* zbar-samples.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "zbar-samples.png"; path = "res/zbar-samples.png"; sourceTree = ""; }; + DC1A49C111FF537000BCDA30 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + DC26004B118631C200FA987B /* ZBarCaptureReader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZBarCaptureReader.m; sourceTree = ""; usesTabs = 0; }; + DC299AF51208B7BD006A023C /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = config.h; path = include/config.h; sourceTree = ""; }; + DC299B051208FC11006A023C /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + DC299BEC1208FE40006A023C /* prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = prefix.pch; path = include/prefix.pch; sourceTree = ""; }; + DC3AAB4C11B71A040021C7B1 /* ZBarCVImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZBarCVImage.h; sourceTree = ""; usesTabs = 0; }; + DC3AAB4D11B71A040021C7B1 /* ZBarCVImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZBarCVImage.m; sourceTree = ""; usesTabs = 0; }; + DC3CE47611FA1622008FAF88 /* ZBarReaderViewImpl_Capture.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZBarReaderViewImpl_Capture.m; sourceTree = ""; usesTabs = 0; }; + DC3CE47711FA1622008FAF88 /* ZBarReaderViewImpl_Simulator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZBarReaderViewImpl_Simulator.m; sourceTree = ""; usesTabs = 0; }; + DC3CEF9D121633C500D7A786 /* readertest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = readertest.app; sourceTree = BUILT_PRODUCTS_DIR; }; + DC3CEFA51216347100D7A786 /* ZBarCaptureReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ZBarCaptureReader.h; path = include/ZBarSDK/ZBarCaptureReader.h; sourceTree = ""; }; + DC3CEFA61216347100D7A786 /* ZBarImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ZBarImage.h; path = include/ZBarSDK/ZBarImage.h; sourceTree = ""; }; + DC3CEFA71216347100D7A786 /* ZBarImageScanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ZBarImageScanner.h; path = include/ZBarSDK/ZBarImageScanner.h; sourceTree = ""; }; + DC3CEFA81216347100D7A786 /* ZBarReaderController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ZBarReaderController.h; path = include/ZBarSDK/ZBarReaderController.h; sourceTree = ""; }; + DC3CEFA91216347100D7A786 /* ZBarReaderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ZBarReaderView.h; path = include/ZBarSDK/ZBarReaderView.h; sourceTree = ""; }; + DC3CEFAA1216347100D7A786 /* ZBarReaderViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ZBarReaderViewController.h; path = include/ZBarSDK/ZBarReaderViewController.h; sourceTree = ""; }; + DC3CEFAB1216347100D7A786 /* ZBarSDK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ZBarSDK.h; path = include/ZBarSDK/ZBarSDK.h; sourceTree = ""; }; + DC3CEFAC1216347100D7A786 /* ZBarSymbol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ZBarSymbol.h; path = include/ZBarSDK/ZBarSymbol.h; sourceTree = ""; }; + DC3CF141121721A100D7A786 /* ChangeLog */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ChangeLog; sourceTree = ""; }; + DC3CF2081218358C00D7A786 /* ReaderSample */ = {isa = PBXFileReference; lastKnownFileType = folder; name = ReaderSample; path = examples/ReaderSample; sourceTree = ""; }; + DC3CF2651218358C00D7A786 /* readertest */ = {isa = PBXFileReference; lastKnownFileType = folder; name = readertest; path = examples/readertest; sourceTree = ""; }; + DC3CF2A212197A7200D7A786 /* zbar-back.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "zbar-back.png"; path = "res/zbar-back.png"; sourceTree = ""; }; + DC3EBB2C119DDB2100107EE9 /* ZBarReaderViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZBarReaderViewController.m; sourceTree = ""; usesTabs = 0; }; + DC48C5401219FE550047193B /* readertest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = readertest.m; path = examples/readertest/readertest.m; sourceTree = ""; }; + DC48C55F121A1E7F0047193B /* ZBarSDK */ = {isa = PBXFileReference; lastKnownFileType = folder; path = ZBarSDK; sourceTree = BUILT_PRODUCTS_DIR; }; + DC48C5A7121B1F840047193B /* Documentation.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; name = Documentation.html; path = doc/Documentation.html; sourceTree = ""; }; + DC4920EC10A70475000E4D43 /* ZBarImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZBarImage.m; sourceTree = ""; usesTabs = 0; }; + DC4920ED10A70475000E4D43 /* ZBarImageScanner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZBarImageScanner.m; sourceTree = ""; usesTabs = 0; }; + DC50453D1203396B009FF359 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; + DC50453F1203396B009FF359 /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; }; + DC5045411203396B009FF359 /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; }; + DC5045431203396B009FF359 /* libiconv.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libiconv.dylib; path = usr/lib/libiconv.dylib; sourceTree = SDKROOT; }; + DC50463512034A4C009FF359 /* Decoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Decoder.h; path = ../include/zbar/Decoder.h; sourceTree = SOURCE_ROOT; }; + DC50463612034A4C009FF359 /* Exception.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Exception.h; path = ../include/zbar/Exception.h; sourceTree = SOURCE_ROOT; }; + DC50463712034A4C009FF359 /* Image.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Image.h; path = ../include/zbar/Image.h; sourceTree = SOURCE_ROOT; }; + DC50463812034A4C009FF359 /* ImageScanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageScanner.h; path = ../include/zbar/ImageScanner.h; sourceTree = SOURCE_ROOT; }; + DC50463912034A4C009FF359 /* Processor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Processor.h; path = ../include/zbar/Processor.h; sourceTree = SOURCE_ROOT; }; + DC50463A12034A4C009FF359 /* Scanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Scanner.h; path = ../include/zbar/Scanner.h; sourceTree = SOURCE_ROOT; }; + DC50463B12034A4C009FF359 /* Symbol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Symbol.h; path = ../include/zbar/Symbol.h; sourceTree = SOURCE_ROOT; }; + DC50463C12034A4C009FF359 /* Video.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Video.h; path = ../include/zbar/Video.h; sourceTree = SOURCE_ROOT; }; + DC50463D12034A4C009FF359 /* Window.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Window.h; path = ../include/zbar/Window.h; sourceTree = SOURCE_ROOT; }; + DC50467B12034D60009FF359 /* README */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README; sourceTree = ""; }; + DC50467C12034D71009FF359 /* COPYING */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = COPYING; path = ../COPYING; sourceTree = SOURCE_ROOT; }; + DC50467D12034D71009FF359 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = SOURCE_ROOT; }; + DC67C08210A079BE0033B702 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + DC67C11710A07A810033B702 /* zbar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = zbar.h; path = ../include/zbar.h; sourceTree = SOURCE_ROOT; usesTabs = 0; }; + DC67C11F10A07AD30033B702 /* config.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = config.c; sourceTree = ""; usesTabs = 0; }; + DC67C12110A07AD30033B702 /* debug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = debug.h; sourceTree = ""; usesTabs = 0; }; + DC67C12310A07AD30033B702 /* code128.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = code128.c; sourceTree = ""; }; + DC67C12410A07AD30033B702 /* code128.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = code128.h; sourceTree = ""; }; + DC67C12510A07AD30033B702 /* code39.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = code39.c; sourceTree = ""; }; + DC67C12610A07AD30033B702 /* code39.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = code39.h; sourceTree = ""; }; + DC67C12710A07AD30033B702 /* ean.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ean.c; sourceTree = ""; }; + DC67C12810A07AD30033B702 /* ean.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ean.h; sourceTree = ""; }; + DC67C12910A07AD30033B702 /* i25.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = i25.c; sourceTree = ""; }; + DC67C12A10A07AD30033B702 /* i25.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = i25.h; sourceTree = ""; }; + DC67C12E10A07AD30033B702 /* qr_finder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = qr_finder.c; sourceTree = ""; }; + DC67C12F10A07AD30033B702 /* qr_finder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = qr_finder.h; sourceTree = ""; }; + DC67C13010A07AD30033B702 /* decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = decoder.c; sourceTree = ""; usesTabs = 0; }; + DC67C13110A07AD30033B702 /* decoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = decoder.h; sourceTree = ""; usesTabs = 0; }; + DC67C13210A07AD30033B702 /* error.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = error.c; sourceTree = ""; usesTabs = 0; }; + DC67C13310A07AD30033B702 /* error.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = error.h; sourceTree = ""; usesTabs = 0; }; + DC67C13510A07AD30033B702 /* image.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = image.c; sourceTree = ""; usesTabs = 0; }; + DC67C13610A07AD30033B702 /* image.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = image.h; sourceTree = ""; usesTabs = 0; }; + DC67C13710A07AD30033B702 /* img_scanner.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = img_scanner.c; sourceTree = ""; usesTabs = 0; }; + DC67C13810A07AD30033B702 /* img_scanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = img_scanner.h; sourceTree = ""; usesTabs = 0; }; + DC67C14710A07AD30033B702 /* bch15_5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bch15_5.c; sourceTree = ""; }; + DC67C14810A07AD30033B702 /* bch15_5.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bch15_5.h; sourceTree = ""; }; + DC67C14910A07AD30033B702 /* binarize.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = binarize.c; sourceTree = ""; }; + DC67C14A10A07AD30033B702 /* binarize.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = binarize.h; sourceTree = ""; }; + DC67C14B10A07AD30033B702 /* isaac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = isaac.c; sourceTree = ""; }; + DC67C14C10A07AD30033B702 /* isaac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = isaac.h; sourceTree = ""; }; + DC67C14D10A07AD30033B702 /* qrdec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = qrdec.c; sourceTree = ""; }; + DC67C14E10A07AD30033B702 /* qrdec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = qrdec.h; sourceTree = ""; }; + DC67C14F10A07AD30033B702 /* qrdectxt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = qrdectxt.c; sourceTree = ""; }; + DC67C15010A07AD30033B702 /* rs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rs.c; sourceTree = ""; }; + DC67C15110A07AD30033B702 /* rs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rs.h; sourceTree = ""; }; + DC67C15210A07AD30033B702 /* util.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = util.c; sourceTree = ""; }; + DC67C15310A07AD30033B702 /* util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = util.h; sourceTree = ""; }; + DC67C15410A07AD30033B702 /* qrcode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = qrcode.h; sourceTree = ""; usesTabs = 0; }; + DC67C15510A07AD30033B702 /* refcnt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = refcnt.c; sourceTree = ""; usesTabs = 0; }; + DC67C15610A07AD30033B702 /* refcnt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = refcnt.h; sourceTree = ""; usesTabs = 0; }; + DC67C15710A07AD30033B702 /* scanner.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = scanner.c; sourceTree = ""; usesTabs = 0; }; + DC67C15A10A07AD30033B702 /* symbol.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = symbol.c; sourceTree = ""; usesTabs = 0; }; + DC67C15B10A07AD30033B702 /* symbol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = symbol.h; sourceTree = ""; usesTabs = 0; }; + DC67C1BE10A07B6B0033B702 /* ZBarHelpController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZBarHelpController.m; sourceTree = ""; usesTabs = 0; }; + DC67C1BF10A07B6B0033B702 /* ZBarReaderController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZBarReaderController.m; sourceTree = ""; usesTabs = 0; }; + DC67C1C010A07B6B0033B702 /* ZBarSymbol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZBarSymbol.m; sourceTree = ""; usesTabs = 0; }; + DC67C1CB10A07BEE0033B702 /* svg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = svg.h; sourceTree = ""; usesTabs = 0; }; + DCB3789A12A43C7E0059B07B /* ZBarHelpController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ZBarHelpController.h; path = include/ZBarSDK/ZBarHelpController.h; sourceTree = ""; }; + DCDC6E2F11ADCA8E00021380 /* ZBarReaderView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZBarReaderView.m; sourceTree = ""; usesTabs = 0; }; + DCE9900B129719F100D2655C /* code93.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = code93.c; sourceTree = ""; }; + DCE9900C129719F100D2655C /* code93.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = code93.h; sourceTree = ""; }; + DCF5C9AB11EA3AD100E7DC21 /* databar.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = databar.c; sourceTree = ""; }; + DCF5C9AC11EA3AD100E7DC21 /* databar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = databar.h; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + DC299A931208B5D4006A023C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DC299B841208FCAB006A023C /* Foundation.framework in Frameworks */, + DC299B061208FC11006A023C /* CoreGraphics.framework in Frameworks */, + DC299B311208FCA3006A023C /* UIKit.framework in Frameworks */, + DC299AA31208B61C006A023C /* QuartzCore.framework in Frameworks */, + DC299AA01208B61C006A023C /* AVFoundation.framework in Frameworks */, + DC299AA11208B61C006A023C /* CoreMedia.framework in Frameworks */, + DC299AA21208B61C006A023C /* CoreVideo.framework in Frameworks */, + DC299AA41208B61C006A023C /* libiconv.dylib in Frameworks */, + DC299A9D1208B5E8006A023C /* libzbar.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 034768DFFF38A50411DB9C8B /* Products */ = { + isa = PBXGroup; + children = ( + D2AAC07E0554694100DB518D /* libzbar.a */, + DC3CEF9D121633C500D7A786 /* readertest.app */, + DC48C55F121A1E7F0047193B /* ZBarSDK */, + ); + name = Products; + sourceTree = ""; + }; + 0867D691FE84028FC02AAC07 /* zbar */ = { + isa = PBXGroup; + children = ( + DC50467B12034D60009FF359 /* README */, + DC3CF141121721A100D7A786 /* ChangeLog */, + DC50467C12034D71009FF359 /* COPYING */, + DC50467D12034D71009FF359 /* LICENSE */, + DC48C5A7121B1F840047193B /* Documentation.html */, + DC299BED1208FE49006A023C /* src */, + DC1A49A111FF338B00BCDA30 /* Resources */, + DC67C11610A07A670033B702 /* include */, + DC3CF2071218356F00D7A786 /* Examples */, + DC48C5361219FE2F0047193B /* readertest */, + 0867D69AFE84028FC02AAC07 /* Frameworks */, + 034768DFFF38A50411DB9C8B /* Products */, + ); + name = zbar; + sourceTree = ""; + }; + 0867D69AFE84028FC02AAC07 /* Frameworks */ = { + isa = PBXGroup; + children = ( + AACBBE490F95108600F1A2B1 /* Foundation.framework */, + DC299B051208FC11006A023C /* CoreGraphics.framework */, + DC67C08210A079BE0033B702 /* UIKit.framework */, + DC1A49C111FF537000BCDA30 /* QuartzCore.framework */, + DC50453D1203396B009FF359 /* AVFoundation.framework */, + DC50453F1203396B009FF359 /* CoreMedia.framework */, + DC5045411203396B009FF359 /* CoreVideo.framework */, + DC5045431203396B009FF359 /* libiconv.dylib */, + ); + name = Frameworks; + sourceTree = ""; + }; + DC1A49A111FF338B00BCDA30 /* Resources */ = { + isa = PBXGroup; + children = ( + DC3CF2A212197A7200D7A786 /* zbar-back.png */, + DC1A49A211FF33B300BCDA30 /* zbar-help.html */, + DC1A49A311FF33B300BCDA30 /* zbar-helpicons.png */, + DC1A49A411FF33B300BCDA30 /* zbar-samples.png */, + ); + name = Resources; + sourceTree = ""; + usesTabs = 0; + }; + DC299BE41208FE09006A023C /* ZBarSDK */ = { + isa = PBXGroup; + children = ( + DC3CEFA51216347100D7A786 /* ZBarCaptureReader.h */, + DCB3789A12A43C7E0059B07B /* ZBarHelpController.h */, + DC3CEFA61216347100D7A786 /* ZBarImage.h */, + DC3CEFA71216347100D7A786 /* ZBarImageScanner.h */, + DC3CEFA81216347100D7A786 /* ZBarReaderController.h */, + DC3CEFA91216347100D7A786 /* ZBarReaderView.h */, + DC3CEFAA1216347100D7A786 /* ZBarReaderViewController.h */, + DC3CEFAB1216347100D7A786 /* ZBarSDK.h */, + DC3CEFAC1216347100D7A786 /* ZBarSymbol.h */, + DC67C11710A07A810033B702 /* zbar.h */, + DC50463412034A1E009FF359 /* zbar */, + ); + name = ZBarSDK; + sourceTree = ""; + }; + DC299BED1208FE49006A023C /* src */ = { + isa = PBXGroup; + children = ( + DC67C1C010A07B6B0033B702 /* ZBarSymbol.m */, + DC4920EC10A70475000E4D43 /* ZBarImage.m */, + DC4920ED10A70475000E4D43 /* ZBarImageScanner.m */, + DC3AAB4D11B71A040021C7B1 /* ZBarCVImage.m */, + DC26004B118631C200FA987B /* ZBarCaptureReader.m */, + DCDC6E2F11ADCA8E00021380 /* ZBarReaderView.m */, + DC3CE47611FA1622008FAF88 /* ZBarReaderViewImpl_Capture.m */, + DC3CE47711FA1622008FAF88 /* ZBarReaderViewImpl_Simulator.m */, + DC67C1BE10A07B6B0033B702 /* ZBarHelpController.m */, + DC3EBB2C119DDB2100107EE9 /* ZBarReaderViewController.m */, + DC67C1BF10A07B6B0033B702 /* ZBarReaderController.m */, + DC67C11E10A07AD30033B702 /* zbar */, + ); + name = src; + sourceTree = ""; + }; + DC3CF2071218356F00D7A786 /* Examples */ = { + isa = PBXGroup; + children = ( + DC3CF2081218358C00D7A786 /* ReaderSample */, + DC3CF2651218358C00D7A786 /* readertest */, + ); + name = Examples; + sourceTree = ""; + }; + DC48C5361219FE2F0047193B /* readertest */ = { + isa = PBXGroup; + children = ( + DC48C5401219FE550047193B /* readertest.m */, + ); + name = readertest; + sourceTree = ""; + }; + DC50463412034A1E009FF359 /* zbar */ = { + isa = PBXGroup; + children = ( + DC50463512034A4C009FF359 /* Decoder.h */, + DC50463612034A4C009FF359 /* Exception.h */, + DC50463712034A4C009FF359 /* Image.h */, + DC50463812034A4C009FF359 /* ImageScanner.h */, + DC50463912034A4C009FF359 /* Processor.h */, + DC50463A12034A4C009FF359 /* Scanner.h */, + DC50463B12034A4C009FF359 /* Symbol.h */, + DC50463C12034A4C009FF359 /* Video.h */, + DC50463D12034A4C009FF359 /* Window.h */, + ); + name = zbar; + sourceTree = ""; + }; + DC67C11610A07A670033B702 /* include */ = { + isa = PBXGroup; + children = ( + DC299BEC1208FE40006A023C /* prefix.pch */, + DC299AF51208B7BD006A023C /* config.h */, + DC3AAB4C11B71A040021C7B1 /* ZBarCVImage.h */, + DC299BE41208FE09006A023C /* ZBarSDK */, + ); + name = include; + sourceTree = ""; + usesTabs = 0; + }; + DC67C11E10A07AD30033B702 /* zbar */ = { + isa = PBXGroup; + children = ( + DC67C11F10A07AD30033B702 /* config.c */, + DC67C12110A07AD30033B702 /* debug.h */, + DC67C12210A07AD30033B702 /* decoder */, + DC67C13010A07AD30033B702 /* decoder.c */, + DC67C13110A07AD30033B702 /* decoder.h */, + DC67C13210A07AD30033B702 /* error.c */, + DC67C13310A07AD30033B702 /* error.h */, + DC67C13510A07AD30033B702 /* image.c */, + DC67C13610A07AD30033B702 /* image.h */, + DC67C13710A07AD30033B702 /* img_scanner.c */, + DC67C13810A07AD30033B702 /* img_scanner.h */, + DC67C14610A07AD30033B702 /* qrcode */, + DC67C15410A07AD30033B702 /* qrcode.h */, + DC67C15510A07AD30033B702 /* refcnt.c */, + DC67C15610A07AD30033B702 /* refcnt.h */, + DC67C15710A07AD30033B702 /* scanner.c */, + DC67C1CB10A07BEE0033B702 /* svg.h */, + DC67C15A10A07AD30033B702 /* symbol.c */, + DC67C15B10A07AD30033B702 /* symbol.h */, + ); + name = zbar; + path = ../zbar; + sourceTree = SOURCE_ROOT; + usesTabs = 0; + }; + DC67C12210A07AD30033B702 /* decoder */ = { + isa = PBXGroup; + children = ( + DCF5C9AB11EA3AD100E7DC21 /* databar.c */, + DCF5C9AC11EA3AD100E7DC21 /* databar.h */, + DC67C12310A07AD30033B702 /* code128.c */, + DC67C12410A07AD30033B702 /* code128.h */, + DCE9900B129719F100D2655C /* code93.c */, + DCE9900C129719F100D2655C /* code93.h */, + DC67C12510A07AD30033B702 /* code39.c */, + DC67C12610A07AD30033B702 /* code39.h */, + DC67C12710A07AD30033B702 /* ean.c */, + DC67C12810A07AD30033B702 /* ean.h */, + DC67C12910A07AD30033B702 /* i25.c */, + DC67C12A10A07AD30033B702 /* i25.h */, + DC67C12E10A07AD30033B702 /* qr_finder.c */, + DC67C12F10A07AD30033B702 /* qr_finder.h */, + ); + path = decoder; + sourceTree = ""; + usesTabs = 0; + }; + DC67C14610A07AD30033B702 /* qrcode */ = { + isa = PBXGroup; + children = ( + DC67C14710A07AD30033B702 /* bch15_5.c */, + DC67C14810A07AD30033B702 /* bch15_5.h */, + DC67C14910A07AD30033B702 /* binarize.c */, + DC67C14A10A07AD30033B702 /* binarize.h */, + DC67C14B10A07AD30033B702 /* isaac.c */, + DC67C14C10A07AD30033B702 /* isaac.h */, + DC67C14D10A07AD30033B702 /* qrdec.c */, + DC67C14E10A07AD30033B702 /* qrdec.h */, + DC67C14F10A07AD30033B702 /* qrdectxt.c */, + DC67C15010A07AD30033B702 /* rs.c */, + DC67C15110A07AD30033B702 /* rs.h */, + DC67C15210A07AD30033B702 /* util.c */, + DC67C15310A07AD30033B702 /* util.h */, + ); + path = qrcode; + sourceTree = ""; + usesTabs = 0; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + D2AAC07D0554694100DB518D /* libzbar */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1DEB921E08733DC00010E9CD /* Build configuration list for PBXNativeTarget "libzbar" */; + buildPhases = ( + D2AAC07B0554694100DB518D /* Sources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = libzbar; + productName = zbar; + productReference = D2AAC07E0554694100DB518D /* libzbar.a */; + productType = "com.apple.product-type.library.static"; + }; + DC299A941208B5D4006A023C /* readertest */ = { + isa = PBXNativeTarget; + buildConfigurationList = DC299A9A1208B5D5006A023C /* Build configuration list for PBXNativeTarget "readertest" */; + buildPhases = ( + DC299A911208B5D4006A023C /* Resources */, + DC299A921208B5D4006A023C /* Sources */, + DC299A931208B5D4006A023C /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + DC299A9F1208B5F1006A023C /* PBXTargetDependency */, + ); + name = readertest; + productName = readertest; + productReference = DC3CEF9D121633C500D7A786 /* readertest.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 0867D690FE84028FC02AAC07 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = 1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "zbar" */; + compatibilityVersion = "Xcode 3.1"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + English, + Japanese, + French, + German, + ); + mainGroup = 0867D691FE84028FC02AAC07 /* zbar */; + productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + D2AAC07D0554694100DB518D /* libzbar */, + DC3CEE821215C7EF00D7A786 /* ZBarSDK */, + DC1A4A4E11FF5D0500BCDA30 /* ZBarSDK.dmg */, + DC299A941208B5D4006A023C /* readertest */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + DC299A911208B5D4006A023C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DC299AA51208B63C006A023C /* zbar-help.html in Resources */, + DC299AA61208B63C006A023C /* zbar-helpicons.png in Resources */, + DC299AA71208B63C006A023C /* zbar-samples.png in Resources */, + DC48C5341219FDDE0047193B /* zbar-back.png in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + DC1A4A4D11FF5D0500BCDA30 /* Make Disk Image */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Make Disk Image"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "exec $SOURCE_ROOT/bin/CreateDMG.sh ZBarSDK"; + }; + DC3CEE891215C88000D7A786 /* Build Universal Library */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(BUILD_DIR)/$(CONFIGURATION)-iphoneos/libzbar.a", + "$(BUILD_DIR)/$(CONFIGURATION)-iphonesimulator/libzbar.a", + ); + name = "Build Universal Library"; + outputPaths = ( + "$(TARGET_BUILD_DIR)/libzbar.a", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "exec $SOURCE_ROOT/bin/BuildUniversal.sh libzbar"; + }; + DC48C585121AC7C20047193B /* Build Documentation */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Build Documentation"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "PATH=${PATH}:/usr/local/bin:/sw/bin\nsphinx-build -W -d $TEMP_DIR $SOURCE_ROOT/doc $TARGET_BUILD_DIR/Documentation"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + D2AAC07B0554694100DB518D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DC67C17110A07AD30033B702 /* config.c in Sources */, + DC67C17410A07AD30033B702 /* code128.c in Sources */, + DC67C17610A07AD30033B702 /* code39.c in Sources */, + DC67C17810A07AD30033B702 /* ean.c in Sources */, + DC67C17A10A07AD30033B702 /* i25.c in Sources */, + DC67C17F10A07AD30033B702 /* qr_finder.c in Sources */, + DC67C18110A07AD30033B702 /* decoder.c in Sources */, + DC67C18310A07AD30033B702 /* error.c in Sources */, + DC67C18610A07AD30033B702 /* image.c in Sources */, + DC67C18810A07AD30033B702 /* img_scanner.c in Sources */, + DC67C19510A07AD30033B702 /* bch15_5.c in Sources */, + DC67C19710A07AD30033B702 /* binarize.c in Sources */, + DC67C19910A07AD30033B702 /* isaac.c in Sources */, + DC67C19B10A07AD30033B702 /* qrdec.c in Sources */, + DC67C19D10A07AD30033B702 /* qrdectxt.c in Sources */, + DC67C19E10A07AD30033B702 /* rs.c in Sources */, + DC67C1A010A07AD30033B702 /* util.c in Sources */, + DC67C1A310A07AD30033B702 /* refcnt.c in Sources */, + DC67C1A510A07AD30033B702 /* scanner.c in Sources */, + DC67C1A810A07AD30033B702 /* symbol.c in Sources */, + DC67C1C210A07B6B0033B702 /* ZBarHelpController.m in Sources */, + DC67C1C310A07B6B0033B702 /* ZBarReaderController.m in Sources */, + DC67C1C410A07B6B0033B702 /* ZBarSymbol.m in Sources */, + DC4920EE10A70475000E4D43 /* ZBarImage.m in Sources */, + DC4920EF10A70475000E4D43 /* ZBarImageScanner.m in Sources */, + DC26004C118631C200FA987B /* ZBarCaptureReader.m in Sources */, + DC3EBB2D119DDB2100107EE9 /* ZBarReaderViewController.m in Sources */, + DCDC6E3011ADCA8E00021380 /* ZBarReaderView.m in Sources */, + DC3AAB4F11B71A040021C7B1 /* ZBarCVImage.m in Sources */, + DCF5C9AD11EA3AD100E7DC21 /* databar.c in Sources */, + DC3CE47811FA1622008FAF88 /* ZBarReaderViewImpl_Capture.m in Sources */, + DC3CE47911FA1622008FAF88 /* ZBarReaderViewImpl_Simulator.m in Sources */, + DCE9900D129719F100D2655C /* code93.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DC299A921208B5D4006A023C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DC48C5411219FE550047193B /* readertest.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + DC299A9F1208B5F1006A023C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D2AAC07D0554694100DB518D /* libzbar */; + targetProxy = DC299A9E1208B5F1006A023C /* PBXContainerItemProxy */; + }; + DC3CEE871215C85400D7A786 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D2AAC07D0554694100DB518D /* libzbar */; + targetProxy = DC3CEE861215C85400D7A786 /* PBXContainerItemProxy */; + }; + DC3CF01F1216366200D7A786 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DC3CEE821215C7EF00D7A786 /* ZBarSDK */; + targetProxy = DC3CF01E1216366200D7A786 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 1DEB921F08733DC00010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = include/prefix.pch; + HEADER_SEARCH_PATHS = ( + include, + ../include, + ../zbar, + ); + OTHER_LDFLAGS = ""; + PRODUCT_NAME = zbar; + }; + name = Debug; + }; + 1DEB922008733DC00010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + GCC_MODEL_TUNING = G5; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = include/prefix.pch; + HEADER_SEARCH_PATHS = ( + include, + ../include, + ../zbar, + ); + OTHER_LDFLAGS = ""; + PRODUCT_NAME = zbar; + }; + name = Release; + }; + 1DEB922308733DC00010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + EXCLUDED_SOURCE_FILE_NAMES = ""; + "EXCLUDED_SOURCE_FILE_NAMES[sdk=iphoneos*][arch=*]" = ZBarReaderViewImpl_Simulator.m; + "EXCLUDED_SOURCE_FILE_NAMES[sdk=iphonesimulator*][arch=*]" = ( + ZBarReaderViewImpl_Capture.m, + ZBarCaptureReader.m, + ); + GCC_C_LANGUAGE_STANDARD = c99; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = "DEBUG_OBJC=1"; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 3.0; + OTHER_LDFLAGS = "-ObjC"; + PREBINDING = NO; + RUN_CLANG_STATIC_ANALYZER = NO; + SDKROOT = iphoneos; + USE_HEADERMAP = NO; + }; + name = Debug; + }; + 1DEB922408733DC00010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + EXCLUDED_SOURCE_FILE_NAMES = ""; + "EXCLUDED_SOURCE_FILE_NAMES[sdk=iphoneos*][arch=*]" = ZBarReaderViewImpl_Simulator.m; + "EXCLUDED_SOURCE_FILE_NAMES[sdk=iphonesimulator*][arch=*]" = ( + ZBarReaderViewImpl_Capture.m, + ZBarCaptureReader.m, + ); + GCC_C_LANGUAGE_STANDARD = c99; + GCC_PREPROCESSOR_DEFINITIONS = "NDEBUG=1"; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 3.0; + OTHER_LDFLAGS = "-ObjC"; + PREBINDING = NO; + RUN_CLANG_STATIC_ANALYZER = NO; + SDKROOT = iphoneos; + USE_HEADERMAP = NO; + }; + name = Release; + }; + DC1A4A4F11FF5D0500BCDA30 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + PRODUCT_NAME = ZBarSDK.dmg; + TARGET_BUILD_DIR = "$(TARGET_BUILD_DIR)/$(PRODUCT_NAME)"; + }; + name = Debug; + }; + DC1A4A5011FF5D0500BCDA30 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + PRODUCT_NAME = ZBarSDK.dmg; + TARGET_BUILD_DIR = "$(TARGET_BUILD_DIR)/$(PRODUCT_NAME)"; + }; + name = Release; + }; + DC299A981208B5D5006A023C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = examples/readertest/prefix.pch; + HEADER_SEARCH_PATHS = ( + include/ZBarSDK, + ../include, + ); + INFOPLIST_FILE = examples/readertest/readertest.plist; + INSTALL_PATH = "$(HOME)/Applications"; + OTHER_LDFLAGS = ( + "-framework", + Foundation, + "-framework", + UIKit, + ); + PREBINDING = NO; + PRODUCT_NAME = readertest; + }; + name = Debug; + }; + DC299A991208B5D5006A023C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = examples/readertest/prefix.pch; + HEADER_SEARCH_PATHS = ( + include/ZBarSDK, + ../include, + ); + INFOPLIST_FILE = examples/readertest/readertest.plist; + INSTALL_PATH = "$(HOME)/Applications"; + OTHER_LDFLAGS = ( + "-framework", + Foundation, + "-framework", + UIKit, + ); + PREBINDING = NO; + PRODUCT_NAME = readertest; + ZERO_LINK = NO; + }; + name = Release; + }; + DC3CEE831215C7EF00D7A786 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + PRODUCT_NAME = ZBarSDK; + TARGET_BUILD_DIR = "$(TARGET_BUILD_DIR)/$(PRODUCT_NAME)"; + }; + name = Debug; + }; + DC3CEE841215C7EF00D7A786 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + PRODUCT_NAME = ZBarSDK; + TARGET_BUILD_DIR = "$(TARGET_BUILD_DIR)/$(PRODUCT_NAME)"; + ZERO_LINK = NO; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1DEB921E08733DC00010E9CD /* Build configuration list for PBXNativeTarget "libzbar" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB921F08733DC00010E9CD /* Debug */, + 1DEB922008733DC00010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "zbar" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB922308733DC00010E9CD /* Debug */, + 1DEB922408733DC00010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DC1A4A5311FF5D3D00BCDA30 /* Build configuration list for PBXAggregateTarget "ZBarSDK.dmg" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DC1A4A4F11FF5D0500BCDA30 /* Debug */, + DC1A4A5011FF5D0500BCDA30 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DC299A9A1208B5D5006A023C /* Build configuration list for PBXNativeTarget "readertest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DC299A981208B5D5006A023C /* Debug */, + DC299A991208B5D5006A023C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DC3CEE851215C83500D7A786 /* Build configuration list for PBXAggregateTarget "ZBarSDK" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DC3CEE831215C7EF00D7A786 /* Debug */, + DC3CEE841215C7EF00D7A786 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 0867D690FE84028FC02AAC07 /* Project object */; +} diff --git a/java/Makefile.am b/java/Makefile.am new file mode 100644 --- /dev/null +++ b/java/Makefile.am @@ -0,0 +1,39 @@ +javadir = $(pkgdatadir)/lib + +PKG = net/sourceforge/zbar +java_DATA = zbar.jar + +java_LTLIBRARIES = libzbarjni.la +libzbarjni_la_CPPFLAGS = $(JAVA_CFLAGS) $(AM_CPPFLAGS) +libzbarjni_la_LIBADD = $(abs_top_builddir)/zbar/libzbar.la + +libzbarjni_la_SOURCES = zbarjni.c zbarjni.h +BUILT_SOURCES = zbarjni.h +MAINTAINERCLEANFILES = zbarjni.h + +zbar_jar_SRCS = \ + $(PKG)/Config.java $(PKG)/Modifier.java $(PKG)/Orientation.java \ + $(PKG)/Symbol.java $(PKG)/SymbolIterator.java $(PKG)/SymbolSet.java \ + $(PKG)/Image.java $(PKG)/ImageScanner.java + +zbar_jar_CLASSES = $(zbar_jar_SRCS:.java=.class) + +test_SRCS = test/TestImage.java test/TestImageScanner.java \ + test/TestScanImage.java +test_CLASSES = TestImage TestImageScanner TestScanImage + +CLEANFILES = zbar.jar $(zbar_jar_CLASSES) $(test_CLASSES:=.class) + +zbarjni.h: $(zbar_jar_SRCS) + $(MAKE) $(AM_MAKEFLAGS) zbar.jar + classes=`echo $(zbar_jar_CLASSES:.class=) | tr / .` ; \ + $(JAVAH) -o $@ $$classes + +zbar.jar: $(zbar_jar_SRCS) + cd $(abs_srcdir); $(JAVAC) -d $(abs_builddir) $(zbar_jar_SRCS) + $(JAR) cf $@ $(zbar_jar_CLASSES) || $(RM) $@ + +check-local: + echo "making check in java" + cd $(abs_srcdir); $(JAVAC) -classpath $(abs_builddir)/zbar.jar:.:$(CLASSPATH) -d $(abs_builddir) $(test_SRCS) + $(top_builddir)/libtool -dlopen $(top_builddir)/zbar/libzbar.la -dlopen libzbarjni.la --mode=execute $(JAVA) -Xcheck:jni -classpath zbar.jar:.:$(CLASSPATH) org.junit.runner.JUnitCore $(test_CLASSES) diff --git a/java/net/sourceforge/zbar/Config.java b/java/net/sourceforge/zbar/Config.java new file mode 100644 --- /dev/null +++ b/java/net/sourceforge/zbar/Config.java @@ -0,0 +1,56 @@ +/*------------------------------------------------------------------------ + * Config + * + * Copyright 2010 (c) Jeff Brown + * + * 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 + *------------------------------------------------------------------------*/ + +package net.sourceforge.zbar; + +/** Decoder configuration options. + */ +public class Config +{ + /** Enable symbology/feature. */ + public static final int ENABLE = 0; + /** Enable check digit when optional. */ + public static final int ADD_CHECK = 1; + /** Return check digit when present. */ + public static final int EMIT_CHECK = 2; + /** Enable full ASCII character set. */ + public static final int ASCII = 3; + + /** Minimum data length for valid decode. */ + public static final int MIN_LEN = 0x20; + /** Maximum data length for valid decode. */ + public static final int MAX_LEN = 0x21; + + /** Required video consistency frames. */ + public static final int UNCERTAINTY = 0x40; + + /** Enable scanner to collect position data. */ + public static final int POSITION = 0x80; + + /** Image scanner vertical scan density. */ + public static final int X_DENSITY = 0x100; + /** Image scanner horizontal scan density. */ + public static final int Y_DENSITY = 0x101; +} diff --git a/java/net/sourceforge/zbar/Image.java b/java/net/sourceforge/zbar/Image.java new file mode 100644 --- /dev/null +++ b/java/net/sourceforge/zbar/Image.java @@ -0,0 +1,163 @@ +/*------------------------------------------------------------------------ + * Image + * + * Copyright 2007-2010 (c) Jeff Brown + * + * 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 + *------------------------------------------------------------------------*/ + +package net.sourceforge.zbar; + +/** stores image data samples along with associated format and size + * metadata. + */ +public class Image +{ + /** C pointer to a zbar_symbol_t. */ + private long peer; + private Object data; + + static + { + System.loadLibrary("zbarjni"); + init(); + } + private static native void init(); + + public Image () + { + peer = create(); + } + + public Image (int width, int height) + { + this(); + setSize(width, height); + } + + public Image (int width, int height, String format) + { + this(); + setSize(width, height); + setFormat(format); + } + + public Image (String format) + { + this(); + setFormat(format); + } + + Image (long peer) + { + this.peer = peer; + } + + /** Create an associated peer instance. */ + private native long create(); + + protected void finalize () + { + destroy(); + } + + /** Clean up native data associated with an instance. */ + public synchronized void destroy () + { + if(peer != 0) { + destroy(peer); + peer = 0; + } + } + + /** Destroy the associated peer instance. */ + private native void destroy(long peer); + + /** Image format conversion. + * @returns a @em new image with the sample data from the original + * image converted to the requested format fourcc. the original + * image is unaffected. + */ + public Image convert (String format) + { + long newpeer = convert(peer, format); + if(newpeer == 0) + return(null); + return(new Image(newpeer)); + } + + private native long convert(long peer, String format); + + /** Retrieve the image format fourcc. */ + public native String getFormat(); + + /** Specify the fourcc image format code for image sample data. */ + public native void setFormat(String format); + + /** Retrieve a "sequence" (page/frame) number associated with this + * image. + */ + public native int getSequence(); + + /** Associate a "sequence" (page/frame) number with this image. */ + public native void setSequence(int seq); + + /** Retrieve the width of the image. */ + public native int getWidth(); + + /** Retrieve the height of the image. */ + public native int getHeight(); + + /** Retrieve the size of the image. */ + public native int[] getSize(); + + /** Specify the pixel size of the image. */ + public native void setSize(int width, int height); + + /** Specify the pixel size of the image. */ + public native void setSize(int[] size); + + /** Retrieve the crop region of the image. */ + public native int[] getCrop(); + + /** Specify the crop region of the image. */ + public native void setCrop(int x, int y, int width, int height); + + /** Specify the crop region of the image. */ + public native void setCrop(int[] crop); + + /** Retrieve the image sample data. */ + public native byte[] getData(); + + /** Specify image sample data. */ + public native void setData(byte[] data); + + /** Specify image sample data. */ + public native void setData(int[] data); + + /** Retrieve the decoded results associated with this image. */ + public SymbolSet getSymbols () + { + return(new SymbolSet(getSymbols(peer))); + } + + private native long getSymbols(long peer); + +} diff --git a/java/net/sourceforge/zbar/ImageScanner.java b/java/net/sourceforge/zbar/ImageScanner.java new file mode 100644 --- /dev/null +++ b/java/net/sourceforge/zbar/ImageScanner.java @@ -0,0 +1,98 @@ +/*------------------------------------------------------------------------ + * ImageScanner + * + * Copyright 2007-2010 (c) Jeff Brown + * + * 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 + *------------------------------------------------------------------------*/ + +package net.sourceforge.zbar; + +/** Read barcodes from 2-D images. + */ +public class ImageScanner +{ + /** C pointer to a zbar_image_scanner_t. */ + private long peer; + + static + { + System.loadLibrary("zbarjni"); + init(); + } + private static native void init(); + + public ImageScanner () + { + peer = create(); + } + + /** Create an associated peer instance. */ + private native long create(); + + protected void finalize () + { + destroy(); + } + + /** Clean up native data associated with an instance. */ + public synchronized void destroy () + { + if(peer != 0) { + destroy(peer); + peer = 0; + } + } + + /** Destroy the associated peer instance. */ + private native void destroy(long peer); + + /** Set config for indicated symbology (0 for all) to specified value. + */ + public native void setConfig(int symbology, int config, int value) + throws IllegalArgumentException; + + /** Parse configuration string and apply to image scanner. */ + public native void parseConfig(String config); + + /** Enable or disable the inter-image result cache (default disabled). + * Mostly useful for scanning video frames, the cache filters duplicate + * results from consecutive images, while adding some consistency + * checking and hysteresis to the results. Invoking this method also + * clears the cache. + */ + public native void enableCache(boolean enable); + + /** Retrieve decode results for last scanned image. + * @returns the SymbolSet result container + */ + public SymbolSet getResults () + { + return(new SymbolSet(getResults(peer))); + } + + private native long getResults(long peer); + + /** Scan for symbols in provided Image. + * The image format must currently be "Y800" or "GRAY". + * @returns the number of symbols successfully decoded from the image. + */ + public native int scanImage(Image image); +} diff --git a/java/net/sourceforge/zbar/Modifier.java b/java/net/sourceforge/zbar/Modifier.java new file mode 100644 --- /dev/null +++ b/java/net/sourceforge/zbar/Modifier.java @@ -0,0 +1,42 @@ +/*------------------------------------------------------------------------ + * Modifier + * + * Copyright 2010 (c) Jeff Brown + * + * 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 + *------------------------------------------------------------------------*/ + +package net.sourceforge.zbar; + +/** Decoder symbology modifiers. + */ +public class Modifier +{ + /** barcode tagged as GS1 (EAN.UCC) reserved + * (eg, FNC1 before first data character). + * data may be parsed as a sequence of GS1 AIs + */ + public static final int GS1 = 0; + + /** barcode tagged as AIM reserved + * (eg, FNC1 after first character or digit pair) + */ + public static final int AIM = 1; +} diff --git a/java/net/sourceforge/zbar/Orientation.java b/java/net/sourceforge/zbar/Orientation.java new file mode 100644 --- /dev/null +++ b/java/net/sourceforge/zbar/Orientation.java @@ -0,0 +1,42 @@ +/*------------------------------------------------------------------------ + * Orientation + * + * Copyright 2010 (c) Jeff Brown + * + * 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 + *------------------------------------------------------------------------*/ + +package net.sourceforge.zbar; + +/** Decoded symbol coarse orientation. + */ +public class Orientation +{ + /** Unable to determine orientation. */ + public static final int UNKNOWN = -1; + /** Upright, read left to right. */ + public static final int UP = 0; + /** sideways, read top to bottom */ + public static final int RIGHT = 1; + /** upside-down, read right to left */ + public static final int DOWN = 2; + /** sideways, read bottom to top */ + public static final int LEFT = 3; +} diff --git a/java/net/sourceforge/zbar/Symbol.java b/java/net/sourceforge/zbar/Symbol.java new file mode 100644 --- /dev/null +++ b/java/net/sourceforge/zbar/Symbol.java @@ -0,0 +1,197 @@ +/*------------------------------------------------------------------------ + * Symbol + * + * Copyright 2007-2010 (c) Jeff Brown + * + * 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 + *------------------------------------------------------------------------*/ + +package net.sourceforge.zbar; + +/** Immutable container for decoded result symbols associated with an image + * or a composite symbol. + */ +public class Symbol +{ + /** No symbol decoded. */ + public static final int NONE = 0; + /** Symbol detected but not decoded. */ + public static final int PARTIAL = 1; + + /** EAN-8. */ + public static final int EAN8 = 8; + /** UPC-E. */ + public static final int UPCE = 9; + /** ISBN-10 (from EAN-13). */ + public static final int ISBN10 = 10; + /** UPC-A. */ + public static final int UPCA = 12; + /** EAN-13. */ + public static final int EAN13 = 13; + /** ISBN-13 (from EAN-13). */ + public static final int ISBN13 = 14; + /** Interleaved 2 of 5. */ + public static final int I25 = 25; + /** DataBar (RSS-14). */ + public static final int DATABAR = 34; + /** DataBar Expanded. */ + public static final int DATABAR_EXP = 35; + /** Code 93. */ + public static final int CODE93 = 93; + /** Code 39. */ + public static final int CODE39 = 39; + /** PDF417. */ + public static final int PDF417 = 57; + /** QR Code. */ + public static final int QRCODE = 64; + /** Code 128. */ + public static final int CODE128 = 128; + + /** C pointer to a zbar_symbol_t. */ + private long peer; + + /** Cached attributes. */ + private int type; + + static + { + System.loadLibrary("zbarjni"); + init(); + } + private static native void init(); + + /** Symbols are only created by other package methods. */ + Symbol (long peer) + { + this.peer = peer; + } + + protected void finalize () + { + destroy(); + } + + /** Clean up native data associated with an instance. */ + public synchronized void destroy () + { + if(peer != 0) { + destroy(peer); + peer = 0; + } + } + + /** Release the associated peer instance. */ + private native void destroy(long peer); + + /** Retrieve type of decoded symbol. */ + public int getType () + { + if(type == 0) + type = getType(peer); + return(type); + } + + private native int getType(long peer); + + /** Retrieve symbology boolean configs settings used during decode. */ + public native int getConfigMask(); + + /** Retrieve symbology characteristics detected during decode. */ + public native int getModifierMask(); + + /** Retrieve data decoded from symbol as a String. */ + public native String getData(); + + /** Retrieve raw data bytes decoded from symbol. */ + public native byte[] getDataBytes(); + + /** Retrieve a symbol confidence metric. Quality is an unscaled, + * relative quantity: larger values are better than smaller + * values, where "large" and "small" are application dependent. + */ + public native int getQuality(); + + /** Retrieve current cache count. When the cache is enabled for + * the image_scanner this provides inter-frame reliability and + * redundancy information for video streams. + * @returns < 0 if symbol is still uncertain + * @returns 0 if symbol is newly verified + * @returns > 0 for duplicate symbols + */ + public native int getCount(); + + /** Retrieve an approximate, axis-aligned bounding box for the + * symbol. + */ + public int[] getBounds () + { + int n = getLocationSize(peer); + if(n <= 0) + return(null); + + int[] bounds = new int[4]; + int xmin = Integer.MAX_VALUE; + int xmax = Integer.MIN_VALUE; + int ymin = Integer.MAX_VALUE; + int ymax = Integer.MIN_VALUE; + + for(int i = 0; i < n; i++) { + int x = getLocationX(peer, i); + if(xmin > x) xmin = x; + if(xmax < x) xmax = x; + + int y = getLocationY(peer, i); + if(ymin > y) ymin = y; + if(ymax < y) ymax = y; + } + bounds[0] = xmin; + bounds[1] = ymin; + bounds[2] = xmax - xmin; + bounds[3] = ymax - ymin; + return(bounds); + } + + private native int getLocationSize(long peer); + private native int getLocationX(long peer, int idx); + private native int getLocationY(long peer, int idx); + + public int[] getLocationPoint (int idx) + { + int[] p = new int[2]; + p[0] = getLocationX(peer, idx); + p[1] = getLocationY(peer, idx); + return(p); + } + + /** Retrieve general axis-aligned, orientation of decoded + * symbol. + */ + public native int getOrientation(); + + /** Retrieve components of a composite result. */ + public SymbolSet getComponents () + { + return(new SymbolSet(getComponents(peer))); + } + + private native long getComponents(long peer); + + native long next(); +} diff --git a/java/net/sourceforge/zbar/SymbolIterator.java b/java/net/sourceforge/zbar/SymbolIterator.java new file mode 100644 --- /dev/null +++ b/java/net/sourceforge/zbar/SymbolIterator.java @@ -0,0 +1,70 @@ +/*------------------------------------------------------------------------ + * SymbolIterator + * + * Copyright 2007-2010 (c) Jeff Brown + * + * 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 + *------------------------------------------------------------------------*/ + +package net.sourceforge.zbar; + +/** Iterator over a SymbolSet. + */ +public class SymbolIterator + implements java.util.Iterator +{ + /** Next symbol to be returned by the iterator. */ + private Symbol current; + + /** SymbolIterators are only created by internal interface methods. */ + SymbolIterator (Symbol first) + { + current = first; + } + + /** Returns true if the iteration has more elements. */ + public boolean hasNext () + { + return(current != null); + } + + /** Retrieves the next element in the iteration. */ + public Symbol next () + { + if(current == null) + throw(new java.util.NoSuchElementException + ("access past end of SymbolIterator")); + + Symbol result = current; + long sym = current.next(); + if(sym != 0) + current = new Symbol(sym); + else + current = null; + return(result); + } + + /** Raises UnsupportedOperationException. */ + public void remove () + { + throw(new UnsupportedOperationException + ("SymbolIterator is immutable")); + } +} diff --git a/java/net/sourceforge/zbar/SymbolSet.java b/java/net/sourceforge/zbar/SymbolSet.java new file mode 100644 --- /dev/null +++ b/java/net/sourceforge/zbar/SymbolSet.java @@ -0,0 +1,82 @@ +/*------------------------------------------------------------------------ + * SymbolSet + * + * Copyright 2007-2010 (c) Jeff Brown + * + * 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 + *------------------------------------------------------------------------*/ + +package net.sourceforge.zbar; + +/** Immutable container for decoded result symbols associated with an image + * or a composite symbol. + */ +public class SymbolSet + extends java.util.AbstractCollection +{ + /** C pointer to a zbar_symbol_set_t. */ + private long peer; + + static + { + System.loadLibrary("zbarjni"); + init(); + } + private static native void init(); + + /** SymbolSets are only created by other package methods. */ + SymbolSet (long peer) + { + this.peer = peer; + } + + protected void finalize () + { + destroy(); + } + + /** Clean up native data associated with an instance. */ + public synchronized void destroy () + { + if(peer != 0) { + destroy(peer); + peer = 0; + } + } + + /** Release the associated peer instance. */ + private native void destroy(long peer); + + /** Retrieve an iterator over the Symbol elements in this collection. */ + public java.util.Iterator iterator () + { + long sym = firstSymbol(peer); + if(sym == 0) + return(new SymbolIterator(null)); + + return(new SymbolIterator(new Symbol(sym))); + } + + /** Retrieve the number of elements in the collection. */ + public native int size(); + + /** Retrieve C pointer to first symbol in the set. */ + private native long firstSymbol(long peer); +} diff --git a/java/test/TestImage.java b/java/test/TestImage.java new file mode 100644 --- /dev/null +++ b/java/test/TestImage.java @@ -0,0 +1,175 @@ + +import org.junit.Test; +import org.junit.Before; +import org.junit.After; +import static org.junit.Assert.*; + +import net.sourceforge.zbar.Image; + +public class TestImage +{ + protected Image image; + + @Before public void setUp () + { + image = new Image(); + } + + @After public void tearDown () + { + image.destroy(); + image = null; + } + + + @Test public void creation () + { + Image img0 = new Image(123, 456); + Image img1 = new Image("BGR3"); + Image img2 = new Image(987, 654, "UYVY"); + + assertEquals(123, img0.getWidth()); + assertEquals(456, img0.getHeight()); + assertEquals(null, img0.getFormat()); + + assertEquals(0, img1.getWidth()); + assertEquals(0, img1.getHeight()); + assertEquals("BGR3", img1.getFormat()); + + assertEquals(987, img2.getWidth()); + assertEquals(654, img2.getHeight()); + assertEquals("UYVY", img2.getFormat()); + } + + @Test public void sequence () + { + assertEquals(0, image.getSequence()); + image.setSequence(42); + assertEquals(42, image.getSequence()); + } + + @Test public void size () + { + assertEquals(0, image.getWidth()); + assertEquals(0, image.getHeight()); + + image.setSize(640, 480); + int[] size0 = { 640, 480 }; + assertArrayEquals(size0, image.getSize()); + + int[] size1 = { 320, 240 }; + image.setSize(size1); + assertEquals(320, image.getWidth()); + assertEquals(240, image.getHeight()); + } + + @Test public void crop () + { + int[] zeros = { 0, 0, 0, 0 }; + assertArrayEquals(zeros, image.getCrop()); + + image.setSize(123, 456); + int[] crop0 = { 0, 0, 123, 456 }; + assertArrayEquals(crop0, image.getCrop()); + + image.setCrop(1, 2, 34, 56); + int[] crop1 = { 1, 2, 34, 56 }; + assertArrayEquals(crop1, image.getCrop()); + + image.setCrop(-20, -20, 200, 500); + assertArrayEquals(crop0, image.getCrop()); + + int[] crop2 = { 7, 8, 90, 12}; + image.setCrop(crop2); + assertArrayEquals(crop2, image.getCrop()); + + image.setSize(654, 321); + int[] crop3 = { 0, 0, 654, 321 }; + assertArrayEquals(crop3, image.getCrop()); + + int[] crop4 = { -10, -10, 700, 400 }; + image.setCrop(crop4); + assertArrayEquals(crop3, image.getCrop()); + } + + @Test public void format () + { + assertNull(image.getFormat()); + image.setFormat("Y800"); + assertEquals("Y800", image.getFormat()); + boolean gotException = false; + try { + image.setFormat("[]"); + } + catch(IllegalArgumentException e) { + // expected + gotException = true; + } + assertTrue("Expected exception", gotException); + assertEquals("Y800", image.getFormat()); + } + + @Test(expected=IllegalArgumentException.class) + public void setFormatInvalid0 () + { + image.setFormat(null); + } + + @Test(expected=IllegalArgumentException.class) + public void setFormatInvalid1 () + { + image.setFormat(""); + } + + @Test(expected=IllegalArgumentException.class) + public void setFormatInvalid2 () + { + image.setFormat("YOMAMA"); + } + + @Test(expected=IllegalArgumentException.class) + public void setFormatInvalid3 () + { + image.setFormat("foo"); + } + + @Test public void data () + { + assertNull(image.getData()); + + int[] ints = new int[24]; + image.setData(ints); + assertSame(ints, image.getData()); + + byte[] bytes = new byte[280]; + image.setData(bytes); + assertSame(bytes, image.getData()); + + image.setData((byte[])null); + assertNull(image.getData()); + } + + @Test public void convert () + { + image.setSize(4, 4); + image.setFormat("RGB4"); + int[] rgb4 = new int[16]; + byte[] exp = new byte[16]; + for(int i = 0; i < 16; i++) { + int c = i * 15; + rgb4[i] = c | (c << 8) | (c << 16) | (c << 24); + exp[i] = (byte)c; + } + image.setData(rgb4); + + Image gray = image.convert("Y800"); + assertEquals(4, gray.getWidth()); + assertEquals(4, gray.getHeight()); + assertEquals("Y800", gray.getFormat()); + + byte[] y800 = gray.getData(); + assertEquals(16, y800.length); + + assertArrayEquals(exp, y800); + } +} diff --git a/java/test/TestImageScanner.java b/java/test/TestImageScanner.java new file mode 100644 --- /dev/null +++ b/java/test/TestImageScanner.java @@ -0,0 +1,53 @@ + +import org.junit.Test; +import org.junit.Before; +import org.junit.After; +import org.junit.Assert.*; + +import net.sourceforge.zbar.ImageScanner; +import net.sourceforge.zbar.Config; + +public class TestImageScanner +{ + protected ImageScanner scanner; + + @Before public void setUp () + { + scanner = new ImageScanner(); + } + + @After public void tearDown () + { + scanner.destroy(); + scanner = null; + } + + + @Test public void creation () + { + // create/destroy + } + + @Test public void callSetConfig () + { + scanner.setConfig(0, Config.X_DENSITY, 2); + scanner.setConfig(0, Config.Y_DENSITY, 4); + } + + @Test public void callParseConfig () + { + scanner.parseConfig("disable"); + } + + @Test(expected=IllegalArgumentException.class) + public void callParseConfigInvalid () + { + scanner.parseConfig("yomama"); + } + + @Test public void callEnableCache () + { + scanner.enableCache(true); + scanner.enableCache(false); + } +} diff --git a/java/test/TestScanImage.java b/java/test/TestScanImage.java new file mode 100644 --- /dev/null +++ b/java/test/TestScanImage.java @@ -0,0 +1,183 @@ + +import org.junit.Test; +import org.junit.Before; +import org.junit.After; +import static org.junit.Assert.*; + +import net.sourceforge.zbar.*; + +import java.text.CharacterIterator; +import java.text.StringCharacterIterator; +import java.util.Iterator; + +public class TestScanImage +{ + protected ImageScanner scanner; + protected Image image; + + @Before public void setUp () + { + scanner = new ImageScanner(); + image = new Image(); + } + + @After public void tearDown () + { + image = null; + scanner = null; + System.gc(); + } + + public static final String encoded_widths = + "9 111 212241113121211311141132 11111 311213121312121332111132 111 9"; + + protected void generateY800 () + { + int width = 114, height = 85; + image.setSize(width, height); + image.setFormat("Y800"); + int datalen = width * height; + byte[] data = new byte[datalen]; + + int y = 0; + int p = 0; + for(; y < 10 && y < height; y++) + for(int x = 0; x < width; x++) + data[p++] = -1; + + for(; y < height - 10; y++) { + int x = 0; + byte color = -1; + CharacterIterator it = new StringCharacterIterator(encoded_widths); + for(char c = it.first(); + c != CharacterIterator.DONE; + c = it.next()) + { + if(c == ' ') + continue; + for(int dx = (int)c - 0x30; dx > 0; dx--) { + data[p++] = color; + x++; + } + color = (byte)~color; + } + for(; x < width; x++) + data[p++] = (byte)~color; + } + + for(; y < height; y++) + for(int x = 0; x < width; x++) + data[p++] = -1; + assert(p == datalen); + + image.setData(data); + } + + protected void checkResults (SymbolSet syms) + { + assertNotNull(syms); + assert(syms.size() == 1); + Iterator it = syms.iterator(); + assertTrue(it.hasNext()); + Symbol sym = it.next(); + assertNotNull(sym); + assertFalse(it.hasNext()); + + assertEquals(Symbol.EAN13, sym.getType()); + assertEquals(sym.EAN13, sym.getType()); // cached + + assertTrue(sym.getQuality() > 1); + assertEquals(0, sym.getCount()); + + SymbolSet comps = sym.getComponents(); + assertNotNull(comps); + assertEquals(0, comps.size()); + it = comps.iterator(); + assertNotNull(it); + assertFalse(it.hasNext()); + + String data = sym.getData(); + assertEquals("6268964977804", data); + + byte[] bytes = sym.getDataBytes(); + byte[] exp = { '6','2','6','8','9','6','4','9','7','7','8','0','4' }; + assertArrayEquals(exp, bytes); + + int[] r = sym.getBounds(); + assertTrue(r[0] > 6); + assertTrue(r[1] > 6); + assertTrue(r[2] < 102); + assertTrue(r[3] < 73); + + assertEquals(Orientation.UP, sym.getOrientation()); + } + + @Test public void generated () + { + generateY800(); + int n = scanner.scanImage(image); + assertEquals(1, n); + + checkResults(image.getSymbols()); + checkResults(scanner.getResults()); + } + + @Test public void config () + { + generateY800(); + scanner.setConfig(Symbol.EAN13, Config.ENABLE, 0); + int n = scanner.scanImage(image); + assertEquals(0, n); + } + + @Test public void cache () + { + generateY800(); + scanner.enableCache(true); + + int n = 0; + for(int i = 0; i < 10; i++) { + n = scanner.scanImage(image); + if(n > 0) { + assertTrue(i > 1); + break; + } + } + + assertEquals(1, n); + checkResults(scanner.getResults()); + } + + @Test public void orientation() + { + generateY800(); + + // flip the image + int width = image.getWidth(); + int height = image.getHeight(); + byte[] data = image.getData(); + int p = 0; + for(int y = 0; y < height; y++) { + for(int x0 = 0; x0 < width / 2; x0++) { + int x1 = width - x0 - 1; + assert(x0 < x1); + byte b = data[p + x0]; + data[p + x0] = data[p + x1]; + data[p + x1] = b; + } + p += width; + } + image.setData(data); + + int n = scanner.scanImage(image); + assertEquals(1, n); + + SymbolSet syms = scanner.getResults(); + assert(syms.size() == 1); + for(Symbol sym : syms) { + assertEquals(Symbol.EAN13, sym.getType()); + assertEquals("6268964977804", sym.getData()); + assertEquals(Orientation.DOWN, sym.getOrientation()); + } + } +} diff --git a/java/zbarjni.c b/java/zbarjni.c new file mode 100644 --- /dev/null +++ b/java/zbarjni.c @@ -0,0 +1,701 @@ +/*------------------------------------------------------------------------ + * Copyright 2010 (c) Jeff Brown + * + * 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 +#include +#include +#include + +static jfieldID SymbolSet_peer; +static jfieldID Symbol_peer; +static jfieldID Image_peer, Image_data; +static jfieldID ImageScanner_peer; + +static struct { + int SymbolSet_create, SymbolSet_destroy; + int Symbol_create, Symbol_destroy; + int Image_create, Image_destroy; + int ImageScanner_create, ImageScanner_destroy; +} stats; + + +#define PEER_CAST(l) \ + ((void*)(uintptr_t)(l)) + +#define GET_PEER(c, o) \ + PEER_CAST((*env)->GetLongField(env, (o), c ## _peer)) + + +static inline void +throw_exc(JNIEnv *env, + const char *name, + const char *msg) +{ + jclass cls = (*env)->FindClass(env, name); + if(cls) + (*env)->ThrowNew(env, cls, msg); + (*env)->DeleteLocalRef(env, cls); +} + +static inline uint32_t +format_to_fourcc(JNIEnv *env, + jstring format) +{ + if(!format) + goto invalid; + + int n = (*env)->GetStringLength(env, format); + if(0 >= n || n > 4) + goto invalid; + + char fmtstr[8]; + (*env)->GetStringUTFRegion(env, format, 0, n, fmtstr); + + uint32_t fourcc = 0; + int i; + for(i = 0; i < n; i++) { + if(fmtstr[i] < ' ' || 'Z' < fmtstr[i] || + ('9' < fmtstr[i] && fmtstr[i] < 'A') || + (' ' < fmtstr[i] && fmtstr[i] < '0')) + goto invalid; + fourcc |= ((uint32_t)fmtstr[i]) << (8 * i); + } + return(fourcc); + +invalid: + throw_exc(env, "java/lang/IllegalArgumentException", + "invalid format fourcc"); + return(0); +} + +static JavaVM *jvm = NULL; + +JNIEXPORT jint JNICALL +JNI_OnLoad (JavaVM *_jvm, + void *reserved) +{ + jvm = _jvm; + return(JNI_VERSION_1_2); +} + +JNIEXPORT void JNICALL +JNI_OnUnload (JavaVM *_jvm, + void *reserved) +{ + assert(stats.SymbolSet_create == stats.SymbolSet_destroy); + assert(stats.Symbol_create == stats.Symbol_destroy); + assert(stats.Image_create == stats.Image_destroy); + assert(stats.ImageScanner_create == stats.ImageScanner_destroy); +} + + +JNIEXPORT void JNICALL +Java_net_sourceforge_zbar_SymbolSet_init (JNIEnv *env, + jclass cls) +{ + SymbolSet_peer = (*env)->GetFieldID(env, cls, "peer", "J"); +} + +JNIEXPORT void JNICALL +Java_net_sourceforge_zbar_SymbolSet_destroy (JNIEnv *env, + jobject obj, + jlong peer) +{ + zbar_symbol_set_ref(PEER_CAST(peer), -1); + stats.SymbolSet_destroy++; +} + +JNIEXPORT jint JNICALL +Java_net_sourceforge_zbar_SymbolSet_size (JNIEnv *env, + jobject obj) +{ + zbar_symbol_set_t *zsyms = GET_PEER(SymbolSet, obj); + if(!zsyms) + return(0); + return(zbar_symbol_set_get_size(zsyms)); +} + +JNIEXPORT jlong JNICALL +Java_net_sourceforge_zbar_SymbolSet_firstSymbol (JNIEnv *env, + jobject obj, + jlong peer) +{ + if(!peer) + return(0); + const zbar_symbol_t *zsym = zbar_symbol_set_first_symbol(PEER_CAST(peer)); + if(zsym) { + zbar_symbol_ref(zsym, 1); + stats.Symbol_create++; + } + return((intptr_t)zsym); +} + + + +JNIEXPORT void JNICALL +Java_net_sourceforge_zbar_Symbol_init (JNIEnv *env, + jclass cls) +{ + Symbol_peer = (*env)->GetFieldID(env, cls, "peer", "J"); +} + +JNIEXPORT void JNICALL +Java_net_sourceforge_zbar_Symbol_destroy (JNIEnv *env, + jobject obj, + jlong peer) +{ + zbar_symbol_ref(PEER_CAST(peer), -1); + stats.Symbol_destroy++; +} + +JNIEXPORT jint JNICALL +Java_net_sourceforge_zbar_Symbol_getType (JNIEnv *env, + jobject obj, + jlong peer) +{ + return(zbar_symbol_get_type(PEER_CAST(peer))); +} + +JNIEXPORT jint JNICALL +Java_net_sourceforge_zbar_Symbol_getConfigMask (JNIEnv *env, + jobject obj) +{ + return(zbar_symbol_get_configs(GET_PEER(Symbol, obj))); +} + +JNIEXPORT jint JNICALL +Java_net_sourceforge_zbar_Symbol_getModifierMask (JNIEnv *env, + jobject obj) +{ + return(zbar_symbol_get_modifiers(GET_PEER(Symbol, obj))); +} + +JNIEXPORT jstring JNICALL +Java_net_sourceforge_zbar_Symbol_getData (JNIEnv *env, + jobject obj) +{ + const char *data = zbar_symbol_get_data(GET_PEER(Symbol, obj)); + return((*env)->NewStringUTF(env, data)); +} + +JNIEXPORT jstring JNICALL +Java_net_sourceforge_zbar_Symbol_getDataBytes (JNIEnv *env, + jobject obj) +{ + const zbar_symbol_t *zsym = GET_PEER(Symbol, obj); + const void *data = zbar_symbol_get_data(zsym); + unsigned long datalen = zbar_symbol_get_data_length(zsym); + if(!data || !datalen) + return(NULL); + + jbyteArray bytes = (*env)->NewByteArray(env, datalen); + if(!bytes) + return(NULL); + + (*env)->SetByteArrayRegion(env, bytes, 0, datalen, data); + return(bytes); +} + +JNIEXPORT jint JNICALL +Java_net_sourceforge_zbar_Symbol_getQuality (JNIEnv *env, + jobject obj) +{ + return(zbar_symbol_get_quality(GET_PEER(Symbol, obj))); +} + +JNIEXPORT jint JNICALL +Java_net_sourceforge_zbar_Symbol_getCount (JNIEnv *env, + jobject obj) +{ + return(zbar_symbol_get_count(GET_PEER(Symbol, obj))); +} + +JNIEXPORT jint JNICALL +Java_net_sourceforge_zbar_Symbol_getLocationSize (JNIEnv *env, + jobject obj, + jlong peer) +{ + return(zbar_symbol_get_loc_size(PEER_CAST(peer))); +} + +JNIEXPORT jint JNICALL +Java_net_sourceforge_zbar_Symbol_getLocationX (JNIEnv *env, + jobject obj, + jlong peer, + jint idx) +{ + return(zbar_symbol_get_loc_x(PEER_CAST(peer), idx)); +} + +JNIEXPORT jint JNICALL +Java_net_sourceforge_zbar_Symbol_getLocationY (JNIEnv *env, + jobject obj, + jlong peer, + jint idx) +{ + return(zbar_symbol_get_loc_y(PEER_CAST(peer), idx)); +} + +JNIEXPORT jint JNICALL +Java_net_sourceforge_zbar_Symbol_getOrientation (JNIEnv *env, + jobject obj) +{ + return(zbar_symbol_get_orientation(GET_PEER(Symbol, obj))); +} + +JNIEXPORT jlong JNICALL +Java_net_sourceforge_zbar_Symbol_getComponents (JNIEnv *env, + jobject obj, + jlong peer) +{ + const zbar_symbol_set_t *zsyms = + zbar_symbol_get_components(PEER_CAST(peer)); + if(zsyms) { + zbar_symbol_set_ref(zsyms, 1); + stats.SymbolSet_create++; + } + return((intptr_t)zsyms); +} + +JNIEXPORT jlong JNICALL +Java_net_sourceforge_zbar_Symbol_next (JNIEnv *env, + jobject obj) +{ + const zbar_symbol_t *zsym = zbar_symbol_next(GET_PEER(Symbol, obj)); + if(zsym) { + zbar_symbol_ref(zsym, 1); + stats.Symbol_create++; + } + return((intptr_t)zsym); +} + + + +static void +Image_cleanupByteArray (zbar_image_t *zimg) +{ + jobject data = zbar_image_get_userdata(zimg); + assert(data); + + JNIEnv *env = NULL; + if((*jvm)->AttachCurrentThread(jvm, (void**)&env, NULL)) + return; + assert(env); + if(env && data) { + void *raw = (void*)zbar_image_get_data(zimg); + assert(raw); + /* const image data is unchanged - abort copy back */ + (*env)->ReleaseByteArrayElements(env, data, raw, JNI_ABORT); + (*env)->DeleteGlobalRef(env, data); + zbar_image_set_userdata(zimg, NULL); + } +} + +static void +Image_cleanupIntArray (zbar_image_t *zimg) +{ + jobject data = zbar_image_get_userdata(zimg); + assert(data); + + JNIEnv *env = NULL; + if((*jvm)->AttachCurrentThread(jvm, (void**)&env, NULL)) + return; + assert(env); + if(env && data) { + void *raw = (void*)zbar_image_get_data(zimg); + assert(raw); + /* const image data is unchanged - abort copy back */ + (*env)->ReleaseIntArrayElements(env, data, raw, JNI_ABORT); + (*env)->DeleteGlobalRef(env, data); + zbar_image_set_userdata(zimg, NULL); + } +} + + +JNIEXPORT void JNICALL +Java_net_sourceforge_zbar_Image_init (JNIEnv *env, + jclass cls) +{ + Image_peer = (*env)->GetFieldID(env, cls, "peer", "J"); + Image_data = (*env)->GetFieldID(env, cls, "data", "Ljava/lang/Object;"); +} + +JNIEXPORT jlong JNICALL +Java_net_sourceforge_zbar_Image_create (JNIEnv *env, + jobject obj) +{ + zbar_image_t *zimg = zbar_image_create(); + if(!zimg) { + throw_exc(env, "java/lang/OutOfMemoryError", NULL); + return(0); + } + stats.Image_create++; + return((intptr_t)zimg); +} + +JNIEXPORT void JNICALL +Java_net_sourceforge_zbar_Image_destroy (JNIEnv *env, + jobject obj, + jlong peer) +{ + zbar_image_ref(PEER_CAST(peer), -1); + stats.Image_destroy++; +} + +JNIEXPORT jlong JNICALL +Java_net_sourceforge_zbar_Image_convert (JNIEnv *env, + jobject obj, + jlong peer, + jstring format) +{ + uint32_t fourcc = format_to_fourcc(env, format); + if(!fourcc) + return(0); + zbar_image_t *zimg = zbar_image_convert(PEER_CAST(peer), fourcc); + if(!zimg) + throw_exc(env, "java/lang/UnsupportedOperationException", + "unsupported image format"); + else + stats.Image_create++; + return((intptr_t)zimg); +} + +JNIEXPORT jstring JNICALL +Java_net_sourceforge_zbar_Image_getFormat (JNIEnv *env, + jobject obj) +{ + uint32_t fourcc = zbar_image_get_format(GET_PEER(Image, obj)); + if(!fourcc) + return(NULL); + char fmtstr[5] = { fourcc, fourcc >> 8, fourcc >> 16, fourcc >> 24, 0 }; + return((*env)->NewStringUTF(env, fmtstr)); +} + +JNIEXPORT void JNICALL +Java_net_sourceforge_zbar_Image_setFormat (JNIEnv *env, + jobject obj, + jstring format) +{ + uint32_t fourcc = format_to_fourcc(env, format); + if(!fourcc) + return; + zbar_image_set_format(GET_PEER(Image, obj), fourcc); +} + +JNIEXPORT jint JNICALL +Java_net_sourceforge_zbar_Image_getSequence (JNIEnv *env, + jobject obj) +{ + return(zbar_image_get_sequence(GET_PEER(Image, obj))); +} + +JNIEXPORT void JNICALL +Java_net_sourceforge_zbar_Image_setSequence (JNIEnv *env, + jobject obj, + jint seq) +{ + zbar_image_set_sequence(GET_PEER(Image, obj), seq); +} + +JNIEXPORT jint JNICALL +Java_net_sourceforge_zbar_Image_getWidth (JNIEnv *env, + jobject obj) +{ + return(zbar_image_get_width(GET_PEER(Image, obj))); +} + +JNIEXPORT jint JNICALL +Java_net_sourceforge_zbar_Image_getHeight (JNIEnv *env, + jobject obj) +{ + return(zbar_image_get_height(GET_PEER(Image, obj))); +} + +JNIEXPORT jobject JNICALL +Java_net_sourceforge_zbar_Image_getSize (JNIEnv *env, + jobject obj) +{ + jintArray size = (*env)->NewIntArray(env, 2); + if(!size) + return(NULL); + + unsigned dims[2]; + zbar_image_get_size(GET_PEER(Image, obj), dims, dims + 1); + jint jdims[2] = { dims[0], dims[1] }; + (*env)->SetIntArrayRegion(env, size, 0, 2, jdims); + return(size); +} + +JNIEXPORT void JNICALL +Java_net_sourceforge_zbar_Image_setSize__II (JNIEnv *env, + jobject obj, + jint width, + jint height) +{ + if(width < 0) width = 0; + if(height < 0) height = 0; + zbar_image_set_size(GET_PEER(Image, obj), width, height); +} + +JNIEXPORT void JNICALL +Java_net_sourceforge_zbar_Image_setSize___3I (JNIEnv *env, + jobject obj, + jintArray size) +{ + if((*env)->GetArrayLength(env, size) != 2) + throw_exc(env, "java/lang/IllegalArgumentException", + "size must be an array of two ints"); + jint dims[2]; + (*env)->GetIntArrayRegion(env, size, 0, 2, dims); + if(dims[0] < 0) dims[0] = 0; + if(dims[1] < 0) dims[1] = 0; + zbar_image_set_size(GET_PEER(Image, obj), dims[0], dims[1]); +} + +JNIEXPORT jobject JNICALL +Java_net_sourceforge_zbar_Image_getCrop (JNIEnv *env, + jobject obj) +{ + jintArray crop = (*env)->NewIntArray(env, 4); + if(!crop) + return(NULL); + + unsigned dims[4]; + zbar_image_get_crop(GET_PEER(Image, obj), dims, dims + 1, + dims + 2, dims + 3); + jint jdims[4] = { dims[0], dims[1], dims[2], dims[3] }; + (*env)->SetIntArrayRegion(env, crop, 0, 4, jdims); + return(crop); +} + +#define VALIDATE_CROP(u, m) \ + if((u) < 0) { \ + (m) += (u); \ + (u) = 0; \ + } + +JNIEXPORT void JNICALL +Java_net_sourceforge_zbar_Image_setCrop__IIII (JNIEnv *env, + jobject obj, + jint x, jint y, + jint w, jint h) +{ + VALIDATE_CROP(x, w); + VALIDATE_CROP(y, h); + zbar_image_set_crop(GET_PEER(Image, obj), x, y, w, h); +} + +JNIEXPORT void JNICALL +Java_net_sourceforge_zbar_Image_setCrop___3I (JNIEnv *env, + jobject obj, + jintArray crop) +{ + if((*env)->GetArrayLength(env, crop) != 4) + throw_exc(env, "java/lang/IllegalArgumentException", + "crop must be an array of four ints"); + jint dims[4]; + (*env)->GetIntArrayRegion(env, crop, 0, 4, dims); + VALIDATE_CROP(dims[0], dims[2]); + VALIDATE_CROP(dims[1], dims[3]); + zbar_image_set_crop(GET_PEER(Image, obj), + dims[0], dims[1], dims[2], dims[3]); +} +#undef VALIDATE_CROP + +JNIEXPORT jobject JNICALL +Java_net_sourceforge_zbar_Image_getData (JNIEnv *env, + jobject obj) +{ + jobject data = (*env)->GetObjectField(env, obj, Image_data); + if(data) + return(data); + + zbar_image_t *zimg = GET_PEER(Image, obj); + data = zbar_image_get_userdata(zimg); + if(data) + return(data); + + unsigned long rawlen = zbar_image_get_data_length(zimg); + const void *raw = zbar_image_get_data(zimg); + if(!rawlen || !raw) + return(NULL); + + data = (*env)->NewByteArray(env, rawlen); + if(!data) + return(NULL); + + (*env)->SetByteArrayRegion(env, data, 0, rawlen, raw); + (*env)->SetObjectField(env, obj, Image_data, data); + return(data); +} + +static inline void +Image_setData (JNIEnv *env, + jobject obj, + jbyteArray data, + void *raw, + unsigned long rawlen, + zbar_image_cleanup_handler_t *cleanup) +{ + if(!data) + cleanup = NULL; + (*env)->SetObjectField(env, obj, Image_data, data); + zbar_image_t *zimg = GET_PEER(Image, obj); + zbar_image_set_data(zimg, raw, rawlen, cleanup); + zbar_image_set_userdata(zimg, (*env)->NewGlobalRef(env, data)); +} + +JNIEXPORT void JNICALL +Java_net_sourceforge_zbar_Image_setData___3B (JNIEnv *env, + jobject obj, + jbyteArray data) +{ + jbyte *raw = NULL; + unsigned long rawlen = 0; + if(data) { + raw = (*env)->GetByteArrayElements(env, data, NULL); + if(!raw) + return; + rawlen = (*env)->GetArrayLength(env, data); + } + Image_setData(env, obj, data, raw, rawlen, Image_cleanupByteArray); +} + +JNIEXPORT void JNICALL +Java_net_sourceforge_zbar_Image_setData___3I (JNIEnv *env, + jobject obj, + jintArray data) +{ + jint *raw = NULL; + unsigned long rawlen = 0; + if(data) { + raw = (*env)->GetIntArrayElements(env, data, NULL); + if(!raw) + return; + rawlen = (*env)->GetArrayLength(env, data) * sizeof(*raw); + } + Image_setData(env, obj, data, raw, rawlen, Image_cleanupIntArray); +} + +JNIEXPORT jlong JNICALL +Java_net_sourceforge_zbar_Image_getSymbols (JNIEnv *env, + jobject obj, + jlong peer) +{ + const zbar_symbol_set_t *zsyms = zbar_image_get_symbols(PEER_CAST(peer)); + if(zsyms) { + zbar_symbol_set_ref(zsyms, 1); + stats.SymbolSet_create++; + } + return((intptr_t)zsyms); +} + + +JNIEXPORT void JNICALL +Java_net_sourceforge_zbar_ImageScanner_init (JNIEnv *env, + jclass cls) +{ + ImageScanner_peer = (*env)->GetFieldID(env, cls, "peer", "J"); +} + +JNIEXPORT jlong JNICALL +Java_net_sourceforge_zbar_ImageScanner_create (JNIEnv *env, + jobject obj) +{ + zbar_image_scanner_t *zscn = zbar_image_scanner_create(); + if(!zscn) { + throw_exc(env, "java/lang/OutOfMemoryError", NULL); + return(0); + } + stats.ImageScanner_create++; + return((intptr_t)zscn); +} + +JNIEXPORT void JNICALL +Java_net_sourceforge_zbar_ImageScanner_destroy (JNIEnv *env, + jobject obj, + jlong peer) +{ + zbar_image_scanner_destroy(PEER_CAST(peer)); + stats.ImageScanner_destroy++; +} + +JNIEXPORT void JNICALL +Java_net_sourceforge_zbar_ImageScanner_setConfig (JNIEnv *env, + jobject obj, + jint symbology, + jint config, + jint value) +{ + zbar_image_scanner_set_config(GET_PEER(ImageScanner, obj), + symbology, config, value); +} + +JNIEXPORT void JNICALL +Java_net_sourceforge_zbar_ImageScanner_parseConfig (JNIEnv *env, + jobject obj, + jstring cfg) +{ + const char *cfgstr = (*env)->GetStringUTFChars(env, cfg, NULL); + if(!cfgstr) + return; + if(zbar_image_scanner_parse_config(GET_PEER(ImageScanner, obj), cfgstr)) + throw_exc(env, "java/lang/IllegalArgumentException", + "unknown configuration"); +} + +JNIEXPORT void JNICALL +Java_net_sourceforge_zbar_ImageScanner_enableCache (JNIEnv *env, + jobject obj, + jboolean enable) +{ + zbar_image_scanner_enable_cache(GET_PEER(ImageScanner, obj), enable); +} + +JNIEXPORT jlong JNICALL +Java_net_sourceforge_zbar_ImageScanner_getResults (JNIEnv *env, + jobject obj, + jlong peer) +{ + const zbar_symbol_set_t *zsyms = + zbar_image_scanner_get_results(PEER_CAST(peer)); + if(zsyms) { + zbar_symbol_set_ref(zsyms, 1); + stats.SymbolSet_create++; + } + return((intptr_t)zsyms); +} + +JNIEXPORT jint JNICALL +Java_net_sourceforge_zbar_ImageScanner_scanImage (JNIEnv *env, + jobject obj, + jobject image) +{ + zbar_image_scanner_t *zscn = GET_PEER(ImageScanner, obj); + zbar_image_t *zimg = GET_PEER(Image, image); + + int n = zbar_scan_image(zscn, zimg); + if(n < 0) + throw_exc(env, "java/lang/UnsupportedOperationException", + "unsupported image format"); + return(n); +} diff --git a/perl/Changes b/perl/Changes --- a/perl/Changes +++ b/perl/Changes @@ -1,5 +1,17 @@ Revision history for Perl extension Barcode::ZBar. +current spadix + * add Symbol orientation and Decoder direction interfaces + +0.04 2009-10-23 spadix + * add result query interfaces to ImageScanner and Processor + +0.03 2009-09-24 spadix + * add support for binary symbol data + * fix symbol leaks + * add symbol quality metric + * add support for QR Code + 0.02 2009-04-16 spadix * project name change: package becomes Barcode::ZBar diff --git a/perl/README b/perl/README --- a/perl/README +++ b/perl/README @@ -1,10 +1,11 @@ Barcode::ZBar Perl module ========================= -The ZBar Bar Code Reader is a library for scanning and decoding bar -codes from various sources such as video streams, image files or raw -intensity sensors. It supports EAN, UPC, Code 128, Code 39 and -Interleaved 2 of 5. These are the Perl bindings for the library. +ZBar Bar Code Reader is an open source software suite for reading bar +codes from various sources, such as video streams, image files and raw +intensity sensors. It supports EAN-13/UPC-A, UPC-E, EAN-8, Code 128, +Code 93, Code 39, Interleaved 2 of 5 and QR Code. These are the Perl +bindings for the library. Check the ZBar project home page for the latest release, mailing lists, etc. @@ -34,4 +35,4 @@ COPYRIGHT AND LICENCE Licensed under the GNU Lesser General Public License, version 2.1. http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt -Copyright 2008-2009 (c) Jeff Brown +Copyright 2008-2010 (c) Jeff Brown diff --git a/perl/ZBar.pm b/perl/ZBar.pm --- a/perl/ZBar.pm +++ b/perl/ZBar.pm @@ -1,5 +1,5 @@ #------------------------------------------------------------------------ -# Copyright 2008-2009 (c) Jeff Brown +# Copyright 2008-2010 (c) Jeff Brown # # This file is part of the ZBar Bar Code Reader. # @@ -137,6 +137,56 @@ Dark area or colored bar segment. =back +Decoder configuration constants: + +=over 4 + +=item Config::ENABLE + +=item Config::ADD_CHECK + +=item Config::EMIT_CHECK + +=item Config::ASCII + +=item Config::MIN_LEN + +=item Config::MAX_LEN + +=item Config::POSITION + +=item Config::X_DENSITY + +=item Config::Y_DENSITY + +=back + +Symbology modifier constants: + +=over 4 + +=item Modifier::GS1 + +=item Modifier::AIM + +=back + +Symbol orientation constants: + +=over 4 + +=item Orient::UNKNOWN + +=item Orient::UP + +=item Orient::RIGHT + +=item Orient::DOWN + +=item Orient::LEFT + +=back + =head1 SEE ALSO @@ -156,7 +206,7 @@ Jeff Brown, Espadix@users.sourceforg =head1 COPYRIGHT AND LICENSE -Copyright 2008-2009 (c) Jeff Brown Espadix@users.sourceforge.netE +Copyright 2008-2010 (c) Jeff Brown Espadix@users.sourceforge.netE 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 diff --git a/perl/ZBar.xs b/perl/ZBar.xs --- a/perl/ZBar.xs +++ b/perl/ZBar.xs @@ -1,5 +1,5 @@ //------------------------------------------------------------------------ -// Copyright 2008-2009 (c) Jeff Brown +// Copyright 2008-2010 (c) Jeff Brown // // This file is part of the ZBar Bar Code Reader. // @@ -53,6 +53,8 @@ static AV *LOOKUP_zbar_color_t = NULL; static AV *LOOKUP_zbar_symbol_type_t = NULL; static AV *LOOKUP_zbar_error_t = NULL; static AV *LOOKUP_zbar_config_t = NULL; +static AV *LOOKUP_zbar_modifier_t = NULL; +static AV *LOOKUP_zbar_orientation_t = NULL; #define CONSTANT(typ, prefix, sym, name) \ do { \ @@ -93,6 +95,15 @@ static inline void check_error (int rc, } \ } while(0); +#define PUSH_ENUM_MASK(typ, TYP, val) \ + do { \ + unsigned mask = (val); \ + int i; \ + for(i = 0; i < ZBAR_ ## TYP ## _NUM; i++, mask >>= 1) \ + if(mask & 1) \ + XPUSHs(LOOKUP_ENUM(typ, i)); \ + } while(0); + static void image_cleanup_handler (zbar_image_t *image) { SV *data = zbar_image_get_userdata(image); @@ -151,7 +162,7 @@ static inline void activate_handler (han SAVETMPS; PUSHMARK(SP); - EXTEND(SP, 2); + EXTEND(SP, 3); PUSHs(sv_mortalcopy(wrap->instance)); if(param) PUSHs(param); @@ -277,11 +288,37 @@ BOOT: CONSTANT(config, CFG_, ASCII, "ascii"); CONSTANT(config, CFG_, MIN_LEN, "min-length"); CONSTANT(config, CFG_, MAX_LEN, "max-length"); + CONSTANT(config, CFG_, UNCERTAINTY, "uncertainty"); CONSTANT(config, CFG_, POSITION, "position"); CONSTANT(config, CFG_, X_DENSITY, "x-density"); CONSTANT(config, CFG_, Y_DENSITY, "y-density"); } +MODULE = Barcode::ZBar PACKAGE = Barcode::ZBar::Modifier PREFIX = zbar_mod_ + +BOOT: + { + HV *stash = gv_stashpv("Barcode::ZBar::Modifier", TRUE); + + LOOKUP_zbar_modifier_t = newAV(); + CONSTANT(modifier, MOD_, GS1, "GS1"); + CONSTANT(modifier, MOD_, AIM, "AIM"); + } + +MODULE = Barcode::ZBar PACKAGE = Barcode::ZBar::Orient PREFIX = zbar_orientation_ + +BOOT: + { + HV *stash = gv_stashpv("Barcode::ZBar::Orient", TRUE); + + LOOKUP_zbar_orientation_t = newAV(); + CONSTANT(orientation, ORIENT_, UNKNOWN, "UNKNOWN"); + CONSTANT(orientation, ORIENT_, UP, "UP"); + CONSTANT(orientation, ORIENT_, RIGHT, "RIGHT"); + CONSTANT(orientation, ORIENT_, DOWN, "DOWN"); + CONSTANT(orientation, ORIENT_, LEFT, "LEFT"); + } + MODULE = Barcode::ZBar PACKAGE = Barcode::ZBar::Symbol PREFIX = zbar_symbol_ @@ -298,10 +335,14 @@ BOOT: CONSTANT(symbol_type, , UPCA, zbar_get_symbol_name(ZBAR_UPCA)); CONSTANT(symbol_type, , EAN13, zbar_get_symbol_name(ZBAR_EAN13)); CONSTANT(symbol_type, , ISBN13, zbar_get_symbol_name(ZBAR_ISBN13)); + CONSTANT(symbol_type, , DATABAR, zbar_get_symbol_name(ZBAR_DATABAR)); + CONSTANT(symbol_type, , DATABAR_EXP, + zbar_get_symbol_name(ZBAR_DATABAR_EXP)); CONSTANT(symbol_type, , I25, zbar_get_symbol_name(ZBAR_I25)); CONSTANT(symbol_type, , CODE39, zbar_get_symbol_name(ZBAR_CODE39)); CONSTANT(symbol_type, , PDF417, zbar_get_symbol_name(ZBAR_PDF417)); CONSTANT(symbol_type, , QRCODE, zbar_get_symbol_name(ZBAR_QRCODE)); + CONSTANT(symbol_type, , CODE93, zbar_get_symbol_name(ZBAR_CODE93)); CONSTANT(symbol_type, , CODE128, zbar_get_symbol_name(ZBAR_CODE128)); } @@ -316,6 +357,18 @@ zbar_symbol_get_type(symbol) Barcode::ZBar::Symbol symbol SV * +zbar_symbol_get_configs(symbol) + Barcode::ZBar::Symbol symbol + PPCODE: + PUSH_ENUM_MASK(config, CFG, zbar_symbol_get_configs(symbol)); + +SV * +zbar_symbol_get_modifiers(symbol) + Barcode::ZBar::Symbol symbol + PPCODE: + PUSH_ENUM_MASK(modifier, MOD, zbar_symbol_get_modifiers(symbol)); + +SV * zbar_symbol_get_data(symbol) Barcode::ZBar::Symbol symbol CODE: @@ -347,6 +400,10 @@ zbar_symbol_get_loc(symbol) av_push(pt, newSVuv(zbar_symbol_get_loc_y(symbol, i))); } +zbar_orientation_t +zbar_symbol_get_orientation(symbol) + Barcode::ZBar::Symbol symbol + SV * get_components(symbol) Barcode::ZBar::Symbol symbol @@ -398,6 +455,19 @@ get_size(image) mPUSHu(zbar_image_get_width(image)); mPUSHu(zbar_image_get_height(image)); +void +get_crop(image) + Barcode::ZBar::Image image + PREINIT: + unsigned x, y, w, h; + PPCODE: + zbar_image_get_crop(image, &x, &y, &w, &h); + EXTEND(SP, 4); + mPUSHu(x); + mPUSHu(y); + mPUSHu(w); + mPUSHu(h); + SV * zbar_image_get_data(image) Barcode::ZBar::Image image @@ -426,8 +496,16 @@ zbar_image_set_sequence(image, seq_num) void zbar_image_set_size(image, width, height) Barcode::ZBar::Image image - unsigned width - unsigned height + int width + if(width < 0) width = 0; + int height + if(height < 0) height = 0; + +void +zbar_image_set_crop(image, x, y, width, height) + Barcode::ZBar::Image image + int x + if(x < 0) { width += x; x = 0; } + int y + if(y < 0) { height += y; y = 0; } + int width + int height void zbar_image_set_data(image, data) @@ -712,6 +790,25 @@ zbar_symbol_type_t zbar_decoder_get_type(decoder) Barcode::ZBar::Decoder decoder +SV * +zbar_decoder_get_configs(decoder, symbology) + Barcode::ZBar::Decoder decoder + zbar_symbol_type_t symbology + PPCODE: + if(symbology == ZBAR_NONE) + symbology = zbar_decoder_get_type(decoder) + PUSH_ENUM_MASK(config, CFG, zbar_decoder_get_configs(decoder, symbology)); + +SV * +zbar_decoder_get_modifiers(decoder) + Barcode::ZBar::Decoder decoder + PPCODE: + PUSH_ENUM_MASK(modifier, MOD, zbar_decoder_get_modifiers(decoder)); + +int +zbar_decoder_get_direction(decoder) + Barcode::ZBar::Decoder decoder + void zbar_decoder_set_handler(decoder, handler = 0, closure = 0) Barcode::ZBar::Decoder decoder diff --git a/perl/ZBar/Image.pod b/perl/ZBar/Image.pod --- a/perl/ZBar/Image.pod +++ b/perl/ZBar/Image.pod @@ -132,7 +132,7 @@ Jeff Brown, Espadix@users.sourceforg =head1 COPYRIGHT AND LICENSE -Copyright 2008-2009 (c) Jeff Brown Espadix@users.sourceforge.netE +Copyright 2008-2010 (c) Jeff Brown Espadix@users.sourceforge.netE 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 diff --git a/perl/ZBar/ImageScanner.pod b/perl/ZBar/ImageScanner.pod --- a/perl/ZBar/ImageScanner.pod +++ b/perl/ZBar/ImageScanner.pod @@ -89,7 +89,7 @@ Jeff Brown, Espadix@users.sourceforg =head1 COPYRIGHT AND LICENSE -Copyright 2008-2009 (c) Jeff Brown Espadix@users.sourceforge.netE +Copyright 2008-2010 (c) Jeff Brown Espadix@users.sourceforge.netE 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 diff --git a/perl/ZBar/Processor.pod b/perl/ZBar/Processor.pod --- a/perl/ZBar/Processor.pod +++ b/perl/ZBar/Processor.pod @@ -137,7 +137,7 @@ Jeff Brown, Espadix@users.sourceforg =head1 COPYRIGHT AND LICENSE -Copyright 2008-2009 (c) Jeff Brown Espadix@users.sourceforge.netE +Copyright 2008-2010 (c) Jeff Brown Espadix@users.sourceforge.netE 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 diff --git a/perl/ZBar/Symbol.pod b/perl/ZBar/Symbol.pod --- a/perl/ZBar/Symbol.pod +++ b/perl/ZBar/Symbol.pod @@ -1,5 +1,5 @@ #------------------------------------------------------------------------ -# Copyright 2008-2009 (c) Jeff Brown +# Copyright 2008-2010 (c) Jeff Brown # # This file is part of the ZBar Bar Code Reader. # @@ -72,6 +72,12 @@ Current cache count of the symbol. This inter-scan reliability and redundancy information if enabled at the Barcode::ZBar::ImageScanner. +=item get_orientation() + +General orientation of decoded symbol. This returns one of the +Barcode::ZBar::Orient constants, which provide a coarse, axis-aligned +indication of symbol orientation. + =item get_components() Components of a composite result. This yields an array of physical @@ -122,6 +128,8 @@ Bar code type "symbology" constants: =item CODE39 +=item CODE93 + =item CODE128 =item QRCODE @@ -144,7 +152,7 @@ Jeff Brown, Espadix@users.sourceforg =head1 COPYRIGHT AND LICENSE -Copyright 2008-2009 (c) Jeff Brown Espadix@users.sourceforge.netE +Copyright 2008-2010 (c) Jeff Brown Espadix@users.sourceforge.netE 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 diff --git a/perl/t/Decoder.t b/perl/t/Decoder.t --- a/perl/t/Decoder.t +++ b/perl/t/Decoder.t @@ -3,7 +3,7 @@ use warnings; use strict; -use Test::More tests => 13; +use Test::More tests => 17; ######################### @@ -19,7 +19,8 @@ isa_ok($decoder, 'Barcode::ZBar::Decoder ######################### can_ok($decoder, qw(set_config parse_config reset new_scan decode_width - get_color get_data get_type set_handler)); + get_color get_configs get_direction get_data get_modifiers + get_type set_handler)); ######################### @@ -58,6 +59,10 @@ is($decoder->get_color(), Barcode::ZBar: ######################### +is($decoder->get_direction(), 0, 'reset direction'); + +######################### + $decoder->set_config(Barcode::ZBar::Symbol::QRCODE, Barcode::ZBar::Config::ENABLE, 0); @@ -74,6 +79,17 @@ is($sym, Barcode::ZBar::Symbol::EAN13, ' ######################### +is_deeply([$decoder->get_configs($sym)], + [Barcode::ZBar::Config::ENABLE, + Barcode::ZBar::Config::EMIT_CHECK], + 'read configs'); + +######################### + +is_deeply([$decoder->get_modifiers()], [], 'read modifiers'); + +######################### + is($decoder->get_data(), '6268964977804', 'EAN-13 data'); ######################### @@ -82,6 +98,10 @@ is($decoder->get_color(), Barcode::ZBar: ######################### +is($decoder->get_direction(), 1, 'decode direction'); + +######################### + is($handler_type, Barcode::ZBar::Symbol::EAN13, 'handler type'); ######################### diff --git a/perl/t/Image.t b/perl/t/Image.t --- a/perl/t/Image.t +++ b/perl/t/Image.t @@ -3,7 +3,7 @@ use warnings; use strict; -use Test::More tests => 22; +use Test::More tests => 29; ######################### @@ -48,6 +48,26 @@ is_deeply([$image->get_size()], [114, 80 ######################### +$image->set_crop(20, 20, 74, 40); +is_deeply([$image->get_crop()], [20, 20, 74, 40], 'crop accessors'); + +######################### + +$image->set_crop(-57, -40, 228, 160); +is_deeply([$image->get_crop()], [0, 0, 114, 80], 'crop clipping'); + +######################### + +$image->set_crop(10, 10, 94, 60); +is_deeply([$image->get_crop()], [10, 10, 94, 60], 'crop accessors'); + +######################### + +$image->set_size(114, 80); +is_deeply([$image->get_crop()], [0, 0, 114, 80], 'crop reset'); + +######################### + # FIXME avoid skipping these (eg embed image vs ImageMagick) SKIP: { eval { require Image::Magick }; @@ -94,7 +114,8 @@ SKIP: { ######################### - can_ok($sym, qw(get_type get_data get_quality get_count get_loc)); + can_ok($sym, qw(get_type get_configs get_modifiers get_data get_quality + get_count get_loc get_orientation)); ######################### @@ -102,6 +123,17 @@ SKIP: { ######################### + is_deeply([$sym->get_configs()], + [Barcode::ZBar::Config::ENABLE, + Barcode::ZBar::Config::EMIT_CHECK], + 'result configs'); + + ######################### + + is_deeply([$sym->get_modifiers()], [], 'result modifiers'); + + ######################### + is($sym->get_data(), '9876543210128', 'result data'); ######################### @@ -133,6 +165,10 @@ SKIP: { ######################### + is($sym->get_orientation(), Barcode::ZBar::Orient::UP, 'orientation'); + + ######################### + my @comps = $sym->get_components(); is(scalar(@comps), 0, 'components size'); diff --git a/perl/t/ZBar.t b/perl/t/ZBar.t --- a/perl/t/ZBar.t +++ b/perl/t/ZBar.t @@ -3,7 +3,7 @@ use warnings; use strict; -use Test::More tests => 3; +use Test::More tests => 37; ######################### @@ -20,3 +20,49 @@ Barcode::ZBar::increase_verbosity(); pass('verbosity'); ######################### + +# performs (2 * n) tests +sub test_enum { + my $name = shift; + foreach my $test (@_) { + my $enum = $test->[0]; + + is($enum, $test->[1], "$name enum/string compare"); + + ######################### + + ok($enum == $test->[2], "$name enum/numeric compare"); + } +} + +test_enum('config', + [Barcode::ZBar::Config::ENABLE, 'enable', 0], + [Barcode::ZBar::Config::ADD_CHECK, 'add-check', 1], + [Barcode::ZBar::Config::EMIT_CHECK, 'emit-check', 2], + [Barcode::ZBar::Config::ASCII, 'ascii', 3], + [Barcode::ZBar::Config::MIN_LEN, 'min-length', 32], + [Barcode::ZBar::Config::MAX_LEN, 'max-length', 33], + [Barcode::ZBar::Config::UNCERTAINTY, 'uncertainty', 64], + [Barcode::ZBar::Config::POSITION, 'position', 128], + [Barcode::ZBar::Config::X_DENSITY, 'x-density', 256], + [Barcode::ZBar::Config::Y_DENSITY, 'y-density', 257], +); + +######################### + +test_enum('modifier', + [Barcode::ZBar::Modifier::GS1, 'GS1', 0], + [Barcode::ZBar::Modifier::AIM, 'AIM', 1], +); + +######################### + +test_enum('orientation', + [Barcode::ZBar::Orient::UNKNOWN, 'UNKNOWN', -1], + [Barcode::ZBar::Orient::UP, 'UP', 0], + [Barcode::ZBar::Orient::RIGHT, 'RIGHT', 1], + [Barcode::ZBar::Orient::DOWN, 'DOWN', 2], + [Barcode::ZBar::Orient::LEFT, 'LEFT', 3], +); + +######################### diff --git a/perl/typemap b/perl/typemap --- a/perl/typemap +++ b/perl/typemap @@ -14,6 +14,8 @@ zbar_color_t T_ENUM zbar_error_t T_ENUM zbar_symbol_type_t T_ENUM zbar_config_t T_ENUM +zbar_modifier_t T_ENUM +zbar_orientation_t T_ENUM # special scalars fourcc_t T_FOURCC @@ -30,14 +32,8 @@ T_ENUM T_FOURCC { if(SvPOK($arg)) { - STRLEN len; - char *str = SvPV($arg, len); - if(len != 4) - croak(\"invalid fourcc: \%s\", str); - $var = ((unsigned long)str[0] | - ((unsigned long)str[1] << 8) | - ((unsigned long)str[2] << 16) | - ((unsigned long)str[3] << 24)); + char *str = SvPV_nolen($arg); + $var = zbar_fourcc_parse(str); } else $var = SvUV($arg); diff --git a/python/MANIFEST.in b/python/MANIFEST.in new file mode 100644 --- /dev/null +++ b/python/MANIFEST.in @@ -0,0 +1,2 @@ +include MANIFEST MANIFEST.in zbarmodule.h test/barcode.png +recursive-include examples *.py diff --git a/python/Makefile.am.inc b/python/Makefile.am.inc --- a/python/Makefile.am.inc +++ b/python/Makefile.am.inc @@ -9,4 +9,5 @@ python_zbar_la_SOURCES = python/zbarmodu python/symboliter.c python/image.c \ python/processor.c python/imagescanner.c python/decoder.c python/scanner.c -EXTRA_DIST += python/test/barcode.png python/test/test_zbar.py +EXTRA_DIST += python/test/barcode.png python/test/test_zbar.py \ + python/examples/processor.py python/examples/read_one.py diff --git a/python/README b/python/README new file mode 100644 --- /dev/null +++ b/python/README @@ -0,0 +1,59 @@ +========================================== +zbar -- read barcodes from images or video +========================================== + +ZBar Bar Code Reader is an open source software suite for reading bar +codes from various sources, such as video streams, image files and raw +intensity sensors. It supports EAN-13/UPC-A, UPC-E, EAN-8, Code 128, +Code 93, Code 39, Interleaved 2 of 5 and QR Code. These are the +Python bindings for the library. + +Check the ZBar project home page for the latest release, mailing +lists, etc. + +* http://zbar.sourceforge.net/ + +Installation +------------ + +To install this module type the following:: + + python setup.py install + +Dependencies +------------ + +This module requires the ZBar Bar Code Reader, which may be obtained +from: + +* http://zbar.sourceforge.net/ + +Windows users please note: the module *will NOT load* unless the ZBar +library DLL (currently libzbar-0.dll) is available in your PATH! + +Examples +-------- + +To scan an image, wrap the raw image data in a ``zbar.Image`` and feed +it to a ``zbar.ImageScanner``:: + + import zbar + scanner = zbar.ImageScanner() + image = zbar.Image(width, height, 'Y800', raw_data) + scanner.scan(image) + for symbol in image: + print 'decoded', symbol.type, 'symbol', '"%s"' % symbol.data + +Complete, runnable examples may be found in the source distribution, +under the ``examples/`` directory. A couple of HOWTOs_ that cover +programming with the library may be found on the project wiki. + +.. _HOWTOs: http://sourceforge.net/apps/mediawiki/zbar/index.php?title=Category:HOWTOs + +Copyright and License +--------------------- + +Licensed under the GNU Lesser General Public License, version 2.1. +http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt + +Copyright 2008-2010 (c) Jeff Brown diff --git a/python/decoder.c b/python/decoder.c --- a/python/decoder.c +++ b/python/decoder.c @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2009 (c) Jeff Brown + * Copyright 2009-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -104,6 +104,23 @@ decoder_get_type (zbarDecoder *self, } static PyObject* +decoder_get_configs (zbarDecoder *self, + void *closure) +{ + unsigned int sym = zbar_decoder_get_type(self->zdcode); + unsigned int mask = zbar_decoder_get_configs(self->zdcode, sym); + return(zbarEnum_SetFromMask(config_enum, mask)); +} + +static PyObject* +decoder_get_modifiers (zbarDecoder *self, + void *closure) +{ + unsigned int mask = zbar_decoder_get_modifiers(self->zdcode); + return(zbarEnum_SetFromMask(modifier_enum, mask)); +} + +static PyObject* decoder_get_data (zbarDecoder *self, void *closure) { @@ -111,10 +128,20 @@ decoder_get_data (zbarDecoder *self, zbar_decoder_get_data_length(self->zdcode))); } +static PyObject* +decoder_get_direction (zbarDecoder *self, + void *closure) +{ + return(PyInt_FromLong(zbar_decoder_get_direction(self->zdcode))); +} + static PyGetSetDef decoder_getset[] = { - { "color", (getter)decoder_get_color, }, - { "type", (getter)decoder_get_type, }, - { "data", (getter)decoder_get_data, }, + { "color", (getter)decoder_get_color, }, + { "type", (getter)decoder_get_type, }, + { "configs", (getter)decoder_get_configs, }, + { "modifiers", (getter)decoder_get_modifiers, }, + { "data", (getter)decoder_get_data, }, + { "direction", (getter)decoder_get_direction }, { NULL, }, }; @@ -139,6 +166,23 @@ decoder_set_config (zbarDecoder *self, } static PyObject* +decoder_get_configs_meth (zbarDecoder *self, + PyObject *args, + PyObject *kwds) +{ + zbar_symbol_type_t sym = ZBAR_NONE; + static char *kwlist[] = { "symbology", NULL }; + if(!PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist, &sym)) + return(NULL); + + if(sym == ZBAR_NONE) + sym = zbar_decoder_get_type(self->zdcode); + + unsigned int mask = zbar_decoder_get_configs(self->zdcode, sym); + return(zbarEnum_SetFromMask(config_enum, mask)); +} + +static PyObject* decoder_parse_config (zbarDecoder *self, PyObject *args, PyObject *kwds) @@ -262,6 +306,8 @@ decoder_decode_width (zbarDecoder *self, static PyMethodDef decoder_methods[] = { { "set_config", (PyCFunction)decoder_set_config, METH_VARARGS | METH_KEYWORDS, }, + { "get_configs", (PyCFunction)decoder_get_configs_meth, + METH_VARARGS | METH_KEYWORDS, }, { "parse_config", (PyCFunction)decoder_parse_config, METH_VARARGS | METH_KEYWORDS, }, { "reset", (PyCFunction)decoder_reset, diff --git a/python/enum.c b/python/enum.c --- a/python/enum.c +++ b/python/enum.c @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2009 (c) Jeff Brown + * Copyright 2009-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -192,3 +192,31 @@ zbarEnum_Add (zbarEnum *self, return(-1); return(0); } + +zbarEnumItem* +zbarEnum_LookupValue (zbarEnum *self, + int val) +{ + PyObject *key = PyInt_FromLong(val); + zbarEnumItem *e = (zbarEnumItem*)PyDict_GetItem(self->byvalue, key); + if(!e) + return((zbarEnumItem*)key); + Py_INCREF((PyObject*)e); + Py_DECREF(key); + return(e); +} + +PyObject* +zbarEnum_SetFromMask (zbarEnum *self, + unsigned int mask) +{ + PyObject *result = PySet_New(NULL); + PyObject *key, *item; + Py_ssize_t i = 0; + while(PyDict_Next(self->byvalue, &i, &key, &item)) { + int val = PyInt_AsLong(item); + if(val < sizeof(mask) * 8 && ((mask >> val) & 1)) + PySet_Add(result, item); + } + return(result); +} diff --git a/examples/processor.py b/python/examples/processor.py rename from examples/processor.py rename to python/examples/processor.py diff --git a/examples/read_one.py b/python/examples/read_one.py rename from examples/read_one.py rename to python/examples/read_one.py diff --git a/examples/scan_image.py b/python/examples/scan_image.py rename from examples/scan_image.py rename to python/examples/scan_image.py diff --git a/python/image.c b/python/image.c --- a/python/image.c +++ b/python/image.c @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2009 (c) Jeff Brown + * Copyright 2009-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -139,16 +139,18 @@ image_set_format (zbarImage *self, PyErr_SetString(PyExc_TypeError, "cannot delete format attribute"); return(-1); } - char *format; + char *format = NULL; Py_ssize_t len; if(PyString_AsStringAndSize(value, &format, &len) || !format || len != 4) { + if(!format) + format = "(nil)"; PyErr_Format(PyExc_ValueError, "format '%.50s' is not a valid four character code", format); return(-1); } - zbar_image_set_format(self->zimg,*((unsigned long*)format)); + zbar_image_set_format(self->zimg, zbar_fourcc_parse(format)); return(0); } @@ -156,9 +158,9 @@ static PyObject* image_get_size (zbarImage *self, void *closure) { - unsigned int width = zbar_image_get_width(self->zimg); - unsigned int height = zbar_image_get_height(self->zimg); - return(PyTuple_Pack(2, PyInt_FromLong(width), PyInt_FromLong(height))); + unsigned int w, h; + zbar_image_get_size(self->zimg, &w, &h); + return(PyTuple_Pack(2, PyInt_FromLong(w), PyInt_FromLong(h))); } static int @@ -170,33 +172,82 @@ image_set_size (zbarImage *self, PyErr_SetString(PyExc_TypeError, "cannot delete size attribute"); return(-1); } - int rc = -1; - PyObject *wobj = NULL, *hobj = NULL; + int rc = -1, i, dims[2]; if(!PySequence_Check(value) || PySequence_Size(value) != 2) goto error; - wobj = PySequence_GetItem(value, 0); - hobj = PySequence_GetItem(value, 1); - if(!wobj || !hobj) - goto error; + for(i = 0; i < 2; i++) { + PyObject *dim = PySequence_GetItem(value, i); + if(!dim) + goto error; + dims[i] = PyInt_AsSsize_t(dim); + Py_DECREF(dim); + if(dims[i] < 0) + goto error; + } - int width = PyInt_AsSsize_t(wobj); - if(width == -1 && PyErr_Occurred()) - goto error; - - int height = PyInt_AsSsize_t(hobj); - if(height == -1 && PyErr_Occurred()) - goto error; - - zbar_image_set_size(self->zimg, width, height); + zbar_image_set_size(self->zimg, dims[0], dims[1]); rc = 0; error: - Py_XDECREF(wobj); - Py_XDECREF(hobj); if(rc) PyErr_SetString(PyExc_ValueError, - "size must be a sequence of two ints"); + "size must be a sequence of two positive ints"); + return(rc); +} + +static PyObject* +image_get_crop (zbarImage *self, + void *closure) +{ + unsigned int x, y, w, h; + zbar_image_get_crop(self->zimg, &x, &y, &w, &h); + return(PyTuple_Pack(4, PyInt_FromLong(x), PyInt_FromLong(y), + PyInt_FromLong(w), PyInt_FromLong(h))); +} + +static int +image_set_crop (zbarImage *self, + PyObject *value, + void *closure) +{ + unsigned w, h; + zbar_image_get_size(self->zimg, &w, &h); + if(!value) { + zbar_image_set_crop(self->zimg, 0, 0, w, h); + return(0); + } + int rc = -1, i, dims[4]; + if(!PySequence_Check(value) || + PySequence_Size(value) != 4) + goto error; + for(i = 0; i < 4; i++) { + PyObject *dim = PySequence_GetItem(value, i); + if(!dim) + goto error; + dims[i] = PyInt_AsSsize_t(dim); + Py_DECREF(dim); + if(dims[i] == -1 && PyErr_Occurred()) + goto error; + } + if(dims[2] < 0 || dims[3] < 0) + goto error; + if(dims[0] < 0) { + dims[2] += dims[0]; + dims[0] = 0; + } + if(dims[1] < 0) { + dims[3] += dims[1]; + dims[1] = 0; + } + + zbar_image_set_crop(self->zimg, dims[0], dims[1], dims[2], dims[3]); + rc = 0; + +error: + if(rc) + PyErr_SetString(PyExc_ValueError, + "crop must be a sequence of four positive ints"); return(rc); } @@ -308,6 +359,7 @@ image_set_data (zbarImage *self, static PyGetSetDef image_getset[] = { { "format", (getter)image_get_format, (setter)image_set_format, }, { "size", (getter)image_get_size, (setter)image_set_size, }, + { "crop", (getter)image_get_crop, (setter)image_set_crop, }, { "width", (getter)image_get_int, (setter)image_set_int, NULL, (void*)0 }, { "height", (getter)image_get_int, (setter)image_set_int, @@ -359,6 +411,7 @@ image_convert (zbarImage *self, format); return(NULL); } + unsigned long fourcc = zbar_fourcc_parse(format); zbarImage *img = PyObject_GC_New(zbarImage, &zbarImage_Type); if(!img) @@ -366,11 +419,9 @@ image_convert (zbarImage *self, img->data = NULL; if(width > 0 && height > 0) img->zimg = - zbar_image_convert_resize(self->zimg, - *((unsigned long*)format), - width, height); + zbar_image_convert_resize(self->zimg, fourcc, width, height); else - img->zimg = zbar_image_convert(self->zimg, *((unsigned long*)format)); + img->zimg = zbar_image_convert(self->zimg, fourcc); if(!img->zimg) { /* FIXME propagate exception */ diff --git a/python/setup.py b/python/setup.py new file mode 100755 --- /dev/null +++ b/python/setup.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +from distutils.core import setup, Extension + +setup( + name = 'zbar', + version = '0.10', + author = 'Jeff Brown', + author_email = 'spadix@users.sourceforge.net', + url = 'http://zbar.sourceforge.net', + description = 'read barcodes from images or video', + license = 'LGPL', + long_description = open('README').read(), + classifiers = [ + 'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)', + 'Development Status :: 4 - Beta', + 'Intended Audience :: Developers', + 'Environment :: Console', + 'Environment :: X11 Applications', + 'Environment :: Win32 (MS Windows)', + 'Operating System :: POSIX', + 'Operating System :: Unix', + 'Operating System :: Microsoft :: Windows', + 'Topic :: Communications', + 'Topic :: Multimedia :: Graphics', + 'Topic :: Software Development :: Libraries', + ], + ext_modules = [ + Extension('zbar', [ + 'zbarmodule.c', + 'enum.c', + 'exception.c', + 'symbol.c', + 'symbolset.c', + 'symboliter.c', + 'image.c', + 'processor.c', + 'imagescanner.c', + 'decoder.c', + 'scanner.c', + ], + libraries = [ 'zbar' ], + ), + ], +) diff --git a/python/symbol.c b/python/symbol.c --- a/python/symbol.c +++ b/python/symbol.c @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2009 (c) Jeff Brown + * Copyright 2009-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -81,6 +81,22 @@ symbol_get_type (zbarSymbol *self, } static PyObject* +symbol_get_configs (zbarSymbol *self, + void *closure) +{ + unsigned int mask = zbar_symbol_get_configs(self->zsym); + return(zbarEnum_SetFromMask(config_enum, mask)); +} + +static PyObject* +symbol_get_modifiers (zbarSymbol *self, + void *closure) +{ + unsigned int mask = zbar_symbol_get_modifiers(self->zsym); + return(zbarEnum_SetFromMask(modifier_enum, mask)); +} + +static PyObject* symbol_get_long (zbarSymbol *self, void *closure) { @@ -128,12 +144,23 @@ symbol_get_location (zbarSymbol *self, return(self->loc); } +static zbarEnumItem* +symbol_get_orientation (zbarSymbol *self, + void *closure) +{ + return(zbarEnum_LookupValue(orient_enum, + zbar_symbol_get_orientation(self->zsym))); +} + static PyGetSetDef symbol_getset[] = { { "type", (getter)symbol_get_type, }, + { "configs", (getter)symbol_get_configs, }, + { "modifiers", (getter)symbol_get_modifiers, }, { "quality", (getter)symbol_get_long, NULL, NULL, (void*)0 }, { "count", (getter)symbol_get_long, NULL, NULL, (void*)1 }, { "data", (getter)symbol_get_data, }, { "location", (getter)symbol_get_location, }, + { "orientation",(getter)symbol_get_orientation, }, { "components", (getter)symbol_get_components, }, { NULL, }, }; diff --git a/python/test/test_zbar.py b/python/test/test_zbar.py --- a/python/test/test_zbar.py +++ b/python/test/test_zbar.py @@ -20,6 +20,9 @@ def load_image(): encoded_widths = \ '9 111 212241113121211311141132 11111 311213121312121332111132 111 9' +databar_widths = \ + '11 31111333 13911 31131231 11214222 11553 21231313 1' + VIDEO_DEVICE = None if 'VIDEO_DEVICE' in os.environ: VIDEO_DEVICE = os.environ['VIDEO_DEVICE'] @@ -59,6 +62,7 @@ class TestZBarFunctions(ut.TestCase): zbar.Config.ASCII, zbar.Config.MIN_LEN, zbar.Config.MAX_LEN, + zbar.Config.UNCERTAINTY, zbar.Config.POSITION, zbar.Config.X_DENSITY, zbar.Config.Y_DENSITY): @@ -66,6 +70,13 @@ class TestZBarFunctions(ut.TestCase): self.assert_(int(cfg) >= 0) self.assert_(is_identifier.match(str(cfg))) + def test_modifiers(self): + for mod in (zbar.Modifier.GS1, + zbar.Modifier.AIM): + self.assert_(isinstance(mod, zbar.EnumItem)) + self.assert_(int(mod) >= 0) + self.assert_(is_identifier.match(str(mod))) + def test_symbologies(self): for sym in (zbar.Symbol.NONE, zbar.Symbol.PARTIAL, @@ -75,15 +86,28 @@ class TestZBarFunctions(ut.TestCase): zbar.Symbol.UPCA, zbar.Symbol.EAN13, zbar.Symbol.ISBN13, + zbar.Symbol.DATABAR, + zbar.Symbol.DATABAR_EXP, zbar.Symbol.I25, zbar.Symbol.CODE39, zbar.Symbol.PDF417, zbar.Symbol.QRCODE, + zbar.Symbol.CODE93, zbar.Symbol.CODE128): self.assert_(isinstance(sym, zbar.EnumItem)) self.assert_(int(sym) >= 0) self.assert_(is_identifier.match(str(sym))) + def test_orientations(self): + for orient in (zbar.Orient.UNKNOWN, + zbar.Orient.UP, + zbar.Orient.RIGHT, + zbar.Orient.DOWN, + zbar.Orient.LEFT): + self.assert_(isinstance(orient, zbar.EnumItem)) + self.assert_(-1 <= int(orient) <= 3) + self.assert_(is_identifier.match(str(orient))) + class TestScanner(ut.TestCase): def setUp(self): self.scn = zbar.Scanner() @@ -135,6 +159,9 @@ class TestDecoder(ut.TestCase): self.dcode.data = data self.assertRaises(AttributeError, set_data, 'yomama') + self.assertRaises(AttributeError, + self.dcode.__setattr__, 'direction', -1) + def test_width(self): sym = self.dcode.decode_width(5) self.assert_(sym is zbar.Symbol.NONE) @@ -150,6 +177,7 @@ class TestDecoder(ut.TestCase): self.assert_(self.dcode.color is zbar.BAR) self.dcode.reset() self.assert_(self.dcode.color is zbar.SPACE) + self.assertEqual(self.dcode.direction, 0) def test_decode(self): inline_sym = [ -1 ] @@ -172,12 +200,33 @@ class TestDecoder(ut.TestCase): else: self.assert_(sym is zbar.Symbol.EAN13) + self.assertEqual(self.dcode.configs, + set((zbar.Config.ENABLE, zbar.Config.EMIT_CHECK))) + self.assertEqual(self.dcode.modifiers, set()) self.assertEqual(self.dcode.data, '6268964977804') self.assert_(self.dcode.color is zbar.BAR) + self.assertEqual(self.dcode.direction, 1) self.assert_(sym is zbar.Symbol.EAN13) self.assert_(inline_sym[0] is zbar.Symbol.EAN13) self.assertEqual(explicit_closure, [ 2 ]) + def test_databar(self): + self.dcode.set_config(zbar.Symbol.QRCODE, zbar.Config.ENABLE, 0) + for (i, width) in enumerate(databar_widths): + if width == ' ': continue + sym = self.dcode.decode_width(int(width)) + if i < len(databar_widths) - 1: + self.assert_(sym is zbar.Symbol.NONE or + sym is zbar.Symbol.PARTIAL) + + self.assert_(sym is zbar.Symbol.DATABAR) + self.assertEqual(self.dcode.get_configs(zbar.Symbol.EAN13), + set((zbar.Config.ENABLE, zbar.Config.EMIT_CHECK))) + self.assertEqual(self.dcode.modifiers, set((zbar.Modifier.GS1,))) + self.assertEqual(self.dcode.data, '0124012345678905') + self.assert_(self.dcode.color is zbar.BAR) + self.assertEqual(self.dcode.direction, 1) + # FIXME test exception during callback class TestImage(ut.TestCase): @@ -194,11 +243,13 @@ class TestImage(ut.TestCase): def test_new(self): self.assertEqual(self.image.format, 'Y800') self.assertEqual(self.image.size, (123, 456)) + self.assertEqual(self.image.crop, (0, 0, 123, 456)) image = zbar.Image() self.assert_(isinstance(image, zbar.Image)) self.assertEqual(image.format, '\0\0\0\0') self.assertEqual(image.size, (0, 0)) + self.assertEqual(image.crop, (0, 0, 0, 0)) def test_format(self): def set_format(fmt): @@ -231,6 +282,23 @@ class TestImage(ut.TestCase): self.assertEqual(self.image.width, 81) self.assertEqual(self.image.height, 64) + def test_crop(self): + def set_crop(crp): + self.image.crop = crp + self.assertRaises(ValueError, set_crop, (1,)) + self.assertRaises(ValueError, set_crop, 1) + self.image.crop = (1, 2, 100, 200) + self.assertRaises(ValueError, set_crop, (1, 2, 3, 4, 5)) + self.assertEqual(self.image.crop, (1, 2, 100, 200)) + self.assertRaises(ValueError, set_crop, "foo") + self.assertEqual(self.image.crop, (1, 2, 100, 200)) + self.image.crop = (-100, -100, 400, 700) + self.assertEqual(self.image.crop, (0, 0, 123, 456)) + self.image.crop = (40, 50, 60, 70) + self.assertEqual(self.image.crop, (40, 50, 60, 70)) + self.image.size = (82, 65) + self.assertEqual(self.image.crop, (0, 0, 82, 65)) + class TestImageScanner(ut.TestCase): def setUp(self): self.scn = zbar.ImageScanner() @@ -289,6 +357,20 @@ class TestImageScan(ut.TestCase): self.assert_(sym.type is zbar.Symbol.EAN13) self.assert_(sym.type is sym.EAN13) self.assertEqual(str(sym.type), 'EAN13') + + cfgs = sym.configs + self.assert_(isinstance(cfgs, set)) + for cfg in cfgs: + self.assert_(isinstance(cfg, zbar.EnumItem)) + self.assertEqual(cfgs, + set((zbar.Config.ENABLE, zbar.Config.EMIT_CHECK))) + + mods = sym.modifiers + self.assert_(isinstance(mods, set)) + for mod in mods: + self.assert_(isinstance(mod, zbar.EnumItem)) + self.assertEqual(mods, set()) + self.assert_(sym.quality > 0) self.assertEqual(sym.count, 0) @@ -310,6 +392,7 @@ class TestImageScan(ut.TestCase): self.assertEqual(len(pt), 2) # FIXME test values (API currently in flux) + self.assert_(sym.orientation is zbar.Orient.UP) self.assert_(data is sym.data) self.assert_(loc is sym.location) @@ -320,6 +403,21 @@ class TestImageScan(ut.TestCase): self.scn.recycle(self.image) self.assertEqual(len(self.image.symbols), 0) + def test_scan_crop(self): + self.image.crop = (0, 71, 114, 9) + self.assertEqual(self.image.crop, (0, 71, 114, 9)) + n = self.scn.scan(self.image) + self.assertEqual(n, 0) + + self.image.crop = (12, 24, 90, 12) + self.assertEqual(self.image.crop, (12, 24, 90, 12)) + n = self.scn.scan(self.image) + self.assertEqual(n, 0) + + self.image.crop = (9, 24, 96, 12) + self.assertEqual(self.image.crop, (9, 24, 96, 12)) + self.test_scan() + def test_scan_again(self): self.test_scan() @@ -370,13 +468,14 @@ class TestProcessor(ut.TestCase): symiter = iter(image) self.assert_(isinstance(symiter, zbar.SymbolIter)) - symbols = tuple(image) - self.assertEqual(len(symbols), 1) - for symbol in symbols: - self.assert_(isinstance(symbol, zbar.Symbol)) - self.assert_(symbol.type is zbar.Symbol.EAN13) - self.assertEqual(symbol.data, '9876543210128') - self.assert_(symbol.quality > 0) + syms = tuple(image) + self.assertEqual(len(syms), 1) + for sym in syms: + self.assert_(isinstance(sym, zbar.Symbol)) + self.assert_(sym.type is zbar.Symbol.EAN13) + self.assertEqual(sym.data, '9876543210128') + self.assert_(sym.quality > 0) + self.assert_(sym.orientation is zbar.Orient.UP) closure[0] += 1 explicit_closure = [ 0 ] diff --git a/python/zbarmodule.c b/python/zbarmodule.c --- a/python/zbarmodule.c +++ b/python/zbarmodule.c @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2009 (c) Jeff Brown + * Copyright 2009-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -23,6 +23,11 @@ #include "zbarmodule.h" +typedef struct enumdef { + const char *strval; + int intval; +} enumdef; + static char *exc_names[] = { "zbar.Exception", NULL, @@ -38,6 +43,55 @@ static char *exc_names[] = { "zbar.WinAPIError", }; +static const enumdef symbol_defs[] = { + { "NONE", ZBAR_NONE }, + { "PARTIAL", ZBAR_PARTIAL }, + { "EAN8", ZBAR_EAN8 }, + { "UPCE", ZBAR_UPCE }, + { "ISBN10", ZBAR_ISBN10 }, + { "UPCA", ZBAR_UPCA }, + { "EAN13", ZBAR_EAN13 }, + { "ISBN13", ZBAR_ISBN13 }, + { "DATABAR", ZBAR_DATABAR }, + { "DATABAR_EXP", ZBAR_DATABAR_EXP }, + { "I25", ZBAR_I25 }, + { "CODE39", ZBAR_CODE39 }, + { "PDF417", ZBAR_PDF417 }, + { "QRCODE", ZBAR_QRCODE }, + { "CODE93", ZBAR_CODE93 }, + { "CODE128", ZBAR_CODE128 }, + { NULL, } +}; + +static const enumdef config_defs[] = { + { "ENABLE", ZBAR_CFG_ENABLE }, + { "ADD_CHECK", ZBAR_CFG_ADD_CHECK }, + { "EMIT_CHECK", ZBAR_CFG_EMIT_CHECK }, + { "ASCII", ZBAR_CFG_ASCII }, + { "MIN_LEN", ZBAR_CFG_MIN_LEN }, + { "MAX_LEN", ZBAR_CFG_MAX_LEN }, + { "UNCERTAINTY", ZBAR_CFG_UNCERTAINTY }, + { "POSITION", ZBAR_CFG_POSITION }, + { "X_DENSITY", ZBAR_CFG_X_DENSITY }, + { "Y_DENSITY", ZBAR_CFG_Y_DENSITY }, + { NULL, } +}; + +static const enumdef modifier_defs[] = { + { "GS1", ZBAR_MOD_GS1 }, + { "AIM", ZBAR_MOD_AIM }, + { NULL, } +}; + +static const enumdef orient_defs[] = { + { "UNKNOWN", ZBAR_ORIENT_UNKNOWN }, + { "UP", ZBAR_ORIENT_UP }, + { "RIGHT", ZBAR_ORIENT_RIGHT }, + { "DOWN", ZBAR_ORIENT_DOWN }, + { "LEFT", ZBAR_ORIENT_LEFT }, + { NULL, } +}; + int object_to_bool (PyObject *obj, int *val) @@ -52,8 +106,10 @@ object_to_bool (PyObject *obj, PyObject *zbar_exc[ZBAR_ERR_NUM]; zbarEnumItem *color_enum[2]; zbarEnum *config_enum; +zbarEnum *modifier_enum; PyObject *symbol_enum; zbarEnumItem *symbol_NONE; +zbarEnum *orient_enum; static PyObject* version (PyObject *self, @@ -105,12 +161,6 @@ static PyMethodDef zbar_functions[] = { PyMODINIT_FUNC initzbar (void) { - /* initialize constant containers */ - config_enum = zbarEnum_New(); - symbol_enum = PyDict_New(); - if(!config_enum || !symbol_enum) - return; - /* initialize types */ zbarEnumItem_Type.tp_base = &PyInt_Type; zbarException_Type.tp_base = (PyTypeObject*)PyExc_Exception; @@ -128,6 +178,14 @@ initzbar (void) PyType_Ready(&zbarScanner_Type) < 0) return; + /* initialize constant containers */ + config_enum = zbarEnum_New(); + modifier_enum = zbarEnum_New(); + symbol_enum = PyDict_New(); + orient_enum = zbarEnum_New(); + if(!config_enum || !modifier_enum || !symbol_enum || !orient_enum) + return; + zbar_exc[0] = (PyObject*)&zbarException_Type; zbar_exc[ZBAR_ERR_NOMEM] = NULL; zbar_error_t ei; @@ -151,6 +209,8 @@ initzbar (void) PyModule_AddObject(mod, "EnumItem", (PyObject*)&zbarEnumItem_Type); PyModule_AddObject(mod, "Image", (PyObject*)&zbarImage_Type); PyModule_AddObject(mod, "Config", (PyObject*)config_enum); + PyModule_AddObject(mod, "Modifier", (PyObject*)modifier_enum); + PyModule_AddObject(mod, "Orient", (PyObject*)orient_enum); PyModule_AddObject(mod, "Symbol", (PyObject*)&zbarSymbol_Type); PyModule_AddObject(mod, "SymbolSet", (PyObject*)&zbarSymbolSet_Type); PyModule_AddObject(mod, "SymbolIter", (PyObject*)&zbarSymbolIter_Type); @@ -170,29 +230,16 @@ initzbar (void) color_enum[ZBAR_BAR] = zbarEnumItem_New(dict, NULL, ZBAR_BAR, "BAR"); - zbarEnum_Add(config_enum, ZBAR_CFG_ENABLE, "ENABLE"); - zbarEnum_Add(config_enum, ZBAR_CFG_ADD_CHECK, "ADD_CHECK"); - zbarEnum_Add(config_enum, ZBAR_CFG_EMIT_CHECK, "EMIT_CHECK"); - zbarEnum_Add(config_enum, ZBAR_CFG_ASCII, "ASCII"); - zbarEnum_Add(config_enum, ZBAR_CFG_MIN_LEN, "MIN_LEN"); - zbarEnum_Add(config_enum, ZBAR_CFG_MAX_LEN, "MAX_LEN"); - zbarEnum_Add(config_enum, ZBAR_CFG_X_DENSITY, "POSITION"); - zbarEnum_Add(config_enum, ZBAR_CFG_X_DENSITY, "X_DENSITY"); - zbarEnum_Add(config_enum, ZBAR_CFG_Y_DENSITY, "Y_DENSITY"); + const enumdef *item; + for(item = config_defs; item->strval; item++) + zbarEnum_Add(config_enum, item->intval, item->strval); + for(item = modifier_defs; item->strval; item++) + zbarEnum_Add(modifier_enum, item->intval, item->strval); + for(item = orient_defs; item->strval; item++) + zbarEnum_Add(orient_enum, item->intval, item->strval); PyObject *tp_dict = zbarSymbol_Type.tp_dict; - symbol_NONE = - zbarEnumItem_New(tp_dict, symbol_enum, ZBAR_NONE, "NONE"); - zbarEnumItem_New(tp_dict, symbol_enum, ZBAR_PARTIAL, "PARTIAL"); - zbarEnumItem_New(tp_dict, symbol_enum, ZBAR_EAN8, "EAN8"); - zbarEnumItem_New(tp_dict, symbol_enum, ZBAR_UPCE, "UPCE"); - zbarEnumItem_New(tp_dict, symbol_enum, ZBAR_ISBN10, "ISBN10"); - zbarEnumItem_New(tp_dict, symbol_enum, ZBAR_UPCA, "UPCA"); - zbarEnumItem_New(tp_dict, symbol_enum, ZBAR_EAN13, "EAN13"); - zbarEnumItem_New(tp_dict, symbol_enum, ZBAR_ISBN13, "ISBN13"); - zbarEnumItem_New(tp_dict, symbol_enum, ZBAR_I25, "I25"); - zbarEnumItem_New(tp_dict, symbol_enum, ZBAR_CODE39, "CODE39"); - zbarEnumItem_New(tp_dict, symbol_enum, ZBAR_PDF417, "PDF417"); - zbarEnumItem_New(tp_dict, symbol_enum, ZBAR_QRCODE, "QRCODE"); - zbarEnumItem_New(tp_dict, symbol_enum, ZBAR_CODE128, "CODE128"); + for(item = symbol_defs; item->strval; item++) + zbarEnumItem_New(tp_dict, symbol_enum, item->intval, item->strval); + symbol_NONE = zbarSymbol_LookupEnum(ZBAR_NONE); } diff --git a/python/zbarmodule.h b/python/zbarmodule.h --- a/python/zbarmodule.h +++ b/python/zbarmodule.h @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2009 (c) Jeff Brown + * Copyright 2009-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -61,6 +61,10 @@ extern zbarEnum *zbarEnum_New(void); extern int zbarEnum_Add(zbarEnum *self, int val, const char *name); +extern zbarEnumItem *zbarEnum_LookupValue(zbarEnum *self, + int val); +extern PyObject *zbarEnum_SetFromMask(zbarEnum *self, + unsigned int mask); typedef struct { PyObject_HEAD @@ -144,8 +148,10 @@ extern PyTypeObject zbarScanner_Type; extern zbarEnumItem *color_enum[2]; extern zbarEnum *config_enum; +extern zbarEnum *modifier_enum; extern PyObject *symbol_enum; extern zbarEnumItem *symbol_NONE; +extern zbarEnum *orient_enum; int object_to_bool(PyObject *obj, int *val); diff --git a/test/Makefile.am.inc b/test/Makefile.am.inc --- a/test/Makefile.am.inc +++ b/test/Makefile.am.inc @@ -67,3 +67,14 @@ EXTRA_DIST += test/test_pygtk.py test/te CLEANFILES += test/.libs/test_decode test/.libs/test_proc \ test/.libs/test_convert test/.libs/test_window \ test/.libs/test_video test/.libs/dbg_scan test/.libs/test_gtk + +check-decoder: test/test_decode + test/test_decode -q + +regress-decoder: test/test_decode + test/test_decode -n 100000 + +check-local: check-decoder check-images +regress: regress-decoder regress-images + +.PHONY: check-decoder check-images regress-decoder regress-images regress diff --git a/test/barcodetest.py b/test/barcodetest.py --- a/test/barcodetest.py +++ b/test/barcodetest.py @@ -124,6 +124,8 @@ def run_zbarimg(images): assert rc in (0, 4), \ 'zbarimg returned error status (%d)\n' % rc + err + assert not err, err + result = ET.XML(xml) assert result.tag == ET.QName(BC, 'barcodes') return result @@ -167,7 +169,8 @@ class BuiltinTestCase(TestCase): href = 'http://zbar.sf.net/test/barcode.png' self.source = src = ET.Element(ET.QName(BC, 'source'), href=href) - sym = ET.SubElement(src, ET.QName(BC, 'symbol'), type='EAN-13') + sym = ET.SubElement(src, ET.QName(BC, 'symbol'), type='EAN-13', + orientation='UP') data = ET.SubElement(sym, ET.QName(BC, 'data')) data.text = '9876543210128' @@ -258,8 +261,9 @@ def compare_indices(expect, actual): def compare_symbols(expect, actual): - pass - + orient = expect.get('orientation') + if orient: + assert actual.get('orientation') == orient # override unittest.TestLoader to populate tests from xml description class TestLoader: diff --git a/test/test_decode.c b/test/test_decode.c --- a/test/test_decode.c +++ b/test/test_decode.c @@ -22,33 +22,86 @@ *------------------------------------------------------------------------*/ #include +#include #include #include #include +#include +#include #include #include zbar_decoder_t *decoder; +zbar_symbol_type_t expect_sym; +char *expect_data = NULL; + +unsigned seed = 0; +int verbosity = 1; +int rnd_size = 9; /* NB should be odd */ +int iter = 0; /* test iteration */ + +#define zprintf(level, format, ...) do { \ + if(verbosity >= (level)) { \ + fprintf(stderr, format , ##__VA_ARGS__); \ + } \ + } while(0) + +static inline void print_sep (int level) +{ + zprintf(level, + "----------------------------------------------------------\n"); +} + static void symbol_handler (zbar_decoder_t *decoder) { zbar_symbol_type_t sym = zbar_decoder_get_type(decoder); - if(sym <= ZBAR_PARTIAL) + if(sym <= ZBAR_PARTIAL || sym == ZBAR_QRCODE) return; - printf("%s%s:%s\n", - zbar_get_symbol_name(sym), - zbar_get_addon_name(sym), - zbar_decoder_get_data(decoder)); - /* FIXME add check! */ + const char *data = zbar_decoder_get_data(decoder); + + int pass = (sym == expect_sym) && !strcmp(data, expect_data) && + zbar_decoder_get_data_length(decoder) == strlen(data); + pass *= 3; + + zprintf(pass, "decode %s:%s\n", zbar_get_symbol_name(sym), data); + + if(!expect_sym) + zprintf(0, "UNEXPECTED!\n"); + else + zprintf(pass, "expect %s:%s\n", zbar_get_symbol_name(expect_sym), + expect_data); + if(!pass) { + zprintf(0, "SEED=%d\n", seed); + abort(); + } + + expect_sym = ZBAR_NONE; + free(expect_data); + expect_data = NULL; +} + +static void expect (zbar_symbol_type_t sym, + const char *data) +{ + if(expect_sym) { + zprintf(0, "MISSING %s:%s\n" + "SEED=%d\n", + zbar_get_symbol_name(expect_sym), expect_data, seed); + abort(); + } + expect_sym = sym; + expect_data = (data) ? strdup(data) : NULL; } static void encode_junk (int n) { - printf("encode random junk...\n"); + if(n > 1) + zprintf(3, "encode random junk...\n"); int i; for(i = 0; i < n; i++) - zbar_decode_width(decoder, 10. * (rand() / (RAND_MAX + 1.))); + zbar_decode_width(decoder, 20. * (rand() / (RAND_MAX + 1.)) + 1); } #define FWD 1 @@ -57,8 +110,8 @@ static void encode_junk (int n) static void encode (uint64_t units, int fwd) { - printf(" raw=%x%x%c\n", (unsigned)(units >> 32), - (unsigned)(units & 0xffffffff), (fwd) ? '<' : '>'); + zprintf(3, " raw=%x%x%c\n", (unsigned)(units >> 32), + (unsigned)(units & 0xffffffff), (fwd) ? '<' : '>'); if(!fwd) while(units && !(units >> 0x3c)) units <<= 4; @@ -122,47 +175,188 @@ static const unsigned int code128[107] = 0x2331112a, /* STOP (6a) */ }; -static void encode_code128b (unsigned char *data) +static void encode_code128b (char *data) { - printf("------------------------------------------------------------\n" - "encode CODE-128(B): %s\n" - " encode START_B: %02x", data, START_B); + assert(zbar_decoder_get_color(decoder) == ZBAR_SPACE); + print_sep(3); + zprintf(2, "CODE-128(B): %s\n", data); + zprintf(3, " encode START_B: %02x", START_B); encode(code128[START_B], 0); int i, chk = START_B; for(i = 0; data[i]; i++) { - printf(" encode '%c': %02x", data[i], data[i] - 0x20); + zprintf(3, " encode '%c': %02x", data[i], data[i] - 0x20); encode(code128[data[i] - 0x20], 0); chk += (i + 1) * (data[i] - 0x20); } chk %= 103; - printf(" encode checksum: %02x", chk); + zprintf(3, " encode checksum: %02x", chk); encode(code128[chk], 0); - printf(" encode STOP: %02x", STOP); + zprintf(3, " encode STOP: %02x", STOP); encode(code128[STOP], 0); - printf("------------------------------------------------------------\n"); + print_sep(3); } -static void encode_code128c (unsigned char *data) +static void encode_code128c (char *data) { - printf("------------------------------------------------------------\n" - "encode CODE-128(C): %s\n" - " encode START_C: %02x", data, START_C); + assert(zbar_decoder_get_color(decoder) == ZBAR_SPACE); + print_sep(3); + zprintf(2, "CODE-128(C): %s\n", data); + zprintf(3, " encode START_C: %02x", START_C); encode(code128[START_C], 0); int i, chk = START_C; for(i = 0; data[i]; i += 2) { assert(data[i] >= '0'); assert(data[i + 1] >= '0'); unsigned char c = (data[i] - '0') * 10 + (data[i + 1] - '0'); - printf(" encode '%c%c': %02d", data[i], data[i + 1], c); + zprintf(3, " encode '%c%c': %02d", data[i], data[i + 1], c); encode(code128[c], 0); chk += (i / 2 + 1) * c; } chk %= 103; - printf(" encode checksum: %02x", chk); + zprintf(3, " encode checksum: %02x", chk); encode(code128[chk], 0); - printf(" encode STOP: %02x", STOP); + zprintf(3, " encode STOP: %02x", STOP); encode(code128[STOP], 0); - printf("------------------------------------------------------------\n"); + print_sep(3); +} + +/*------------------------------------------------------------*/ +/* Code 93 encoding */ + +#define CODE93_START_STOP 0x2f + +static const unsigned int code93[47 + 1] = { + 0x131112, 0x111213, 0x111312, 0x111411, /* 00 */ + 0x121113, 0x121212, 0x121311, 0x111114, + 0x131211, 0x141111, 0x211113, 0x211212, /* 08 */ + 0x211311, 0x221112, 0x221211, 0x231111, + 0x112113, 0x112212, 0x112311, 0x122112, /* 10 */ + 0x132111, 0x111123, 0x111222, 0x111321, + 0x121122, 0x131121, 0x212112, 0x212211, /* 18 */ + 0x211122, 0x211221, 0x221121, 0x222111, + 0x112122, 0x112221, 0x122121, 0x123111, /* 20 */ + 0x121131, 0x311112, 0x311211, 0x321111, + 0x112131, 0x113121, 0x211131, 0x121221, /* 28 */ + 0x312111, 0x311121, 0x122211, + 0x111141, /* START/STOP (2f) */ +}; + +#define S1 0x2b00| +#define S2 0x2c00| +#define S3 0x2d00| +#define S4 0x2e00| + +static const unsigned short code93_ext[0x80] = { + S2'U', S1'A', S1'B', S1'C', S1'D', S1'E', S1'F', S1'G', + S1'H', S1'I', S1'J', S1'K', S1'L', S1'M', S1'N', S1'O', + S1'P', S1'Q', S1'R', S1'S', S1'T', S1'U', S1'V', S1'W', + S1'X', S1'Y', S1'Z', S2'A', S2'B', S2'C', S2'D', S2'E', + 0x26, S3'A', S3'B', S3'C', 0x27, 0x2a, S3'F', S3'G', + S3'H', S3'I', S3'J', 0x29, S3'L', 0x24, 0x25, 0x28, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, S3'Z', S2'F', S2'G', S2'H', S2'I', S2'J', + S2'V', 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, + 0x21, 0x22, 0x23, S2'K', S2'L', S2'M', S2'N', S2'O', + S2'W', S4'A', S4'B', S4'C', S4'D', S4'E', S4'F', S4'G', + S4'H', S4'I', S4'J', S4'K', S4'L', S4'M', S4'N', S4'O', + S4'P', S4'Q', S4'R', S4'S', S4'T', S4'U', S4'V', S4'W', + S4'X', S4'Y', S4'Z', S2'P', S2'Q', S2'R', S2'S', S2'T', +}; + +#undef S1 +#undef S2 +#undef S3 +#undef S4 + +static void encode_char93 (unsigned char c, + int dir) +{ + unsigned ext = code93_ext[c]; + unsigned shift = ext >> 8; + assert(shift < 0x30); + c = ext & 0xff; + if(shift) { + assert(c < 0x80); + c = code93_ext[c]; + } + assert(c < 0x30); + + if(shift) { + encode(code93[(dir) ? shift : c], dir ^ 1); + encode(code93[(dir) ? c : shift], dir ^ 1); + } + else + encode(code93[c], dir ^ 1); +} + +static void encode_code93 (char *data, + int dir) +{ + assert(zbar_decoder_get_color(decoder) == ZBAR_SPACE); + print_sep(3); + + /* calculate checksums */ + int i, j, chk_c = 0, chk_k = 0, n = 0; + for(i = 0; data[i]; i++, n++) { + unsigned c = data[i], ext; + assert(c < 0x80); + ext = code93_ext[c]; + n += ext >> 13; + } + + for(i = 0, j = 0; data[i]; i++, j++) { + unsigned ext = code93_ext[(unsigned)data[i]]; + unsigned shift = ext >> 8; + unsigned c = ext & 0xff; + if(shift) { + chk_c += shift * (((n - 1 - j) % 20) + 1); + chk_k += shift * (((n - j) % 15) + 1); + j++; + c = code93_ext[c]; + } + chk_c += c * (((n - 1 - j) % 20) + 1); + chk_k += c * (((n - j) % 15) + 1); + } + chk_c %= 47; + chk_k += chk_c; + chk_k %= 47; + + zprintf(2, "CODE-93: %s (n=%x C=%02x K=%02x)\n", data, n, chk_c, chk_k); + encode(0xa, 0); /* leading quiet */ + + zprintf(3, " encode %s:", (dir) ? "START" : "STOP"); + if(!dir) + encode(0x1, REV); + encode(code93[CODE93_START_STOP], dir ^ 1); + if(!dir) { + zprintf(3, " encode checksum (K): %02x", chk_k); + encode(code93[chk_k], REV ^ 1); + zprintf(3, " encode checksum (C): %02x", chk_c); + encode(code93[chk_c], REV ^ 1); + } + + n = strlen(data); + for(i = 0; i < n; i++) { + unsigned char c = data[(dir) ? i : (n - i - 1)]; + zprintf(3, " encode '%c':", c); + encode_char93(c, dir); + } + + if(dir) { + zprintf(3, " encode checksum (C): %02x", chk_c); + encode(code93[chk_c], FWD ^ 1); + zprintf(3, " encode checksum (K): %02x", chk_k); + encode(code93[chk_k], FWD ^ 1); + } + zprintf(3, " encode %s:", (dir) ? "STOP" : "START"); + encode(code93[CODE93_START_STOP], dir ^ 1); + if(dir) + encode(0x1, FWD); + + encode(0xa, 0); /* trailing quiet */ + print_sep(3); } /*------------------------------------------------------------*/ @@ -182,14 +376,29 @@ static const unsigned int code39[91-32] /* FIXME configurable/randomized ratio, ics */ /* FIXME check digit option, ASCII escapes */ +static void convert_code39 (char *data) +{ + char *src, *dst; + for(src = data, dst = data; *src; src++) { + char c = *src; + if(c >= 'a' && c <= 'z') + *(dst++) = c - ('a' - 'A'); + else if(c == ' ' || + c == '$' || c == '%' || + c == '+' || c == '-' || + (c >= '.' && c <= '9') || + (c >= 'A' && c <= 'Z')) + *(dst++) = c; + else + /* skip (FIXME) */; + } + *dst = 0; +} + static void encode_char39 (unsigned char c, unsigned ics) { - if(c >= 'a' && c <= 'z') - c -= 'a' - 'A'; - else if(c < 0x20 || c > 0x5a) - return; /* skip (FIXME) */ - + assert(0x20 <= c && c <= 0x5a); unsigned int raw = code39[c - 0x20]; if(!raw) return; /* skip (FIXME) */ @@ -201,15 +410,16 @@ static void encode_char39 (unsigned char raw <<= 1; } enc = (enc << 4) | ics; - printf(" encode '%c': %02x%08x: ", c, - (unsigned)(enc >> 32), (unsigned)(enc & 0xffffffff)); + zprintf(3, " encode '%c': %02x%08x: ", c, + (unsigned)(enc >> 32), (unsigned)(enc & 0xffffffff)); encode(enc, REV); } -static void encode_code39 (unsigned char *data) +static void encode_code39 (char *data) { - printf("------------------------------------------------------------\n" - "encode CODE-39: %s\n", data); + assert(zbar_decoder_get_color(decoder) == ZBAR_SPACE); + print_sep(3); + zprintf(2, "CODE-39: %s\n", data); encode(0xa, 0); /* leading quiet */ encode_char39('*', 1); int i; @@ -217,10 +427,10 @@ static void encode_code39 (unsigned char if(data[i] != '*') /* skip (FIXME) */ encode_char39(data[i], 1); encode_char39('*', 0xa); /* w/trailing quiet */ - printf("------------------------------------------------------------\n"); + print_sep(3); } - +#if 0 /*------------------------------------------------------------*/ /* PDF417 encoding */ @@ -258,32 +468,33 @@ static void encode_row417 (int r, { int k = r % 3; - printf(" [%d] encode %s:", r, (dir) ? "stop" : "start"); + zprintf(3, " [%d] encode %s:", r, (dir) ? "stop" : "start"); encode((dir) ? PDF417_STOP : PDF417_START, dir); int cw = calc_ind417(k + !dir, r, cols); - printf(" [%d,%c] encode %03d(%d): ", r, (dir) ? 'R' : 'L', cw, k); + zprintf(3, " [%d,%c] encode %03d(%d): ", r, (dir) ? 'R' : 'L', cw, k); encode(pdf417_encode[cw][k], dir); int c; for(c = 0; c < cols; c++) { cw = cws[c]; - printf(" [%d,%d] encode %03d(%d): ", r, c, cw, k); + zprintf(3, " [%d,%d] encode %03d(%d): ", r, c, cw, k); encode(pdf417_encode[cw][k], dir); } cw = calc_ind417(k + dir, r, cols); - printf(" [%d,%c] encode %03d(%d): ", r, (dir) ? 'L' : 'R', cw, k); + zprintf(3, " [%d,%c] encode %03d(%d): ", r, (dir) ? 'L' : 'R', cw, k); encode(pdf417_encode[cw][k], dir); - printf(" [%d] encode %s:", r, (dir) ? "start" : "stop"); + zprintf(3, " [%d] encode %s:", r, (dir) ? "start" : "stop"); encode((dir) ? PDF417_START : PDF417_STOP, dir); } -static void encode_pdf417 (unsigned char *data) +static void encode_pdf417 (char *data) { - printf("------------------------------------------------------------\n" - "encode PDF417: hello world\n"); + assert(zbar_decoder_get_color(decoder) == ZBAR_SPACE); + print_sep(3); + zprintf(2, "PDF417: hello world\n"); encode(0xa, 0); int r; @@ -292,9 +503,9 @@ static void encode_pdf417 (unsigned char encode(0xa, 0); } - printf("------------------------------------------------------------\n"); + print_sep(3); } - +#endif /*------------------------------------------------------------*/ /* Interleaved 2 of 5 encoding */ @@ -303,21 +514,22 @@ static const unsigned char i25[10] = { 0x06, 0x11, 0x09, 0x18, 0x05, 0x14, 0x0c, 0x03, 0x12, 0x0a, }; -static void encode_i25 (unsigned char *data, +static void encode_i25 (char *data, int dir) { - printf("------------------------------------------------------------\n" - "encode Interleaved 2 of 5: %s\n" - " encode start:", data); + assert(zbar_decoder_get_color(decoder) == ZBAR_SPACE); + print_sep(3); + zprintf(2, "Interleaved 2 of 5: %s\n", data); + zprintf(3, " encode start:"); encode((dir) ? 0xa1111 : 0xa112, 0); /* FIXME rev case data reversal */ int i; - for(i = (strlen((char*)data) & 1) ? -1 : 0; i < 0 || data[i]; i += 2) { + for(i = (strlen(data) & 1) ? -1 : 0; i < 0 || data[i]; i += 2) { /* encode 2 digits */ unsigned char c0 = (i < 0) ? 0 : data[i] - '0'; unsigned char c1 = data[i + 1] - '0'; - printf(" encode '%d%d':", c0, c1); + zprintf(3, " encode '%d%d':", c0, c1); assert(c0 < 10); assert(c1 < 10); @@ -337,12 +549,252 @@ static void encode_i25 (unsigned char *d encode(enc, dir); } - printf(" encode end:"); + zprintf(3, " encode end:"); encode((dir) ? 0x211a : 0x1111a, 0); - printf("------------------------------------------------------------\n"); + print_sep(3); } /*------------------------------------------------------------*/ +/* DataBar encoding */ + + +/* character encoder reference algorithm from ISO/IEC 24724:2009 */ + +struct rss_group { + int T_odd, T_even, n_odd, w_max; +}; + +static const struct rss_group databar_groups_outside[] = { + { 161, 1, 12, 8 }, + { 80, 10, 10, 6 }, + { 31, 34, 8, 4 }, + { 10, 70, 6, 3 }, + { 1, 126, 4, 1 }, + { 0, } +}; + +static const struct rss_group databar_groups_inside[] = { + { 4, 84, 5, 2 }, + { 20, 35, 7, 4 }, + { 48, 10, 9, 6 }, + { 81, 1, 11, 8 }, + { 0, } +}; + +static const uint32_t databar_finders[9] = { + 0x38211, 0x35511, 0x33711, 0x31911, 0x27411, + 0x25611, 0x23811, 0x15711, 0x13911, +}; + +int combins (int n, + int r) +{ + int i, j; + int maxDenom, minDenom; + int val; + if(n-r > r) { + minDenom = r; + maxDenom = n-r; + } + else { + minDenom = n-r; + maxDenom = r; + } + val = 1; + j = 1; + for(i = n; i > maxDenom; i--) { + val *= i; + if(j <= minDenom) { + val /= j; + j++; + } + } + for(; j <= minDenom; j++) + val /= j; + return(val); +} + +void getRSSWidths (int val, + int n, + int elements, + int maxWidth, + int noNarrow, + int *widths) +{ + int narrowMask = 0; + int bar; + for(bar = 0; bar < elements - 1; bar++) { + int elmWidth, subVal; + for(elmWidth = 1, narrowMask |= (1<= elements-bar-1)) + subVal -= combins(n-elmWidth-(elements-bar), elements-bar-2); + if(elements-bar-1 > 1) { + int mxwElement, lessVal = 0; + for (mxwElement = n-elmWidth-(elements-bar-2); + mxwElement > maxWidth; + mxwElement--) + lessVal += combins(n-elmWidth-mxwElement-1, elements-bar-3); + subVal -= lessVal * (elements-1-bar); + } + else if (n-elmWidth > maxWidth) + subVal--; + val -= subVal; + if(val < 0) + break; + } + val += subVal; + n -= elmWidth; + widths[bar] = elmWidth; + } + widths[bar] = n; +} + +static uint64_t encode_databar_char (unsigned val, + const struct rss_group *grp, + int nmodules, + int nelems, + int dir) +{ + int G_sum = 0; + while(1) { + assert(grp->T_odd); + int sum = G_sum + grp->T_odd * grp->T_even; + if(val >= sum) + G_sum = sum; + else + break; + grp++; + } + + zprintf(3, "char=%d", val); + + int V_grp = val - G_sum; + int V_odd, V_even; + if(!dir) { + V_odd = V_grp / grp->T_even; + V_even = V_grp % grp->T_even; + } + else { + V_even = V_grp / grp->T_odd; + V_odd = V_grp % grp->T_odd; + } + + zprintf(3, " G_sum=%d T_odd=%d T_even=%d n_odd=%d w_max=%d V_grp=%d\n", + G_sum, grp->T_odd, grp->T_even, grp->n_odd, grp->w_max, V_grp); + + int odd[16]; + getRSSWidths(V_odd, grp->n_odd, nelems, grp->w_max, !dir, odd); + zprintf(3, " V_odd=%d odd=%d%d%d%d", + V_odd, odd[0], odd[1], odd[2], odd[3]); + + int even[16]; + getRSSWidths(V_even, nmodules - grp->n_odd, nelems, 9 - grp->w_max, + dir, even); + zprintf(3, " V_even=%d even=%d%d%d%d", + V_even, even[0], even[1], even[2], even[3]); + + uint64_t units = 0; + int i; + for(i = 0; i < nelems; i++) + units = (units << 8) | (odd[i] << 4) | even[i]; + + zprintf(3, " raw=%"PRIx64"\n", units); + return(units); +} + +#define SWAP(a, b) do { \ + uint32_t tmp = (a); \ + (a) = (b); \ + (b) = tmp; \ + } while(0); + +static void encode_databar (char *data, + int dir) +{ + assert(zbar_decoder_get_color(decoder) == ZBAR_SPACE); + + print_sep(3); + zprintf(2, "DataBar: %s\n", data); + + uint32_t v[4] = { 0, }; + int i, j; + for(i = 0; i < 14; i++) { + for(j = 0; j < 4; j++) + v[j] *= 10; + assert(data[i]); + v[0] += data[i] - '0'; + v[1] += v[0] / 1597; + v[0] %= 1597; + v[2] += v[1] / 2841; + v[1] %= 2841; + v[3] += v[2] / 1597; + v[2] %= 1597; + /*printf(" [%d] %c (%d,%d,%d,%d)\n", + i, data[i], v[0], v[1], v[2], v[3]);*/ + } + zprintf(3, "chars=(%d,%d,%d,%d)\n", v[3], v[2], v[1], v[0]); + + uint32_t c[4] = { + encode_databar_char(v[3], databar_groups_outside, 16, 4, 0), + encode_databar_char(v[2], databar_groups_inside, 15, 4, 1), + encode_databar_char(v[1], databar_groups_outside, 16, 4, 0), + encode_databar_char(v[0], databar_groups_inside, 15, 4, 1), + }; + + int chk = 0, w = 1; + for(i = 0; i < 4; i++, chk %= 79, w %= 79) + for(j = 0; j < 8; j++, w *= 3) + chk += ((c[i] >> (28 - j * 4)) & 0xf) * w; + zprintf(3, "chk=%d\n", chk); + + if(chk >= 8) chk++; + if(chk >= 72) chk++; + int C_left = chk / 9; + int C_right = chk % 9; + + if(dir == REV) { + SWAP(C_left, C_right); + SWAP(c[0], c[2]); + SWAP(c[1], c[3]); + SWAP(v[0], v[2]); + SWAP(v[1], v[3]); + } + + zprintf(3, " encode start guard:"); + encode_junk(dir); + encode(0x1, FWD); + + zprintf(3, "encode char[0]=%d", v[3]); + encode(c[0], REV); + + zprintf(3, "encode left finder=%d", C_left); + encode(databar_finders[C_left], REV); + + zprintf(3, "encode char[1]=%d", v[2]); + encode(c[1], FWD); + + zprintf(3, "encode char[3]=%d", v[0]); + encode(c[3], REV); + + zprintf(3, "encode right finder=%d", C_right); + encode(databar_finders[C_right], FWD); + + zprintf(3, "encode char[2]=%d", v[1]); + encode(c[2], FWD); + + zprintf(3, " encode end guard:"); + encode(0x1, FWD); + encode_junk(!dir); + print_sep(3); +} + + +/*------------------------------------------------------------*/ /* EAN/UPC encoding */ static const unsigned int ean_digits[10] = { @@ -372,7 +824,7 @@ static const unsigned char ean_parity_en 0x25, /* ABBABA = 9 */ }; -static void calc_ean_parity (unsigned char *data, +static void calc_ean_parity (char *data, int n) { int i, chk = 0; @@ -387,119 +839,293 @@ static void calc_ean_parity (unsigned ch data[i] = 0; } -static void encode_ean13 (unsigned char *data) +static void encode_ean13 (char *data) { int i; unsigned char par = ean_parity_encode[data[0] - '0']; + assert(zbar_decoder_get_color(decoder) == ZBAR_SPACE); - printf("------------------------------------------------------------\n" - "encode EAN-13: %s (%02x)\n" - " encode start guard:", - data, par); + print_sep(3); + zprintf(2, "EAN-13: %s (%02x)\n", data, par); + zprintf(3, " encode start guard:"); encode(ean_guard[3], FWD); for(i = 1; i < 7; i++, par <<= 1) { - printf(" encode %x%c:", (par >> 5) & 1, data[i]); - encode(ean_digits[data[i] - '0'], REV ^ ((par >> 5) & 1)); + zprintf(3, " encode %x%c:", (par >> 5) & 1, data[i]); + encode(ean_digits[data[i] - '0'], (par >> 5) & 1); } - printf(" encode center guard:"); + zprintf(3, " encode center guard:"); encode(ean_guard[5], FWD); for(; i < 13; i++) { - printf(" encode %x%c:", 0, data[i]); + zprintf(3, " encode %x%c:", 0, data[i]); encode(ean_digits[data[i] - '0'], FWD); } - printf(" encode end guard:"); + zprintf(3, " encode end guard:"); encode(ean_guard[3], REV); - printf("------------------------------------------------------------\n"); + print_sep(3); } -static void encode_ean8 (unsigned char *data) +static void encode_ean8 (char *data) { int i; - printf("------------------------------------------------------------\n" - "encode EAN-8: %s\n" - " encode start guard:", - data); + assert(zbar_decoder_get_color(decoder) == ZBAR_SPACE); + print_sep(3); + zprintf(2, "EAN-8: %s\n", data); + zprintf(3, " encode start guard:"); encode(ean_guard[3], FWD); for(i = 0; i < 4; i++) { - printf(" encode %c:", data[i]); + zprintf(3, " encode %c:", data[i]); encode(ean_digits[data[i] - '0'], FWD); } - printf(" encode center guard:"); + zprintf(3, " encode center guard:"); encode(ean_guard[5], FWD); for(; i < 8; i++) { - printf(" encode %c:", data[i]); + zprintf(3, " encode %c:", data[i]); encode(ean_digits[data[i] - '0'], FWD); } - printf(" encode end guard:"); + zprintf(3, " encode end guard:"); encode(ean_guard[3], REV); - printf("------------------------------------------------------------\n"); + print_sep(3); } /*------------------------------------------------------------*/ /* main test flow */ -int main (int argc, char **argv) +int test_databar_F_1 () { - int i; - int rnd_size = 9; /* should be odd */ - srand(0xbabeface); + expect(ZBAR_DATABAR, "0124012345678905"); + assert(zbar_decoder_get_color(decoder) == ZBAR_SPACE); + encode(0x11, 0); + encode(0x31111333, 0); + encode(0x13911, 0); + encode(0x31131231, 0); + encode(0x11214222, 0); + encode(0x11553, 0); + encode(0x21231313, 0); + encode(0x1, 0); + encode_junk(rnd_size); + return(0); +} - /* FIXME TBD: - * - random module width (!= 1.0) - * - simulate scan speed variance - * - simulate dark "swelling" and light "blooming" - * - inject parity errors - */ - decoder = zbar_decoder_create(); - zbar_decoder_set_handler(decoder, symbol_handler); - zbar_decoder_set_config(decoder, 0, ZBAR_CFG_MIN_LEN, 0); +int test_databar_F_3 () +{ + expect(ZBAR_DATABAR_EXP, "1012A"); + assert(zbar_decoder_get_color(decoder) == ZBAR_SPACE); + encode(0x11, 0); + encode(0x11521151, 0); + encode(0x18411, 0); + encode(0x13171121, 0); + encode(0x11521232, 0); + encode(0x11481, 0); + encode(0x23171111, 0); + encode(0x1, 0); + encode_junk(rnd_size); + return(0); +} - encode_junk(rnd_size + 1); +int test_orange () +{ + char data[32] = "0100845963000052"; + expect(ZBAR_DATABAR, data); + assert(zbar_decoder_get_color(decoder) == ZBAR_SPACE); + encode(0x1, 0); + encode(0x23212321, 0); // data[0] + encode(0x31911, 0); // finder[?] = 3 + encode(0x21121215, 1); // data[1] + encode(0x41111133, 0); // data[3] + encode(0x23811, 1); // finder[?] = 6 + encode(0x11215141, 1); // data[2] + encode(0x11, 0); + encode_junk(rnd_size); - unsigned char data[32] = { 0 }; - for(i = 0; i < 12; i++) - data[i] = (rand() % 10) + '0'; + expect(ZBAR_DATABAR, data); + data[1] = '0'; + encode_databar(data + 1, FWD); + encode_junk(rnd_size); + return(0); +} - calc_ean_parity(data, 12); - encode_ean13(data); +int test_numeric (char *data) +{ + char tmp[32] = "01"; + strncpy(tmp + 2, data + 1, 13); + calc_ean_parity(tmp + 2, 13); + expect(ZBAR_DATABAR, tmp); + + tmp[1] = data[0] & '1'; + encode_databar(tmp + 1, (rand() >> 8) & 1); encode_junk(rnd_size); - data[i] = 0; + data[strlen(data) & ~1] = 0; + expect(ZBAR_CODE128, data); encode_code128c(data); encode_junk(rnd_size); + expect(ZBAR_I25, data); encode_i25(data, FWD); encode_junk(rnd_size); +#if 0 /* FIXME encoding broken */ encode_i25(data, REV); encode_junk(rnd_size); +#endif - calc_ean_parity(data, 7); - encode_ean8(data); + calc_ean_parity(data + 2, 12); + expect(ZBAR_EAN13, data + 2); + encode_ean13(data + 2); encode_junk(rnd_size); + calc_ean_parity(data + 7, 7); + expect(ZBAR_EAN8, data + 7); + encode_ean8(data + 7); + + encode_junk(rnd_size); + + expect(ZBAR_NONE, NULL); + return(0); +} + +int test_alpha (char *data) +{ + expect(ZBAR_CODE128, data); + encode_code128b(data); + + encode_junk(rnd_size); + + expect(ZBAR_CODE93, data); + encode_code93(data, FWD); + + encode_junk(rnd_size); + + expect(ZBAR_CODE93, data); + encode_code93(data, REV); + + encode_junk(rnd_size); + + /*encode_code39("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%");*/ + convert_code39(data); + expect(ZBAR_CODE39, data); + encode_code39(data); + + encode_junk(rnd_size); + +#if 0 /* FIXME decoder unfinished */ + encode_pdf417(data); + + encode_junk(rnd_size); +#endif + + expect(ZBAR_NONE, NULL); + return(0); +} + +int test1 () +{ + print_sep(2); + if(!seed) + seed = 0xbabeface; + zprintf(1, "[%d] SEED=%d\n", iter++, seed); + srand(seed); + + int i; + char data[32] = { 0, }; + for(i = 0; i < 14; i++) + data[i] = (rand() % 10) + '0'; + + test_numeric(data); + for(i = 0; i < 10; i++) data[i] = (rand() % 0x5f) + 0x20; data[i] = 0; - encode_code128b(data); + test_alpha(data); + return(0); +} - encode_junk(rnd_size); +/* FIXME TBD: + * - random module width (!= 1.0) + * - simulate scan speed variance + * - simulate dark "swelling" and light "blooming" + * - inject parity errors + */ - /*encode_code39("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%");*/ - encode_code39(data); +int main (int argc, char **argv) +{ + int n, i, j; + char *end; - encode_junk(rnd_size); + decoder = zbar_decoder_create(); + zbar_decoder_set_handler(decoder, symbol_handler); - encode_pdf417(data); + encode_junk(rnd_size + 1); - encode_junk(rnd_size); + for(i = 1; i < argc; i++) { + if(argv[i][0] != '-') { + fprintf(stderr, "ERROR: unknown argument: %s\n", argv[i]); + return(2); + } + for(j = 1; argv[i][j]; j++) { + switch(argv[i][j]) + { + case 'q': verbosity = 0; break; + case 'v': verbosity++; break; + case 'r': + seed = time(NULL); + srand(seed); + seed = (rand() << 8) ^ rand(); + zprintf(0, "-r SEED=%d\n", seed); + break; + + case 's': + if(!argv[i][++j] && !(j = 0) && ++i >= argc) { + fprintf(stderr, "ERROR: -s needs argument\n"); + return(2); + } + seed = strtol(argv[i] + j, &end, 0); + if((!isdigit(argv[i][j]) && argv[i][j] != '-') || + !seed || seed == LONG_MAX || seed == LONG_MIN) { + fprintf(stderr, "ERROR: invalid : \"%s\"\n", + argv[i] + j); + return(2); + } + j = end - argv[i] - 1; + break; + + case 'n': + if(!argv[i][++j] && !(j = 0) && ++i >= argc) { + fprintf(stderr, "ERROR: -n needs argument\n"); + return(2); + } + n = strtol(argv[i] + j, &end, 0); + if(!isdigit(argv[i][j]) || !n) { + fprintf(stderr, "ERROR: invalid : \"%s\"\n", + argv[i] + j); + return(2); + } + j = end - argv[i] - 1; + + while(n--) { + test1(); + seed = (rand() << 8) ^ rand(); + } + break; + } + } + } + + if(!iter) { + test_databar_F_1(); + test_databar_F_3(); + test_orange(); + test1(); + } + + /* FIXME "Ran %d iterations in %gs\n\nOK\n" */ zbar_decoder_destroy(decoder); return(0); diff --git a/test/test_images.h b/test/test_images.h --- a/test/test_images.h +++ b/test/test_images.h @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2007-2009 (c) Jeff Brown + * Copyright 2007-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -23,10 +23,7 @@ #ifndef _TEST_IMAGES_H_ #define _TEST_IMAGES_H_ -/* adapted from v4l2 spec */ -#define fourcc(a, b, c, d) \ - ((uint32_t)(a) | ((uint32_t)(b) << 8) | \ - ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24)) +#define fourcc zbar_fourcc int test_image_check_cleanup(void); int test_image_bars(zbar_image_t*); diff --git a/zbar/Makefile.am.inc b/zbar/Makefile.am.inc --- a/zbar/Makefile.am.inc +++ b/zbar/Makefile.am.inc @@ -19,9 +19,15 @@ EXTRA_zbar_libzbar_la_SOURCES = zbar/svg if ENABLE_EAN zbar_libzbar_la_SOURCES += zbar/decoder/ean.h zbar/decoder/ean.c endif +if ENABLE_DATABAR +zbar_libzbar_la_SOURCES += zbar/decoder/databar.h zbar/decoder/databar.c +endif if ENABLE_CODE128 zbar_libzbar_la_SOURCES += zbar/decoder/code128.h zbar/decoder/code128.c endif +if ENABLE_CODE93 +zbar_libzbar_la_SOURCES += zbar/decoder/code93.h zbar/decoder/code93.c +endif if ENABLE_CODE39 zbar_libzbar_la_SOURCES += zbar/decoder/code39.h zbar/decoder/code39.c endif diff --git a/zbar/config.c b/zbar/config.c --- a/zbar/config.c +++ b/zbar/config.c @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2008-2009 (c) Jeff Brown + * Copyright 2008-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -24,20 +24,24 @@ #include #include /* strtol */ #include /* strchr, strncmp, strlen */ -#include -#include +#ifdef HAVE_ERRNO_H +# include +#endif #include int zbar_parse_config (const char *cfgstr, - zbar_symbol_type_t *sym, - zbar_config_t *cfg, - int *val) + zbar_symbol_type_t *sym, + zbar_config_t *cfg, + int *val) { + const char *dot, *eq; + int len; + char negate; if(!cfgstr) return(1); - const char *dot = strchr(cfgstr, '.'); + dot = strchr(cfgstr, '.'); if(dot) { int len = dot - cfgstr; if(!len || (len == 1 && !strncmp(cfgstr, "*", len))) @@ -46,6 +50,8 @@ int zbar_parse_config (const char *cfgst return(1); else if(!strncmp(cfgstr, "qrcode", len)) *sym = ZBAR_QRCODE; + else if(!strncmp(cfgstr, "db", len)) + *sym = ZBAR_DATABAR; else if(len < 3) return(1); else if(!strncmp(cfgstr, "upca", len)) @@ -66,6 +72,8 @@ int zbar_parse_config (const char *cfgst *sym = ZBAR_ISBN13; else if(!strncmp(cfgstr, "isbn10", len)) *sym = ZBAR_ISBN10; + else if(!strncmp(cfgstr, "db-exp", len)) + *sym = ZBAR_DATABAR_EXP; #if 0 /* FIXME addons are configured per-main symbol type */ else if(!strncmp(cfgstr, "addon2", len)) @@ -75,6 +83,8 @@ int zbar_parse_config (const char *cfgst #endif 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)) @@ -83,6 +93,10 @@ int zbar_parse_config (const char *cfgst 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; @@ -90,13 +104,13 @@ int zbar_parse_config (const char *cfgst else *sym = 0; - int len = strlen(cfgstr); - const char *eq = strchr(cfgstr, '='); + len = strlen(cfgstr); + eq = strchr(cfgstr, '='); if(eq) len = eq - cfgstr; else *val = 1; /* handle this here so we can override later */ - char negate = 0; + negate = 0; if(len > 3 && !strncmp(cfgstr, "no-", 3)) { negate = 1; @@ -130,16 +144,22 @@ int zbar_parse_config (const char *cfgst *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, "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; diff --git a/zbar/convert.c b/zbar/convert.c --- a/zbar/convert.c +++ b/zbar/convert.c @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2007-2009 (c) Jeff Brown + * Copyright 2007-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -236,12 +236,13 @@ static inline void uv_round (zbar_image_ 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; - unsigned xmask = (1 << fmt->p.yuv.xsub2) - 1; + xmask = (1 << fmt->p.yuv.xsub2) - 1; if(img->width & xmask) img->width = (img->width + xmask) & ~xmask; - unsigned ymask = (1 << fmt->p.yuv.ysub2) - 1; + ymask = (1 << fmt->p.yuv.ysub2) - 1; if(img->height & ymask) img->height = (img->height + ymask) & ~ymask; } @@ -306,16 +307,18 @@ static inline void convert_y_resize (zba 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; } - uint8_t *psrc = (void*)src->data; - uint8_t *pdst = (void*)dst->data; - unsigned width = (dst->width > src->width) ? src->width : dst->width; - unsigned xpad = (dst->width > src->width) ? dst->width - src->width : 0; - unsigned height = (dst->height > src->height) ? src->height : dst->height; - unsigned y; + 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; @@ -344,10 +347,10 @@ static void convert_copy (zbar_image_t * { 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; - zbar_image_t *s = (zbar_image_t*)src; dst->next = s; _zbar_image_refcnt(s, 1); } @@ -362,9 +365,10 @@ static void convert_uvp_append (zbar_ima 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; - unsigned long n = dst->width * dst->height; + 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", @@ -373,7 +377,7 @@ static void convert_uvp_append (zbar_ima dst->data = malloc(dst->datalen); if(!dst->data) return; convert_y_resize(dst, dstfmt, src, srcfmt, n); - memset((void*)dst->data + n, 0x80, dst->datalen - n); + memset((uint8_t*)dst->data + n, 0x80, dst->datalen - n); } /* interleave YUV planes into packed YUV */ @@ -382,32 +386,35 @@ static void convert_yuv_pack (zbar_image 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; - uint8_t *dstp = (void*)dst->data; + dstp = (void*)dst->data; - unsigned long srcm = uvp_size(src, srcfmt); - unsigned long srcn = src->width * src->height; + srcm = uvp_size(src, srcfmt); + srcn = src->width * src->height; assert(src->datalen >= srcn + 2 * srcn); - uint8_t flags = dstfmt->p.yuv.packorder ^ srcfmt->p.yuv.packorder; - uint8_t *srcy = (void*)src->data; - const uint8_t *srcu, *srcv; + flags = dstfmt->p.yuv.packorder ^ srcfmt->p.yuv.packorder; + srcy = (void*)src->data; if(flags & 1) { - srcv = src->data + srcn; + srcv = (uint8_t*)src->data + srcn; srcu = srcv + srcm; } else { - srcu = src->data + srcn; + srcu = (uint8_t*)src->data + srcn; srcv = srcu + srcm; } flags = dstfmt->p.yuv.packorder & 2; - unsigned srcl = src->width >> srcfmt->p.yuv.xsub2; - unsigned xmask = (1 << srcfmt->p.yuv.xsub2) - 1; - unsigned ymask = (1 << srcfmt->p.yuv.ysub2) - 1; - unsigned x, y; - uint8_t y0 = 0, y1 = 0, u = 0x80, v = 0x80; + 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; @@ -448,25 +455,29 @@ static void convert_yuv_unpack (zbar_ima 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); - unsigned long dstn = dst->width * dst->height; - unsigned long dstm2 = uvp_size(dst, dstfmt) * 2; + 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((void*)dst->data + dstn, 0x80, dstm2); - uint8_t *dsty = (void*)dst->data; + memset((uint8_t*)dst->data + dstn, 0x80, dstm2); + dsty = (uint8_t*)dst->data; - uint8_t flags = srcfmt->p.yuv.packorder ^ dstfmt->p.yuv.packorder; + flags = srcfmt->p.yuv.packorder ^ dstfmt->p.yuv.packorder; flags &= 2; - const uint8_t *srcp = src->data; + srcp = src->data; if(flags) srcp++; - unsigned srcl = src->width + (src->width >> srcfmt->p.yuv.xsub2); - unsigned x, y; - uint8_t y0 = 0, y1 = 0; + srcl = src->width + (src->width >> srcfmt->p.yuv.xsub2); for(y = 0; y < dst->height; y++) { if(y >= src->height) srcp -= srcl; @@ -491,15 +502,16 @@ static void convert_uvp_resample (zbar_i const zbar_image_t *src, const zbar_format_def_t *srcfmt) { + unsigned long dstn, dstm2; uv_roundup(dst, dstfmt); - unsigned long dstn = dst->width * dst->height; - unsigned long dstm2 = uvp_size(dst, dstfmt) * 2; + 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((void*)dst->data + dstn, 0x80, dstm2); + memset((uint8_t*)dst->data + dstn, 0x80, dstm2); } /* rearrange interleaved UV componets */ @@ -508,19 +520,23 @@ static void convert_uv_resample (zbar_im 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); - unsigned long dstn = dst->width * dst->height; + dstn = dst->width * dst->height; dst->datalen = dstn + uvp_size(dst, dstfmt) * 2; dst->data = malloc(dst->datalen); if(!dst->data) return; - uint8_t *dstp = (void*)dst->data; + dstp = (void*)dst->data; - uint8_t flags = (srcfmt->p.yuv.packorder ^ dstfmt->p.yuv.packorder) & 1; - const uint8_t *srcp = src->data; + flags = (srcfmt->p.yuv.packorder ^ dstfmt->p.yuv.packorder) & 1; + srcp = src->data; - unsigned srcl = src->width + (src->width >> srcfmt->p.yuv.xsub2); - unsigned x, y; - uint8_t y0 = 0, y1 = 0, u = 0x80, v = 0x80; + srcl = src->width + (src->width >> srcfmt->p.yuv.xsub2); for(y = 0; y < dst->height; y++) { if(y >= src->height) srcp -= srcl; @@ -560,25 +576,29 @@ static void convert_yuvp_to_rgb (zbar_im 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; - uint8_t *dstp = (void*)dst->data; + dstp = (void*)dst->data; - int drbits = RGB_SIZE(dstfmt->p.rgb.red); - int drbit0 = RGB_OFFSET(dstfmt->p.rgb.red); - int dgbits = RGB_SIZE(dstfmt->p.rgb.green); - int dgbit0 = RGB_OFFSET(dstfmt->p.rgb.green); - int dbbits = RGB_SIZE(dstfmt->p.rgb.blue); - int dbbit0 = RGB_OFFSET(dstfmt->p.rgb.blue); + 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); - unsigned long srcm = uvp_size(src, srcfmt); - unsigned long srcn = src->width * src->height; + srcm = uvp_size(src, srcfmt); + srcn = src->width * src->height; assert(src->datalen >= srcn + 2 * srcm); - uint8_t *srcy = (void*)src->data; + srcy = (void*)src->data; - unsigned x, y; - uint32_t p = 0; for(y = 0; y < dst->height; y++) { if(y >= src->height) srcy -= src->width; @@ -606,29 +626,34 @@ static void convert_rgb_to_yuvp (zbar_im 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); - unsigned long dstn = dst->width * dst->height; - unsigned long dstm2 = uvp_size(dst, dstfmt) * 2; + 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((void*)dst->data + dstn, 0x80, dstm2); - uint8_t *dsty = (void*)dst->data; + memset((uint8_t*)dst->data + dstn, 0x80, dstm2); + dsty = (void*)dst->data; assert(src->datalen >= (src->width * src->height * srcfmt->p.rgb.bpp)); - const uint8_t *srcp = src->data; + srcp = src->data; - int rbits = RGB_SIZE(srcfmt->p.rgb.red); - int rbit0 = RGB_OFFSET(srcfmt->p.rgb.red); - int gbits = RGB_SIZE(srcfmt->p.rgb.green); - int gbit0 = RGB_OFFSET(srcfmt->p.rgb.green); - int bbits = RGB_SIZE(srcfmt->p.rgb.blue); - int bbit0 = RGB_OFFSET(srcfmt->p.rgb.blue); + 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); - unsigned srcl = src->width * srcfmt->p.rgb.bpp; - unsigned x, y; - uint16_t y0 = 0; + srcl = src->width * srcfmt->p.rgb.bpp; for(y = 0; y < dst->height; y++) { if(y >= src->height) srcp -= srcl; @@ -659,29 +684,33 @@ static void convert_yuv_to_rgb (zbar_ima 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; - uint8_t *dstp = (void*)dst->data; + dstp = (void*)dst->data; - int drbits = RGB_SIZE(dstfmt->p.rgb.red); - int drbit0 = RGB_OFFSET(dstfmt->p.rgb.red); - int dgbits = RGB_SIZE(dstfmt->p.rgb.green); - int dgbit0 = RGB_OFFSET(dstfmt->p.rgb.green); - int dbbits = RGB_SIZE(dstfmt->p.rgb.blue); - int dbbit0 = RGB_OFFSET(dstfmt->p.rgb.blue); + 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)); - const uint8_t *srcp = src->data; + srcp = src->data; if(srcfmt->p.yuv.packorder & 2) srcp++; assert(srcfmt->p.yuv.xsub2 == 1); - unsigned srcl = src->width + (src->width >> 1); - unsigned x, y; - uint32_t p = 0; + srcl = src->width + (src->width >> 1); for(y = 0; y < dst->height; y++) { if(y >= src->height) srcp -= srcl; @@ -717,26 +746,30 @@ static void convert_rgb_to_yuv (zbar_ima 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; - uint8_t *dstp = (void*)dst->data; - uint8_t flags = dstfmt->p.yuv.packorder & 2; + dstp = (void*)dst->data; + flags = dstfmt->p.yuv.packorder & 2; assert(src->datalen >= (src->width * src->height * srcfmt->p.rgb.bpp)); - const uint8_t *srcp = src->data; + srcp = src->data; - int rbits = RGB_SIZE(srcfmt->p.rgb.red); - int rbit0 = RGB_OFFSET(srcfmt->p.rgb.red); - int gbits = RGB_SIZE(srcfmt->p.rgb.green); - int gbit0 = RGB_OFFSET(srcfmt->p.rgb.green); - int bbits = RGB_SIZE(srcfmt->p.rgb.blue); - int bbit0 = RGB_OFFSET(srcfmt->p.rgb.blue); + 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); - unsigned srcl = src->width * srcfmt->p.rgb.bpp; - unsigned x, y; - uint16_t y0 = 0; + srcl = src->width * srcfmt->p.rgb.bpp; for(y = 0; y < dst->height; y++) { if(y >= src->height) srcp -= srcl; @@ -773,31 +806,36 @@ static void convert_rgb_resample (zbar_i 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; - uint8_t *dstp = (void*)dst->data; + dstp = (void*)dst->data; - int drbits = RGB_SIZE(dstfmt->p.rgb.red); - int drbit0 = RGB_OFFSET(dstfmt->p.rgb.red); - int dgbits = RGB_SIZE(dstfmt->p.rgb.green); - int dgbit0 = RGB_OFFSET(dstfmt->p.rgb.green); - int dbbits = RGB_SIZE(dstfmt->p.rgb.blue); - int dbbit0 = RGB_OFFSET(dstfmt->p.rgb.blue); + 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)); - const uint8_t *srcp = src->data; + srcp = src->data; - int srbits = RGB_SIZE(srcfmt->p.rgb.red); - int srbit0 = RGB_OFFSET(srcfmt->p.rgb.red); - int sgbits = RGB_SIZE(srcfmt->p.rgb.green); - int sgbit0 = RGB_OFFSET(srcfmt->p.rgb.green); - int sbbits = RGB_SIZE(srcfmt->p.rgb.blue); - int sbbit0 = RGB_OFFSET(srcfmt->p.rgb.blue); + 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); - unsigned srcl = src->width * srcfmt->p.rgb.bpp; - unsigned x, y; - uint32_t p = 0; + srcl = src->width * srcfmt->p.rgb.bpp; for(y = 0; y < dst->height; y++) { if(y >= src->height) y -= srcl; @@ -928,14 +966,12 @@ static void convert_jpeg (zbar_image_t * if(!src->src) { tmp = zbar_image_create(); tmp->format = fourcc('Y','8','0','0'); - tmp->width = dst->width; - tmp->height = dst->height; + _zbar_image_copy_size(tmp, dst); } else { tmp = src->src->jpeg_img; assert(tmp); - dst->width = tmp->width; - dst->height = tmp->height; + _zbar_image_copy_size(dst, tmp); } const zbar_format_def_t *tmpfmt = _zbar_format_lookup(tmp->format); @@ -945,8 +981,7 @@ static void convert_jpeg (zbar_image_t * _zbar_convert_jpeg_to_y(tmp, tmpfmt, src, srcfmt); /* now convert to dst */ - dst->width = tmp->width; - dst->height = tmp->height; + _zbar_image_copy_size(dst, tmp); conversion_handler_t *func = conversions[tmpfmt->group][dstfmt->group].func; @@ -963,10 +998,14 @@ zbar_image_t *zbar_image_convert_resize 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) { @@ -974,8 +1013,8 @@ zbar_image_t *zbar_image_convert_resize return(dst); } - const zbar_format_def_t *srcfmt = _zbar_format_lookup(src->format); - const zbar_format_def_t *dstfmt = _zbar_format_lookup(dst->format); + srcfmt = _zbar_format_lookup(src->format); + dstfmt = _zbar_format_lookup(dst->format); if(!srcfmt || !dstfmt) /* FIXME free dst */ return(NULL); @@ -988,8 +1027,7 @@ zbar_image_t *zbar_image_convert_resize return(dst); } - conversion_handler_t *func = - conversions[srcfmt->group][dstfmt->group].func; + func = conversions[srcfmt->group][dstfmt->group].func; dst->cleanup = zbar_image_free_data; func(dst, dstfmt, src, srcfmt); @@ -1021,6 +1059,9 @@ 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) @@ -1031,17 +1072,16 @@ int _zbar_best_format (uint32_t src, *dst = src; return(0); } - const zbar_format_def_t *srcfmt = _zbar_format_lookup(src); + srcfmt = _zbar_format_lookup(src); if(!srcfmt) return(-1); zprintf(8, "from %.4s(%08" PRIx32 ") to", (char*)&src, src); - unsigned min_cost = -1; for(; *dsts; dsts++) { const zbar_format_def_t *dstfmt = _zbar_format_lookup(*dsts); + int cost; if(!dstfmt) continue; - int cost; if(srcfmt->group == dstfmt->group && srcfmt->p.cmp == dstfmt->p.cmp) cost = 0; @@ -1065,13 +1105,20 @@ int _zbar_best_format (uint32_t src, 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 && !win) return(0); if(win) (void)window_lock(win); - errinfo_t *errdst = (vdo) ? &vdo->err : &win->err; + errdst = (vdo) ? &vdo->err : &win->err; if(verify_format_sort()) { if(win) (void)window_unlock(win); @@ -1086,19 +1133,16 @@ int zbar_negotiate_format (zbar_video_t "no input or output formats available")); } - static const uint32_t y800[2] = { fourcc('Y','8','0','0'), 0 }; - const uint32_t *srcs = (vdo) ? vdo->formats : y800; - const uint32_t *dsts = (win) ? win->formats : y800; + srcs = (vdo) ? vdo->formats : y800; + dsts = (win) ? win->formats : y800; - unsigned min_cost = -1; - uint32_t min_fmt = 0; - const uint32_t *fmt; 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; - uint32_t win_fmt = 0; - int cost = _zbar_best_format(*fmt, &win_fmt, dsts); + cost = _zbar_best_format(*fmt, &win_fmt, dsts); if(cost < 0) { zprintf(4, "%.4s(%08" PRIx32 ") -> ? (unsupported)\n", (char*)fmt, *fmt); diff --git a/zbar/debug.h b/zbar/debug.h --- a/zbar/debug.h +++ b/zbar/debug.h @@ -27,10 +27,10 @@ # ifdef __GNUC__ /* older versions of gcc (< 2.95) require a named varargs parameter */ -# define dprintf(args...) +# define dprintf(args...) while(0) # else /* unfortunately named vararg parameter is a gcc-specific extension */ -# define dprintf(...) +# define dprintf(...) while(0) # endif #else @@ -38,13 +38,15 @@ # include # ifdef __GNUC__ -# define dprintf(level, args...) \ - if((level) <= DEBUG_LEVEL) \ - fprintf(stderr, args) +# define dprintf(level, args...) do { \ + if((level) <= DEBUG_LEVEL) \ + fprintf(stderr, args); \ + } while(0) # else -# define dprintf(level, ...) \ - if((level) <= DEBUG_LEVEL) \ - fprintf(stderr, __VA_ARGS__) +# define dprintf(level, ...) do { \ + if((level) <= DEBUG_LEVEL) \ + fprintf(stderr, __VA_ARGS__); \ + } while(0) # endif #endif /* DEBUG_LEVEL */ diff --git a/zbar/decoder.c b/zbar/decoder.c --- a/zbar/decoder.c +++ b/zbar/decoder.c @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2007-2009 (c) Jeff Brown + * Copyright 2007-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -27,15 +27,15 @@ #include /* memset, strlen */ #include -#include "decoder.h" -#if defined(DEBUG_DECODER) || defined(DEBUG_EAN) || \ - defined(DEBUG_CODE39) || defined(DEBUG_I25) || \ +#if defined(DEBUG_DECODER) || defined(DEBUG_EAN) || defined(DEBUG_CODE93) || \ + defined(DEBUG_CODE39) || defined(DEBUG_I25) || defined(DEBUG_DATABAR) || \ defined(DEBUG_CODE128) || 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 () { @@ -59,10 +59,21 @@ zbar_decoder_t *zbar_decoder_create () dcode->i25.config = 1 << ZBAR_CFG_ENABLE; CFG(dcode->i25, ZBAR_CFG_MIN_LEN) = 6; #endif +#ifdef ENABLE_DATABAR + 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 #ifdef ENABLE_CODE39 dcode->code39.config = 1 << ZBAR_CFG_ENABLE; CFG(dcode->code39, ZBAR_CFG_MIN_LEN) = 1; #endif +#ifdef ENABLE_CODE93 + dcode->code93.config = 1 << ZBAR_CFG_ENABLE; +#endif #ifdef ENABLE_CODE128 dcode->code128.config = 1 << ZBAR_CFG_ENABLE; #endif @@ -79,6 +90,10 @@ zbar_decoder_t *zbar_decoder_create () void zbar_decoder_destroy (zbar_decoder_t *dcode) { +#ifdef ENABLE_DATABAR + if(dcode->databar.segs) + free(dcode->databar.segs); +#endif if(dcode->buf) free(dcode->buf); free(dcode); @@ -93,9 +108,15 @@ void zbar_decoder_reset (zbar_decoder_t #ifdef ENABLE_I25 i25_reset(&dcode->i25); #endif +#ifdef ENABLE_DATABAR + databar_reset(&dcode->databar); +#endif #ifdef ENABLE_CODE39 code39_reset(&dcode->code39); #endif +#ifdef ENABLE_CODE93 + code93_reset(&dcode->code93); +#endif #ifdef ENABLE_CODE128 code128_reset(&dcode->code128); #endif @@ -113,15 +134,22 @@ void zbar_decoder_new_scan (zbar_decoder memset(dcode->w, 0, sizeof(dcode->w)); dcode->lock = 0; dcode->idx = 0; + dcode->s6 = 0; #ifdef ENABLE_EAN ean_new_scan(&dcode->ean); #endif #ifdef ENABLE_I25 i25_reset(&dcode->i25); #endif +#ifdef ENABLE_DATABAR + databar_new_scan(&dcode->databar); +#endif #ifdef ENABLE_CODE39 code39_reset(&dcode->code39); #endif +#ifdef ENABLE_CODE93 + code93_reset(&dcode->code93); +#endif #ifdef ENABLE_CODE128 code128_reset(&dcode->code128); #endif @@ -149,9 +177,14 @@ unsigned int zbar_decoder_get_data_lengt 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 *handler) { zbar_decoder_handler_t *result = dcode->handler; dcode->handler = handler; @@ -174,62 +207,82 @@ zbar_symbol_type_t zbar_decoder_get_type 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; dprintf(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 */ - zbar_symbol_type_t sym = dcode->type = ZBAR_NONE; - #ifdef ENABLE_EAN if((dcode->ean.enable) && - (sym = _zbar_decode_ean(dcode))) - dcode->type = sym; + (tmp = _zbar_decode_ean(dcode))) + sym = tmp; +#endif +#ifdef ENABLE_QRCODE + if(TEST_CFG(dcode->qrf.config, ZBAR_CFG_ENABLE) && + (tmp = _zbar_find_qr(dcode)) > ZBAR_PARTIAL) + sym = tmp; #endif #ifdef ENABLE_CODE39 if(TEST_CFG(dcode->code39.config, ZBAR_CFG_ENABLE) && - (sym = _zbar_decode_code39(dcode)) > ZBAR_PARTIAL) - dcode->type = sym; + (tmp = _zbar_decode_code39(dcode)) > ZBAR_PARTIAL) + sym = tmp; +#endif +#ifdef ENABLE_CODE93 + if(TEST_CFG(dcode->code93.config, ZBAR_CFG_ENABLE) && + (tmp = _zbar_decode_code93(dcode)) > ZBAR_PARTIAL) + sym = tmp; #endif #ifdef ENABLE_CODE128 if(TEST_CFG(dcode->code128.config, ZBAR_CFG_ENABLE) && - (sym = _zbar_decode_code128(dcode)) > ZBAR_PARTIAL) - dcode->type = sym; + (tmp = _zbar_decode_code128(dcode)) > ZBAR_PARTIAL) + sym = tmp; +#endif +#ifdef ENABLE_DATABAR + if(TEST_CFG(dcode->databar.config | dcode->databar.config_exp, + ZBAR_CFG_ENABLE) && + (tmp = _zbar_decode_databar(dcode)) > ZBAR_PARTIAL) + sym = tmp; #endif #ifdef ENABLE_I25 if(TEST_CFG(dcode->i25.config, ZBAR_CFG_ENABLE) && - (sym = _zbar_decode_i25(dcode)) > ZBAR_PARTIAL) - dcode->type = sym; + (tmp = _zbar_decode_i25(dcode)) > ZBAR_PARTIAL) + sym = tmp; #endif #ifdef ENABLE_PDF417 if(TEST_CFG(dcode->pdf417.config, ZBAR_CFG_ENABLE) && - (sym = _zbar_decode_pdf417(dcode)) > ZBAR_PARTIAL) - dcode->type = sym; -#endif -#ifdef ENABLE_QRCODE - if(TEST_CFG(dcode->qrf.config, ZBAR_CFG_ENABLE) && - (sym = _zbar_find_qr(dcode)) > ZBAR_PARTIAL) - dcode->type = sym; + (tmp = _zbar_decode_pdf417(dcode)) > ZBAR_PARTIAL) + sym = tmp; #endif dcode->idx++; - if(dcode->type) { + dcode->type = sym; + if(sym) { + if(dcode->lock && sym > ZBAR_PARTIAL && sym != ZBAR_QRCODE) + release_lock(dcode, sym); if(dcode->handler) dcode->handler(dcode); - if(dcode->lock && dcode->type > ZBAR_PARTIAL) - dcode->lock = 0; } - return(dcode->type); + return(sym); } -static inline int decoder_set_config_bool (zbar_decoder_t *dcode, - zbar_symbol_type_t sym, - zbar_config_t cfg, - int val) +static inline const unsigned int* +decoder_get_configp (const zbar_decoder_t *dcode, + zbar_symbol_type_t sym) { - unsigned *config = NULL; + const unsigned int *config; switch(sym) { #ifdef ENABLE_EAN case ZBAR_EAN13: @@ -263,12 +316,27 @@ static inline int decoder_set_config_boo break; #endif +#ifdef ENABLE_DATABAR + case ZBAR_DATABAR: + config = &dcode->databar.config; + break; + case ZBAR_DATABAR_EXP: + config = &dcode->databar.config_exp; + break; +#endif + #ifdef ENABLE_CODE39 case ZBAR_CODE39: config = &dcode->code39.config; break; #endif +#ifdef ENABLE_CODE93 + case ZBAR_CODE93: + config = &dcode->code93.config; + break; +#endif + #ifdef ENABLE_CODE128 case ZBAR_CODE128: config = &dcode->code128.config; @@ -290,8 +358,26 @@ static inline int decoder_set_config_boo /* FIXME handle addons */ default: - return(1); + 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); @@ -332,6 +418,11 @@ static inline int decoder_set_config_int CFG(dcode->code39, cfg) = val; break; #endif +#ifdef ENABLE_CODE93 + case ZBAR_CODE93: + CFG(dcode->code93, cfg) = val; + break; +#endif #ifdef ENABLE_CODE128 case ZBAR_CODE128: CFG(dcode->code128, cfg) = val; @@ -362,7 +453,10 @@ int zbar_decoder_set_config (zbar_decode zbar_decoder_set_config(dcode, ZBAR_ISBN10, cfg, val); zbar_decoder_set_config(dcode, ZBAR_ISBN13, cfg, val); zbar_decoder_set_config(dcode, ZBAR_I25, cfg, val); + zbar_decoder_set_config(dcode, ZBAR_DATABAR, cfg, val); + zbar_decoder_set_config(dcode, ZBAR_DATABAR_EXP, cfg, val); zbar_decoder_set_config(dcode, ZBAR_CODE39, cfg, val); + zbar_decoder_set_config(dcode, ZBAR_CODE93, cfg, val); zbar_decoder_set_config(dcode, ZBAR_CODE128, cfg, val); zbar_decoder_set_config(dcode, ZBAR_PDF417, cfg, val); zbar_decoder_set_config(dcode, ZBAR_QRCODE, cfg, val); @@ -385,16 +479,18 @@ const char *_zbar_decoder_buf_dump (unsi 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; } - char *p = decoder_dump + + p = decoder_dump + snprintf(decoder_dump, 12, "buf[%04x]=", (buflen > 0xffff) ? 0xffff : buflen); - int i; 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 --- a/zbar/decoder.h +++ b/zbar/decoder.h @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2007-2009 (c) Jeff Brown + * Copyright 2007-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -36,9 +36,15 @@ #ifdef ENABLE_I25 # include "decoder/i25.h" #endif +#ifdef ENABLE_DATABAR +# include "decoder/databar.h" +#endif #ifdef ENABLE_CODE39 # include "decoder/code39.h" #endif +#ifdef ENABLE_CODE93 +# include "decoder/code93.h" +#endif #ifdef ENABLE_CODE128 # include "decoder/code128.h" #endif @@ -73,6 +79,7 @@ #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 { @@ -80,6 +87,9 @@ struct zbar_decoder_s { 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 */ @@ -95,9 +105,15 @@ struct zbar_decoder_s { #ifdef ENABLE_I25 i25_decoder_t i25; /* Interleaved 2 of 5 decode state */ #endif +#ifdef ENABLE_DATABAR + databar_decoder_t databar; /* DataBar decode state */ +#endif #ifdef ENABLE_CODE39 code39_decoder_t code39; /* Code 39 decode state */ #endif +#ifdef ENABLE_CODE93 + code93_decoder_t code93; /* Code 93 decode state */ +#endif #ifdef ENABLE_CODE128 code128_decoder_t code128; /* Code 128 decode state */ #endif @@ -168,19 +184,32 @@ static inline int decode_e (unsigned e, } /* acquire shared state lock */ -static inline char get_lock (zbar_decoder_t *dcode, - zbar_symbol_type_t req) +static inline char acquire_lock (zbar_decoder_t *dcode, + zbar_symbol_type_t req) { - if(dcode->lock) + if(dcode->lock) { + dprintf(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 < dcode->buf_alloc) /* FIXME size reduction heuristic? */ return(0); @@ -191,7 +220,7 @@ static inline char size_buf (zbar_decode if(len > BUFFER_MAX) len = BUFFER_MAX; } - unsigned char *buf = realloc(dcode->buf, len); + buf = realloc(dcode->buf, len); if(!buf) return(1); dcode->buf = buf; @@ -200,6 +229,6 @@ static inline char size_buf (zbar_decode } extern const char *_zbar_decoder_buf_dump (unsigned char *buf, - unsigned int buflen); + unsigned int buflen); #endif diff --git a/zbar/decoder/code128.c b/zbar/decoder/code128.c --- a/zbar/decoder/code128.c +++ b/zbar/decoder/code128.c @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2007-2009 (c) Jeff Brown + * Copyright 2007-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -25,12 +25,12 @@ #include /* memmove */ #include -#include "decoder.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 */ @@ -120,6 +120,8 @@ static inline signed char decode_lo (int ((sig >> 5) & 0x18) | ((sig >> 7) & 0x60)); unsigned char idx = lo_offset[offset]; + unsigned char base, c; + if(sig & 1) idx &= 0xf; else @@ -127,14 +129,14 @@ static inline signed char decode_lo (int if(idx == 0xf) return(-1); - unsigned char base = (sig >> 11) | ((sig >> 9) & 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); - unsigned char c = characters[idx]; + c = characters[idx]; dprintf(2, " %02x(%x(%02x)/%x(%02x)) => %02x", idx, base, lo_base[base], offset, lo_offset[offset], (unsigned char)c); @@ -144,6 +146,7 @@ static inline signed char decode_lo (int 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) | @@ -151,7 +154,6 @@ static inline signed char decode_hi (int ((sig << 12) & 0xf000)); dprintf(2, " rev=%x", rev != 0); - unsigned char idx; switch(sig) { case 0x0014: idx = 0x0; break; case 0x0025: idx = 0x1; break; @@ -171,7 +173,7 @@ static inline signed char decode_hi (int } if(rev) idx += 0xe; - unsigned char c = characters[0x51 + idx]; + c = characters[0x51 + idx]; dprintf(2, " %02x => %02x", idx, c); return(c); } @@ -190,13 +192,18 @@ static inline unsigned char calc_check ( 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; + dprintf(2, " s=%d", s); if(s < 5) return(-1); /* calculate similar edge measurements */ - int sig = (get_color(dcode) == ZBAR_BAR) + 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) | @@ -209,16 +216,16 @@ static inline signed char decode6 (zbar_ return(-1); dprintf(2, " sig=%04x", sig); /* lookup edge signature */ - signed char c = (sig & 0x4444) ? decode_hi(sig) : decode_lo(sig); + c = (sig & 0x4444) ? decode_hi(sig) : decode_lo(sig); if(c == -1) return(-1); /* character validation */ - unsigned bars = (get_color(dcode) == ZBAR_BAR) + 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; - unsigned char chk = calc_check(c); + chk = calc_check(c); dprintf(2, " bars=%d chk=%d", bars, chk); if(chk - 7 > bars || bars > chk + 7) return(-1); @@ -228,18 +235,20 @@ static inline signed char decode6 (zbar_ 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 */ - unsigned idx = (dcode128->direction) ? dcode128->character - 1 : 0; - unsigned sum = dcode->buf[idx]; + idx = (dcode128->direction) ? dcode128->character - 1 : 0; + sum = dcode->buf[idx]; if(sum >= 103) sum -= 103; /* calculate sum in reverse to avoid multiply operations */ - unsigned i, acc = 0; 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, @@ -258,9 +267,9 @@ static inline unsigned char validate_che /* and compare to check character */ idx = (dcode128->direction) ? 1 : dcode128->character - 2; - unsigned char check = dcode->buf[idx]; + check = dcode->buf[idx]; dprintf(2, " chk=%02x(%02x)", sum, check); - unsigned char err = (sum != check); + err = (sum != check); if(err) dprintf(1, " [checksum error]\n"); return(err); @@ -272,6 +281,8 @@ static inline unsigned postprocess_c (zb unsigned end, unsigned dst) { + unsigned i, j; + /* expand buffer to accomodate 2x set C characters (2 digits per-char) */ unsigned delta = end - start; unsigned newlen = dcode->code128.character + delta; @@ -282,7 +293,6 @@ static inline unsigned postprocess_c (zb dcode->code128.character - start); dcode->code128.character = newlen; - unsigned i, j; 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]; @@ -317,10 +327,12 @@ static inline unsigned postprocess_c (zb /* 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; dprintf(2, "\n postproc len=%d", dcode128->character); - unsigned i, j; - unsigned char code = 0; + dcode->modifiers = 0; + dcode->direction = 1 - 2 * dcode128->direction; if(dcode128->direction) { /* reverse buffer */ dprintf(2, " (rev)"); @@ -343,8 +355,8 @@ static inline unsigned char postprocess zassert(code >= START_A && code <= START_C, 1, "%s\n", _zbar_decoder_buf_dump(dcode->buf, dcode->code128.character)); - unsigned char charset = code - START_A; - unsigned cexp = (code == START_C) ? 1 : 0; + charset = code - START_A; + cexp = (code == START_C) ? 1 : 0; dprintf(2, " start=%c", 'A' + charset); for(i = 1, j = 0; i < dcode128->character - 2; i++) { @@ -370,12 +382,13 @@ static inline unsigned char postprocess else { dprintf(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)); - unsigned delta = postprocess_c(dcode, cexp, i, j); + delta = postprocess_c(dcode, cexp, i, j); i += delta; j += delta * 2; cexp = 0; @@ -383,28 +396,39 @@ static inline unsigned char postprocess 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 == FNC2) { + /* FIXME FNC2 - message append */ + } + else if(code == FNC3) { + /* FIXME FNC3 - initialize */ + } } - else if(code == FNC1) - /* FIXME FNC1 - Code 128 subsets or ASCII 0x1d */; + 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) { dprintf(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)); - unsigned char newset = CODE_A - code; if(newset != charset) charset = newset; - else - /* FIXME FNC4 - extended ASCII */; + else { + /* FIXME FNC4 - extended ASCII */ + } } if(charset & 0x2) cexp = i + 1; @@ -426,16 +450,18 @@ static inline unsigned char postprocess 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(/* process every 6th element of active symbol */ - (dcode128->character >= 0 && - (++dcode128->element) != 6) || - /* decode color based on direction */ - (get_color(dcode) != dcode128->direction)) + 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; @@ -443,49 +469,73 @@ zbar_symbol_type_t _zbar_decode_code128 (dcode128->direction) ? '<' : '>', dcode128->character, dcode128->element); - signed char c = decode6(dcode); + c = decode6(dcode); if(dcode128->character < 0) { + unsigned qz; dprintf(2, " c=%02x", c); if(c < START_A || c > STOP_REV || c == STOP_FWD) { dprintf(2, " [invalid]\n"); return(0); } - unsigned qz = get_width(dcode, 6); - if(qz && qz < (dcode->code128.s6 * 3) / 4) { + qz = get_width(dcode, 6); + if(qz && qz < (dcode128->s6 * 3) / 4) { dprintf(2, " [invalid qz %d]\n", qz); return(0); } - /* lock shared resources */ - if(get_lock(dcode, ZBAR_CODE128)) { - dprintf(2, " [locked %d]\n", dcode->lock); - dcode128->character = -1; - return(0); - } /* decoded valid start/stop */ /* initialize state */ - dcode128->character = 0; + dcode128->character = 1; if(c == STOP_REV) { dcode128->direction = ZBAR_BAR; dcode128->element = 7; } else dcode128->direction = ZBAR_SPACE; - dprintf(2, " dir=%x [valid start]", dcode128->direction); + dcode128->start = c; + dcode128->width = dcode128->s6; + dprintf(2, " dir=%x [valid start]\n", dcode128->direction); + return(0); } else if((c < 0) || ((dcode128->character >= BUFFER_MIN) && size_buf(dcode, dcode128->character + 1))) { dprintf(1, (c < 0) ? " [aborted]\n" : " [overflow]\n"); - dcode->lock = 0; + 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) { + dprintf(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 && @@ -506,7 +556,7 @@ zbar_symbol_type_t _zbar_decode_code128 dprintf(2, " [valid end]\n"); dcode128->character = -1; if(!sym) - dcode->lock = 0; + release_lock(dcode, ZBAR_CODE128); return(sym); } diff --git a/zbar/decoder/code128.h b/zbar/decoder/code128.h --- a/zbar/decoder/code128.h +++ b/zbar/decoder/code128.h @@ -28,7 +28,9 @@ 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 */ diff --git a/zbar/decoder/code39.c b/zbar/decoder/code39.c --- a/zbar/decoder/code39.c +++ b/zbar/decoder/code39.c @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2008-2009 (c) Jeff Brown + * Copyright 2008-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -25,12 +25,12 @@ #include /* memmove */ #include -#include "decoder.h" #ifdef DEBUG_CODE39 # define DEBUG_LEVEL (DEBUG_CODE39) #endif #include "debug.h" +#include "decoder.h" #define NUM_CHARS (0x2c) @@ -130,11 +130,11 @@ static inline unsigned char code39_decod unsigned e, unsigned s) { - unsigned char E = decode_e(e, s, 36); - if(E > 7) + unsigned char E = decode_e(e, s, 72); + if(E > 18) return(0xff); enc <<= 1; - if(E > 2) { + if(E > 6) { enc |= 1; dprintf(2, "1"); } @@ -147,7 +147,6 @@ static inline signed char code39_decode9 { code39_decoder_t *dcode39 = &dcode->code39; - dprintf(2, " s=%d ", dcode39->s9); if(dcode39->s9 < 9) return(-1); @@ -192,14 +191,14 @@ static inline signed char code39_decode9 static inline signed char code39_decode_start (zbar_decoder_t *dcode) { code39_decoder_t *dcode39 = &dcode->code39; + dprintf(2, " s=%d ", dcode39->s9); signed char c = code39_decode9(dcode); - if(c == 0x19) - dcode39->direction ^= 1; - else if(c != 0x2b) { + if(c != 0x19 && c != 0x2b) { dprintf(2, "\n"); return(ZBAR_NONE); } + dcode39->direction ^= (c == 0x19); /* check leading quiet zone - spec is 10x */ unsigned quiet = get_width(dcode, 9); @@ -217,6 +216,7 @@ static inline signed char code39_decode_ static inline void code39_postprocess (zbar_decoder_t *dcode) { code39_decoder_t *dcode39 = &dcode->code39; + dcode->direction = 1 - 2 * dcode39->direction; int i; if(dcode39->direction) { /* reverse buffer */ @@ -234,6 +234,17 @@ static inline void code39_postprocess (z : '?'); dcode->buflen = i; dcode->buf[i] = '\0'; + dcode->modifiers = 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) @@ -284,27 +295,36 @@ zbar_symbol_type_t _zbar_decode_code39 ( } dcode39->character = -1; if(!sym) - dcode->lock = 0; + release_lock(dcode, ZBAR_CODE39); return(sym); } if(space > dcode39->width / 2) { /* inter-character space check failure */ - dcode->lock = 0; + dprintf(2, " ics>%d [invalid ics]", dcode39->width); + if(dcode39->character) + release_lock(dcode, ZBAR_CODE39); dcode39->character = -1; - dprintf(2, " ics>%d [invalid ics]", dcode39->width); } dcode39->element = 0; dprintf(2, "\n"); return(ZBAR_NONE); } + dprintf(2, " s=%d ", dcode39->s9); + if(!check_width(dcode39->width, dcode39->s9)) { + dprintf(2, " [width]\n"); + if(dcode39->character) + release_lock(dcode, ZBAR_CODE39); + dcode39->character = -1; + return(ZBAR_NONE); + } + signed char c = code39_decode9(dcode); dprintf(2, " c=%d", c); /* lock shared resources */ - if(!dcode39->character && get_lock(dcode, ZBAR_CODE39)) { + if(!dcode39->character && acquire_lock(dcode, ZBAR_CODE39)) { dcode39->character = -1; - dprintf(1, " [locked %d]\n", dcode->lock); return(ZBAR_PARTIAL); } @@ -312,7 +332,7 @@ zbar_symbol_type_t _zbar_decode_code39 ( ((dcode39->character >= BUFFER_MIN) && size_buf(dcode, dcode39->character + 1))) { dprintf(1, (c < 0) ? " [aborted]\n" : " [overflow]\n"); - dcode->lock = 0; + release_lock(dcode, ZBAR_CODE39); dcode39->character = -1; return(ZBAR_NONE); } diff --git a/zbar/decoder/code93.c b/zbar/decoder/code93.c new file mode 100644 --- /dev/null +++ b/zbar/decoder/code93.c @@ -0,0 +1,398 @@ +/*------------------------------------------------------------------------ + * Copyright 2010 (c) Jeff Brown + * + * 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 +#include + +#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; + + dprintf(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; + dprintf(2, "%d", c); + } + dprintf(2, " sig=%03x", sig); + + return(sig); +} + +static inline int +validate_sig (int sig) +{ + int i, sum = 0, emin = 0, sig0 = 0, sig1 = 0; + dprintf(3, " sum=0"); + for(i = 3; --i >= 0; ) { + int e = sig & 3; + sig >>= 2; + sum = e - sum; + sig1 <<= 4; + sig1 += sum; + dprintf(3, "%d", sum); + if(!i) + break; + + e = sig & 3; + sig >>= 2; + sum = e - sum; + sig0 <<= 4; + if(emin > sum) + emin = sum; + sig0 += sum; + dprintf(3, "%d", sum); + } + + dprintf(3, " emin=%d sig=%03x/%03x", emin, sig1 & 0xfff, sig0 & 0xfff); + + emin = emin + (emin << 4) + (emin << 8); + sig0 -= emin; + sig1 += emin; + + dprintf(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; + dprintf(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; + + dprintf(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) { + dprintf(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; + + dprintf(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) + dprintf(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]; + dprintf(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]; + dprintf(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"; + + dprintf(2, "\n postproc len=%d", n); + dcode->direction = 1 - 2 * dcode93->direction; + if(dcode93->direction) { + /* reverse buffer */ + dprintf(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; + } + + 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); + dprintf(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; + + dprintf(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")); + + dprintf(2, " [valid end]\n"); + dprintf(3, " %s\n", + _zbar_decoder_buf_dump(dcode->buf, dcode93->character)); + + dcode93->character = -1; + return(ZBAR_CODE93); + } + + if(dcode93->character >= BUFFER_MIN && + 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++; + + dprintf(2, "\n"); + return(ZBAR_NONE); +} diff --git a/zbar/decoder/code93.h b/zbar/decoder/code93.h new file mode 100644 --- /dev/null +++ b/zbar/decoder/code93.h @@ -0,0 +1,49 @@ +/*------------------------------------------------------------------------ + * Copyright 2010 (c) Jeff Brown + * + * 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 --- /dev/null +++ b/zbar/decoder/databar.c @@ -0,0 +1,1277 @@ +/*------------------------------------------------------------------------ + * Copyright 2010 (c) Jeff Brown + * + * 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 +#include + +#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--; \ + dprintf(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++); + dprintf(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; + } + dprintf(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; + dprintf(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; + dprintf(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; + dprintf(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); + dprintf(2, " [%d+%d]", i, len); + + if(scheme == SCH_NUM) { + int n1; + i -= 4; + if(i < 0) + break; + if(!((d >> i) & 0xf)) { + scheme = SCH_ALNUM; + dprintf(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; + dprintf(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; + dprintf(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); + dprintf(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) { + dprintf(2, "%d%c", scheme, c); + *(buf++) = c; + } + } + } + /* FIXME check pad? */ + } + + *buf = 0; + i = buf - dcode->buf; + dcode->buflen = i; + if(i && *--buf == GS) { + *buf = 0; + dcode->buflen--; + } + + dprintf(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]) +{ + 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'; + + dprintf(2, "\n d={%d,%d,%d,%d}", d[0], d[1], d[2], d[3]); + unsigned long 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; + dprintf(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; + } + + dprintf(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; + dprintf(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]; + dprintf(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; + + dprintf(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; + dprintf(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, }; + + 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; + dprintf(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; + + dprintf(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; + dprintf(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); + + unsigned d[4]; + 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 unsigned +lookup_sequence (databar_segment_t *seg, + int fixed, + int seq[22]) +{ + unsigned n = seg->data / 211, i; + const unsigned char *p; + i = (n + 1) / 2 + 1; + n += 4; + i = (i * i) / 4; + dprintf(2, " {%d,%d:", i, n); + p = exp_sequences + i; + + 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; + dprintf(2, "%x", s); + seq[i++] = s++; + seq[i++] = s; + } + dprintf(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; + + dprintf(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; + dprintf(2, " %d", iseg[i]); + } + + for(i = 0; ; i--) { + if(!i) + dprintf(2, "\n "); + for(; i >= 0 && seq[i] >= 0; i--) { + int j; + dprintf(2, " [%d]%d", i, seq[i]); + + if(seq[i] == fixed) { + seg = db->segs + ifixed; + if(segs[i] < 0 && check_width(width, seg->width, 14)) { + dprintf(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) { + if(!lookup_sequence(seg, fixed, seq)) { + dprintf(2, "[nf]"); + continue; + } + width = seg->width; + dprintf(2, " A00@%d", j); + } + else { + width = (width + seg->width) / 2; + dprintf(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]; + unsigned 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; + } + + unsigned data0 = db->segs[segs[0]].data; + unsigned chk0 = data0 % 211; + chk %= 211; + + dprintf(2, " chk=%d ?= %d", chk, chk0); + if(chk != chk0) + continue; + + dprintf(2, " cnt=%d age=%d", cnt, age); + if(maxcnt > cnt || (maxcnt == cnt && maxage <= age)) + continue; + + dprintf(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); +} +#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; + } + dprintf(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 v = 0; + n--; + + unsigned w0 = (sig >> 12) & 0xf; + if(w0 > 1) { + if(w0 > wmax) + return(-1); + unsigned n0 = n - w0; + unsigned sk20 = (n - 1) * n * (2 * n - 1); + unsigned 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; + + unsigned 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; + + unsigned 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; + + unsigned 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; + + if(seg->exp) + n = 17; + else if(seg->side) + n = 15; + else + n = 16; + emin[1] = -n; + + dprintf(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); + dprintf(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); + dprintf(2, "%d", e); + sum = e - sum; + off += dir; + sig0 <<= 4; + if(emin[0] > sum) + emin[0] = sum; + sig0 += sum; + } + + int diff = emin[~n & 1]; + diff = diff + (diff << 4); + diff = diff + (diff << 8); + + sig0 -= diff; + sig1 += diff; + + dprintf(2, " emin=%d,%d el=%04x/%04x", emin[0], emin[1], sig0, sig1); + + unsigned sum0 = sig0 + (sig0 >> 8); + unsigned sum1 = sig1 + (sig1 >> 8); + sum0 += sum0 >> 4; + sum1 += sum1 >> 4; + sum0 &= 0xf; + sum1 &= 0xf; + + dprintf(2, " sum=%d/%d", sum0, sum1); + + if(sum0 + sum1 + 8 != n) { + dprintf(2, " [SUM]"); + return(ZBAR_NONE); + } + + if(((sum0 ^ (n >> 1)) | (sum1 ^ (n >> 1) ^ n)) & 1) { + dprintf(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); + struct group_s *g = groups + i; + dprintf(2, "\n g=%d(%d,%d,%d/%d)", + i, g->sum, g->wmax, g->todd, g->teven); + + int vodd = calc_value4(sig0 + 0x1111, sum0 + 4, g->wmax, ~n & 1); + dprintf(2, " v=%d", vodd); + if(vodd < 0 || vodd > g->todd) + return(ZBAR_NONE); + + int veven = calc_value4(sig1 + 0x1111, sum1 + 4, 9 - g->wmax, n & 1); + dprintf(2, "/%d", veven); + if(veven < 0 || veven > g->teven) + return(ZBAR_NONE); + + int v = g->sum; + if(n & 2) + v += vodd + veven * g->todd; + else + v += veven + vodd * g->teven; + + dprintf(2, " f=%d(%x%x%x)", seg->finder, seg->exp, seg->color, seg->side); + + unsigned 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; + } + dprintf(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) { + dprintf(2, " free@%d", i); + return(i); + } + age = (db->epoch - seg->epoch) & 0xff; + if(age >= 128 && seg->count < 2) { + seg->finder = -1; + dprintf(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; + dprintf(2, " old@%d(%u)", i, age); + } + } + + if(csegs < DATABAR_MAX_SEGMENTS) { + dprintf(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; + dprintf(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; + dprintf(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)); + dprintf(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; + dprintf(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; + + int rc = decode_char(dcode, seg, 12 - dir, -1); + if(!rc) + seg->partial = 1; + else + db->epoch++; + + int 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); + dprintf(2, "\n"); + + iseg = db->chars[i]; + if(iseg < 0) + return(sym); + + db->chars[i] = -1; + seg = db->segs + iseg; + dprintf(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++; + dprintf(2, "\n"); + + return(sym); +} diff --git a/zbar/decoder/databar.h b/zbar/decoder/databar.h new file mode 100644 --- /dev/null +++ b/zbar/decoder/databar.h @@ -0,0 +1,80 @@ +/*------------------------------------------------------------------------ + * Copyright 2010 (c) Jeff Brown + * + * 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 --- a/zbar/decoder/ean.c +++ b/zbar/decoder/ean.c @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2007-2009 (c) Jeff Brown + * Copyright 2007-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -23,12 +23,12 @@ #include #include -#include "decoder.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 { @@ -109,25 +109,35 @@ static inline const unsigned char *dspri } #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) { + if(!fwd && qz && qz <= s * 3 / 4) { dprintf(2, " [invalid quiet]"); return(-1); } dprintf(2, " ("); - signed char code = 0; - unsigned char i; + code = 0; for(i = 1 - fwd; i < 3 + fwd; i++) { unsigned e = get_width(dcode, i) + get_width(dcode, i + 1); dprintf(2, " %d", e); @@ -147,27 +157,32 @@ static inline signed char aux_end (zbar_ static inline signed char aux_start (zbar_decoder_t *dcode) { /* FIXME NB add-on has no guard in reverse */ - unsigned e2 = get_width(dcode, 5) + get_width(dcode, 6); + 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)) { dprintf(2, " [invalid any]"); return(/*FIXME (get_color(dcode) == ZBAR_SPACE) ? STATE_ADDON : */-1); } - unsigned e1 = get_width(dcode, 4) + get_width(dcode, 5); - unsigned char E1 = decode_e(e1, dcode->ean.s4, 7); + 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(!qz || qz > dcode->ean.s4 * 3 / 4) { if(!E1) { dprintf(2, " [valid normal]"); return(0); /* normal symbol start */ } +#if 0 else if(E1 == 1) { dprintf(2, " [valid add-on]"); return(STATE_ADDON); /* add-on symbol start */ } +#endif } dprintf(2, " [invalid start]"); return(-1); @@ -176,7 +191,9 @@ static inline signed char aux_start (zba if(!E1) { /* attempting decode from SPACE => validate center guard */ unsigned e3 = get_width(dcode, 6) + get_width(dcode, 7); - if(!decode_e(e3, dcode->ean.s4, 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)) { dprintf(2, " [valid center]"); return(0); /* start after center guard */ } @@ -188,6 +205,8 @@ static inline signed char aux_start (zba /* 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) @@ -195,9 +214,12 @@ static inline signed char decode4 (zbar_ unsigned e2 = get_width(dcode, 1) + get_width(dcode, 2); dprintf(2, "\n e1=%d e2=%d", e1, e2); + if(dcode->ean.s4 < 6) + return(-1); + /* create compacted encoding for direct lookup */ - signed char code = ((decode_e(e1, dcode->ean.s4, 7) << 2) | - decode_e(e2, dcode->ean.s4, 7)); + code = ((decode_e(e1, dcode->ean.s4, 7) << 2) | + decode_e(e2, dcode->ean.s4, 7)); if(code < 0) return(-1); dprintf(2, " code=%x", code); @@ -209,15 +231,16 @@ static inline signed char decode4 (zbar_ 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; - unsigned char mid = (((1 << code) & 0x0420) + mid = (((1 << code) & 0x0420) ? 3 /* E1E2 in 33,44 */ : 4); /* E1E2 in 34,43 */ - unsigned char alt = d2 > (mid * dcode->ean.s4); + alt = d2 > (mid * dcode->ean.s4); if(alt) code = ((code >> 1) & 3) | 0x10; /* compress code space */ dprintf(2, " (d2=%d(%d) alt=%d)", d2, mid * dcode->ean.s4, alt); @@ -245,6 +268,7 @@ static inline zbar_symbol_type_t ean_par 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]; @@ -292,8 +316,9 @@ static inline zbar_symbol_type_t ean_par return(ZBAR_NONE); if(!par == fwd) { + unsigned char i; + pass->state |= STATE_REV; /* reverse sampled digits */ - unsigned char i; for(i = 1; i < 4; i++) { unsigned char tmp = pass->raw[i]; pass->raw[i] = pass->raw[7 - i]; @@ -324,33 +349,52 @@ static inline zbar_symbol_type_t ean_par static inline zbar_symbol_type_t decode_pass (zbar_decoder_t *dcode, ean_pass_t *pass) { + unsigned char idx, fwd; pass->state++; - unsigned char idx = pass->state & STATE_IDX; - unsigned char fwd = pass->state & 1; + idx = pass->state & STATE_IDX; + fwd = pass->state & 1; if(get_color(dcode) == ZBAR_SPACE && (idx == 0x10 || idx == 0x11) && TEST_CFG(dcode->ean.ean8_config, ZBAR_CFG_ENABLE) && !aux_end(dcode, fwd)) { + zbar_symbol_type_t part; dprintf(2, " fwd=%x", fwd); - zbar_symbol_type_t part = ean_part_end4(pass, fwd); + part = ean_part_end4(pass, fwd); + if(part) + dcode->ean.direction = (pass->state & STATE_REV) != 0; pass->state = -1; return(part); } 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; } - signed char code = decode4(dcode); - if(code < 0) + 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 + dprintf(2, " [bad width]"); + + if(code < 0 && idx != 0x10) pass->state = -1; + else if(code < 0) + pass->raw[5] = 0xff; else { dprintf(2, "\n raw[%x]=%02x =>", idx >> 2, digits[(unsigned char)code]); @@ -367,8 +411,10 @@ static inline zbar_symbol_type_t decode_ (idx == 0x18 || idx == 0x19)) { zbar_symbol_type_t part = ZBAR_NONE; dprintf(2, " fwd=%x", fwd); - if(!aux_end(dcode, 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); } @@ -379,7 +425,7 @@ static inline signed char ean_verify_che int n) { unsigned char chk = 0; - unsigned char i; + 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, @@ -397,7 +443,7 @@ static inline signed char ean_verify_che _zbar_decoder_buf_dump((void*)ean->buf, 18)); if(chk) chk = 10 - chk; - unsigned char d = ean->buf[n]; + 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) { @@ -431,10 +477,11 @@ static inline void ean_expand_upce (ean_ ean_pass_t *pass) { int i = 0; + unsigned char decode; /* parity encoded digit is checksum */ ean->buf[12] = pass->raw[i++]; - unsigned char decode = pass->raw[6] & 0xf; + decode = pass->raw[6] & 0xf; ean->buf[0] = 0; ean->buf[1] = 0; ean->buf[2] = pass->raw[i++] & 0xf; @@ -446,17 +493,19 @@ static inline void ean_expand_upce (ean_ 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; + 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, right = !!(part & EAN_RIGHT); /* copy raw data into holding buffer */ /* if same partial is not consistent, reset others */ - dprintf(2, " integrate part=%x (%s)", part, dsprintbuf(ean)); - signed char i, j; + dprintf(2, " integrate part=%x (%s) w=%d=>%d", + part, dsprintbuf(ean), ean->width, pass->width); + if(part & ZBAR_ADDON) { /* FIXME TBD */ for(i = (part == ZBAR_ADDON5) ? 4 : 1; i >= 0; i--) { @@ -477,7 +526,13 @@ static inline zbar_symbol_type_t integra ean->left = ean->right = ean->addon = ZBAR_NONE; } - if(part & EAN_RIGHT) { + if((ean->left || ean->right || ean->addon) && + !check_width(ean->width, pass->width)) { + dprintf(2, " rst(width %d)", pass->width); + ean->left = ean->right = ean->addon = ZBAR_NONE; + } + + if(right) { part &= ZBAR_SYMBOL; j = (part == ZBAR_EAN13) ? 12 : 7; for(i = (part == ZBAR_EAN13) ? 6 : 4; i; i--, j--) { @@ -507,6 +562,7 @@ static inline zbar_symbol_type_t integra else /* ZBAR_UPCE */ ean_expand_upce(ean, pass); } + ean->width = pass->width; if((part & ZBAR_SYMBOL) != ZBAR_UPCE) { part = (ean->left & ean->right); @@ -516,9 +572,14 @@ static inline zbar_symbol_type_t integra if(((part == ZBAR_EAN13 || part == ZBAR_UPCE) && ean_verify_checksum(ean, 12)) || - (part == ZBAR_EAN8 && ean_verify_checksum(ean, 7))) - /* invalid parity */ + (part == ZBAR_EAN8 && ean_verify_checksum(ean, 7))) { + /* invalid checksum */ + if(right) + ean->left = ZBAR_NONE; + else + ean->right = ZBAR_NONE; part = ZBAR_NONE; + } if(part == ZBAR_EAN13) { /* special case EAN-13 subsets */ @@ -556,7 +617,8 @@ static inline zbar_symbol_type_t integra if(part > ZBAR_PARTIAL) part |= ean->addon; - dprintf(2, " %x/%x=%x", ean->left, ean->right, part); + dprintf(2, " dir=%d %x/%x=%x", + ean->direction, ean->left, ean->right, part); return(part); } @@ -596,6 +658,8 @@ static inline void postprocess (zbar_dec dcode->buf[j] = ean->buf[i] + '0'; dcode->buflen = j; dcode->buf[j] = '\0'; + dcode->direction = 1 - 2 * ean->direction; + dcode->modifiers = 0; } zbar_symbol_type_t _zbar_decode_ean (zbar_decoder_t *dcode) @@ -603,20 +667,21 @@ zbar_symbol_type_t _zbar_decode_ean (zba /* 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); - unsigned char i; 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; dprintf(2, " ean[%x/%x]: idx=%x st=%d s=%d", pass_idx, i, dcode->idx, pass->state, dcode->ean.s4); - zbar_symbol_type_t part = decode_pass(dcode, pass); + part = decode_pass(dcode, pass); if(part) { /* update accumulated data from new partial decode */ sym = integrate_partial(&dcode->ean, pass, part); @@ -626,7 +691,7 @@ zbar_symbol_type_t _zbar_decode_ean (zba 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(!get_lock(dcode, ZBAR_EAN13)) + if(!acquire_lock(dcode, sym)) postprocess(dcode, sym); else { dprintf(1, " [locked %d]", dcode->lock); diff --git a/zbar/decoder/ean.h b/zbar/decoder/ean.h --- a/zbar/decoder/ean.h +++ b/zbar/decoder/ean.h @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2007-2009 (c) Jeff Brown + * Copyright 2007-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -26,18 +26,21 @@ /* 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 0x1f /* 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 left; /* current holding buffer contents */ zbar_symbol_type_t right; zbar_symbol_type_t addon; - unsigned s4; /* character width */ + int direction; /* scan direction */ + unsigned s4, width; /* character width */ signed char buf[18]; /* holding buffer */ signed char enable; diff --git a/zbar/decoder/i25.c b/zbar/decoder/i25.c --- a/zbar/decoder/i25.c +++ b/zbar/decoder/i25.c @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2008-2009 (c) Jeff Brown + * Copyright 2008-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -25,12 +25,12 @@ #include /* memmove */ #include -#include "decoder.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, @@ -111,7 +111,7 @@ static inline signed char i25_decode_sta * we require 5.25n for w=2n to 6.75n for w=3n * (FIXME should really factor in w:n ratio) */ - unsigned quiet = get_width(dcode, i++); + unsigned quiet = get_width(dcode, i); if(quiet && quiet < dcode25->s10 * 3 / 8) { dprintf(3, " i25: s=%d enc=%x q=%d [invalid qz]\n", dcode25->s10, enc, quiet); @@ -124,6 +124,21 @@ static inline signed char i25_decode_sta 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) { i25_decoder_t *dcode25 = &dcode->i25; @@ -133,7 +148,8 @@ static inline signed char i25_decode_end 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) { - dprintf(3, " s=%d q=%d [invalid qz]\n", dcode25->width, quiet); + dprintf(3, " i25: s=%d q=%d [invalid qz]\n", + dcode25->width, quiet); return(ZBAR_NONE); } @@ -145,6 +161,11 @@ static inline signed char i25_decode_end 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) { /* reverse buffer */ dprintf(2, " (rev)"); @@ -161,13 +182,14 @@ static inline signed char i25_decode_end (CFG(*dcode25, ZBAR_CFG_MAX_LEN) > 0 && dcode25->character > CFG(*dcode25, ZBAR_CFG_MAX_LEN))) { dprintf(2, " [invalid len]\n"); - dcode->lock = 0; + release_lock(dcode, ZBAR_I25); dcode25->character = -1; return(ZBAR_NONE); } dcode->buflen = dcode25->character; dcode->buf[dcode25->character] = '\0'; + dcode->modifiers = 0; dprintf(2, " [valid end]\n"); dcode25->character = -1; return(ZBAR_I25); @@ -197,38 +219,45 @@ zbar_symbol_type_t _zbar_decode_i25 (zba (dcode25->direction) ? '<' : '>', dcode25->character, dcode25->element); - /* lock shared resources */ - if(!dcode25->character && get_lock(dcode, ZBAR_I25)) { - dcode25->character = -1; - dprintf(2, " [locked %d]\n", dcode->lock); + if(dcode25->character == 4 && i25_acquire_lock(dcode)) return(ZBAR_PARTIAL); - } unsigned char c = i25_decode10(dcode, 1); dprintf(2, " c=%x", c); + if(c > 9) { + dprintf(2, " [aborted]\n"); + goto reset; + } - if(c > 9 || - ((dcode25->character >= BUFFER_MIN) && - size_buf(dcode, dcode25->character + 2))) { - dprintf(2, (c > 9) ? " [aborted]\n" : " [overflow]\n"); - dcode->lock = 0; - dcode25->character = -1; - return(ZBAR_NONE); + if((dcode25->character >= BUFFER_MIN) && + size_buf(dcode, dcode25->character + 2)) { + dprintf(2, " [overflow]\n"); + goto reset; } - dcode->buf[dcode25->character++] = c + '0'; + + unsigned char *buf; + if(dcode25->character >= 4) + buf = dcode->buf; + else + buf = dcode25->buf; + buf[dcode25->character++] = c + '0'; c = i25_decode10(dcode, 0); dprintf(2, " c=%x", c); if(c > 9) { dprintf(2, " [aborted]\n"); - dcode->lock = 0; - dcode25->character = -1; - return(ZBAR_NONE); + goto reset; } else dprintf(2, "\n"); - dcode->buf[dcode25->character++] = c + '0'; + 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 --- a/zbar/decoder/i25.h +++ b/zbar/decoder/i25.h @@ -30,6 +30,7 @@ typedef struct i25_decoder_s { 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 */ diff --git a/zbar/decoder/pdf417.c b/zbar/decoder/pdf417.c --- a/zbar/decoder/pdf417.c +++ b/zbar/decoder/pdf417.c @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2008-2009 (c) Jeff Brown + * Copyright 2008-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -24,14 +24,14 @@ #include #include -#include "decoder.h" - -#include "pdf417_hash.h" #ifdef DEBUG_PDF417 # define DEBUG_LEVEL (DEBUG_PDF417) #endif #include "debug.h" +#include "decoder.h" + +#include "pdf417_hash.h" #define PDF417_STOP 0xbff @@ -152,7 +152,7 @@ static inline signed char pdf417_decode_ } /* lock shared resources */ - if(get_lock(dcode, ZBAR_PDF417)) { + if(acquire_lock(dcode, ZBAR_PDF417)) { dprintf(2, " [locked %d]\n", dcode->lock); return(0); } @@ -190,7 +190,7 @@ zbar_symbol_type_t _zbar_decode_pdf417 ( if(get_color(dcode) != dcode417->direction) { int c = dcode417->character; - dcode->lock = 0; + 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", @@ -203,7 +203,7 @@ zbar_symbol_type_t _zbar_decode_pdf417 ( ((dcode417->character >= BUFFER_MIN) && size_buf(dcode, dcode417->character + 1))) { dprintf(1, (c < 0) ? " [aborted]\n" : " [overflow]\n"); - dcode->lock = 0; + release_lock(dcode, ZBAR_PDF417); dcode417->character = -1; return(0); } @@ -213,7 +213,9 @@ zbar_symbol_type_t _zbar_decode_pdf417 ( if(c == PDF417_STOP) { dprintf(1, " [valid stop]"); /* FIXME check trailing bar and qz */ - dcode->lock = 0; + dcode->direction = 1 - 2 * dcode417->direction; + dcode->modifiers = 0; + release_lock(dcode, ZBAR_PDF417); dcode417->character = -1; } diff --git a/zbar/decoder/qr_finder.c b/zbar/decoder/qr_finder.c --- a/zbar/decoder/qr_finder.c +++ b/zbar/decoder/qr_finder.c @@ -1,13 +1,36 @@ +/*------------------------------------------------------------------------ + * Copyright 2009-2010 (c) Jeff Brown + * + * 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 #include #include -#include "decoder.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 @@ -20,18 +43,20 @@ qr_finder_line *_zbar_decoder_get_qr_fin 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); - unsigned s = qrf->s5; + s = qrf->s5; if(get_color(dcode) != ZBAR_SPACE || s < 7) return(0); dprintf(2, " qrf: s=%d", s); - int ei = decode_e(pair_width(dcode, 1), s, 7); + ei = decode_e(pair_width(dcode, 1), s, 7); dprintf(2, " %d", ei); if(ei) goto invalid; @@ -54,8 +79,8 @@ zbar_symbol_type_t _zbar_find_qr (zbar_d /* valid QR finder symbol * mark positions needed by decoder */ - unsigned qz = get_width(dcode, 0); - unsigned w = get_width(dcode, 1); + 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); @@ -67,6 +92,8 @@ zbar_symbol_type_t _zbar_find_qr (zbar_d qrf->line.boffs, qrf->line.pos[0], qrf->line.len, qrf->line.eoffs); + dcode->direction = 0; + dcode->buflen = 0; return(ZBAR_QRCODE); invalid: diff --git a/zbar/error.c b/zbar/error.c --- a/zbar/error.c +++ b/zbar/error.c @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2007-2009 (c) Jeff Brown + * Copyright 2007-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -36,7 +36,7 @@ static const char * const mod_str[] = { }; #define MOD_MAX (strlen(mod_str[ZBAR_MOD_IMAGE_SCANNER])) -static const char const * err_str[] = { +static const char * const err_str[] = { "no error", /* OK */ "out of memory", /* NOMEM */ "internal library error", /* INTERNAL */ @@ -99,32 +99,32 @@ zbar_error_t _zbar_get_error_code (const 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); - const char *sev; if(err->sev >= SEV_FATAL && err->sev <= SEV_NOTE) sev = sev_str[err->sev + 2]; else sev = sev_str[1]; - const char *mod; if(err->module >= ZBAR_MOD_PROCESSOR && err->module < ZBAR_MOD_UNKNOWN) mod = mod_str[err->module]; else mod = mod_str[ZBAR_MOD_UNKNOWN]; - const char *func = (err->func) ? err->func : ""; + func = (err->func) ? err->func : ""; - const char *type; if(err->type >= 0 && err->type < ZBAR_ERR_NUM) type = err_str[err->type]; else type = err_str[ZBAR_ERR_NUM]; - char basefmt[] = "%s: zbar %s in %s():\n %s: "; - int len = SEV_MAX + MOD_MAX + ERR_MAX + strlen(func) + sizeof(basefmt); + 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) @@ -150,12 +150,14 @@ const char *_zbar_error_string (const vo return(""); } +#ifdef HAVE_ERRNO_H if(err->type == ZBAR_ERR_SYSTEM) { - char sysfmt[] = ": %s (%d)\n"; + 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; diff --git a/zbar/error.h b/zbar/error.h --- a/zbar/error.h +++ b/zbar/error.h @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2007-2009 (c) Jeff Brown + * Copyright 2007-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -30,7 +30,9 @@ #include #include #include -#include +#ifdef HAVE_ERRNO_H +# include +#endif #include #include @@ -146,10 +148,12 @@ static inline int err_capture (const voi { 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 - else if(type == ZBAR_ERR_WINAPI) + if(type == ZBAR_ERR_WINAPI) err->errnum = GetLastError(); #endif err->sev = sev; diff --git a/zbar/image.c b/zbar/image.c --- a/zbar/image.c +++ b/zbar/image.c @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2007-2009 (c) Jeff Brown + * Copyright 2007-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -74,6 +74,26 @@ unsigned zbar_image_get_height (const zb 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); @@ -100,8 +120,28 @@ void zbar_image_set_size (zbar_image_t * unsigned w, unsigned h) { - img->width = w; - img->height = 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_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; + + unsigned 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) @@ -109,9 +149,10 @@ inline void zbar_image_free_data (zbar_i if(!img) return; if(img->src) { + zbar_image_t *newimg; /* replace video image w/new copy */ assert(img->refcnt); /* FIXME needs lock */ - zbar_image_t *newimg = zbar_image_create(); + newimg = zbar_image_create(); memcpy(newimg, img, sizeof(zbar_image_t)); /* recycle video image */ newimg->cleanup(newimg); @@ -162,8 +203,7 @@ zbar_image_t *zbar_image_copy (const zba { zbar_image_t *dst = zbar_image_create(); dst->format = src->format; - dst->width = src->width; - dst->height = src->height; + _zbar_image_copy_size(dst, src); dst->datalen = src->datalen; dst->data = malloc(src->datalen); assert(dst->data); @@ -180,11 +220,11 @@ const zbar_symbol_set_t *zbar_image_get_ 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; - if(syms) - zbar_symbol_set_ref(img->syms, 1); } const zbar_symbol_t *zbar_image_first_symbol (const zbar_image_t *img) @@ -202,29 +242,34 @@ int zbar_image_write (const zbar_image_t const char *filebase) { int len = strlen(filebase) + 16; - char filename[len]; + char *filename = malloc(len); + int n = 0, rc = 0; + FILE *f; + zimg_hdr_t hdr; strcpy(filename, filebase); - int n = 0; - if(*(char*)&img->format >= ' ') + 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); - filename[len] = '\0'; + assert(n < len - 1); + filename[len - 1] = '\0'; zprintf(1, "dumping %.4s(%08" PRIx32 ") image to %s\n", (char*)&img->format, img->format, filename); - FILE *f = fopen(filename, "w"); + f = fopen(filename, "w"); if(!f) { - int rc = errno; +#ifdef HAVE_ERRNO_H + rc = errno; zprintf(1, "ERROR opening %s: %s\n", filename, strerror(rc)); - return(rc); +#else + rc = 1; +#endif + goto error; } - zimg_hdr_t hdr; hdr.magic = 0x676d697a; hdr.format = img->format; hdr.width = img->width; @@ -233,12 +278,21 @@ int zbar_image_write (const zbar_image_t if(fwrite(&hdr, sizeof(hdr), 1, f) != 1 || fwrite(img->data, 1, img->datalen, f) != img->datalen) { - int rc = errno; +#ifdef HAVE_ERRNO_H + rc = errno; zprintf(1, "ERROR writing %s: %s\n", filename, strerror(rc)); +#else + rc = 1; +#endif fclose(f); - return(rc); + goto error; } - return(fclose(f)); + + rc = fclose(f); + +error: + free(filename); + return(rc); } #ifdef DEBUG_SVG diff --git a/zbar/image.h b/zbar/image.h --- a/zbar/image.h +++ b/zbar/image.h @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2007-2009 (c) Jeff Brown + * Copyright 2007-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -35,10 +35,7 @@ #include "symbol.h" #include "refcnt.h" -/* adapted from v4l2 spec */ -#define fourcc(a, b, c, d) \ - ((uint32_t)(a) | ((uint32_t)(b) << 8) | \ - ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24)) +#define fourcc zbar_fourcc /* unpack size/location of component */ #define RGB_SIZE(c) ((c) >> 5) @@ -65,6 +62,8 @@ struct zbar_image_s { 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 */ @@ -129,4 +128,15 @@ static inline void _zbar_image_swap_symb 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; +} + #endif diff --git a/zbar/img_scanner.c b/zbar/img_scanner.c --- a/zbar/img_scanner.c +++ b/zbar/img_scanner.c @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2007-2009 (c) Jeff Brown + * Copyright 2007-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -22,19 +22,20 @@ *------------------------------------------------------------------------*/ #include -#include +#ifdef HAVE_UNISTD_H +# include +#endif #ifdef HAVE_INTTYPES_H # include #endif #include /* malloc, free */ -#include /* clock_gettime */ -#include /* gettimeofday */ #include /* memcmp, memset, memcpy */ #include #include #include "error.h" #include "image.h" +#include "timer.h" #ifdef ENABLE_QRCODE # include "qrcode.h" #endif @@ -50,11 +51,6 @@ /* FIXME cache setting configurability */ -/* number of times the same result must be detected - * in "nearby" images before being reported - */ -#define CACHE_CONSISTENCY 3 /* images */ - /* time interval for which two images are considered "nearby" */ #define CACHE_PROXIMITY 1000 /* ms */ @@ -112,6 +108,7 @@ struct zbar_image_scanner_s { /* configuration settings */ unsigned config; /* config flags */ int configs[NUM_SCN_CFGS]; /* int valued configurations */ + int sym_configs[1][NUM_SYMS]; /* per-symbology configurations */ #ifndef NO_STATS int stat_syms_new; @@ -136,6 +133,8 @@ void _zbar_image_scanner_recycle_syms (z sym->next = NULL; } else { + int i; + recycle_bucket_t *bucket; /* recycle unreferenced symbol */ if(!sym->data_alloc) { sym->data = NULL; @@ -149,7 +148,6 @@ void _zbar_image_scanner_recycle_syms (z _zbar_symbol_set_free(sym->syms); sym->syms = NULL; } - int i; for(i = 0; i < RECYCLE_BUCKETS; i++) if(sym->data_alloc < 1 << (i * 2)) break; @@ -160,7 +158,7 @@ void _zbar_image_scanner_recycle_syms (z sym->data_alloc = 0; i = 0; } - recycle_bucket_t *bucket = &iscn->recycle[i]; + bucket = &iscn->recycle[i]; /* FIXME cap bucket fill */ bucket->nsyms++; sym->next = bucket->head; @@ -196,18 +194,14 @@ inline void zbar_image_scanner_recycle_i syms = img->syms; img->syms = NULL; - if(syms && recycle_syms(iscn, syms)) { + if(syms && recycle_syms(iscn, syms)) STAT(img_syms_inuse); - syms = iscn->syms; - } else if(syms) { STAT(img_syms_recycle); /* select one set to resurrect, destroy the other */ - if(iscn->syms) { + if(iscn->syms) _zbar_symbol_set_free(syms); - syms = iscn->syms; - } else iscn->syms = syms; } @@ -219,12 +213,12 @@ _zbar_image_scanner_alloc_sym (zbar_imag 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; - zbar_symbol_t *sym = NULL; for(; i > 0; i--) if((sym = iscn->recycle[i].head)) { STAT(sym_recycle[i]); @@ -246,6 +240,7 @@ _zbar_image_scanner_alloc_sym (zbar_imag 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); @@ -295,27 +290,33 @@ static inline void cache_sym (zbar_image 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 = -CACHE_CONSISTENCY; + entry->cache_count = 0; /* add to cache */ entry->next = iscn->cache; iscn->cache = entry; } /* consistency check and hysteresis */ - uint32_t age = sym->time - entry->time; + age = sym->time - entry->time; entry->time = sym->time; - int near_thresh = (age < CACHE_PROXIMITY); - int far_thresh = (age >= CACHE_HYSTERESIS); - int dup = (entry->cache_count >= 0); - if((!dup && !near_thresh) || far_thresh) - entry->cache_count = -CACHE_CONSISTENCY; + 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++; @@ -328,9 +329,10 @@ static inline void cache_sym (zbar_image void _zbar_image_scanner_add_sym(zbar_image_scanner_t *iscn, zbar_symbol_t *sym) { + zbar_symbol_set_t *syms; cache_sym(iscn, sym); - zbar_symbol_set_t *syms = iscn->syms; + syms = iscn->syms; if(sym->cache_count || !syms->tail) { sym->next = syms->head; syms->head = sym; @@ -358,10 +360,12 @@ extern qr_finder_line *_zbar_decoder_get 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); - unsigned u = zbar_scanner_get_edge(iscn->scn, line->pos[0], - QR_FINDER_SUBPREC); + 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, @@ -372,12 +376,12 @@ static inline void qr_handler (zbar_imag u = QR_FIXED(iscn->umin, 0) + iscn->du * u; if(iscn->du < 0) { - u -= line->len; int tmp = line->boffs; line->boffs = line->eoffs; line->eoffs = tmp; + u -= line->len; } - int vert = !iscn->dx; + vert = !iscn->dx; line->pos[vert] = u; line->pos[!vert] = QR_FIXED(iscn->v, 1); @@ -389,10 +393,10 @@ static void symbol_handler (zbar_decoder { zbar_image_scanner_t *iscn = zbar_decoder_get_userdata(dcode); zbar_symbol_type_t type = zbar_decoder_get_type(dcode); - /* FIXME assert(type == ZBAR_PARTIAL) */ - /* FIXME debug flag to save/display all PARTIALs */ - if(type <= ZBAR_PARTIAL) - return; + int x = 0, y = 0, dir; + const char *data; + unsigned datalen; + zbar_symbol_t *sym; #ifdef ENABLE_QRCODE if(type == ZBAR_QRCODE) { @@ -403,10 +407,6 @@ static void symbol_handler (zbar_decoder assert(type != ZBAR_QRCODE); #endif - const char *data = zbar_decoder_get_data(dcode); - unsigned datalen = zbar_decoder_get_data_length(dcode); - - int x = 0, y = 0; if(TEST_CFG(iscn, ZBAR_CFG_POSITION)) { /* tmp position fixup */ int w = zbar_scanner_get_width(iscn->scn); @@ -421,13 +421,23 @@ static void symbol_handler (zbar_decoder } } + /* 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 */ - zbar_symbol_t *sym; 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 */ @@ -436,12 +446,21 @@ static void symbol_handler (zbar_decoder } 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)) + 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); } @@ -468,19 +487,24 @@ zbar_image_scanner_t *zbar_image_scanner CFG(iscn, ZBAR_CFG_X_DENSITY) = 1; CFG(iscn, ZBAR_CFG_Y_DENSITY) = 1; zbar_image_scanner_set_config(iscn, 0, ZBAR_CFG_POSITION, 1); + zbar_image_scanner_set_config(iscn, 0, ZBAR_CFG_UNCERTAINTY, 2); + zbar_image_scanner_set_config(iscn, ZBAR_QRCODE, ZBAR_CFG_UNCERTAINTY, 0); + zbar_image_scanner_set_config(iscn, ZBAR_CODE128, ZBAR_CFG_UNCERTAINTY, 0); + zbar_image_scanner_set_config(iscn, ZBAR_CODE93, ZBAR_CFG_UNCERTAINTY, 0); + zbar_image_scanner_set_config(iscn, ZBAR_CODE39, ZBAR_CFG_UNCERTAINTY, 0); 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); - int i; for(i = 0; i < RECYCLE_BUCKETS; i++) zprintf(1, " recycled[%d] = %-4d\n", i, iscn->stat_sym_recycle[i]); @@ -489,6 +513,7 @@ static inline void dump_stats (const zba void zbar_image_scanner_destroy (zbar_image_scanner_t *iscn) { + int i; dump_stats(iscn); if(iscn->syms) { if(iscn->syms->refcnt) @@ -503,7 +528,6 @@ void zbar_image_scanner_destroy (zbar_im if(iscn->dcode) zbar_decoder_destroy(iscn->dcode); iscn->dcode = NULL; - int i; for(i = 0; i < RECYCLE_BUCKETS; i++) { zbar_symbol_t *sym, *next; for(sym = iscn->recycle[i].head; sym; sym = next) { @@ -536,9 +560,24 @@ int zbar_image_scanner_set_config (zbar_ zbar_config_t cfg, int val) { - if(cfg < ZBAR_CFG_POSITION) + if(cfg < ZBAR_CFG_UNCERTAINTY) return(zbar_decoder_set_config(iscn->dcode, sym, cfg, val)); + if(cfg < ZBAR_CFG_POSITION) { + int c, i; + if(cfg > ZBAR_CFG_UNCERTAINTY) + return(1); + c = cfg - ZBAR_CFG_UNCERTAINTY; + if(sym > ZBAR_PARTIAL) { + i = _zbar_get_symbol_hash(sym); + iscn->sym_configs[c][i] = val; + } + else + for(i = 0; i < NUM_SYMS; i++) + iscn->sym_configs[c][i] = val; + return(0); + } + if(sym > ZBAR_PARTIAL) return(1); @@ -591,30 +630,28 @@ static inline void quiet_border (zbar_im #define movedelta(dx, dy) do { \ x += (dx); \ y += (dy); \ - p += (dx) + ((intptr_t)(dy) * w); \ + p += (dx) + ((uintptr_t)(dy) * w); \ } while(0); int zbar_scan_image (zbar_image_scanner_t *iscn, zbar_image_t *img) { + zbar_symbol_set_t *syms; + const uint8_t *data; + zbar_scanner_t *scn = iscn->scn; + unsigned w, h, cx1, cy1; + int density; + /* timestamp image * FIXME prefer video timestamp */ -#if _POSIX_TIMERS > 0 - struct timespec abstime; - clock_gettime(CLOCK_REALTIME, &abstime); - iscn->time = (abstime.tv_sec * 1000) + ((abstime.tv_nsec / 500000) + 1) / 2; -#else - struct timeval abstime; - gettimeofday(&abstime, NULL); - iscn->time = (abstime.tv_sec * 1000) + ((abstime.tv_usec / 500) + 1) / 2; -#endif + iscn->time = _zbar_timer_now(); #ifdef ENABLE_QRCODE _zbar_qr_reset(iscn->qr); #endif - /* get grayscale image, convert if necessary */ + /* image must be in grayscale format */ if(img->format != fourcc('Y','8','0','0') && img->format != fourcc('G','R','E','Y')) return(-1); @@ -622,7 +659,7 @@ int zbar_scan_image (zbar_image_scanner_ /* recycle previous scanner and image results */ zbar_image_scanner_recycle_image(iscn, img); - zbar_symbol_set_t *syms = iscn->syms; + syms = iscn->syms; if(!syms) { syms = iscn->syms = _zbar_symbol_set_create(); STAT(syms_new); @@ -632,37 +669,43 @@ int zbar_scan_image (zbar_image_scanner_ zbar_symbol_set_ref(syms, 2); img->syms = syms; - unsigned w = img->width; - unsigned h = img->height; - const uint8_t *data = img->data; + 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_t *scn = iscn->scn; + zbar_scanner_new_scan(scn); - int density = CFG(iscn, ZBAR_CFG_Y_DENSITY); + density = CFG(iscn, ZBAR_CFG_Y_DENSITY); if(density > 0) { - svg_group_start("scanner", 0, 1, 1, 0, 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; - int border = (((h - 1) % density) + 1) / 2; - if(border > h / 2) - border = h / 2; - movedelta(0, border); + movedelta(img->crop_x, border); iscn->v = y; - zbar_scanner_new_scan(scn); - - while(y < h) { + 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 = 0; - while(x < w) { + iscn->umin = cx0; + while(x < cx1) { uint8_t d = *p; movedelta(1, 0); zbar_scan_y(scn, d); @@ -673,14 +716,14 @@ int zbar_scan_image (zbar_image_scanner_ movedelta(-1, density); iscn->v = y; - if(y >= h) + 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 = w; - while(x >= 0) { + iscn->umin = cx1; + while(x >= cx0) { uint8_t d = *p; movedelta(-1, 0); zbar_scan_y(scn, d); @@ -698,22 +741,25 @@ int zbar_scan_image (zbar_image_scanner_ density = CFG(iscn, ZBAR_CFG_X_DENSITY); if(density > 0) { - svg_group_start("scanner", 90, 1, -1, 0, 0); const uint8_t *p = data; int x = 0, y = 0; - int border = (((w - 1) % density) + 1) / 2; - if(border > w / 2) - border = w / 2; - movedelta(border, 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 < w) { + 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 = 0; - while(y < h) { + iscn->umin = cy0; + while(y < cy1) { uint8_t d = *p; movedelta(0, 1); zbar_scan_y(scn, d); @@ -724,14 +770,14 @@ int zbar_scan_image (zbar_image_scanner_ movedelta(density, -1); iscn->v = x; - if(x >= w) + 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 = h; - while(y >= 0) { + iscn->umin = cy1; + while(y >= cy0) { uint8_t d = *p; movedelta(0, -1); zbar_scan_y(scn, d); @@ -757,8 +803,9 @@ int zbar_scan_image (zbar_image_scanner_ (density == 1 || CFG(iscn, ZBAR_CFG_Y_DENSITY) == 1)) { zbar_symbol_t **symp = &syms->head, *sym; while((sym = *symp)) { - if(sym->type < ZBAR_I25 && sym->type > ZBAR_PARTIAL && - sym->quality < 3) { + if(((sym->type < ZBAR_I25 && sym->type > ZBAR_PARTIAL) || + sym->type == ZBAR_DATABAR || sym->type == ZBAR_DATABAR_EXP) && + sym->quality < 4) { /* recycle */ *symp = sym->next; syms->nsyms--; diff --git a/zbar/jpeg.c b/zbar/jpeg.c --- a/zbar/jpeg.c +++ b/zbar/jpeg.c @@ -195,10 +195,16 @@ void _zbar_convert_jpeg_to_y (zbar_image jpeg_start_decompress(cinfo); /* adjust dst image parameters to match(?) decompressor */ - if(dst->width < cinfo->output_width) + if(dst->width < cinfo->output_width) { dst->width = cinfo->output_width; - if(dst->height < cinfo->output_height) + 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); diff --git a/zbar/processor.c b/zbar/processor.c --- a/zbar/processor.c +++ b/zbar/processor.c @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2007-2009 (c) Jeff Brown + * Copyright 2007-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -95,11 +95,12 @@ int _zbar_process_image (zbar_processor_ while(sym) { zbar_symbol_type_t type = zbar_symbol_get_type(sym); int count = zbar_symbol_get_count(sym); - zprintf(8, "%s%s: %s (%d pts) (q=%d) (%s)\n", + zprintf(8, "%s%s: %s (%d pts) (dir=%d) (q=%d) (%s)\n", zbar_get_symbol_name(type), zbar_get_addon_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"); @@ -288,6 +289,10 @@ void zbar_processor_destroy (zbar_proces { 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; diff --git a/zbar/qrcode/qrdec.c b/zbar/qrcode/qrdec.c --- a/zbar/qrcode/qrdec.c +++ b/zbar/qrcode/qrdec.c @@ -3,6 +3,7 @@ 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 #include #include #include @@ -2577,8 +2578,8 @@ static inline void qr_svg_points(const c qr_point *p, int n) { + int i; svg_path_start(cls, 1, 0, 0); - int i; for(i = 0; i < n; i++, p++) svg_path_moveto(SVG_ABS, p[0][0], p[0][1]); svg_path_end(); @@ -3913,7 +3914,7 @@ int _zbar_qr_decode (qr_reader *reader, zbar_image_scanner_t *iscn, zbar_image_t *img) { - int nqrdata = 0; + int nqrdata = 0, ncenters; qr_finder_edge_pt *edge_pts = NULL; qr_finder_center *centers = NULL; @@ -3923,7 +3924,7 @@ int _zbar_qr_decode (qr_reader *reader, svg_group_start("finder", 0, 1. / (1 << QR_FINDER_SUBPREC), 0, 0, 0); - int ncenters = qr_finder_centers_locate(¢ers, &edge_pts, reader, 0, 0); + ncenters = qr_finder_centers_locate(¢ers, &edge_pts, reader, 0, 0); zprintf(14, "%dx%d finders, %d centers:\n", reader->finder_lines[0].nlines, diff --git a/zbar/qrcode/qrdectxt.c b/zbar/qrcode/qrdectxt.c --- a/zbar/qrcode/qrdectxt.c +++ b/zbar/qrcode/qrdectxt.c @@ -1,8 +1,9 @@ -/*Copyright (C) 2008-2009 Timothy B. Terriberry (tterribe@xiph.org) +/*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 #include #include #include @@ -50,12 +51,10 @@ int qr_code_data_list_extract_text(const const qr_code_data *qrdata; int nqrdata; unsigned char *mark; - char **text; int ntext; int i; qrdata=_qrlist->qrdata; nqrdata=_qrlist->nqrdata; - text=(char **)malloc(nqrdata*sizeof(*text)); mark=(unsigned char *)calloc(nqrdata,sizeof(*mark)); ntext=0; /*This is the encoding the standard says is the default.*/ @@ -79,6 +78,10 @@ int qr_code_data_list_extract_text(const int err; int j; int k; + zbar_symbol_t *syms = NULL, **sym = &syms; + qr_point dir; + int horiz; + /*Step 0: Collect the other QR codes belonging to this S-A group.*/ if(qrdata[i].sa_size){ unsigned sa_parity; @@ -138,7 +141,6 @@ int qr_code_data_list_extract_text(const enc_list[2]=utf8_cd; eci_cd=(iconv_t)-1; err=0; - zbar_symbol_t *syms = NULL, **sym = &syms; 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; @@ -167,6 +169,14 @@ int qr_code_data_list_extract_text(const 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;knentries&&!err;k++){ size_t inleft; size_t outleft; @@ -331,26 +341,27 @@ int qr_code_data_list_extract_text(const } 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)); } - zbar_symbol_t *sa_sym; 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; - /* cheap out w/axis aligned bbox for now */ - int xmin = img->width, xmax = -2; - int ymin = img->height, ymax = -2; - /* 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; @@ -364,7 +375,7 @@ int qr_code_data_list_extract_text(const if(ymax <= u) ymax = u + 1; } syms->data = sa_text + syms->datalen; - int next = (syms->next) ? syms->next->datalen : sa_ntext; + next = (syms->next) ? syms->next->datalen : sa_ntext; assert(next > syms->datalen); syms->datalen = next - syms->datalen - 1; } diff --git a/zbar/refcnt.c b/zbar/refcnt.c --- a/zbar/refcnt.c +++ b/zbar/refcnt.c @@ -23,7 +23,7 @@ #include "refcnt.h" -#ifdef HAVE_LIBPTHREAD +#if !defined(_WIN32) && !defined(TARGET_OS_MAC) && defined(HAVE_LIBPTHREAD) pthread_once_t initialized = PTHREAD_ONCE_INIT; pthread_mutex_t _zbar_reflock; diff --git a/zbar/refcnt.h b/zbar/refcnt.h --- a/zbar/refcnt.h +++ b/zbar/refcnt.h @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2007-2009 (c) Jeff Brown + * Copyright 2007-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -29,7 +29,7 @@ #if defined(_WIN32) # include -typedef volatile LONG refcnt_t; /* FIXME where did volatile come from? */ +typedef LONG refcnt_t; static inline int _zbar_refcnt (refcnt_t *cnt, int delta) @@ -45,6 +45,18 @@ static inline int _zbar_refcnt (refcnt_t return(rc); } +#elif defined(TARGET_OS_MAC) +# include + +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 diff --git a/zbar/scanner.c b/zbar/scanner.c --- a/zbar/scanner.c +++ b/zbar/scanner.c @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2007-2009 (c) Jeff Brown + * Copyright 2007-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -23,6 +23,7 @@ #include #include /* malloc, free, abs */ +#include #include /* memset */ #include @@ -91,7 +92,7 @@ void zbar_scanner_destroy (zbar_scanner_ zbar_symbol_type_t zbar_scanner_reset (zbar_scanner_t *scn) { - memset(&scn->x, 0, sizeof(zbar_scanner_t) + (void*)scn - (void*)&scn->x); + 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); @@ -125,14 +126,15 @@ zbar_color_t zbar_scanner_get_color (con static inline unsigned calc_thresh (zbar_scanner_t *scn) { /* threshold 1st to improve noise rejection */ - unsigned thresh = scn->y1_thresh; + unsigned dx, thresh = scn->y1_thresh; + unsigned long t; if((thresh <= scn->y1_min_thresh) || !scn->width) { dprintf(1, " tmin=%d", scn->y1_min_thresh); return(scn->y1_min_thresh); } /* slowly return threshold to min */ - unsigned dx = (scn->x << ZBAR_FIXED) - scn->last_edge; - unsigned long t = thresh * dx; + dx = (scn->x << ZBAR_FIXED) - scn->last_edge; + t = thresh * dx; t /= scn->width; t /= ZBAR_SCANNER_THRESH_FADE; dprintf(1, " thr=%d t=%ld x=%d last=%d.%d (%d)", @@ -174,14 +176,15 @@ static inline zbar_symbol_type_t process inline zbar_symbol_type_t zbar_scanner_flush (zbar_scanner_t *scn) { + unsigned x; if(!scn->y1_sign) return(ZBAR_NONE); - unsigned x = (scn->x << ZBAR_FIXED) + ROUND; + 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); dprintf(1, "flush0:"); - zbar_symbol_type_t edge = process_edge(scn, -scn->y1_sign); scn->cur_edge = x; scn->y1_sign = -scn->y1_sign; return(edge); @@ -203,7 +206,7 @@ zbar_symbol_type_t zbar_scanner_new_scan } /* reset scanner and associated decoder */ - memset(&scn->x, 0, sizeof(zbar_scanner_t) + (void*)scn - (void*)&scn->x); + 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); @@ -218,6 +221,8 @@ zbar_symbol_type_t zbar_scan_y (zbar_sca 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; @@ -225,10 +230,10 @@ zbar_symbol_type_t zbar_scan_y (zbar_sca } else y0_0 = y0_1 = scn->y0[0] = scn->y0[1] = scn->y0[2] = scn->y0[3] = y; - register int y0_2 = scn->y0[(x - 2) & 3]; - register int y0_3 = scn->y0[(x - 3) & 3]; + y0_2 = scn->y0[(x - 2) & 3]; + y0_3 = scn->y0[(x - 3) & 3]; /* 1st differential @ x-1 */ - register int y1_1 = y0_1 - y0_2; + y1_1 = y0_1 - y0_2; { register int y1_2 = y0_2 - y0_3; if((abs(y1_1) < abs(y1_2)) && @@ -237,13 +242,13 @@ zbar_symbol_type_t zbar_scan_y (zbar_sca } /* 2nd differentials @ x-1 & x-2 */ - register int y2_1 = y0_0 - (y0_1 * 2) + y0_2; - register int y2_2 = y0_1 - (y0_2 * 2) + y0_3; + y2_1 = y0_0 - (y0_1 * 2) + y0_2; + y2_2 = y0_1 - (y0_2 * 2) + y0_3; dprintf(1, "scan: x=%d y=%d y0=%d y1=%d y2=%d", x, y, y0_1, y1_1, y2_1); - zbar_symbol_type_t edge = ZBAR_NONE; + 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)) && @@ -256,6 +261,7 @@ zbar_symbol_type_t zbar_scan_y (zbar_sca 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 */ @@ -266,7 +272,7 @@ zbar_symbol_type_t zbar_scan_y (zbar_sca scn->y1_thresh = scn->y1_min_thresh; /* update current edge */ - int d = y2_1 - y2_2; + d = y2_1 - y2_2; scn->cur_edge = 1 << ZBAR_FIXED; if(!d) scn->cur_edge >>= 1; @@ -298,14 +304,14 @@ void zbar_scanner_get_state (const zbar_ 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(cur_edge) *cur_edge = scn->cur_edge; 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) */ - zbar_scanner_t *mut_scn = (zbar_scanner_t*)scn; + mut_scn = (zbar_scanner_t*)scn; if(y1_thresh) *y1_thresh = calc_thresh(mut_scn); dprintf(1, "\n"); } diff --git a/zbar/symbol.c b/zbar/symbol.c --- a/zbar/symbol.c +++ b/zbar/symbol.c @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2007-2009 (c) Jeff Brown + * Copyright 2007-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -39,7 +39,10 @@ const char *zbar_get_symbol_name (zbar_s case ZBAR_EAN13: return("EAN-13"); case ZBAR_ISBN13: return("ISBN-13"); case ZBAR_I25: return("I2/5"); + case ZBAR_DATABAR: return("DataBar"); + case ZBAR_DATABAR_EXP: return("DataBar-Exp"); 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"); @@ -56,6 +59,58 @@ const char *zbar_get_addon_name (zbar_sy } } +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_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"); + } +} + +int _zbar_get_symbol_hash (zbar_symbol_type_t sym) +{ + static const signed char hash[0x20] = { + 0x00, 0x01, 0x0d, 0x0e, -1, -1, -1, 0x09, + 0x05, 0x06, 0x08, -1, 0x04, 0x03, 0x07, -1, + -1, -1, -1, -1, -1, -1, -1, 0x02, + -1, 0x00, 0x0f, 0x0c, 0x0b, 0x00, 0x0a, 0x00, + }; + int g0 = hash[sym & 0x1f]; + int g1 = hash[~(sym >> 4) & 0x1f]; + assert(g0 >= 0 && g1 >= 0); + if(g0 < 0 || g1 < 0) + return(0); + return((g0 + g1) & 0x1f); +} void _zbar_symbol_free (zbar_symbol_t *sym) { @@ -82,6 +137,16 @@ zbar_symbol_type_t zbar_symbol_get_type 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); @@ -125,6 +190,11 @@ int zbar_symbol_get_loc_y (const zbar_sy 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); @@ -142,26 +212,100 @@ const zbar_symbol_t *zbar_symbol_first_c } -static const char *xmlfmt[] = { - "", +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 suspect... */ +#define MAX_STATIC 256 +#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 datalen, maxlen; + int i, n = 0; + const char *type = zbar_get_symbol_name(sym->type); - /* FIXME binary data */ - unsigned datalen = strlen(sym->data); - unsigned maxlen = (strlen(xmlfmt[0]) + strlen(xmlfmt[1]) + - strlen(xmlfmt[2]) + strlen(xmlfmt[3]) + - strlen(type) + datalen + MAX_INT_DIGITS + 1); + 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, "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); + unsigned int mods = sym->modifiers; + if(mods) + maxlen += MAX_MOD; + unsigned int 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); @@ -170,32 +314,50 @@ char *zbar_symbol_xml (const zbar_symbol *len = maxlen; } - int n = snprintf(*buf, maxlen, xmlfmt[0], type, sym->quality); - assert(n > 0); + TMPL_FMT("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(">datalen); + TMPL_COPY(">data, sym->datalen + 1); + n += sym->datalen; + } + else { + TMPL_COPY("\n"); + n += base64_encode(*buf + n, sym->data, sym->datalen); + } assert(n <= maxlen); - if(sym->cache_count) { - int i = snprintf(*buf + n, maxlen - n, xmlfmt[1], sym->cache_count); - assert(i > 0); - n += i; - assert(n <= maxlen); - } - - int i = strlen(xmlfmt[2]); - memcpy(*buf + n, xmlfmt[2], i + 1); - n += i; - assert(n <= maxlen); - - /* FIXME binary data */ - /* FIXME handle "]]>" */ - strncpy(*buf + n, sym->data, datalen + 1); - n += datalen; - assert(n <= maxlen); - - i = strlen(xmlfmt[3]); - memcpy(*buf + n, xmlfmt[3], i + 1); - n += i; - assert(n <= maxlen); + TMPL_COPY("]]>"); *len = n; return(*buf); @@ -242,3 +404,9 @@ zbar_symbol_set_first_symbol (const zbar 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 --- a/zbar/symbol.h +++ b/zbar/symbol.h @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2007-2009 (c) Jeff Brown + * Copyright 2007-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -27,6 +27,8 @@ #include #include "refcnt.h" +#define NUM_SYMS 16 + typedef struct point_s { int x, y; } point_t; @@ -40,6 +42,8 @@ struct zbar_symbol_set_s { 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 */ @@ -47,6 +51,7 @@ struct zbar_symbol_s { 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) */ @@ -56,6 +61,8 @@ struct zbar_symbol_s { 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); diff --git a/zbar/timer.h b/zbar/timer.h --- a/zbar/timer.h +++ b/zbar/timer.h @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2007-2009 (c) Jeff Brown + * Copyright 2007-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -24,7 +24,9 @@ #define _ZBAR_TIMER_H_ #include -#include /* gettimeofday */ +#ifdef HAVE_SYS_TIME_H +# include /* gettimeofday */ +#endif /* platform timer abstraction * @@ -41,6 +43,13 @@ 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) { @@ -56,13 +65,14 @@ static inline zbar_timer_t *_zbar_timer_ static inline int _zbar_timer_check (zbar_timer_t *timer) { + struct timespec now; + int delay; if(!timer) return(-1); - struct timespec now; clock_gettime(CLOCK_REALTIME, &now); - int delay = ((timer->tv_sec - now.tv_sec) * 1000 + - (timer->tv_nsec - now.tv_nsec) / 1000000); + delay = ((timer->tv_sec - now.tv_sec) * 1000 + + (timer->tv_nsec - now.tv_nsec) / 1000000); return((delay >= 0) ? delay : 0); } @@ -73,6 +83,11 @@ static inline int _zbar_timer_check (zba 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) { @@ -85,18 +100,26 @@ static inline zbar_timer_t *_zbar_timer_ static inline int _zbar_timer_check (zbar_timer_t *timer) { + int delay; if(!timer) return(INFINITE); - int delay = *timer - timeGetTime(); + delay = *timer - timeGetTime(); return((delay >= 0) ? delay : 0); } -#else +#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) { @@ -112,15 +135,17 @@ static inline zbar_timer_t *_zbar_timer_ static inline int _zbar_timer_check (zbar_timer_t *timer) { + struct timeval now; if(!timer) return(-1); - struct timeval now; 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 --- a/zbar/video.c +++ b/zbar/video.c @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2007-2009 (c) Jeff Brown + * Copyright 2007-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -58,6 +58,7 @@ static void _zbar_video_recycle_shadow ( 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); @@ -73,7 +74,6 @@ zbar_video_t *zbar_video_create () return(NULL); } - int i; for(i = 0; i < ZBAR_VIDEO_IMAGES_MAX; i++) { zbar_image_t *img = vdo->images[i] = zbar_image_create(); if(!img) { @@ -97,7 +97,7 @@ void zbar_video_destroy (zbar_video_t *v int i; for(i = 0; i < ZBAR_VIDEO_IMAGES_MAX; i++) if(vdo->images[i]) - free(vdo->images[i]); + _zbar_image_free(vdo->images[i]); free(vdo->images); } while(vdo->shadow_image) { @@ -130,6 +130,8 @@ void zbar_video_destroy (zbar_video_t *v 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) { @@ -145,7 +147,6 @@ int zbar_video_open (zbar_video_t *vdo, if(!dev) return(0); - char *ldev = NULL; if((unsigned char)dev[0] < 0x10) { /* default linux device, overloaded for other platforms */ int id = dev[0]; @@ -153,7 +154,7 @@ int zbar_video_open (zbar_video_t *vdo, ldev[10] = '0' + id; } - int rc = _zbar_video_open(vdo, dev); + rc = _zbar_video_open(vdo, dev); if(ldev) free(ldev); @@ -227,12 +228,12 @@ uint32_t zbar_video_get_format (const zb 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 = malloc(vdo->buflen); + vdo->buf = calloc(1, vdo->buflen); if(!vdo->buf) return(err_capture(vdo, SEV_FATAL, ZBAR_ERR_NOMEM, __func__, "unable to allocate image buffers")); @@ -240,16 +241,14 @@ static inline int video_init_images (zba (vdo->iomode == VIDEO_READWRITE) ? "READ" : "USERPTR", vdo->buflen); } - int i; for(i = 0; i < vdo->num_images; i++) { zbar_image_t *img = vdo->images[i]; img->format = vdo->format; - img->width = vdo->width; - img->height = vdo->height; + zbar_image_set_size(img, vdo->width, vdo->height); if(vdo->iomode != VIDEO_MMAP) { + unsigned long offset = i * vdo->datalen; img->datalen = vdo->datalen; - unsigned long offset = i * vdo->datalen; - img->data = vdo->buf + offset; + img->data = (uint8_t*)vdo->buf + offset; zprintf(2, " [%02d] @%08lx\n", i, offset); } } @@ -259,6 +258,9 @@ static inline int video_init_images (zba 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__, @@ -270,8 +272,9 @@ int zbar_video_init (zbar_video_t *vdo, if(video_init_images(vdo)) return(-1); #ifdef HAVE_LIBJPEG - const zbar_format_def_t *vidfmt = _zbar_format_lookup(fmt); + 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(); @@ -279,10 +282,9 @@ int zbar_video_init (zbar_video_t *vdo, zbar_image_destroy(vdo->jpeg_img); /* create intermediate image for decoder to use*/ - zbar_image_t *img = vdo->jpeg_img = zbar_image_create(); + img = vdo->jpeg_img = zbar_image_create(); img->format = fourcc('Y','8','0','0'); - img->width = vdo->width; - img->height = vdo->height; + zbar_image_set_size(img, vdo->width, vdo->height); img->datalen = vdo->width * vdo->height; } #endif @@ -333,6 +335,9 @@ int zbar_video_enable (zbar_video_t *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) { @@ -340,8 +345,8 @@ zbar_image_t *zbar_video_next_image (zba return(NULL); } - unsigned frame = vdo->frame++; - zbar_image_t *img = vdo->dq(vdo); + frame = vdo->frame++; + img = vdo->dq(vdo); if(img) { img->seq = frame; if(vdo->num_images < 2) { @@ -362,8 +367,7 @@ zbar_image_t *zbar_video_next_image (zba /* recycle the shadow images */ img->format = vdo->format; - img->width = vdo->width; - img->height = vdo->height; + zbar_image_set_size(img, vdo->width, vdo->height); img->datalen = vdo->datalen; img->data = malloc(vdo->datalen); } diff --git a/zbar/window.c b/zbar/window.c --- a/zbar/window.c +++ b/zbar/window.c @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2007-2009 (c) Jeff Brown + * Copyright 2007-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -23,8 +23,11 @@ #include "window.h" #include "image.h" +#include "timer.h" #include /* clock_gettime */ -#include /* gettimeofday */ +#ifdef HAVE_SYS_TIME_H +# include /* gettimeofday */ +#endif zbar_window_t *zbar_window_create () { @@ -117,20 +120,11 @@ static inline int window_draw_overlay (z if(w->overlay >= 2) { /* calculate/display frame rate */ - unsigned long time; -#if _POSIX_TIMERS > 0 - struct timespec abstime; - clock_gettime(CLOCK_REALTIME, &abstime); - time = (abstime.tv_sec * 1000) + ((abstime.tv_nsec / 500000) + 1) / 2; -#else - struct timeval abstime; - gettimeofday(&abstime, NULL); - time = (abstime.tv_sec * 1000) + ((abstime.tv_usec / 500) + 1) / 2; -#endif - point_t p = { -8, -1 }; - char text[32]; + 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); } @@ -141,6 +135,8 @@ static inline int window_draw_overlay (z 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)) { @@ -148,8 +144,7 @@ inline int zbar_window_redraw (zbar_wind return(-1); } - int rc = 0; - zbar_image_t *img = w->image; + img = w->image; if(w->init && w->draw_image && img) { int format_change = (w->src_format != img->format && w->format != img->format); @@ -163,6 +158,7 @@ inline int zbar_window_redraw (zbar_wind } 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, @@ -173,7 +169,6 @@ inline int zbar_window_redraw (zbar_wind w->src_height = img->height; } - point_t size = { w->width, w->height }; if(size.x > w->max_width) size.x = w->max_width; if(size.y > w->max_height) @@ -226,9 +221,10 @@ inline int zbar_window_redraw (zbar_wind } if(!rc) { + point_t org; rc = w->draw_image(w, img); - point_t org = w->scaled_offset; + org = w->scaled_offset; if(org.x > 0) { point_t p = { 0, org.y }; point_t s = { org.x, w->scaled_size.y }; @@ -300,9 +296,10 @@ void zbar_window_set_overlay (zbar_windo 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); - int lvl = w->overlay; + lvl = w->overlay; (void)window_unlock(ncw); return(lvl); } diff --git a/zbarcam/zbarcam.c b/zbarcam/zbarcam.c --- a/zbarcam/zbarcam.c +++ b/zbarcam/zbarcam.c @@ -105,17 +105,27 @@ static void data_handler (zbar_image_t * if(type == ZBAR_PARTIAL) continue; - if(!format) - printf("%s%s:%s\n", - zbar_get_symbol_name(type), zbar_get_addon_name(type), - zbar_symbol_get_data(sym)); - else if(format == RAW) - printf("%s\n", zbar_symbol_get_data(sym)); + if(!format) { + printf("%s:", zbar_get_symbol_name(type)); + if(fwrite(zbar_symbol_get_data(sym), + zbar_symbol_get_data_length(sym), + 1, stdout) != 1) + continue; + } + else if(format == RAW) { + if(fwrite(zbar_symbol_get_data(sym), + zbar_symbol_get_data_length(sym), + 1, stdout) != 1) + continue; + } else if(format == XML) { if(!n) printf("\n", zbar_image_get_sequence(img)); - printf("%s\n", zbar_symbol_xml(sym, &xml_buf, &xml_len)); + zbar_symbol_xml(sym, &xml_buf, &xml_len); + if(fwrite(xml_buf, xml_len, 1, stdout) != 1) + continue; } + printf("\n"); n++; } diff --git a/zbarcam/zbarcam.rc b/zbarcam/zbarcam.rc --- a/zbarcam/zbarcam.rc +++ b/zbarcam/zbarcam.rc @@ -19,7 +19,7 @@ VS_VERSION_INFO VERSIONINFO VALUE "FileDescription", "Scan bar codes from video devices" - VALUE "LegalCopyright", "Copyright 2007-2009 (c) Jeff Brown " + VALUE "LegalCopyright", "Copyright 2007-2010 (c) Jeff Brown " } } BLOCK "VarFileInfo" { diff --git a/zbarimg/Makefile.am.inc b/zbarimg/Makefile.am.inc --- a/zbarimg/Makefile.am.inc +++ b/zbarimg/Makefile.am.inc @@ -12,10 +12,10 @@ endif EXTRA_DIST += test/barcodetest.py -check-local: +check-images: zbarimg/zbarimg $(PYTHON) $(srcdir)/test/barcodetest.py -regress: +regress-images: zbarimg/zbarimg suite='$(ZBAR_TESTS)'; \ if test "x$$suite" = "x"; then suite='http://zbar.sf.net/test'; fi ; \ $(PYTHON) $(srcdir)/test/barcodetest.py $$suite diff --git a/zbarimg/zbarimg.c b/zbarimg/zbarimg.c --- a/zbarimg/zbarimg.c +++ b/zbarimg/zbarimg.c @@ -1,5 +1,5 @@ /*------------------------------------------------------------------------ - * Copyright 2007-2009 (c) Jeff Brown + * Copyright 2007-2010 (c) Jeff Brown * * This file is part of the ZBar Bar Code Reader. * @@ -45,7 +45,7 @@ * how does MagickGetAuthenticImagePixels fit in?) * ref http://bugs.gentoo.org/247292 */ -#if MagickLibVersion < 0x645 +#if MagickLibVersion <= 0x645 # define MagickExportImagePixels MagickGetImagePixels #endif @@ -76,8 +76,8 @@ static const char *warning_not_found = " things to check:\n" " - is the barcode type supported?" " currently supported symbologies are:\n" - " EAN/UPC (EAN-13, EAN-8, UPC-A, UPC-E, ISBN-10, ISBN-13),\n" - " Code 128, Code 39 and Interleaved 2 of 5\n" + " EAN/UPC (EAN-13, EAN-8, UPC-A, UPC-E, ISBN-10, ISBN-13), Code 128,\n" + " Code 93, Code 39, DataBar, DataBar Expanded, and Interleaved 2 of 5\n" " - is the barcode large enough in the image?\n" " - is the barcode mostly in focus?\n" " - is there sufficient contrast/illumination?\n" @@ -137,7 +137,7 @@ static int scan_image (const char *filen zbar_image_t *zimage = zbar_image_create(); assert(zimage); - zbar_image_set_format(zimage, *(unsigned long*)"Y800"); + zbar_image_set_format(zimage, zbar_fourcc('Y','8','0','0')); int width = MagickGetImageWidth(images); int height = MagickGetImageHeight(images); @@ -165,23 +165,30 @@ static int scan_image (const char *filen const zbar_symbol_t *sym = zbar_image_first_symbol(zimage); for(; sym; sym = zbar_symbol_next(sym)) { zbar_symbol_type_t typ = zbar_symbol_get_type(sym); + unsigned len = zbar_symbol_get_data_length(sym); if(typ == ZBAR_PARTIAL) continue; - else if(!xmllvl) - printf("%s%s:%s\n", - zbar_get_symbol_name(typ), - zbar_get_addon_name(typ), - zbar_symbol_get_data(sym)); - else if(xmllvl < 0) - printf("%s\n", zbar_symbol_get_data(sym)); + else if(xmllvl <= 0) { + if(!xmllvl) + printf("%s:", zbar_get_symbol_name(typ)); + if(len && + fwrite(zbar_symbol_get_data(sym), len, 1, stdout) != 1) { + exit_code = 1; + return(-1); + } + } else { if(xmllvl < 3) { xmllvl++; printf("\n", seq); } zbar_symbol_xml(sym, &xmlbuf, &xmlbuflen); - printf("%s\n", xmlbuf); + if(fwrite(xmlbuf, xmlbuflen, 1, stdout) != 1) { + exit_code = 1; + return(-1); + } } + printf("\n"); found++; num_symbols++; } diff --git a/zbarimg/zbarimg.rc b/zbarimg/zbarimg.rc --- a/zbarimg/zbarimg.rc +++ b/zbarimg/zbarimg.rc @@ -19,7 +19,7 @@ VS_VERSION_INFO VERSIONINFO VALUE "FileDescription", "Scan bar codes from image files" - VALUE "LegalCopyright", "Copyright 2007-2009 (c) Jeff Brown " + VALUE "LegalCopyright", "Copyright 2007-2010 (c) Jeff Brown " } } BLOCK "VarFileInfo" {